Skip to content

Commit

Permalink
Implement most array.* instructions for the GC proposal (bytecodeal…
Browse files Browse the repository at this point in the history
…liance#9326)

* Implement most `array.*` instructions for the GC proposal

This does not implement `array.copy` and `array.init_elem` yet, but implements
all other `array.*` instructions:

* `array.new`
* `array.new_fixed`
* `array.new_default`
* `array.new_data`
* `array.new_elem`
* `array.fill`
* `array.init_data`
* `array.len`
* `array.get`
* `array.get_s`
* `array.get_u`
* `array.set`

Note that the initial plumbing for `array.{copy,init_elem}` is in place, but the
instructions themselves are not implemented yet.

* Fix no-gc builds

* Fix some clippy warnings

* cargo fmt

* Fix another clippy error

* Fix more clippy errors

* Remove debug logging

* Add array.fill helper

* exit scope even on panic
  • Loading branch information
fitzgen authored Sep 30, 2024
1 parent 5fbbf68 commit ec3b2d2
Show file tree
Hide file tree
Showing 59 changed files with 3,465 additions and 668 deletions.
26 changes: 13 additions & 13 deletions cranelift/codegen/src/ir/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,18 +99,6 @@ fn midpoint(a: SequenceNumber, b: SequenceNumber) -> Option<SequenceNumber> {
}
}

#[test]
fn test_midpoint() {
assert_eq!(midpoint(0, 1), None);
assert_eq!(midpoint(0, 2), Some(1));
assert_eq!(midpoint(0, 3), Some(1));
assert_eq!(midpoint(0, 4), Some(2));
assert_eq!(midpoint(1, 4), Some(2));
assert_eq!(midpoint(2, 4), Some(3));
assert_eq!(midpoint(3, 4), None);
assert_eq!(midpoint(3, 4), None);
}

impl Layout {
/// Compare the program points `a` and `b` in the same block relative to this program order.
///
Expand Down Expand Up @@ -763,13 +751,25 @@ mod serde {

#[cfg(test)]
mod tests {
use super::Layout;
use super::*;
use crate::cursor::{Cursor, CursorPosition};
use crate::entity::EntityRef;
use crate::ir::{Block, Inst, SourceLoc};
use alloc::vec::Vec;
use core::cmp::Ordering;

#[test]
fn test_midpoint() {
assert_eq!(midpoint(0, 1), None);
assert_eq!(midpoint(0, 2), Some(1));
assert_eq!(midpoint(0, 3), Some(1));
assert_eq!(midpoint(0, 4), Some(2));
assert_eq!(midpoint(1, 4), Some(2));
assert_eq!(midpoint(2, 4), Some(3));
assert_eq!(midpoint(3, 4), None);
assert_eq!(midpoint(3, 4), None);
}

struct LayoutCursor<'f> {
/// Borrowed function layout. Public so it can be re-borrowed from this cursor.
pub layout: &'f mut Layout,
Expand Down
3 changes: 2 additions & 1 deletion cranelift/codegen/src/ir/memflags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ impl MemFlags {
0b1010 => Some(TrapCode::Interrupt),
0b1011 => Some(TrapCode::NullReference),
0b1100 => Some(TrapCode::ArrayOutOfBounds),
// 0b1101 => {} not allocated
0b1101 => Some(TrapCode::AllocationTooLarge),
// 0b1110 => {} not allocated
0b1111 => None,
_ => unreachable!(),
Expand Down Expand Up @@ -391,6 +391,7 @@ impl MemFlags {
Some(TrapCode::Interrupt) => 0b1010,
Some(TrapCode::NullReference) => 0b1011,
Some(TrapCode::ArrayOutOfBounds) => 0b1100,
Some(TrapCode::AllocationTooLarge) => 0b1101,
None => 0b1111,

Some(TrapCode::User(_)) => panic!("cannot set user trap code in mem flags"),
Expand Down
6 changes: 6 additions & 0 deletions cranelift/codegen/src/ir/trapcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ pub enum TrapCode {

/// A null reference was encountered which was required to be non-null.
NullReference,

/// A requested memory allocation was too large: beyond implementation
/// limits, would trigger overflows, or etc...
AllocationTooLarge,
}

impl TrapCode {
Expand Down Expand Up @@ -96,6 +100,7 @@ impl Display for TrapCode {
User(x) => return write!(f, "user{x}"),
NullReference => "null_reference",
ArrayOutOfBounds => "array_oob",
AllocationTooLarge => "alloc_too_large",
};
f.write_str(identifier)
}
Expand All @@ -120,6 +125,7 @@ impl FromStr for TrapCode {
"interrupt" => Ok(Interrupt),
"null_reference" => Ok(NullReference),
"array_oob" => Ok(ArrayOutOfBounds),
"alloc_too_large" => Ok(AllocationTooLarge),
_ if s.starts_with("user") => s[4..].parse().map(User).map_err(|_| ()),
_ => Err(()),
}
Expand Down
19 changes: 12 additions & 7 deletions cranelift/codegen/src/isa/aarch64/inst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,6 @@ fn count_zero_half_words(mut value: u64, num_half_words: u8) -> usize {
count
}

#[test]
fn inst_size_test() {
// This test will help with unintentionally growing the size
// of the Inst enum.
assert_eq!(32, std::mem::size_of::<Inst>());
}

impl Inst {
/// Create an instruction that loads a constant, using one of several options (MOVZ, MOVN,
/// logical immediate, or constant pool).
Expand Down Expand Up @@ -3049,3 +3042,15 @@ impl MachInstLabelUse for LabelUse {
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn inst_size_test() {
// This test will help with unintentionally growing the size
// of the Inst enum.
assert_eq!(32, std::mem::size_of::<Inst>());
}
}
23 changes: 14 additions & 9 deletions cranelift/isle/isle/src/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
//! term and build a tree of Rust control-flow constructs corresponding to each
//! partition. The root of such a tree is a [Block], and [serialize] constructs
//! it.
use std::cmp::Reverse;

use crate::disjointsets::DisjointSets;
use crate::lexer::Pos;
use crate::trie_again::{Binding, BindingId, Constraint, Rule, RuleSet};
use std::cmp::Reverse;

/// Decomposes the rule-set into a tree of [Block]s.
pub fn serialize(rules: &RuleSet) -> Block {
Expand Down Expand Up @@ -835,12 +835,17 @@ fn group_by_mut<T: Eq>(
})
}

#[test]
fn test_group_mut() {
let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2];
let mut iter = group_by_mut(slice, |a, b| a == b);
assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
assert_eq!(iter.next(), Some(&mut [3, 3][..]));
assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
assert_eq!(iter.next(), None);
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_group_mut() {
let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2];
let mut iter = group_by_mut(slice, |a, b| a == b);
assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
assert_eq!(iter.next(), Some(&mut [3, 3][..]));
assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
assert_eq!(iter.next(), None);
}
}
158 changes: 143 additions & 15 deletions cranelift/wasm/src/code_translator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ use itertools::Itertools;
use smallvec::SmallVec;
use std::vec::Vec;
use wasmparser::{FuncValidator, MemArg, Operator, WasmModuleResources};
use wasmtime_types::{DataIndex, ElemIndex};

/// Given a `Reachability<T>`, unwrap the inner `T` or, when unreachable, set
/// `state.reachable = false` and return.
Expand Down Expand Up @@ -2583,6 +2584,147 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
));
}

Operator::ArrayNew { array_type_index } => {
let array_type_index = TypeIndex::from_u32(*array_type_index);
let (elem, len) = state.pop2();
let array_ref = environ.translate_array_new(builder, array_type_index, elem, len)?;
state.push1(array_ref);
}
Operator::ArrayNewDefault { array_type_index } => {
let array_type_index = TypeIndex::from_u32(*array_type_index);
let len = state.pop1();
let array_ref = environ.translate_array_new_default(builder, array_type_index, len)?;
state.push1(array_ref);
}
Operator::ArrayNewFixed {
array_type_index,
array_size,
} => {
let array_type_index = TypeIndex::from_u32(*array_type_index);
let array_size = usize::try_from(*array_size).unwrap();
let elems = state.peekn(array_size);
let array_ref = environ.translate_array_new_fixed(builder, array_type_index, elems)?;
state.popn(array_size);
state.push1(array_ref);
}
Operator::ArrayNewData {
array_type_index,
array_data_index,
} => {
let array_type_index = TypeIndex::from_u32(*array_type_index);
let array_data_index = DataIndex::from_u32(*array_data_index);
let (data_offset, len) = state.pop2();
let array_ref = environ.translate_array_new_data(
builder,
array_type_index,
array_data_index,
data_offset,
len,
)?;
state.push1(array_ref);
}
Operator::ArrayNewElem {
array_type_index,
array_elem_index,
} => {
let array_type_index = TypeIndex::from_u32(*array_type_index);
let array_elem_index = ElemIndex::from_u32(*array_elem_index);
let (elem_offset, len) = state.pop2();
let array_ref = environ.translate_array_new_elem(
builder,
array_type_index,
array_elem_index,
elem_offset,
len,
)?;
state.push1(array_ref);
}
Operator::ArrayCopy {
array_type_index_dst,
array_type_index_src,
} => {
let array_type_index_dst = TypeIndex::from_u32(*array_type_index_dst);
let array_type_index_src = TypeIndex::from_u32(*array_type_index_src);
let (dst_array, dst_index, src_array, src_index, len) = state.pop5();
environ.translate_array_copy(
builder,
array_type_index_dst,
dst_array,
dst_index,
array_type_index_src,
src_array,
src_index,
len,
)?;
}
Operator::ArrayFill { array_type_index } => {
let array_type_index = TypeIndex::from_u32(*array_type_index);
let (array, index, val, len) = state.pop4();
environ.translate_array_fill(builder, array_type_index, array, index, val, len)?;
}
Operator::ArrayInitData {
array_type_index,
array_data_index,
} => {
let array_type_index = TypeIndex::from_u32(*array_type_index);
let array_data_index = DataIndex::from_u32(*array_data_index);
let (array, dst_index, src_index, len) = state.pop4();
environ.translate_array_init_data(
builder,
array_type_index,
array,
dst_index,
array_data_index,
src_index,
len,
)?;
}
Operator::ArrayInitElem {
array_type_index,
array_elem_index,
} => {
let array_type_index = TypeIndex::from_u32(*array_type_index);
let array_elem_index = ElemIndex::from_u32(*array_elem_index);
let (array, dst_index, src_index, len) = state.pop4();
environ.translate_array_init_elem(
builder,
array_type_index,
array,
dst_index,
array_elem_index,
src_index,
len,
)?;
}
Operator::ArrayLen => {
let array = state.pop1();
let len = environ.translate_array_len(builder, array)?;
state.push1(len);
}
Operator::ArrayGet { array_type_index } => {
let array_type_index = TypeIndex::from_u32(*array_type_index);
let (array, index) = state.pop2();
let elem = environ.translate_array_get(builder, array_type_index, array, index)?;
state.push1(elem);
}
Operator::ArrayGetS { array_type_index } => {
let array_type_index = TypeIndex::from_u32(*array_type_index);
let (array, index) = state.pop2();
let elem = environ.translate_array_get_s(builder, array_type_index, array, index)?;
state.push1(elem);
}
Operator::ArrayGetU { array_type_index } => {
let array_type_index = TypeIndex::from_u32(*array_type_index);
let (array, index) = state.pop2();
let elem = environ.translate_array_get_u(builder, array_type_index, array, index)?;
state.push1(elem);
}
Operator::ArraySet { array_type_index } => {
let array_type_index = TypeIndex::from_u32(*array_type_index);
let (array, index, elem) = state.pop3();
environ.translate_array_set(builder, array_type_index, array, index, elem)?;
}

Operator::RefEq
| Operator::RefTestNonNull { .. }
| Operator::RefTestNullable { .. }
Expand All @@ -2591,21 +2733,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
| Operator::BrOnCast { .. }
| Operator::BrOnCastFail { .. }
| Operator::AnyConvertExtern
| Operator::ExternConvertAny
| Operator::ArrayNew { .. }
| Operator::ArrayNewDefault { .. }
| Operator::ArrayNewFixed { .. }
| Operator::ArrayNewData { .. }
| Operator::ArrayNewElem { .. }
| Operator::ArrayGet { .. }
| Operator::ArrayGetU { .. }
| Operator::ArrayGetS { .. }
| Operator::ArraySet { .. }
| Operator::ArrayLen { .. }
| Operator::ArrayFill { .. }
| Operator::ArrayCopy { .. }
| Operator::ArrayInitData { .. }
| Operator::ArrayInitElem { .. } => {
| Operator::ExternConvertAny => {
return Err(wasm_unsupported!("GC operators are not yet implemented"));
}

Expand Down
Loading

0 comments on commit ec3b2d2

Please sign in to comment.