Repository: bytecodealliance/wasmtime
Pull request #9141 (2024-08-19 19:17:33)

Implement the extended-const proposal
This commit builds on the support added in #8450 to extend our simple interpreter with support for the `extended-const` proposal to WebAssembly. This is required when updating the spec-test-submodule since `extended-const` was merged into the mainline specification and some proposals are starting to rebase on that.

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs
index 0ba5cd953..c5940b3e5 100644
--- a/crates/types/src/lib.rs
+++ b/crates/types/src/lib.rs
@@ -1484,6 +1484,12 @@ pub enum ConstOp {
     RefI31,
     RefNull,
     RefFunc(FuncIndex),
+    I32Add,
+    I32Sub,
+    I32Mul,
+    I64Add,
+    I64Sub,
+    I64Mul,
 }
 
 impl ConstOp {
@@ -1500,6 +1506,12 @@ impl ConstOp {
             O::RefFunc { function_index } => Self::RefFunc(FuncIndex::from_u32(function_index)),
             O::GlobalGet { global_index } => Self::GlobalGet(GlobalIndex::from_u32(global_index)),
             O::RefI31 => Self::RefI31,
+            O::I32Add => Self::I32Add,
+            O::I32Sub => Self::I32Sub,
+            O::I32Mul => Self::I32Mul,
+            O::I64Add => Self::I64Add,
+            O::I64Sub => Self::I64Sub,
+            O::I64Mul => Self::I64Mul,
             op => {
                 return Err(wasm_unsupported!(
                     "unsupported opcode in const expression at offset {offset:#x}: {op:?}",
diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs
index 555c3d714..a25cfd35c 100644
--- a/crates/wasmtime/src/config.rs
+++ b/crates/wasmtime/src/config.rs
@@ -259,9 +259,6 @@ impl Config {
             ret.cranelift_opt_level(OptLevel::Speed);
         }
 
-        // Not yet implemented in Wasmtime
-        ret.features.set(WasmFeatures::EXTENDED_CONST, false);
-
         // Conditionally enabled features depending on compile-time crate
         // features. Note that if these features are disabled then `Config` has
         // no way of re-enabling them.
@@ -285,6 +282,7 @@ impl Config {
         ret.wasm_multi_value(true);
         ret.wasm_bulk_memory(true);
         ret.wasm_simd(true);
+        ret.wasm_extended_const(true);
         ret.wasm_backtrace_details(WasmBacktraceDetails::Environment);
 
         ret
@@ -963,6 +961,17 @@ impl Config {
         self
     }
 
+    /// Configures whether the WebAssembly extended-const [proposal] will
+    /// be enabled for compilation.
+    ///
+    /// This is `true` by default.
+    ///
+    /// [proposal]: https://github.com/webassembly/extended-const
+    pub fn wasm_extended_const(&mut self, enable: bool) -> &mut Self {
+        self.features.set(WasmFeatures::EXTENDED_CONST, enable);
+        self
+    }
+
     /// Configures whether the WebAssembly component-model [proposal] will
     /// be enabled for compilation.
     ///
diff --git a/crates/wasmtime/src/runtime/vm/const_expr.rs b/crates/wasmtime/src/runtime/vm/const_expr.rs
index 55a6d0a8f..198673b93 100644
--- a/crates/wasmtime/src/runtime/vm/const_expr.rs
+++ b/crates/wasmtime/src/runtime/vm/const_expr.rs
@@ -78,6 +78,36 @@ impl ConstExprEvaluator {
                     let raw = VMGcRef::from_i31(i31).as_raw_u32();
                     self.stack.push(ValRaw::anyref(raw));
                 }
+                wasmtime_environ::ConstOp::I32Add => {
+                    let b = self.pop()?.get_i32();
+                    let a = self.pop()?.get_i32();
+                    self.stack.push(ValRaw::i32(a.wrapping_add(b)));
+                }
+                wasmtime_environ::ConstOp::I32Sub => {
+                    let b = self.pop()?.get_i32();
+                    let a = self.pop()?.get_i32();
+                    self.stack.push(ValRaw::i32(a.wrapping_sub(b)));
+                }
+                wasmtime_environ::ConstOp::I32Mul => {
+                    let b = self.pop()?.get_i32();
+                    let a = self.pop()?.get_i32();
+                    self.stack.push(ValRaw::i32(a.wrapping_mul(b)));
+                }
+                wasmtime_environ::ConstOp::I64Add => {
+                    let b = self.pop()?.get_i64();
+                    let a = self.pop()?.get_i64();
+                    self.stack.push(ValRaw::i64(a.wrapping_add(b)));
+                }
+                wasmtime_environ::ConstOp::I64Sub => {
+                    let b = self.pop()?.get_i64();
+                    let a = self.pop()?.get_i64();
+                    self.stack.push(ValRaw::i64(a.wrapping_sub(b)));
+                }
+                wasmtime_environ::ConstOp::I64Mul => {
+                    let b = self.pop()?.get_i64();
+                    let a = self.pop()?.get_i64();
+                    self.stack.push(ValRaw::i64(a.wrapping_mul(b)));
+                }
             }
         }
 


Test patch:
diff --git a/tests/wast.rs b/tests/wast.rs
index 68ead3224..e4983d7d3 100644
--- a/tests/wast.rs
+++ b/tests/wast.rs
@@ -74,10 +74,6 @@ fn ignore(test: &Path, strategy: Strategy) -> bool {
         if part == "exception-handling" {
             return true;
         }
-        // Not implemented in Wasmtime yet
-        if part == "extended-const" {
-            return true;
-        }
         // Wasmtime doesn't implement the table64 extension yet.
         if part == "memory64" {
             if [
@@ -219,6 +215,7 @@ fn run_wast(wast: &Path, strategy: Strategy, pooling: bool) -> anyhow::Result<()
     let tail_call = feature_found(wast, "tail-call") || feature_found(wast, "function-references");
     let use_shared_memory = feature_found_src(&wast_bytes, "shared_memory")
         || feature_found_src(&wast_bytes, "shared)");
+    let extended_const = feature_found(wast, "extended-const");
 
     if pooling && use_shared_memory {
         log::warn!("skipping pooling test with shared memory");
@@ -240,6 +237,7 @@ fn run_wast(wast: &Path, strategy: Strategy, pooling: bool) -> anyhow::Result<()
         .wasm_relaxed_simd(relaxed_simd)
         .wasm_tail_call(tail_call)
         .wasm_custom_page_sizes(custom_page_sizes)
+        .wasm_extended_const(extended_const)
         .strategy(strategy);
 
     if is_cranelift {