Repository: bytecodealliance/wasmtime
Pull request #7472 (2023-11-03 14:47:08)

egraphs: Recognize `rot{r,l}` from shifts
👋 Hey,

This PR adds a egraphs rule to recognize rotates from a combination of shifts. It also adds a few rules that rewrite the shift amount to be smaller than `ty_bits`. This not only helps the rotate rule fire more often, but also some other rules in this file.


Patch:
diff --git a/cranelift/codegen/src/machinst/vcode.rs b/cranelift/codegen/src/machinst/vcode.rs
index c743fcd20..90bb2a286 100644
--- a/cranelift/codegen/src/machinst/vcode.rs
+++ b/cranelift/codegen/src/machinst/vcode.rs
@@ -1738,6 +1738,15 @@ impl VCodeConstants {
     pub fn get(&self, c: VCodeConstant) -> &VCodeConstantData {
         &self.constants[c]
     }
+
+    /// Checks if the given [VCodeConstantData] is registered as
+    /// used by the pool.
+    pub fn pool_uses(&self, constant: &VCodeConstantData) -> bool {
+        match constant {
+            VCodeConstantData::Pool(c, _) => self.pool_uses.contains_key(c),
+            _ => false,
+        }
+    }
 }
 
 /// A use of a constant by one or more VCode instructions; see [VCodeConstants].
diff --git a/cranelift/codegen/src/opts/shifts.isle b/cranelift/codegen/src/opts/shifts.isle
index acf53946c..272c5c90b 100644
--- a/cranelift/codegen/src/opts/shifts.isle
+++ b/cranelift/codegen/src/opts/shifts.isle
@@ -268,3 +268,43 @@
       (rotl ty x (isub_uextend y z)))
 (rule (simplify (rotl ty (rotr ty x y @ (value_type kty)) z @ (value_type kty)))
       (rotr ty x (isub_uextend y z)))
+
+;; Convert shifts into rotates. We always normalize into a rotate left.
+;;
+;; (bor (ishl x k1) (ushr x k2)) == (rotl x k1) if k2 == ty_bits - k1
+;; (bor (ushr x k2) (ishl x k1)) == (rotl x k1) if k2 == ty_bits - k1
+;;
+;; TODO: This rule is restricted to scalars since no backend currently
+;; supports SIMD rotates.
+(rule (simplify (bor (ty_int ty)
+                      (ishl ty x k @ (iconst _ (u64_from_imm64 k1)))
+                      (ushr ty x (iconst _ (u64_from_imm64 k2)))))
+      (if-let $true (u64_eq k2 (u64_sub (ty_bits_u64 (lane_type ty)) k1)))
+      (rotl ty x k))
+(rule (simplify (bor (ty_int ty)
+                      (ushr ty x (iconst _ (u64_from_imm64 k2)))
+                      (ishl ty x k @ (iconst _ (u64_from_imm64 k1)))))
+      (if-let $true (u64_eq k2 (u64_sub (ty_bits_u64 (lane_type ty)) k1)))
+      (rotl ty x k))
+
+;; Normalize the shift amount. Some rules can't fire unless the shift amount
+;; is normalized. This also helps us materialize fewer and smaller constants.
+;;
+;; (op x k) == (op x (and k (ty_shift_mask ty)))
+;;
+;; where `op` is one of ishl, ushr, sshr, rotl, rotr
+(rule (simplify (ishl ty x (iconst kty (u64_from_imm64 k))))
+      (if-let $false (u64_eq k (u64_and k (ty_shift_mask ty))))
+      (ishl ty x (iconst kty (imm64 (u64_and k (ty_shift_mask ty))))))
+(rule (simplify (ushr ty x (iconst kty (u64_from_imm64 k))))
+      (if-let $false (u64_eq k (u64_and k (ty_shift_mask ty))))
+      (ushr ty x (iconst kty (imm64 (u64_and k (ty_shift_mask ty))))))
+(rule (simplify (sshr ty x (iconst kty (u64_from_imm64 k))))
+      (if-let $false (u64_eq k (u64_and k (ty_shift_mask ty))))
+      (sshr ty x (iconst kty (imm64 (u64_and k (ty_shift_mask ty))))))
+(rule (simplify (rotr ty x (iconst kty (u64_from_imm64 k))))
+      (if-let $false (u64_eq k (u64_and k (ty_shift_mask ty))))
+      (rotr ty x (iconst kty (imm64 (u64_and k (ty_shift_mask ty))))))
+(rule (simplify (rotl ty x (iconst kty (u64_from_imm64 k))))
+      (if-let $false (u64_eq k (u64_and k (ty_shift_mask ty))))
+      (rotl ty x (iconst kty (imm64 (u64_and k (ty_shift_mask ty))))))
diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h
index 44e4e3d2c..ce7d46e0f 100644
--- a/crates/c-api/include/wasmtime.h
+++ b/crates/c-api/include/wasmtime.h
@@ -181,6 +181,7 @@
 #define WASMTIME_API_H
 
 #include <wasi.h>
+// IWYU pragma: begin_exports
 #include <wasmtime/config.h>
 #include <wasmtime/engine.h>
 #include <wasmtime/error.h>
@@ -196,6 +197,7 @@
 #include <wasmtime/trap.h>
 #include <wasmtime/val.h>
 #include <wasmtime/async.h>
+// IWYU pragma: end_exports
 
 /**
  * \brief Wasmtime version string.
diff --git a/crates/wasmtime/src/compiler.rs b/crates/wasmtime/src/compiler.rs
index 33856f4b2..5022ef9da 100644
--- a/crates/wasmtime/src/compiler.rs
+++ b/crates/wasmtime/src/compiler.rs
@@ -28,7 +28,7 @@ use std::collections::{btree_map, BTreeMap, BTreeSet};
 use std::{any::Any, collections::HashMap};
 use wasmtime_environ::{
     Compiler, DefinedFuncIndex, FuncIndex, FunctionBodyData, ModuleTranslation, ModuleType,
-    ModuleTypes, PrimaryMap, SignatureIndex, StaticModuleIndex, Tunables, WasmFunctionInfo,
+    ModuleTypes, PrimaryMap, SignatureIndex, StaticModuleIndex, WasmFunctionInfo,
 };
 use wasmtime_jit::{CompiledFunctionInfo, CompiledModuleInfo};
 
@@ -447,8 +447,7 @@ impl FunctionIndices {
     pub fn link_and_append_code<'a>(
         mut self,
         mut obj: object::write::Object<'static>,
-        tunables: &'a Tunables,
-        compiler: &dyn Compiler,
+        engine: &'a Engine,
         compiled_funcs: Vec<(String, Box<dyn Any + Send>)>,
         translations: PrimaryMap<StaticModuleIndex, ModuleTranslation<'_>>,
     ) -> Result<(wasmtime_jit::ObjectBuilder<'a>, Artifacts)> {
@@ -457,6 +456,8 @@ impl FunctionIndices {
         // The result is a vector parallel to `compiled_funcs` where
         // `symbol_ids_and_locs[i]` is the symbol ID and function location of
         // `compiled_funcs[i]`.
+        let compiler = engine.compiler();
+        let tunables = &engine.config().tunables;
         let symbol_ids_and_locs = compiler.append_code(
             &mut obj,
             &compiled_funcs,
@@ -559,7 +560,22 @@ impl FunctionIndices {
 
         artifacts.modules = translations
             .into_iter()
-            .map(|(module, translation)| {
+            .map(|(module, mut translation)| {
+                // If configured attempt to use static memory initialization which
+                // can either at runtime be implemented as a single memcpy to
+                // initialize memory or otherwise enabling virtual-memory-tricks
+                // such as mmap'ing from a file to get copy-on-write.
+                if engine.config().memory_init_cow {
+                    let align = compiler.page_size_align();
+                    let max_always_allowed = engine.config().memory_guaranteed_dense_image_size;
+                    translation.try_static_init(align, max_always_allowed);
+                }
+
+                // Attempt to convert table initializer segments to
+                // FuncTable representation where possible, to enable
+                // table lazy init.
+                translation.try_func_table_init();
+
                 let funcs: PrimaryMap<DefinedFuncIndex, CompiledFunctionInfo> =
                     wasm_functions_for_module(&mut wasm_functions, module)
                         .map(|(key, wasm_func_index)| {
diff --git a/crates/wasmtime/src/component/component.rs b/crates/wasmtime/src/component/component.rs
index 013f93bd7..a57165ed4 100644
--- a/crates/wasmtime/src/component/component.rs
+++ b/crates/wasmtime/src/component/component.rs
@@ -206,8 +206,7 @@ impl Component {
 
         let (mut object, compilation_artifacts) = function_indices.link_and_append_code(
             object,
-            &engine.config().tunables,
-            compiler,
+            engine,
             compiled_funcs,
             module_translations,
         )?;
@@ -473,3 +472,34 @@ impl ComponentRuntimeInfo for ComponentInner {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use crate::component::Component;
+    use crate::{Config, Engine};
+    use wasmtime_environ::MemoryInitialization;
+
+    #[test]
+    fn cow_on_by_default() {
+        let mut config = Config::new();
+        config.wasm_component_model(true);
+        let engine = Engine::new(&config).unwrap();
+        let component = Component::new(
+            &engine,
+            r#"
+                (component
+                    (core module
+                        (memory 1)
+                        (data (i32.const 100) "abcd")
+                    )
+                )
+            "#,
+        )
+        .unwrap();
+
+        for (_, module) in component.inner.static_modules.iter() {
+            let init = &module.env_module().memory_initialization;
+            assert!(matches!(init, MemoryInitialization::Static { .. }));
+        }
+    }
+}
diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs
index 38a51fcd7..a5553ff94 100644
--- a/crates/wasmtime/src/module.rs
+++ b/crates/wasmtime/src/module.rs
@@ -402,7 +402,6 @@ impl Module {
         use crate::compiler::CompileInputs;
 
         let tunables = &engine.config().tunables;
-        let compiler = engine.compiler();
 
         // First a `ModuleEnvironment` is created which records type information
         // about the wasm module. This is where the WebAssembly is parsed and
@@ -437,25 +436,9 @@ impl Module {
         engine.append_compiler_info(&mut object);
         engine.append_bti(&mut object);
 
-        // If configured attempt to use static memory initialization which
-        // can either at runtime be implemented as a single memcpy to
-        // initialize memory or otherwise enabling virtual-memory-tricks
-        // such as mmap'ing from a file to get copy-on-write.
-        if engine.config().memory_init_cow {
-            let align = engine.compiler().page_size_align();
-            let max_always_allowed = engine.config().memory_guaranteed_dense_image_size;
-            translation.try_static_init(align, max_always_allowed);
-        }
-
-        // Attempt to convert table initializer segments to
-        // FuncTable representation where possible, to enable
-        // table lazy init.
-        translation.try_func_table_init();
-
         let (mut object, compilation_artifacts) = function_indices.link_and_append_code(
             object,
-            tunables,
-            compiler,
+            engine,
             compiled_funcs,
             std::iter::once(translation).collect(),
         )?;
@@ -1361,3 +1344,27 @@ fn memory_images(engine: &Engine, module: &CompiledModule) -> Result<Option<Modu
     };
     ModuleMemoryImages::new(module.module(), module.code_memory().wasm_data(), mmap)
 }
+
+#[cfg(test)]
+mod tests {
+    use crate::{Engine, Module};
+    use wasmtime_environ::MemoryInitialization;
+
+    #[test]
+    fn cow_on_by_default() {
+        let engine = Engine::default();
+        let module = Module::new(
+            &engine,
+            r#"
+                (module
+                    (memory 1)
+                    (data (i32.const 100) "abcd")
+                )
+            "#,
+        )
+        .unwrap();
+
+        let init = &module.env_module().memory_initialization;
+        assert!(matches!(init, MemoryInitialization::Static { .. }));
+    }
+}
diff --git a/src/commands/serve.rs b/src/commands/serve.rs
index 31276a1aa..927cc755b 100644
--- a/src/commands/serve.rs
+++ b/src/commands/serve.rs
@@ -373,12 +373,16 @@ impl hyper::service::Service<Request> for ProxyHandler {
                 wasmtime_wasi_http::proxy::Proxy::instantiate_pre(&mut store, &inner.instance_pre)
                     .await?;
 
-            proxy
+            if let Err(e) = proxy
                 .wasi_http_incoming_handler()
                 .call_handle(store, req, out)
-                .await?;
+                .await
+            {
+                log::error!("[{req_id}] :: {:#?}", e);
+                return Err(e);
+            }
 
-            Ok::<_, anyhow::Error>(())
+            Ok(())
         });
 
         Box::pin(async move {
diff --git a/winch/codegen/src/isa/x64/asm.rs b/winch/codegen/src/isa/x64/asm.rs
index 98f7723c6..bd791784e 100644
--- a/winch/codegen/src/isa/x64/asm.rs
+++ b/winch/codegen/src/isa/x64/asm.rs
@@ -202,8 +202,14 @@ impl Assembler {
                 // instrunction.
                 let constant_data = pool.get(*c);
                 let data = VCodeConstantData::Pool(*c, constant_data.clone());
+                // If the constaant data is not marked as used, it will be
+                // inserted, therefore, it needs to be registered.
+                let needs_registration = !constants.pool_uses(&data);
                 let constant = constants.insert(VCodeConstantData::Pool(*c, constant_data.clone()));
-                buffer.register_constant(&constant, &data);
+
+                if needs_registration {
+                    buffer.register_constant(&constant, &data);
+                }
                 SyntheticAmode::ConstantOffset(constant)
             }
         }


Test patch:
diff --git a/cranelift/filetests/filetests/egraph/shifts.clif b/cranelift/filetests/filetests/egraph/shifts.clif
index dbe984c84..fbe308ddb 100644
--- a/cranelift/filetests/filetests/egraph/shifts.clif
+++ b/cranelift/filetests/filetests/egraph/shifts.clif
@@ -74,9 +74,9 @@ block0(v0: i8):
     v2 = sshr v0, v1
     v3 = ishl v2, v1
     return v3
-    ; check: v4 = iconst.i8 224
-    ; check: v5 = band v0, v4
-    ; check: return v5
+    ; check: v8 = iconst.i8 224
+    ; check: v9 = band v0, v8
+    ; check: return v9
 }
 
 function %sextend_shift_32_64_unsigned(i32) -> i64 {
@@ -596,9 +596,9 @@ block0(v0: i128):
     return v3
 }
 
-; check: v4 = iconst.i64 0
-; nextln: v5 = uextend.i128 v4  ; v4 = 0
-; nextln: return v5
+; check: v7 = iconst.i64 0
+; nextln: v8 = uextend.i128 v7
+; nextln: return v8
 
 
 function %i128_ishl_overflow_becomes_0(i128) -> i128 {
@@ -609,9 +609,9 @@ block0(v0: i128):
     return v3
 }
 
-; check: v4 = iconst.i64 0
-; nextln: v5 = uextend.i128 v4  ; v4 = 0
-; nextln: return v5
+; check: v7 = iconst.i64 0
+; nextln: v8 = uextend.i128 v7
+; nextln: return v8
 
 function %simd_shift_does_not_panic(i8x16) -> i8x16 {
 block0(v0: i8x16):
@@ -636,11 +636,10 @@ block0(v0: i8x16):
     return v5
 }
 
-; check: v1 = iconst.i16 30
-; nextln: v3 = sshr v0, v1  ; v1 = 30
-; nextln: v6 = iconst.i16 6
-; nextln: v7 = sshr v3, v6  ; v6 = 6
-; check: return v7
+; check: v6 = iconst.i16 6
+; check: v7 = sshr v0, v6
+; check: v9 = sshr v7, v6
+; check: return v9
 
 
 function %rotr_rotr_prop(i8) -> i8 {
@@ -758,3 +757,57 @@ block0(v0: i8, v1: i8):
 }
 
 ; check: return v0
+
+function %shifts_to_rotl(i64) -> i64 {
+block0(v0: i64):
+    v1 = iconst.i16 3
+    v2 = iconst.i32 61
+    v3 = ishl v0, v1
+    v4 = ushr v0, v2
+    v5 = bor v3, v4
+    return v5
+}
+
+; check: v1 = iconst.i16 3
+; nextln: v6 = rotl v0, v1  ; v1 = 3
+; check: return v6
+
+
+function %shifts_to_rotl_reverse(i64) -> i64 {
+block0(v0: i64):
+    v1 = iconst.i16 32
+    v2 = iconst.i32 32
+    v3 = ishl v0, v1
+    v4 = ushr v0, v2
+    v5 = bor v4, v3
+    return v5
+}
+
+; check: v1 = iconst.i16 32
+; nextln: v6 = rotl v0, v1  ; v1 = 32
+; check: return v6
+
+function %shifts_to_rotr_equivalent(i64) -> i64 {
+block0(v0: i64):
+    v1 = iconst.i16 3
+    v2 = iconst.i32 61
+    v3 = ishl v0, v2
+    v4 = ushr v0, v1
+    v5 = bor v3, v4
+    return v5
+}
+
+; check: v2 = iconst.i32 61
+; nextln: v6 = rotl v0, v2  ; v2 = 61
+; check: return v6
+
+function %ishl_normalize_amount(i64) -> i64 {
+block0(v0: i64):
+    v1 = iconst.i32 99
+    v2 = ishl v0, v1
+    return v2
+}
+
+; check: v3 = iconst.i32 35
+; check: v4 = ishl v0, v3
+; check: return v4
diff --git a/cranelift/filetests/filetests/isa/riscv64/wasm/zbb.wat b/cranelift/filetests/filetests/isa/riscv64/wasm/zbb.wat
index e307e6a6a..f0cc3f4cc 100644
--- a/cranelift/filetests/filetests/isa/riscv64/wasm/zbb.wat
+++ b/cranelift/filetests/filetests/isa/riscv64/wasm/zbb.wat
@@ -76,7 +76,7 @@
 ;; block0:
 ;;   j label1
 ;; block1:
-;;   roriw a0,a0,100
+;;   roriw a0,a0,4
 ;;   ret
 ;;
 ;; function u0:7:
diff --git a/tests/misc_testsuite/winch/local_get.wast b/tests/misc_testsuite/winch/local_get.wast
new file mode 100644
index 000000000..16e06113d
--- /dev/null
+++ b/tests/misc_testsuite/winch/local_get.wast
@@ -0,0 +1,103 @@
+;; Test `local.get` operator
+
+(module
+  ;; Typing
+
+  (func (export "type-local-i32") (result i32) (local i32) (local.get 0))
+  (func (export "type-local-i64") (result i64) (local i64) (local.get 0))
+  (func (export "type-local-f32") (result f32) (local f32) (local.get 0))
+  (func (export "type-local-f64") (result f64) (local f64) (local.get 0))
+
+  (func (export "type-param-i32") (param i32) (result i32) (local.get 0))
+  (func (export "type-param-i64") (param i64) (result i64) (local.get 0))
+  (func (export "type-param-f32") (param f32) (result f32) (local.get 0))
+  (func (export "type-param-f64") (param f64) (result f64) (local.get 0))
+
+  (func (export "type-mixed") (param i64 f32 f64 i32 i32)
+    (local f32 i64 i64 f64)
+    (drop (i64.eqz (local.get 0)))
+    (drop (f32.neg (local.get 1)))
+    (drop (f64.neg (local.get 2)))
+    (drop (i32.eqz (local.get 3)))
+    (drop (i32.eqz (local.get 4)))
+    (drop (f32.neg (local.get 5)))
+    (drop (i64.eqz (local.get 6)))
+    (drop (i64.eqz (local.get 7)))
+    (drop (f64.neg (local.get 8)))
+  )
+
+
+  ;; As parameter of control constructs and instructions
+
+  (func (export "as-block-value") (param i32) (result i32)
+    (block (result i32) (local.get 0))
+  )
+  (func (export "as-loop-value") (param i32) (result i32)
+    (loop (result i32) (local.get 0))
+  )
+  (func (export "as-br-value") (param i32) (result i32)
+    (block (result i32) (br 0 (local.get 0)))
+  )
+  (func (export "as-br_if-value") (param i32) (result i32)
+    (block $l0 (result i32) (br_if $l0 (local.get 0) (i32.const 1)))
+  )
+
+  (func (export "as-br_if-value-cond") (param i32) (result i32)
+    (block (result i32)
+      (br_if 0 (local.get 0) (local.get 0))
+    )
+  )
+  (func (export "as-br_table-value") (param i32) (result i32)
+    (block
+      (block
+        (block
+          (br_table 0 1 2 (local.get 0))
+          (return (i32.const 0))
+        )
+        (return (i32.const 1))
+      )
+      (return (i32.const 2))
+    )
+    (i32.const 3)
+  )
+
+  (func (export "as-return-value") (param i32) (result i32)
+    (return (local.get 0))
+  )
+
+  (func (export "as-if-then") (param i32) (result i32)
+    (if (result i32) (local.get 0) (then (local.get 0)) (else (i32.const 0)))
+  )
+  (func (export "as-if-else") (param i32) (result i32)
+    (if (result i32) (local.get 0) (then (i32.const 1)) (else (local.get 0)))
+  )
+)
+
+(assert_return (invoke "type-local-i32") (i32.const 0))
+(assert_return (invoke "type-local-i64") (i64.const 0))
+(assert_return (invoke "type-local-f32") (f32.const 0))
+(assert_return (invoke "type-local-f64") (f64.const 0))
+
+(assert_return (invoke "type-param-i32" (i32.const 2)) (i32.const 2))
+(assert_return (invoke "type-param-i64" (i64.const 3)) (i64.const 3))
+(assert_return (invoke "type-param-f32" (f32.const 4.4)) (f32.const 4.4))
+(assert_return (invoke "type-param-f64" (f64.const 5.5)) (f64.const 5.5))
+
+(assert_return (invoke "as-block-value" (i32.const 6)) (i32.const 6))
+(assert_return (invoke "as-loop-value" (i32.const 7)) (i32.const 7))
+
+(assert_return (invoke "as-br-value" (i32.const 8)) (i32.const 8))
+(assert_return (invoke "as-br_if-value" (i32.const 9)) (i32.const 9))
+(assert_return (invoke "as-br_if-value-cond" (i32.const 10)) (i32.const 10))
+(assert_return (invoke "as-br_table-value" (i32.const 1)) (i32.const 2))
+
+(assert_return (invoke "as-return-value" (i32.const 0)) (i32.const 0))
+
+(assert_return (invoke "as-if-then" (i32.const 1)) (i32.const 1))
+(assert_return (invoke "as-if-else" (i32.const 0)) (i32.const 0))
+
+(assert_return
+  (invoke "type-mixed"
+    (i64.const 1) (f32.const 2.2) (f64.const 3.3) (i32.const 4) (i32.const 5)
+  )
+)
diff --git a/tests/misc_testsuite/winch/local_set.wast b/tests/misc_testsuite/winch/local_set.wast
new file mode 100644
index 000000000..f368f7bf3
--- /dev/null
+++ b/tests/misc_testsuite/winch/local_set.wast
@@ -0,0 +1,97 @@
+;; Test `local.set` operator
+
+(module
+  ;; Typing
+
+  (func (export "type-local-i32") (local i32) (local.set 0 (i32.const 0)))
+  (func (export "type-local-i64") (local i64) (local.set 0 (i64.const 0)))
+  (func (export "type-local-f32") (local f32) (local.set 0 (f32.const 0)))
+  (func (export "type-local-f64") (local f64) (local.set 0 (f64.const 0)))
+
+  (func (export "type-param-i32") (param i32) (local.set 0 (i32.const 10)))
+  (func (export "type-param-i64") (param i64) (local.set 0 (i64.const 11)))
+  (func (export "type-param-f32") (param f32) (local.set 0 (f32.const 11.1)))
+  (func (export "type-param-f64") (param f64) (local.set 0 (f64.const 12.2)))
+
+  (func (export "type-mixed") (param i64 f32 f64 i32 i32) (local f32 i64 i64 f64)
+    (local.set 0 (i64.const 0))
+    (local.set 1 (f32.const 0))
+    (local.set 2 (f64.const 0))
+    (local.set 3 (i32.const 0))
+    (local.set 4 (i32.const 0))
+    (local.set 5 (f32.const 0))
+    (local.set 6 (i64.const 0))
+    (local.set 7 (i64.const 0))
+    (local.set 8 (f64.const 0))
+  )
+
+  ;; As parameter of control constructs and instructions
+
+  (func (export "as-block-value") (param i32)
+    (block (local.set 0 (i32.const 1)))
+  )
+  (func (export "as-loop-value") (param i32)
+    (loop (local.set 0 (i32.const 3)))
+  )
+
+  (func (export "as-br-value") (param i32)
+    (block (br 0 (local.set 0 (i32.const 9))))
+  )
+  (func (export "as-br_if-value") (param i32)
+    (block
+      (br_if 0 (local.set 0 (i32.const 8)) (i32.const 1))
+    )
+  )
+  (func (export "as-br_if-value-cond") (param i32)
+    (block
+      (br_if 0 (i32.const 6) (local.set 0 (i32.const 9)))
+    )
+  )
+  (func (export "as-br_table-value") (param i32)
+    (block
+      (br_table 0 (local.set 0 (i32.const 10)) (i32.const 1))
+    )
+  )
+
+  (func (export "as-return-value") (param i32)
+    (return (local.set 0 (i32.const 7)))
+  )
+
+  (func (export "as-if-then") (param i32)
+    (if (local.get 0) (then (local.set 0 (i32.const 3))))
+  )
+  (func (export "as-if-else") (param i32)
+    (if (local.get 0) (then) (else (local.set 0 (i32.const 1))))
+  )
+)
+
+(assert_return (invoke "type-local-i32"))
+(assert_return (invoke "type-local-i64"))
+(assert_return (invoke "type-local-f32"))
+(assert_return (invoke "type-local-f64"))
+
+(assert_return (invoke "type-param-i32" (i32.const 2)))
+(assert_return (invoke "type-param-i64" (i64.const 3)))
+(assert_return (invoke "type-param-f32" (f32.const 4.4)))
+(assert_return (invoke "type-param-f64" (f64.const 5.5)))
+
+(assert_return (invoke "as-block-value" (i32.const 0)))
+(assert_return (invoke "as-loop-value" (i32.const 0)))
+
+(assert_return (invoke "as-br-value" (i32.const 0)))
+(assert_return (invoke "as-br_if-value" (i32.const 0)))
+(assert_return (invoke "as-br_if-value-cond" (i32.const 0)))
+(assert_return (invoke "as-br_table-value" (i32.const 0)))
+
+(assert_return (invoke "as-return-value" (i32.const 0)))
+
+(assert_return (invoke "as-if-then" (i32.const 1)))
+(assert_return (invoke "as-if-else" (i32.const 0)))
+
+(assert_return
+  (invoke "type-mixed"
+    (i64.const 1) (f32.const 2.2) (f64.const 3.3) (i32.const 4) (i32.const 5)
+  )
+)
+
+