Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement reference-types Wasm proposal #635

Merged
merged 121 commits into from
Jan 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
fd00d48
add reference_types flag to Config
Robbepop Jan 25, 2023
5cefe50
enable all Wasm spec tests again
Robbepop Jan 25, 2023
dc24b16
make table.copy and table.init instructions take table parameters
Robbepop Jan 25, 2023
77a0f11
make call.indirect take table parameter
Robbepop Jan 25, 2023
f13935f
Merge branch 'master' into rf-implement-reference-types
Robbepop Jan 25, 2023
1b17a82
make FuncIdx use NonZeroU32
Robbepop Jan 26, 2023
3741a9d
remove some From impls for Value
Robbepop Jan 26, 2023
2249fdd
create FuncRef type in wasmi
Robbepop Jan 26, 2023
012a8fd
add more Func and FuncRef size_of tests
Robbepop Jan 26, 2023
b029367
add test comment
Robbepop Jan 26, 2023
45effa9
add FuncRef <-> UntypedValue conversions
Robbepop Jan 26, 2023
2b11559
add ValueType::FuncRef
Robbepop Jan 26, 2023
e187eff
make Value no longer Copy
Robbepop Jan 26, 2023
b1a1c9c
add ExternRef type to wasmi
Robbepop Jan 26, 2023
19b9d84
fix no_std compilation
Robbepop Jan 26, 2023
de6f533
fix another silly no_std compile error
Robbepop Jan 26, 2023
061f659
remove From<&Value> impl for UntypedValue
Robbepop Jan 27, 2023
fae6050
re-design ExternRef
Robbepop Jan 27, 2023
04095e0
add accessor methods to Value
Robbepop Jan 27, 2023
2a9f144
apply clippy suggestion
Robbepop Jan 27, 2023
c3d4fa9
make Value no longer impl PartialEq
Robbepop Jan 27, 2023
5c191f8
improve docs
Robbepop Jan 27, 2023
7363309
add ExternRef to Value
Robbepop Jan 27, 2023
e6a4ccc
remove Display impl for FuncType
Robbepop Jan 27, 2023
efab6a7
add Default derive for ExternRef
Robbepop Jan 27, 2023
561ac69
remove PartialEq impl of FuncRef
Robbepop Jan 27, 2023
1ed309c
remove TryInto API from Value
Robbepop Jan 27, 2023
81ae13b
remove Value::try_into
Robbepop Jan 27, 2023
7d9703f
add From<FuncRef> and From<ExternRef> for Value
Robbepop Jan 27, 2023
d28de74
fix UntypedValue <-> [Func|Extern]Ref bugs with null values
Robbepop Jan 27, 2023
21a5f80
apply rustfmt
Robbepop Jan 27, 2023
247e1e3
fix internal doc link
Robbepop Jan 27, 2023
aecd7a5
add additional conversion checks for safety
Robbepop Jan 27, 2023
3257595
apply rustfmt
Robbepop Jan 27, 2023
613be2d
add element type to TableType
Robbepop Jan 27, 2023
4c2c076
make Table use ValueType and Value
Robbepop Jan 27, 2023
9bed627
fix bug in Arena::get_pair_mut
Robbepop Jan 27, 2023
23bd5c3
make Table::get return Option instead of Result
Robbepop Jan 28, 2023
38b6755
implement Table::{get, set} instructions
Robbepop Jan 28, 2023
ef79dd7
add docs
Robbepop Jan 28, 2023
416e9cd
implement table.size Wasm instruction
Robbepop Jan 28, 2023
f6e26f2
implement table.grow Wasm instruction
Robbepop Jan 28, 2023
a555103
add docs to TableEntity::grow_untyped
Robbepop Jan 28, 2023
f16e0a4
implement Table[Entity]::fill method
Robbepop Jan 28, 2023
62083b5
implement table.fill Wasm instruction
Robbepop Jan 28, 2023
17ef451
add missing spectest global_i64 definition
Robbepop Jan 28, 2023
22a37cc
implement more Wast decodings for reference-types
Robbepop Jan 28, 2023
546aeda
implement typed_select, ref.null and ref.is_null Wasm instructions
Robbepop Jan 28, 2023
f18be54
allow reftypes in Element parsing
Robbepop Jan 28, 2023
0ea8d54
cleanup assert
Robbepop Jan 28, 2023
89a6898
add ValueType::{is_num, is_ref} methods
Robbepop Jan 28, 2023
4ba8c15
properly adjust stack height for new table instructions
Robbepop Jan 28, 2023
a3aeca0
allow RefNull and FuncRef in InitExpr
Robbepop Jan 28, 2023
c6bbd5f
dispatch on ValueType for evaluating InitExpr
Robbepop Jan 29, 2023
43b85ba
change error message to be closer to what Wasm spec testsuite expects
Robbepop Jan 29, 2023
2b70fdf
improve wast testsuite
Robbepop Jan 29, 2023
1b38020
fix bug in ExternObjectEntity::data and add test
Robbepop Jan 29, 2023
56758c4
extend wast testsuite
Robbepop Jan 29, 2023
ad475ac
adjust TableOutOfBounds trap message and relax assertions
Robbepop Jan 29, 2023
763eec7
apply rustfmt
Robbepop Jan 29, 2023
a86a28a
apply clippy suggestions
Robbepop Jan 29, 2023
cef6ecc
adjust trap message
Robbepop Jan 29, 2023
b4b00c3
improve trap message
Robbepop Jan 29, 2023
1f7b499
handle Wasm declared element segments
Robbepop Jan 29, 2023
b8ba528
account for element type when matching table types
Robbepop Jan 29, 2023
cadb9ae
cleanup instantiation code a bit
Robbepop Jan 29, 2023
7b48141
perform some renames and further cleanups
Robbepop Jan 29, 2023
2680393
more cleanups
Robbepop Jan 29, 2023
de9d19d
more linker cleanups
Robbepop Jan 29, 2023
57aa6f5
improve LinkerError
Robbepop Jan 29, 2023
1aa9309
more cleanups
Robbepop Jan 29, 2023
db069b4
apply rustfmt
Robbepop Jan 29, 2023
d9bdffb
update docs for satisfies methods
Robbepop Jan 29, 2023
bdea66a
apply clippy suggestion
Robbepop Jan 29, 2023
bcfe552
fix internal doc link
Robbepop Jan 29, 2023
1ce1aec
improve docs
Robbepop Jan 29, 2023
c950aa3
rename methods
Robbepop Jan 29, 2023
b075ac7
refactor check_subtype
Robbepop Jan 29, 2023
b1a3f79
apply clippy suggestions
Robbepop Jan 29, 2023
e99fff0
implement ref.func Wasm instruction
Robbepop Jan 29, 2023
d6ad7f9
fix ModuleResources::global_get method
Robbepop Jan 29, 2023
9689550
improve global.get optimization
Robbepop Jan 29, 2023
27bb1eb
minor cleanup
Robbepop Jan 29, 2023
5fd2c16
update Wasm spec testsuite
Robbepop Jan 29, 2023
913f26f
remove debug println
Robbepop Jan 29, 2023
a3bed01
cleanup testsuite runner
Robbepop Jan 29, 2023
3dccd33
remove unnecessary match arm
Robbepop Jan 29, 2023
49249e7
add print_i64 to Wasm spec testsuite module
Robbepop Jan 29, 2023
2398c6e
use Self were possible
Robbepop Jan 29, 2023
2a23358
add temporary fix
Robbepop Jan 29, 2023
3ca7e47
fix TableEntity::copy_within
Robbepop Jan 29, 2023
6a61f80
improve Table docs
Robbepop Jan 29, 2023
b6756b3
fix bug in Arena::get_pair_mut
Robbepop Jan 29, 2023
106a3da
cleanup code after bug fix
Robbepop Jan 29, 2023
4983c92
add element type check to table.copy
Robbepop Jan 29, 2023
e7f9b78
move comment
Robbepop Jan 29, 2023
94a4ba2
add missing #[repr(transparent)]
Robbepop Jan 29, 2023
3014571
cleanup error handling
Robbepop Jan 29, 2023
6e46ef5
move more into chained iter in TableEntity::init
Robbepop Jan 29, 2023
0bfacb5
fix bug in table.init procedures
Robbepop Jan 30, 2023
025d64e
improve Wasm spec testsuite runner
Robbepop Jan 30, 2023
0507f49
simplify MemoryType::is_subtype_of check
Robbepop Jan 30, 2023
fe7af12
simplify TableType::is_subtype_of procesures
Robbepop Jan 30, 2023
3aea9e5
cleanup
Robbepop Jan 30, 2023
1408c7a
rename method
Robbepop Jan 30, 2023
ba3ba55
improve MemoryError and TableError for subtyping
Robbepop Jan 30, 2023
aa0a33b
cleanup linker errors for invalid subtypes
Robbepop Jan 30, 2023
f6fddb4
improve Wasm spec testsuite runner error reporting
Robbepop Jan 30, 2023
bbaad89
apply rustfmt
Robbepop Jan 30, 2023
7851d5a
add Memory::import_ty method
Robbepop Jan 30, 2023
8c3e334
add Table::import_ty method for same reason as Memory::import_ty
Robbepop Jan 30, 2023
96aafed
make Table::import_ty pub(crate)
Robbepop Jan 30, 2023
7a6c4b7
rename import_ty -> dynamic_ty
Robbepop Jan 30, 2023
0a401f4
remove clippy allowance
Robbepop Jan 30, 2023
ff49303
table.init bail out earlier for len == 0
Robbepop Jan 30, 2023
e2821d5
apply rustfmt
Robbepop Jan 30, 2023
852e131
canonicalize FuncRef::null instances
Robbepop Jan 31, 2023
95ced32
fi spelling in doc
Robbepop Jan 31, 2023
81a5277
move safety comment to unsafe block
Robbepop Jan 31, 2023
43bcba8
canonicalize ExternRef::null() values properly
Robbepop Jan 31, 2023
e795b37
fix bug in Executor::visit_table_copy
Robbepop Jan 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions crates/arena/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ mod tests;
pub use self::{component_vec::ComponentVec, dedup::DedupArena, guarded::GuardedEntity};
use alloc::vec::Vec;
use core::{
cmp::max,
iter::{DoubleEndedIterator, Enumerate, ExactSizeIterator},
marker::PhantomData,
ops::{Index, IndexMut},
Expand Down Expand Up @@ -159,8 +158,15 @@ where
pub fn get_pair_mut(&mut self, fst: Idx, snd: Idx) -> Option<(&mut T, &mut T)> {
let fst_index = fst.into_usize();
let snd_index = snd.into_usize();
let max_index = max(fst_index, snd_index);
let (fst_set, snd_set) = self.entities.split_at_mut(max_index);
if fst_index == snd_index {
return None;
}
if fst_index > snd_index {
let (fst, snd) = self.get_pair_mut(snd, fst)?;
return Some((snd, fst));
}
// At this point we know that fst_index < snd_index.
let (fst_set, snd_set) = self.entities.split_at_mut(snd_index);
let fst = fst_set.get_mut(fst_index)?;
let snd = snd_set.get_mut(0)?;
Some((fst, snd))
Expand Down
169 changes: 164 additions & 5 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use anyhow::{anyhow, bail, Result};
use clap::Parser;
use core::fmt::Write;
use std::{
ffi::OsStr,
fmt::{self, Write},
fs,
path::{Path, PathBuf},
};
Expand Down Expand Up @@ -221,8 +221,9 @@ fn type_check_arguments(
) -> Result<Vec<Value>> {
if func_type.params().len() != func_args.len() {
bail!(
"invalid number of arguments given for {func_name} of type {func_type}. \
"invalid number of arguments given for {func_name} of type {}. \
expected {} argument but got {}",
DisplayFuncType(func_type),
func_type.params().len(),
func_args.len()
);
Expand Down Expand Up @@ -256,6 +257,12 @@ fn type_check_arguments(
.map(F64::from)
.map(Value::from)
.map_err(make_err!()),
ValueType::FuncRef => {
bail!("the wasmi CLI cannot take arguments of type funcref")
}
ValueType::ExternRef => {
bail!("the wasmi CLI cannot take arguments of type externref")
}
}
})
.collect::<Result<Vec<_>, _>>()?;
Expand All @@ -276,17 +283,39 @@ fn prepare_results_buffer(func_type: &FuncType) -> Vec<Value> {
fn print_execution_start(wasm_file: &Path, func_name: &str, func_args: &[Value]) {
print!("executing {wasm_file:?}::{func_name}(");
if let Some((first_arg, rest_args)) = func_args.split_first() {
print!("{first_arg}");
for arg in rest_args {
print!("{}", DisplayValue(first_arg));
for arg in rest_args.iter().map(DisplayValue) {
print!(", {arg}");
}
}
println!(") ...");
}

/// Wrapper type that implements `Display` for [`Value`].
struct DisplayValue<'a>(&'a Value);

impl<'a> fmt::Display for DisplayValue<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
Value::I32(value) => write!(f, "{value}"),
Value::I64(value) => write!(f, "{value}"),
Value::F32(value) => write!(f, "{value}"),
Value::F64(value) => write!(f, "{value}"),
Value::FuncRef(value) => panic!("cannot display funcref values but found {value:?}"),
Value::ExternRef(value) => {
panic!("cannot display externref values but found {value:?}")
}
}
}
}

/// Prints the results of the Wasm computation in a human readable form.
fn print_pretty_results(results: &[Value]) {
let pretty_results = results.iter().map(Value::to_string).collect::<Vec<_>>();
let pretty_results = results
.iter()
.map(DisplayValue)
.map(|v| v.to_string())
.collect::<Vec<_>>();
match pretty_results.len() {
1 => {
println!("{}", pretty_results[0]);
Expand All @@ -303,3 +332,133 @@ fn print_pretty_results(results: &[Value]) {
}
}
}

/// Wrapper type around [`FuncType`] that implements `Display` for it.
struct DisplayFuncType<'a>(&'a FuncType);

impl fmt::Display for DisplayFuncType<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "fn(")?;
let params = self.0.params();
let results = self.0.results();
write_slice(f, params, ",")?;
write!(f, ")")?;
if let Some((first, rest)) = results.split_first() {
write!(f, " -> ")?;
if !rest.is_empty() {
write!(f, "(")?;
}
write!(f, "{first}")?;
for result in rest {
write!(f, ", {result}")?;
}
if !rest.is_empty() {
write!(f, ")")?;
}
}
Ok(())
}
}

/// Writes the elements of a `slice` separated by the `separator`.
fn write_slice<T>(f: &mut fmt::Formatter, slice: &[T], separator: &str) -> fmt::Result
where
T: fmt::Display,
{
if let Some((first, rest)) = slice.split_first() {
write!(f, "{first}")?;
for param in rest {
write!(f, "{separator} {param}")?;
}
}
Ok(())
}

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

fn assert_display(func_type: impl Borrow<FuncType>, expected: &str) {
assert_eq!(
format!("{}", DisplayFuncType(func_type.borrow())),
String::from(expected),
);
}

#[test]
fn display_0in_0out() {
assert_display(FuncType::new([], []), "fn()");
}

#[test]
fn display_1in_0out() {
assert_display(FuncType::new([ValueType::I32], []), "fn(i32)");
}

#[test]
fn display_0in_1out() {
assert_display(FuncType::new([], [ValueType::I32]), "fn() -> i32");
}

#[test]
fn display_1in_1out() {
assert_display(
FuncType::new([ValueType::I32], [ValueType::I32]),
"fn(i32) -> i32",
);
}

#[test]
fn display_4in_0out() {
assert_display(
FuncType::new(
[
ValueType::I32,
ValueType::I64,
ValueType::F32,
ValueType::F64,
],
[],
),
"fn(i32, i64, f32, f64)",
);
}

#[test]
fn display_0in_4out() {
assert_display(
FuncType::new(
[],
[
ValueType::I32,
ValueType::I64,
ValueType::F32,
ValueType::F64,
],
),
"fn() -> (i32, i64, f32, f64)",
);
}

#[test]
fn display_4in_4out() {
assert_display(
FuncType::new(
[
ValueType::I32,
ValueType::I64,
ValueType::F32,
ValueType::F64,
],
[
ValueType::I32,
ValueType::I64,
ValueType::F32,
ValueType::F64,
],
),
"fn(i32, i64, f32, f64) -> (i32, i64, f32, f64)",
);
}
}
6 changes: 3 additions & 3 deletions crates/core/src/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,10 @@ impl TrapCode {
/// other uses since it avoid heap memory allocation in certain cases.
pub fn trap_message(&self) -> &'static str {
match self {
Self::UnreachableCodeReached => "unreachable",
Self::UnreachableCodeReached => "wasm `unreachable` instruction executed",
Self::MemoryOutOfBounds => "out of bounds memory access",
Self::TableOutOfBounds => "undefined element",
Self::IndirectCallToNull => "uninitialized element",
Self::TableOutOfBounds => "undefined element: out of bounds table access",
Self::IndirectCallToNull => "uninitialized element 2", // TODO: fixme, remove the trailing " 2" again
Self::IntegerDivisionByZero => "integer divide by zero",
Self::IntegerOverflow => "integer overflow",
Self::BadConversionToInteger => "invalid conversion to integer",
Expand Down
23 changes: 23 additions & 0 deletions crates/core/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,27 @@ pub enum ValueType {
F32,
/// 64-bit IEEE 754-2008 floating point number.
F64,
/// A nullable function reference.
FuncRef,
/// A nullable external reference.
ExternRef,
}

impl ValueType {
/// Returns `true` if [`ValueType`] is a Wasm numeric type.
///
/// This is `true` for [`ValueType::I32`], [`ValueType::I64`],
/// [`ValueType::F32`] and [`ValueType::F64`].
pub fn is_num(&self) -> bool {
matches!(self, Self::I32 | Self::I64 | Self::F32 | Self::F64)
}

/// Returns `true` if [`ValueType`] is a Wasm reference type.
///
/// This is `true` for [`ValueType::FuncRef`] and [`ValueType::ExternRef`].
pub fn is_ref(&self) -> bool {
matches!(self, Self::ExternRef | Self::FuncRef)
}
}

impl Display for ValueType {
Expand All @@ -28,6 +49,8 @@ impl Display for ValueType {
Self::I64 => write!(f, "i64"),
Self::F32 => write!(f, "f32"),
Self::F64 => write!(f, "f64"),
Self::FuncRef => write!(f, "funcref"),
Self::ExternRef => write!(f, "externref"),
}
}
}
Expand Down
Loading