diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7bc79de5..bb0b5b53 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,11 +14,22 @@ jobs: submodules: true - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - - name: Install wabt + + # Build an up-to-date version of wabt since the releases don't always have + # all the features we want. + - uses: actions/checkout@v2 + with: + repository: WebAssembly/wabt + ref: 9068d3927b404ce1e9c600473255a90504034eee + path: wabt + - name: Build wabt run: | set -e - curl -L https://github.com/WebAssembly/wabt/releases/download/1.0.13/wabt-1.0.13-linux.tar.gz | tar xzf - - echo "##[add-path]`pwd`/wabt-1.0.13" + cd wabt + cmake . -DBUILD_TESTS=OFF + make -j$(nproc) wast2json spectest-interp wasm-interp + echo ::add-path::`pwd` + - name: Install binaryen run: | set -e diff --git a/Cargo.toml b/Cargo.toml index fb08ca6d..7e4d8998 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ leb128 = "0.2.4" log = "0.4.8" rayon = { version = "1.1.0", optional = true } walrus-macro = { path = './crates/macro', version = '=0.15.0' } -wasmparser = "0.51.0" +wasmparser = "0.52.0" [features] parallel = ['rayon', 'id-arena/rayon'] diff --git a/crates/tests/tests/round_trip/simd.wat b/crates/tests/tests/round_trip/simd.wat index d081fffc..263b29b4 100644 --- a/crates/tests/tests/round_trip/simd.wat +++ b/crates/tests/tests/round_trip/simd.wat @@ -309,10 +309,6 @@ local.get 0 local.get 1 i8x16.sub_saturate_s) - (func $i8x16.mul (export "i8x16.mul") (param v128 v128) (result v128) - local.get 0 - local.get 1 - i8x16.mul) (func $i16x8.neg (export "i16x8.neg") (param v128) (result v128) local.get 0 @@ -401,12 +397,6 @@ (func $i64x2.neg (export "i64x2.neg") (param v128) (result v128) local.get 0 i64x2.neg) - (func $i64x2.any_true (export "i64x2.any_true") (param v128) (result i32) - local.get 0 - i64x2.any_true) - (func $i64x2.all_true (export "i64x2.all_true") (param v128) (result i32) - local.get 0 - i64x2.all_true) (func $i64x2.shl (export "i64x2.shl") (param v128 i32) (result v128) local.get 0 local.get 1 @@ -759,10 +749,6 @@ local.get 0 local.get 1 i8x16.sub_saturate_s) - (func $i8x16.mul (type 15) (param v128 v128) (result v128) - local.get 0 - local.get 1 - i8x16.mul) (func $i16x8.shl (type 11) (param v128 i32) (result v128) local.get 0 local.get 1 @@ -973,12 +959,6 @@ (func $i64x2.neg (type 10) (param v128) (result v128) local.get 0 i64x2.neg) - (func $i64x2.any_true (type 6) (param v128) (result i32) - local.get 0 - i64x2.any_true) - (func $i64x2.all_true (type 6) (param v128) (result i32) - local.get 0 - i64x2.all_true) (func $f32x4.abs (type 10) (param v128) (result v128) local.get 0 f32x4.abs) @@ -1089,7 +1069,6 @@ (export "i8x16.sub" (func $i8x16.sub)) (export "i8x16.sub_saturate_u" (func $i8x16.sub_saturate_u)) (export "i8x16.sub_saturate_s" (func $i8x16.sub_saturate_s)) - (export "i8x16.mul" (func $i8x16.mul)) (export "i16x8.neg" (func $i16x8.neg)) (export "i16x8.any_true" (func $i16x8.any_true)) (export "i16x8.all_true" (func $i16x8.all_true)) @@ -1113,8 +1092,6 @@ (export "i32x4.sub" (func $i32x4.sub)) (export "i32x4.mul" (func $i32x4.mul)) (export "i64x2.neg" (func $i64x2.neg)) - (export "i64x2.any_true" (func $i64x2.any_true)) - (export "i64x2.all_true" (func $i64x2.all_true)) (export "i64x2.shl" (func $i64x2.shl)) (export "i64x2.shr_s" (func $i64x2.shr_s)) (export "i64x2.shr_u" (func $i64x2.shr_u)) diff --git a/crates/tests/tests/spec-tests b/crates/tests/tests/spec-tests index c7225cc2..da56298d 160000 --- a/crates/tests/tests/spec-tests +++ b/crates/tests/tests/spec-tests @@ -1 +1 @@ -Subproject commit c7225cc210e9a089e7ce87ecac053334eb4af0aa +Subproject commit da56298dddb441d1af38492ee98fe001e625d156 diff --git a/crates/tests/tests/spec-tests.rs b/crates/tests/tests/spec-tests.rs index c48b398a..fbd28eb7 100644 --- a/crates/tests/tests/spec-tests.rs +++ b/crates/tests/tests/spec-tests.rs @@ -1,4 +1,4 @@ -use anyhow::Context; +use anyhow::{bail, Context}; use std::fs; use std::path::Path; use std::process::Command; @@ -10,6 +10,12 @@ struct Test { commands: Vec, } +const STABLE_FEATURES: &[&str] = &[ + "--enable-multi-value", + "--enable-saturating-float-to-int", + "--enable-sign-extension", +]; + fn run(wast: &Path) -> Result<(), anyhow::Error> { static INIT_LOGS: std::sync::Once = std::sync::Once::new(); INIT_LOGS.call_once(|| { @@ -23,11 +29,13 @@ fn run(wast: &Path) -> Result<(), anyhow::Error> { .next() .map(|s| s.to_str().unwrap()); let extra_args: &[&str] = match proposal { - None => &[], - Some("mutable-global") => &[], - Some("sign-extension-ops") => &["--enable-sign-extension"], - Some("multi-value") => &["--enable-multi-value"], - Some("nontrapping-float-to-int-conversions") => &["--enable-saturating-float-to-int"], + // stable features + None + | Some("multi-value") + | Some("nontrapping-float-to-int-conversions") + | Some("sign-extension-ops") + | Some("mutable-global") => &[], + Some("reference-types") => &["--enable-reference-types", "--enable-bulk-memory"], Some("bulk-memory-operations") => &["--enable-bulk-memory"], @@ -38,6 +46,9 @@ fn run(wast: &Path) -> Result<(), anyhow::Error> { // TODO: should get tail-call working Some("tail-call") => return Ok(()), + // not a walrus thing, but not implemented in wabt fully yet anyway + Some("annotations") => return Ok(()), + // Some("threads") => &["--enable-threads"], Some(other) => panic!("unknown wasm proposal: {}", other), }; @@ -48,11 +59,14 @@ fn run(wast: &Path) -> Result<(), anyhow::Error> { .arg(wast) .arg("-o") .arg(&json) + .args(STABLE_FEATURES) .args(extra_args) .status() .context("executing `wast2json`")?; assert!(status.success()); + let wabt_ok = run_spectest_interp(tempdir.path(), extra_args).is_ok(); + let contents = fs::read_to_string(&json).context("failed to read file")?; let test: Test = serde_json::from_str(&contents).context("failed to parse file")?; let mut files = Vec::new(); @@ -128,6 +142,12 @@ fn run(wast: &Path) -> Result<(), anyhow::Error> { } } + // If wabt didn't succeed before we ran walrus there's no hope of it passing + // after we run walrus. + if !wabt_ok { + return Ok(()); + } + // First up run the spec-tests as-is after we round-tripped through walrus. // This should for sure work correctly run_spectest_interp(tempdir.path(), extra_args)?; @@ -159,6 +179,7 @@ fn run_spectest_interp(cwd: &Path, extra_args: &[&str]) -> Result<(), anyhow::Er let output = Command::new("spectest-interp") .current_dir(cwd) .arg("foo.json") + .args(STABLE_FEATURES) .args(extra_args) .output() .context("executing `spectest-interp`")?; @@ -181,7 +202,7 @@ fn run_spectest_interp(cwd: &Path, extra_args: &[&str]) -> Result<(), anyhow::Er println!("status: {}", output.status); println!("stdout:\n{}", String::from_utf8_lossy(&output.stdout)); println!("stderr:\n{}", String::from_utf8_lossy(&output.stderr)); - panic!("failed"); + bail!("failed"); } include!(concat!(env!("OUT_DIR"), "/spec-tests.rs")); diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 00772807..13ecbaeb 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -619,7 +619,7 @@ impl Value { encoder.f64(n); } Value::V128(n) => { - encoder.raw(&[0xfd, 0x02]); // v128.const + encoder.raw(&[0xfd, 0x0c]); // v128.const for i in 0..16 { encoder.byte((n >> (i * 8)) as u8); } @@ -796,7 +796,6 @@ pub enum BinaryOp { I8x16Sub, I8x16SubSaturateS, I8x16SubSaturateU, - I8x16Mul, I16x8Shl, I16x8ShrS, I16x8ShrU, @@ -935,18 +934,19 @@ pub enum UnaryOp { V128Not, + I8x16Abs, I8x16Neg, I8x16AnyTrue, I8x16AllTrue, + I16x8Abs, I16x8Neg, I16x8AnyTrue, I16x8AllTrue, + I32x4Abs, I32x4Neg, I32x4AnyTrue, I32x4AllTrue, I64x2Neg, - I64x2AnyTrue, - I64x2AllTrue, F32x4Abs, F32x4Neg, @@ -955,14 +955,10 @@ pub enum UnaryOp { F64x2Neg, F64x2Sqrt, - I32x4TruncSF32x4Sat, - I32x4TruncUF32x4Sat, - I64x2TruncSF64x2Sat, - I64x2TruncUF64x2Sat, - F32x4ConvertSI32x4, - F32x4ConvertUI32x4, - F64x2ConvertSI64x2, - F64x2ConvertUI64x2, + I32x4TruncSatF32x4S, + I32x4TruncSatF32x4U, + F32x4ConvertI32x4S, + F32x4ConvertI32x4U, I32TruncSSatF32, I32TruncUSatF32, diff --git a/src/module/functions/local_function/emit.rs b/src/module/functions/local_function/emit.rs index c6fc3b51..e815b6c5 100644 --- a/src/module/functions/local_function/emit.rs +++ b/src/module/functions/local_function/emit.rs @@ -261,131 +261,131 @@ impl<'instr> Visitor<'instr> for Emit<'_, '_> { F64Max => self.encoder.byte(0xa5), F64Copysign => self.encoder.byte(0xa6), - I8x16ReplaceLane { idx } => self.encoder.raw(&[0xfd, 0x07, idx]), - I16x8ReplaceLane { idx } => self.encoder.raw(&[0xfd, 0x0b, idx]), - I32x4ReplaceLane { idx } => self.encoder.raw(&[0xfd, 0x0e, idx]), - I64x2ReplaceLane { idx } => self.encoder.raw(&[0xfd, 0x11, idx]), - F32x4ReplaceLane { idx } => self.encoder.raw(&[0xfd, 0x14, idx]), - F64x2ReplaceLane { idx } => self.encoder.raw(&[0xfd, 0x17, idx]), - - I8x16Eq => self.simd(0x18), - I8x16Ne => self.simd(0x19), - I8x16LtS => self.simd(0x1a), - I8x16LtU => self.simd(0x1b), - I8x16GtS => self.simd(0x1c), - I8x16GtU => self.simd(0x1d), - I8x16LeS => self.simd(0x1e), - I8x16LeU => self.simd(0x1f), - I8x16GeS => self.simd(0x20), - I8x16GeU => self.simd(0x21), - - I16x8Eq => self.simd(0x22), - I16x8Ne => self.simd(0x23), - I16x8LtS => self.simd(0x24), - I16x8LtU => self.simd(0x25), - I16x8GtS => self.simd(0x26), - I16x8GtU => self.simd(0x27), - I16x8LeS => self.simd(0x28), - I16x8LeU => self.simd(0x29), - I16x8GeS => self.simd(0x2a), - I16x8GeU => self.simd(0x2b), - - I32x4Eq => self.simd(0x2c), - I32x4Ne => self.simd(0x2d), - I32x4LtS => self.simd(0x2e), - I32x4LtU => self.simd(0x2f), - I32x4GtS => self.simd(0x30), - I32x4GtU => self.simd(0x31), - I32x4LeS => self.simd(0x32), - I32x4LeU => self.simd(0x33), - I32x4GeS => self.simd(0x34), - I32x4GeU => self.simd(0x35), - - F32x4Eq => self.simd(0x40), - F32x4Ne => self.simd(0x41), - F32x4Lt => self.simd(0x42), - F32x4Gt => self.simd(0x43), - F32x4Le => self.simd(0x44), - F32x4Ge => self.simd(0x45), - - F64x2Eq => self.simd(0x46), - F64x2Ne => self.simd(0x47), - F64x2Lt => self.simd(0x48), - F64x2Gt => self.simd(0x49), - F64x2Le => self.simd(0x4a), - F64x2Ge => self.simd(0x4b), - - V128And => self.simd(0x4d), - V128AndNot => self.simd(0xd8), - V128Or => self.simd(0x4e), - V128Xor => self.simd(0x4f), - - I8x16Shl => self.simd(0x54), - I8x16ShrS => self.simd(0x55), - I8x16ShrU => self.simd(0x56), - I8x16Add => self.simd(0x57), - I8x16AddSaturateS => self.simd(0x58), - I8x16AddSaturateU => self.simd(0x59), - I8x16Sub => self.simd(0x5a), - I8x16SubSaturateS => self.simd(0x5b), - I8x16SubSaturateU => self.simd(0x5c), - I8x16Mul => self.simd(0x5d), - I16x8Shl => self.simd(0x65), - I16x8ShrS => self.simd(0x66), - I16x8ShrU => self.simd(0x67), - I16x8Add => self.simd(0x68), - I16x8AddSaturateS => self.simd(0x69), - I16x8AddSaturateU => self.simd(0x6a), - I16x8Sub => self.simd(0x6b), - I16x8SubSaturateS => self.simd(0x6c), - I16x8SubSaturateU => self.simd(0x6d), - I16x8Mul => self.simd(0x6e), - I32x4Shl => self.simd(0x76), - I32x4ShrS => self.simd(0x77), - I32x4ShrU => self.simd(0x78), - I32x4Add => self.simd(0x79), - I32x4Sub => self.simd(0x7c), - I32x4Mul => self.simd(0x7f), - I64x2Shl => self.simd(0x87), - I64x2ShrS => self.simd(0x88), - I64x2ShrU => self.simd(0x89), - I64x2Add => self.simd(0x8a), - I64x2Sub => self.simd(0x8d), - I64x2Mul => self.simd(0x90), - - F32x4Add => self.simd(0x9a), - F32x4Sub => self.simd(0x9b), - F32x4Mul => self.simd(0x9c), - F32x4Div => self.simd(0x9d), - F32x4Min => self.simd(0x9e), - F32x4Max => self.simd(0x9f), - F64x2Add => self.simd(0xa5), - F64x2Sub => self.simd(0xa6), - F64x2Mul => self.simd(0xa7), - F64x2Div => self.simd(0xa8), - F64x2Min => self.simd(0xa9), - F64x2Max => self.simd(0xaa), - - I8x16NarrowI16x8S => self.simd(0xc6), - I8x16NarrowI16x8U => self.simd(0xc7), - I16x8NarrowI32x4S => self.simd(0xc8), - I16x8NarrowI32x4U => self.simd(0xc8), - - I8x16RoundingAverageU => self.simd(0xd9), - I16x8RoundingAverageU => self.simd(0xda), - - I8x16MinS => self.simd(0x5e), - I8x16MinU => self.simd(0x5f), - I8x16MaxS => self.simd(0x60), - I8x16MaxU => self.simd(0x61), - I16x8MinS => self.simd(0x6f), - I16x8MinU => self.simd(0x70), - I16x8MaxS => self.simd(0x71), - I16x8MaxU => self.simd(0x72), - I32x4MinS => self.simd(0x80), - I32x4MinU => self.simd(0x81), - I32x4MaxS => self.simd(0x82), - I32x4MaxU => self.simd(0x83), + I8x16ReplaceLane { idx } => self.encoder.raw(&[0xfd, 0x17, idx]), + I16x8ReplaceLane { idx } => self.encoder.raw(&[0xfd, 0x1a, idx]), + I32x4ReplaceLane { idx } => self.encoder.raw(&[0xfd, 0x1c, idx]), + I64x2ReplaceLane { idx } => self.encoder.raw(&[0xfd, 0x1e, idx]), + F32x4ReplaceLane { idx } => self.encoder.raw(&[0xfd, 0x20, idx]), + F64x2ReplaceLane { idx } => self.encoder.raw(&[0xfd, 0x22, idx]), + + I8x16Eq => self.simd(0x23), + I8x16Ne => self.simd(0x24), + I8x16LtS => self.simd(0x25), + I8x16LtU => self.simd(0x26), + I8x16GtS => self.simd(0x27), + I8x16GtU => self.simd(0x28), + I8x16LeS => self.simd(0x29), + I8x16LeU => self.simd(0x2a), + I8x16GeS => self.simd(0x2b), + I8x16GeU => self.simd(0x2c), + + I16x8Eq => self.simd(0x2d), + I16x8Ne => self.simd(0x2e), + I16x8LtS => self.simd(0x2f), + I16x8LtU => self.simd(0x30), + I16x8GtS => self.simd(0x31), + I16x8GtU => self.simd(0x32), + I16x8LeS => self.simd(0x33), + I16x8LeU => self.simd(0x34), + I16x8GeS => self.simd(0x35), + I16x8GeU => self.simd(0x36), + + I32x4Eq => self.simd(0x37), + I32x4Ne => self.simd(0x38), + I32x4LtS => self.simd(0x39), + I32x4LtU => self.simd(0x3a), + I32x4GtS => self.simd(0x3b), + I32x4GtU => self.simd(0x3c), + I32x4LeS => self.simd(0x3d), + I32x4LeU => self.simd(0x3e), + I32x4GeS => self.simd(0x3f), + I32x4GeU => self.simd(0x40), + + F32x4Eq => self.simd(0x41), + F32x4Ne => self.simd(0x42), + F32x4Lt => self.simd(0x43), + F32x4Gt => self.simd(0x44), + F32x4Le => self.simd(0x45), + F32x4Ge => self.simd(0x46), + + F64x2Eq => self.simd(0x47), + F64x2Ne => self.simd(0x48), + F64x2Lt => self.simd(0x49), + F64x2Gt => self.simd(0x4a), + F64x2Le => self.simd(0x4b), + F64x2Ge => self.simd(0x4c), + + V128And => self.simd(0x4e), + V128AndNot => self.simd(0x4f), + V128Or => self.simd(0x50), + V128Xor => self.simd(0x51), + + I8x16NarrowI16x8S => self.simd(0x65), + I8x16NarrowI16x8U => self.simd(0x66), + I8x16Shl => self.simd(0x6b), + I8x16ShrS => self.simd(0x6c), + I8x16ShrU => self.simd(0x6d), + I8x16Add => self.simd(0x6e), + I8x16AddSaturateS => self.simd(0x6f), + I8x16AddSaturateU => self.simd(0x70), + I8x16Sub => self.simd(0x71), + I8x16SubSaturateS => self.simd(0x72), + I8x16SubSaturateU => self.simd(0x73), + I8x16MinS => self.simd(0x76), + I8x16MinU => self.simd(0x77), + I8x16MaxS => self.simd(0x78), + I8x16MaxU => self.simd(0x79), + I8x16RoundingAverageU => self.simd(0x7b), + + I16x8NarrowI32x4S => self.simd(0x85), + I16x8NarrowI32x4U => self.simd(0x86), + I16x8Shl => self.simd(0x8b), + I16x8ShrS => self.simd(0x8c), + I16x8ShrU => self.simd(0x8d), + I16x8Add => self.simd(0x8e), + I16x8AddSaturateS => self.simd(0x8f), + I16x8AddSaturateU => self.simd(0x90), + I16x8Sub => self.simd(0x91), + I16x8SubSaturateS => self.simd(0x92), + I16x8SubSaturateU => self.simd(0x93), + I16x8Mul => self.simd(0x95), + I16x8MinS => self.simd(0x96), + I16x8MinU => self.simd(0x97), + I16x8MaxS => self.simd(0x98), + I16x8MaxU => self.simd(0x99), + I16x8RoundingAverageU => self.simd(0x9b), + + I32x4Shl => self.simd(0xab), + I32x4ShrS => self.simd(0xac), + I32x4ShrU => self.simd(0xad), + I32x4Add => self.simd(0xae), + I32x4Sub => self.simd(0xb1), + I32x4Mul => self.simd(0xb5), + I32x4MinS => self.simd(0xb6), + I32x4MinU => self.simd(0xb7), + I32x4MaxS => self.simd(0xb8), + I32x4MaxU => self.simd(0xb9), + + I64x2Shl => self.simd(0xcb), + I64x2ShrS => self.simd(0xcc), + I64x2ShrU => self.simd(0xcd), + I64x2Add => self.simd(0xce), + I64x2Sub => self.simd(0xd1), + I64x2Mul => self.simd(0xd5), + + F32x4Add => self.simd(0xe4), + F32x4Sub => self.simd(0xe5), + F32x4Mul => self.simd(0xe6), + F32x4Div => self.simd(0xe7), + F32x4Min => self.simd(0xe8), + F32x4Max => self.simd(0xe9), + + F64x2Add => self.simd(0xf0), + F64x2Sub => self.simd(0xf1), + F64x2Mul => self.simd(0xf2), + F64x2Div => self.simd(0xf3), + F64x2Min => self.simd(0xf4), + F64x2Max => self.simd(0xf5), } } @@ -453,75 +453,84 @@ impl<'instr> Visitor<'instr> for Emit<'_, '_> { I64Extend16S => self.encoder.byte(0xc3), I64Extend32S => self.encoder.byte(0xc4), - I8x16Splat => self.simd(0x04), + I8x16Splat => self.simd(0x0f), + I16x8Splat => self.simd(0x10), + I32x4Splat => self.simd(0x11), + I64x2Splat => self.simd(0x12), + F32x4Splat => self.simd(0x13), + F64x2Splat => self.simd(0x14), I8x16ExtractLaneS { idx } => { - self.simd(0x05); + self.simd(0x15); self.encoder.byte(idx); } I8x16ExtractLaneU { idx } => { - self.simd(0x06); + self.simd(0x16); self.encoder.byte(idx); } - I16x8Splat => self.simd(0x08), I16x8ExtractLaneS { idx } => { - self.simd(0x09); + self.simd(0x18); self.encoder.byte(idx); } I16x8ExtractLaneU { idx } => { - self.simd(0x0a); + self.simd(0x19); self.encoder.byte(idx); } - I32x4Splat => self.simd(0x0c), I32x4ExtractLane { idx } => { - self.simd(0x0d); + self.simd(0x1b); self.encoder.byte(idx); } - I64x2Splat => self.simd(0x0f), I64x2ExtractLane { idx } => { - self.simd(0x10); + self.simd(0x1d); self.encoder.byte(idx); } - F32x4Splat => self.simd(0x12), F32x4ExtractLane { idx } => { - self.simd(0x13); + self.simd(0x1f); self.encoder.byte(idx); } - F64x2Splat => self.simd(0x15), F64x2ExtractLane { idx } => { - self.simd(0x16); + self.simd(0x21); self.encoder.byte(idx); } - V128Not => self.simd(0x4c), - - I8x16Neg => self.simd(0x51), - I8x16AnyTrue => self.simd(0x52), - I8x16AllTrue => self.simd(0x53), - I16x8Neg => self.simd(0x62), - I16x8AnyTrue => self.simd(0x63), - I16x8AllTrue => self.simd(0x64), - I32x4Neg => self.simd(0x73), - I32x4AnyTrue => self.simd(0x74), - I32x4AllTrue => self.simd(0x75), - I64x2Neg => self.simd(0x84), - I64x2AnyTrue => self.simd(0x85), - I64x2AllTrue => self.simd(0x86), - - F32x4Abs => self.simd(0x95), - F32x4Neg => self.simd(0x96), - F32x4Sqrt => self.simd(0x97), - F64x2Abs => self.simd(0xa0), - F64x2Neg => self.simd(0xa1), - F64x2Sqrt => self.simd(0xa2), - - I32x4TruncSF32x4Sat => self.simd(0xab), - I32x4TruncUF32x4Sat => self.simd(0xac), - I64x2TruncSF64x2Sat => self.simd(0xad), - I64x2TruncUF64x2Sat => self.simd(0xae), - F32x4ConvertSI32x4 => self.simd(0xaf), - F32x4ConvertUI32x4 => self.simd(0xb0), - F64x2ConvertSI64x2 => self.simd(0xb1), - F64x2ConvertUI64x2 => self.simd(0xb2), + V128Not => self.simd(0x4d), + + I8x16Abs => self.simd(0x60), + I8x16Neg => self.simd(0x61), + I8x16AnyTrue => self.simd(0x62), + I8x16AllTrue => self.simd(0x63), + + I16x8Abs => self.simd(0x80), + I16x8Neg => self.simd(0x81), + I16x8AnyTrue => self.simd(0x82), + I16x8AllTrue => self.simd(0x83), + I16x8WidenLowI8x16S => self.simd(0x87), + I16x8WidenHighI8x16S => self.simd(0x88), + I16x8WidenLowI8x16U => self.simd(0x89), + I16x8WidenHighI8x16U => self.simd(0x8a), + + I32x4Abs => self.simd(0xa0), + I32x4Neg => self.simd(0xa1), + I32x4AnyTrue => self.simd(0xa2), + I32x4AllTrue => self.simd(0xa3), + I32x4WidenLowI16x8S => self.simd(0xa7), + I32x4WidenHighI16x8S => self.simd(0xa8), + I32x4WidenLowI16x8U => self.simd(0xa9), + I32x4WidenHighI16x8U => self.simd(0xaa), + + I64x2Neg => self.simd(0xc1), + + F32x4Abs => self.simd(0xe0), + F32x4Neg => self.simd(0xe1), + F32x4Sqrt => self.simd(0xe3), + + F64x2Abs => self.simd(0xec), + F64x2Neg => self.simd(0xed), + F64x2Sqrt => self.simd(0xef), + + I32x4TruncSatF32x4S => self.simd(0xf8), + I32x4TruncSatF32x4U => self.simd(0xf9), + F32x4ConvertI32x4S => self.simd(0xfa), + F32x4ConvertI32x4U => self.simd(0xfb), I32TruncSSatF32 => self.encoder.raw(&[0xfc, 0x00]), I32TruncUSatF32 => self.encoder.raw(&[0xfc, 0x01]), @@ -531,15 +540,6 @@ impl<'instr> Visitor<'instr> for Emit<'_, '_> { I64TruncUSatF32 => self.encoder.raw(&[0xfc, 0x05]), I64TruncSSatF64 => self.encoder.raw(&[0xfc, 0x06]), I64TruncUSatF64 => self.encoder.raw(&[0xfc, 0x07]), - - I16x8WidenLowI8x16S => self.simd(0xca), - I16x8WidenHighI8x16S => self.simd(0xcb), - I16x8WidenLowI8x16U => self.simd(0xcc), - I16x8WidenHighI8x16U => self.simd(0xcd), - I32x4WidenLowI16x8S => self.simd(0xce), - I32x4WidenHighI16x8S => self.simd(0xcf), - I32x4WidenLowI16x8U => self.simd(0xd0), - I32x4WidenHighI16x8U => self.simd(0xd1), } } @@ -665,7 +665,7 @@ impl<'instr> Visitor<'instr> for Emit<'_, '_> { I64 { atomic: true } => self.encoder.raw(&[0xfe, 0x18]), // i64.atomic.store F32 => self.encoder.byte(0x38), // f32.store F64 => self.encoder.byte(0x39), // f64.store - V128 => self.simd(0x01), // v128.store + V128 => self.simd(0x0b), // v128.store I32_8 { atomic: false } => self.encoder.byte(0x3a), // i32.store8 I32_8 { atomic: true } => self.encoder.raw(&[0xfe, 0x19]), // i32.atomic.store8 I32_16 { atomic: false } => self.encoder.byte(0x3b), // i32.store16 @@ -811,27 +811,27 @@ impl<'instr> Visitor<'instr> for Emit<'_, '_> { } V128Bitselect(_) => { - self.simd(0x50); - } - V128Swizzle(_) => { - self.simd(0xc0); + self.simd(0x52); } V128Shuffle(e) => { - self.simd(0xc1); + self.simd(0x0d); self.encoder.raw(&e.indices); } + V128Swizzle(_) => { + self.simd(0x0e); + } LoadSimd(e) => { match e.kind { - LoadSimdKind::Splat8 => self.simd(0xc2), - LoadSimdKind::Splat16 => self.simd(0xc3), - LoadSimdKind::Splat32 => self.simd(0xc4), - LoadSimdKind::Splat64 => self.simd(0xc5), - LoadSimdKind::I16x8Load8x8S => self.simd(0xd2), - LoadSimdKind::I16x8Load8x8U => self.simd(0xd3), - LoadSimdKind::I32x4Load16x4S => self.simd(0xd4), - LoadSimdKind::I32x4Load16x4U => self.simd(0xd5), - LoadSimdKind::I64x2Load32x2S => self.simd(0xd6), - LoadSimdKind::I64x2Load32x2U => self.simd(0xd7), + LoadSimdKind::I16x8Load8x8S => self.simd(0x01), + LoadSimdKind::I16x8Load8x8U => self.simd(0x02), + LoadSimdKind::I32x4Load16x4S => self.simd(0x03), + LoadSimdKind::I32x4Load16x4U => self.simd(0x04), + LoadSimdKind::I64x2Load32x2S => self.simd(0x05), + LoadSimdKind::I64x2Load32x2U => self.simd(0x06), + LoadSimdKind::Splat8 => self.simd(0x07), + LoadSimdKind::Splat16 => self.simd(0x08), + LoadSimdKind::Splat32 => self.simd(0x09), + LoadSimdKind::Splat64 => self.simd(0x0a), } self.memarg(e.memory, &e.arg); } @@ -842,8 +842,8 @@ impl<'instr> Visitor<'instr> for Emit<'_, '_> { } TableCopy(e) => { self.encoder.raw(&[0xfc, 0x0e]); - self.encoder.u32(self.indices.get_table_index(e.src)); self.encoder.u32(self.indices.get_table_index(e.dst)); + self.encoder.u32(self.indices.get_table_index(e.src)); } ElemDrop(e) => { self.encoder.raw(&[0xfc, 0x0d]); diff --git a/src/module/functions/local_function/mod.rs b/src/module/functions/local_function/mod.rs index 9084c90b..d4445135 100644 --- a/src/module/functions/local_function/mod.rs +++ b/src/module/functions/local_function/mod.rs @@ -1352,7 +1352,6 @@ fn validate_instruction<'context>( Operator::I8x16Sub => binop(ctx, V128, BinaryOp::I8x16Sub)?, Operator::I8x16SubSaturateS => binop(ctx, V128, BinaryOp::I8x16SubSaturateS)?, Operator::I8x16SubSaturateU => binop(ctx, V128, BinaryOp::I8x16SubSaturateU)?, - Operator::I8x16Mul => binop(ctx, V128, BinaryOp::I8x16Mul)?, Operator::I16x8Neg => unop(ctx, V128, UnaryOp::I16x8Neg)?, Operator::I16x8AnyTrue => one_op(ctx, V128, I32, UnaryOp::I16x8AnyTrue)?, @@ -1379,8 +1378,6 @@ fn validate_instruction<'context>( Operator::I32x4Mul => binop(ctx, V128, BinaryOp::I32x4Mul)?, Operator::I64x2Neg => unop(ctx, V128, UnaryOp::I64x2Neg)?, - Operator::I64x2AnyTrue => one_op(ctx, V128, I32, UnaryOp::I64x2AnyTrue)?, - Operator::I64x2AllTrue => one_op(ctx, V128, I32, UnaryOp::I64x2AllTrue)?, Operator::I64x2Shl => two_ops(ctx, V128, I32, V128, BinaryOp::I64x2Shl)?, Operator::I64x2ShrS => two_ops(ctx, V128, I32, V128, BinaryOp::I64x2ShrS)?, Operator::I64x2ShrU => two_ops(ctx, V128, I32, V128, BinaryOp::I64x2ShrU)?, @@ -1408,14 +1405,18 @@ fn validate_instruction<'context>( Operator::F64x2Min => binop(ctx, V128, BinaryOp::F64x2Min)?, Operator::F64x2Max => binop(ctx, V128, BinaryOp::F64x2Max)?, - Operator::I32x4TruncSatF32x4S => unop(ctx, V128, UnaryOp::I32x4TruncSF32x4Sat)?, - Operator::I32x4TruncSatF32x4U => unop(ctx, V128, UnaryOp::I32x4TruncUF32x4Sat)?, - Operator::I64x2TruncSatF64x2S => unop(ctx, V128, UnaryOp::I64x2TruncSF64x2Sat)?, - Operator::I64x2TruncSatF64x2U => unop(ctx, V128, UnaryOp::I64x2TruncUF64x2Sat)?, - Operator::F32x4ConvertI32x4S => unop(ctx, V128, UnaryOp::F32x4ConvertSI32x4)?, - Operator::F32x4ConvertI32x4U => unop(ctx, V128, UnaryOp::F32x4ConvertUI32x4)?, - Operator::F64x2ConvertI64x2S => unop(ctx, V128, UnaryOp::F64x2ConvertSI64x2)?, - Operator::F64x2ConvertI64x2U => unop(ctx, V128, UnaryOp::F64x2ConvertUI64x2)?, + Operator::I32x4TruncSatF32x4S => unop(ctx, V128, UnaryOp::I32x4TruncSatF32x4S)?, + Operator::I32x4TruncSatF32x4U => unop(ctx, V128, UnaryOp::I32x4TruncSatF32x4U)?, + Operator::F32x4ConvertI32x4S => unop(ctx, V128, UnaryOp::F32x4ConvertI32x4S)?, + Operator::F32x4ConvertI32x4U => unop(ctx, V128, UnaryOp::F32x4ConvertI32x4U)?, + + Operator::I64x2AnyTrue + | Operator::I64x2AllTrue + | Operator::I64x2TruncSatF64x2S + | Operator::I64x2TruncSatF64x2U + | Operator::F64x2ConvertI64x2S + | Operator::F64x2ConvertI64x2U + | Operator::I8x16Mul => bail!("instruction removed from spec"), Operator::I32TruncSatF32S => one_op(ctx, F32, I32, UnaryOp::I32TruncSSatF32)?, Operator::I32TruncSatF32U => one_op(ctx, F32, I32, UnaryOp::I32TruncUSatF32)?, @@ -1479,6 +1480,11 @@ fn validate_instruction<'context>( } => { let src = ctx.indices.get_table(src_table)?; let dst = ctx.indices.get_table(dst_table)?; + let src_ty = ctx.module.tables.get(src).element_ty; + let dst_ty = ctx.module.tables.get(dst).element_ty; + if !src_ty.is_subtype_of(dst_ty) { + bail!("type mismatch: cannot copy between tables of different types"); + } ctx.pop_operand_expected(Some(I32))?; ctx.pop_operand_expected(Some(I32))?; ctx.pop_operand_expected(Some(I32))?; @@ -1488,6 +1494,11 @@ fn validate_instruction<'context>( Operator::TableInit { segment, table } => { let elem = ctx.indices.get_element(segment)?; let table = ctx.indices.get_table(table)?; + let elem_ty = ctx.module.elements.get(elem).ty; + let table_ty = ctx.module.tables.get(table).element_ty; + if !elem_ty.is_subtype_of(table_ty) { + bail!("type mismatch: cannot initialize table of different type"); + } ctx.pop_operand_expected(Some(I32))?; ctx.pop_operand_expected(Some(I32))?; ctx.pop_operand_expected(Some(I32))?; diff --git a/src/passes/used.rs b/src/passes/used.rs index d78b60f0..7e479f44 100644 --- a/src/passes/used.rs +++ b/src/passes/used.rs @@ -128,8 +128,14 @@ impl Used { } } for elem in module.elements.iter() { - if let ElementKind::Active { .. } = &elem.kind { - stack.push_element(elem.id()); + match elem.kind { + // Active segments are rooted because they initialize imported + // or exported tables. Declared segments can probably get gc'd + // but for now we're conservative and we root them. + ElementKind::Active { .. } | ElementKind::Declared => { + stack.push_element(elem.id()); + } + ElementKind::Passive => {} } } diff --git a/src/passes/validate.rs b/src/passes/validate.rs index fb295650..001e8562 100644 --- a/src/passes/validate.rs +++ b/src/passes/validate.rs @@ -5,7 +5,7 @@ use crate::ir::*; use crate::ValType; -use crate::{Function, FunctionKind, InitExpr, Result}; +use crate::{ElementKind, Function, FunctionId, FunctionKind, InitExpr, Result}; use crate::{Global, GlobalKind, Memory, MemoryId, Module, Table}; use anyhow::{anyhow, bail, Context}; use std::collections::HashSet; @@ -32,8 +32,14 @@ pub fn run(module: &Module) -> Result<()> { for table in module.tables.iter() { validate_table(table)?; } + let mut defined_funcs = HashSet::new(); + for element in module.elements.iter() { + if let ElementKind::Declared = element.kind { + defined_funcs.extend(element.members.iter().cloned().filter_map(|x| x)); + } + } for global in module.globals.iter() { - validate_global(module, global)?; + validate_global(module, global, &defined_funcs)?; } validate_exports(module)?; @@ -60,6 +66,7 @@ pub fn run(module: &Module) -> Result<()> { errs: &mut errs, function, module, + defined_funcs: &defined_funcs, }; dfs_in_order(&mut cx, local, local.entry_block()); errs @@ -120,7 +127,11 @@ fn validate_exports(module: &Module) -> Result<()> { Ok(()) } -fn validate_global(module: &Module, global: &Global) -> Result<()> { +fn validate_global( + module: &Module, + global: &Global, + defined_funcs: &HashSet, +) -> Result<()> { match global.kind { GlobalKind::Import(_) => return Ok(()), GlobalKind::Local(InitExpr::Value(value)) => { @@ -143,10 +154,13 @@ fn validate_global(module: &Module, global: &Global) -> Result<()> { bail!("invalid type on global"); } } - GlobalKind::Local(InitExpr::RefFunc(_)) => { + GlobalKind::Local(InitExpr::RefFunc(idx)) => { if !ValType::Funcref.is_subtype_of(global.ty) { bail!("invalid type on global"); } + if !defined_funcs.contains(&idx) { + bail!("referenced function in global not declared in element segment"); + } } } Ok(()) @@ -168,6 +182,7 @@ struct Validate<'a> { errs: &'a mut Vec, function: &'a Function, module: &'a Module, + defined_funcs: &'a HashSet, } impl Validate<'_> { @@ -243,4 +258,10 @@ impl<'a> Visitor<'a> for Validate<'a> { self.err("cannot mutate immutable global"); } } + + fn visit_ref_func(&mut self, e: &RefFunc) { + if !self.defined_funcs.contains(&e.func) { + self.err("referenced function not declared in element segment"); + } + } }