Repository: bytecodealliance/wasmtime
Pull request #5749 (2023-02-08 18:04:42)

Support plugging external profilers into the Cranelift timing infrastructure
This for example allows plugging in an adapter for rustc's measureme profiler allowing details for both rustc and Cranelift to be present in a single profile. Measureme has several tools that process profiles. The flamegraph tool for example produced the following flamegraph:

![rustc](https://user-images.githubusercontent.com/17426603/217613582-c809928e-d21c-4522-b3dc-a49d8896227e.svg)

(Download the svg and open it to make it interactive. Github's CSP policy prevents executing javascript.)

bjorn3: This is the code necessary on cg_clif's side for this:

```rust
struct MeasuremeProfiler(SelfProfilerRef);

struct TimingGuard {
    profiler: std::mem::ManuallyDrop<SelfProfilerRef>,
    inner: Option<rustc_data_structures::profiling::TimingGuard<'static>>,
}

impl Drop for TimingGuard {
    fn drop(&mut self) {
        self.inner.take();
        unsafe {
            std::mem::ManuallyDrop::drop(&mut self.profiler);
        }
    }
}

impl cranelift_codegen::timing::Profiler for MeasuremeProfiler {
    fn start_pass(
        &self,
        pass: cranelift_codegen::timing::Pass,
    ) -> Box<dyn std::any::Any> {
        let mut timing_guard = TimingGuard {
            profiler: std::mem::ManuallyDrop::new(self.0.clone()),
            inner: None,
        };
        timing_guard.inner = Some(
            unsafe {
                &*(&*timing_guard.profiler as &SelfProfilerRef
                    as *const SelfProfilerRef)
            }
            .generic_activity(pass.description()),
        );
        Box::new(timing_guard)
    }
}

cranelift_codegen::timing::set_thread_profiler(Box::new(MeasuremeProfiler(
    cx.profiler.clone(),
)));
```

Most of it deals with a lifetime issue on measureme's side.
bjorn3: Measureme also supports outputting chrome profiler profiles, but chrome's profiler seems to suffer from float rounding errors causing some issues.
jameysharp: I've reviewed everything in this PR except the most important commit ("Introduce a Profiler trait"), which I want to take a little longer to think about. I think it's a good idea! I just want to dig into the details.

Everything else I think is easy to approve: it's a bunch of good cleanups.

If you'd like to split that one commit to a separate PR I'd be happy to merge the rest now and either I or somebody else will review the Profiler trait parts later. But it's also okay if you'd like to just wait for it to all be reviewed and merged at once.

I'm not familiar with the measureme profiler. Is that something we could use in other Cranelift drivers like Wasmtime as well, or is it a module in rustc? I'd love to get flamegraphs like your demo for other uses of Cranelift as well.
bjorn3: > If you'd like to split that one commit to a separate PR I'd be happy to merge the rest now and either I or somebody else will review the Profiler trait parts later. But it's also okay if you'd like to just wait for it to all be reviewed and merged at once.

I'm fine with waiting until the entire PR is reviewed, but if you prefer a smaller PR I can split it out.

> I'm not familiar with the measureme profiler. Is that something we could use in other Cranelift drivers like Wasmtime as well, or is it a module in rustc? I'd love to get flamegraphs like your demo for other uses of Cranelift as well.

See https://github.com/rust-lang/measureme/. Having a measureme based profiler in Cranelift would be possible, but the `Profiler` interface is still necessary as it wouldn't be possible for Cranelift to use the exact same version of measureme as rustc without `#![feature(rustc_private)]`. Measureme is designed to be as low overhead as possible (the rustc benchmark suite runs with it enabled by default to allow for more detailed investigation.) It supports both wall time based and on x86 instruction based profiling. It supports both interval and instant events and allows extra metadata (like the name of the function being processed or the size of a compiled artifact).
bjorn3: @jameysharp gentle ping
jameysharp: Oh, I think you'll need to rebase before I can merge since the set of required CI jobs changed.
bjorn3: Rebased

> If you feel like opening another PR to make that change I'd be happy to take it.

Sure

Patch:
diff --git a/build.rs b/build.rs
index b735f076b..564133438 100644
--- a/build.rs
+++ b/build.rs
@@ -30,12 +30,6 @@ fn main() -> anyhow::Result<()> {
             test_directory_module(out, "tests/misc_testsuite/threads", strategy)?;
             test_directory_module(out, "tests/misc_testsuite/memory64", strategy)?;
             test_directory_module(out, "tests/misc_testsuite/component-model", strategy)?;
-
-            // NB: these are copied from upstream and updated to wasmtime's
-            // current version of `wast`. This local copy should go away when
-            // all of Wasmtime's tooling is updated and the upstream
-            // `testsuite` module is additionally updated.
-            test_directory_module(out, "tests/misc_testsuite/relaxed-simd", strategy)?;
             Ok(())
         })?;
 
@@ -51,6 +45,11 @@ fn main() -> anyhow::Result<()> {
                     strategy,
                 )?;
                 test_directory_module(out, "tests/spec_testsuite/proposals/threads", strategy)?;
+                test_directory_module(
+                    out,
+                    "tests/spec_testsuite/proposals/relaxed-simd",
+                    strategy,
+                )?;
             } else {
                 println!(
                     "cargo:warning=The spec testsuite is disabled. To enable, run `git submodule \
@@ -180,6 +179,12 @@ fn write_testsuite_tests(
 /// Ignore tests that aren't supported yet.
 fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
     assert_eq!(strategy, "Cranelift");
+
+    // This is an empty file right now which the `wast` crate doesn't parse
+    if testname.contains("memory_copy1") {
+        return true;
+    }
+
     match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() {
         "s390x" => {
             // FIXME: These tests fail under qemu due to a qemu bug.
diff --git a/cranelift/codegen/src/context.rs b/cranelift/codegen/src/context.rs
index 94c5722fd..bd8ae3b64 100644
--- a/cranelift/codegen/src/context.rs
+++ b/cranelift/codegen/src/context.rs
@@ -213,7 +213,6 @@ impl Context {
     ///
     /// Returns information about the function's code and read-only data.
     pub fn compile(&mut self, isa: &dyn TargetIsa) -> CompileResult<&CompiledCode> {
-        let _tt = timing::compile();
         let stencil = self.compile_stencil(isa).map_err(|error| CompileError {
             inner: error,
             func: &self.func,
@@ -379,6 +378,8 @@ impl Context {
 
     /// Run optimizations via the egraph infrastructure.
     pub fn egraph_pass(&mut self) -> CodegenResult<()> {
+        let _tt = timing::egraph();
+
         trace!(
             "About to optimize with egraph phase:\n{}",
             self.func.display()
diff --git a/cranelift/codegen/src/timing.rs b/cranelift/codegen/src/timing.rs
index 65412c5df..50a0b66ad 100644
--- a/cranelift/codegen/src/timing.rs
+++ b/cranelift/codegen/src/timing.rs
@@ -3,8 +3,11 @@
 //! This modules provides facilities for timing the execution of individual compilation passes.
 
 use core::fmt;
-
-pub use self::details::{add_to_current, take_current, PassTimes, TimingToken};
+use std::any::Any;
+use std::boxed::Box;
+use std::cell::{Cell, RefCell};
+use std::mem;
+use std::time::{Duration, Instant};
 
 // Each pass that can be timed is predefined with the `define_passes!` macro. Each pass has a
 // snake_case name and a plain text description used when printing out the timing report.
@@ -16,22 +19,26 @@ pub use self::details::{add_to_current, take_current, PassTimes, TimingToken};
 // - A const array of pass descriptions.
 // - A public function per pass used to start the timing of that pass.
 macro_rules! define_passes {
-    { $enum:ident, $num_passes:ident, $descriptions:ident;
-      $($pass:ident: $desc:expr,)+
-    } => {
+    ($($pass:ident: $desc:expr,)+) => {
+        /// A single profiled pass.
         #[allow(non_camel_case_types)]
         #[derive(Clone, Copy, Debug, PartialEq, Eq)]
-        enum $enum { $($pass,)+ None}
+        pub enum Pass {
+            $(#[doc=$desc] $pass,)+
+            /// No active pass.
+            None,
+        }
 
-        const $num_passes: usize = $enum::None as usize;
+        /// The amount of profiled passes.
+        pub const NUM_PASSES: usize = Pass::None as usize;
 
-        const $descriptions: [&str; $num_passes] = [ $($desc),+ ];
+        const DESCRIPTIONS: [&str; NUM_PASSES] = [ $($desc),+ ];
 
         $(
             #[doc=$desc]
             #[must_use]
-            pub fn $pass() -> TimingToken {
-                details::start_pass($enum::$pass)
+            pub fn $pass() -> Box<dyn Any> {
+                start_pass(Pass::$pass)
             }
         )+
     }
@@ -39,8 +46,6 @@ macro_rules! define_passes {
 
 // Pass definitions.
 define_passes! {
-    Pass, NUM_PASSES, DESCRIPTIONS;
-
     // All these are used in other crates but defined here so they appear in the unified
     // `PassTimes` output.
     process_file: "Processing test file",
@@ -58,6 +63,7 @@ define_passes! {
     loop_analysis: "Loop analysis",
     preopt: "Pre-legalization rewriting",
     dce: "Dead code elimination",
+    egraph: "Egraph based optimizations",
     gvn: "Global value numbering",
     licm: "Loop invariant code motion",
     unreachable_code: "Remove unreachable blocks",
@@ -75,179 +81,181 @@ define_passes! {
 }
 
 impl Pass {
-    pub fn idx(self) -> usize {
+    fn idx(self) -> usize {
         self as usize
     }
+
+    /// Description of the pass.
+    pub fn description(self) -> &'static str {
+        match DESCRIPTIONS.get(self.idx()) {
+            Some(s) => s,
+            None => "<no pass>",
+        }
+    }
 }
 
 impl fmt::Display for Pass {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match DESCRIPTIONS.get(self.idx()) {
-            Some(s) => f.write_str(s),
-            None => f.write_str("<no pass>"),
-        }
+        f.write_str(self.description())
     }
 }
 
-/// Implementation details.
-///
-/// This whole module can be gated on a `cfg` feature to provide a dummy implementation for
-/// performance-sensitive builds or restricted environments. The dummy implementation must provide
-/// `TimingToken` and `PassTimes` types and `take_current`, `add_to_current`, and `start_pass` funcs
-#[cfg(feature = "std")]
-mod details {
-    use super::{Pass, DESCRIPTIONS, NUM_PASSES};
-    use std::cell::{Cell, RefCell};
-    use std::fmt;
-    use std::mem;
-    use std::time::{Duration, Instant};
-
-    /// A timing token is responsible for timing the currently running pass. Timing starts when it
-    /// is created and ends when it is dropped.
+/// A profiler.
+pub trait Profiler {
+    /// Start a profiling pass.
+    ///
+    /// Will return a token which when dropped indicates the end of the pass.
     ///
     /// Multiple passes can be active at the same time, but they must be started and stopped in a
     /// LIFO fashion.
-    pub struct TimingToken {
-        /// Start time for this pass.
-        start: Instant,
+    fn start_pass(&self, pass: Pass) -> Box<dyn Any>;
+}
 
-        // Pass being timed by this token.
-        pass: Pass,
+// Information about passes in a single thread.
+thread_local! {
+    static PROFILER: RefCell<Box<dyn Profiler>> = RefCell::new(Box::new(DefaultProfiler));
+}
 
-        // The previously active pass which will be restored when this token is dropped.
-        prev: Pass,
-    }
+/// Set the profiler for the current thread.
+///
+/// Returns the old profiler.
+pub fn set_thread_profiler(new_profiler: Box<dyn Profiler>) -> Box<dyn Profiler> {
+    PROFILER.with(|profiler| std::mem::replace(&mut *profiler.borrow_mut(), new_profiler))
+}
+
+/// Start timing `pass` as a child of the currently running pass, if any.
+///
+/// This function is called by the publicly exposed pass functions.
+fn start_pass(pass: Pass) -> Box<dyn Any> {
+    PROFILER.with(|profiler| profiler.borrow().start_pass(pass))
+}
+
+/// A timing token is responsible for timing the currently running pass. Timing starts when it
+/// is created and ends when it is dropped.
+///
+/// Multiple passes can be active at the same time, but they must be started and stopped in a
+/// LIFO fashion.
+struct DefaultTimingToken {
+    /// Start time for this pass.
+    start: Instant,
 
-    /// Accumulated timing information for a single pass.
-    #[derive(Default, Copy, Clone)]
-    struct PassTime {
-        /// Total time spent running this pass including children.
-        total: Duration,
+    // Pass being timed by this token.
+    pass: Pass,
 
-        /// Time spent running in child passes.
-        child: Duration,
+    // The previously active pass which will be restored when this token is dropped.
+    prev: Pass,
+}
+
+/// Accumulated timing information for a single pass.
+#[derive(Default, Copy, Clone)]
+struct PassTime {
+    /// Total time spent running this pass including children.
+    total: Duration,
+
+    /// Time spent running in child passes.
+    child: Duration,
+}
+
+/// Accumulated timing for all passes.
+pub struct PassTimes {
+    pass: [PassTime; NUM_PASSES],
+}
+
+impl PassTimes {
+    /// Add `other` to the timings of this `PassTimes`.
+    pub fn add(&mut self, other: &Self) {
+        for (a, b) in self.pass.iter_mut().zip(&other.pass[..]) {
+            a.total += b.total;
+            a.child += b.child;
+        }
     }
 
-    /// Accumulated timing for all passes.
-    pub struct PassTimes {
-        pass: [PassTime; NUM_PASSES],
+    /// Returns the total amount of time taken by all the passes measured.
+    pub fn total(&self) -> Duration {
+        self.pass.iter().map(|p| p.total - p.child).sum()
     }
+}
 
-    impl PassTimes {
-        /// Returns the total amount of time taken by all the passes measured.
-        pub fn total(&self) -> Duration {
-            self.pass.iter().map(|p| p.total - p.child).sum()
+impl Default for PassTimes {
+    fn default() -> Self {
+        Self {
+            pass: [Default::default(); NUM_PASSES],
         }
     }
+}
 
-    impl Default for PassTimes {
-        fn default() -> Self {
-            Self {
-                pass: [Default::default(); NUM_PASSES],
+impl fmt::Display for PassTimes {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(f, "======== ========  ==================================")?;
+        writeln!(f, "   Total     Self  Pass")?;
+        writeln!(f, "-------- --------  ----------------------------------")?;
+        for (time, desc) in self.pass.iter().zip(&DESCRIPTIONS[..]) {
+            // Omit passes that haven't run.
+            if time.total == Duration::default() {
+                continue;
             }
-        }
-    }
 
-    impl fmt::Display for PassTimes {
-        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-            writeln!(f, "======== ========  ==================================")?;
-            writeln!(f, "   Total     Self  Pass")?;
-            writeln!(f, "-------- --------  ----------------------------------")?;
-            for (time, desc) in self.pass.iter().zip(&DESCRIPTIONS[..]) {
-                // Omit passes that haven't run.
-                if time.total == Duration::default() {
-                    continue;
-                }
-
-                // Write a duration as secs.millis, trailing space.
-                fn fmtdur(mut dur: Duration, f: &mut fmt::Formatter) -> fmt::Result {
-                    // Round to nearest ms by adding 500us.
-                    dur += Duration::new(0, 500_000);
-                    let ms = dur.subsec_millis();
-                    write!(f, "{:4}.{:03} ", dur.as_secs(), ms)
-                }
-
-                fmtdur(time.total, f)?;
-                if let Some(s) = time.total.checked_sub(time.child) {
-                    fmtdur(s, f)?;
-                }
-                writeln!(f, " {}", desc)?;
+            // Write a duration as secs.millis, trailing space.
+            fn fmtdur(mut dur: Duration, f: &mut fmt::Formatter) -> fmt::Result {
+                // Round to nearest ms by adding 500us.
+                dur += Duration::new(0, 500_000);
+                let ms = dur.subsec_millis();
+                write!(f, "{:4}.{:03} ", dur.as_secs(), ms)
             }
-            writeln!(f, "======== ========  ==================================")
+
+            fmtdur(time.total, f)?;
+            if let Some(s) = time.total.checked_sub(time.child) {
+                fmtdur(s, f)?;
+            }
+            writeln!(f, " {}", desc)?;
         }
+        writeln!(f, "======== ========  ==================================")
     }
+}
 
-    // Information about passes in a single thread.
-    thread_local! {
-        static CURRENT_PASS: Cell<Pass> = const { Cell::new(Pass::None) };
-        static PASS_TIME: RefCell<PassTimes> = RefCell::new(Default::default());
-    }
+// Information about passes in a single thread.
+thread_local! {
+    static CURRENT_PASS: Cell<Pass> = const { Cell::new(Pass::None) };
+    static PASS_TIME: RefCell<PassTimes> = RefCell::new(Default::default());
+}
 
-    /// Start timing `pass` as a child of the currently running pass, if any.
-    ///
-    /// This function is called by the publicly exposed pass functions.
-    pub(super) fn start_pass(pass: Pass) -> TimingToken {
+/// The default profiler. You can get the results using [`take_current`].
+pub struct DefaultProfiler;
+
+impl Profiler for DefaultProfiler {
+    fn start_pass(&self, pass: Pass) -> Box<dyn Any> {
         let prev = CURRENT_PASS.with(|p| p.replace(pass));
         log::debug!("timing: Starting {}, (during {})", pass, prev);
-        TimingToken {
+        Box::new(DefaultTimingToken {
             start: Instant::now(),
             pass,
             prev,
-        }
-    }
-
-    /// Dropping a timing token indicated the end of the pass.
-    impl Drop for TimingToken {
-        fn drop(&mut self) {
-            let duration = self.start.elapsed();
-            log::debug!("timing: Ending {}", self.pass);
-            let old_cur = CURRENT_PASS.with(|p| p.replace(self.prev));
-            debug_assert_eq!(self.pass, old_cur, "Timing tokens dropped out of order");
-            PASS_TIME.with(|rc| {
-                let mut table = rc.borrow_mut();
-                table.pass[self.pass.idx()].total += duration;
-                if let Some(parent) = table.pass.get_mut(self.prev.idx()) {
-                    parent.child += duration;
-                }
-            })
-        }
-    }
-
-    /// Take the current accumulated pass timings and reset the timings for the current thread.
-    pub fn take_current() -> PassTimes {
-        PASS_TIME.with(|rc| mem::replace(&mut *rc.borrow_mut(), Default::default()))
+        })
     }
+}
 
-    /// Add `timings` to the accumulated timings for the current thread.
-    pub fn add_to_current(times: &PassTimes) {
+/// Dropping a timing token indicated the end of the pass.
+impl Drop for DefaultTimingToken {
+    fn drop(&mut self) {
+        let duration = self.start.elapsed();
+        log::debug!("timing: Ending {}", self.pass);
+        let old_cur = CURRENT_PASS.with(|p| p.replace(self.prev));
+        debug_assert_eq!(self.pass, old_cur, "Timing tokens dropped out of order");
         PASS_TIME.with(|rc| {
-            for (a, b) in rc.borrow_mut().pass.iter_mut().zip(&times.pass[..]) {
-                a.total += b.total;
-                a.child += b.child;
+            let mut table = rc.borrow_mut();
+            table.pass[self.pass.idx()].total += duration;
+            if let Some(parent) = table.pass.get_mut(self.prev.idx()) {
+                parent.child += duration;
             }
         })
     }
 }
 
-/// Dummy `debug` implementation
-#[cfg(not(feature = "std"))]
-mod details {
-    use super::Pass;
-    /// Dummy `TimingToken`
-    pub struct TimingToken;
-    /// Dummy `PassTimes`
-    pub struct PassTimes;
-    /// Returns dummy `PassTimes`
-    pub fn take_current() -> PassTimes {
-        PassTimes
-    }
-    /// does nothing
-    pub fn add_to_current(_times: PassTimes) {}
-
-    /// does nothing
-    pub(super) fn start_pass(_pass: Pass) -> TimingToken {
-        TimingToken
-    }
+/// Take the current accumulated pass timings and reset the timings for the current thread.
+///
+/// Only applies when [`DefaultProfiler`] is used.
+pub fn take_current() -> PassTimes {
+    PASS_TIME.with(|rc| mem::take(&mut *rc.borrow_mut()))
 }
 
 #[cfg(test)]
diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs
index 3488096ac..cb9f0ef82 100644
--- a/crates/cli-flags/src/lib.rs
+++ b/crates/cli-flags/src/lib.rs
@@ -196,6 +196,11 @@ pub struct CommonOptions {
     #[clap(long, value_name = "SIZE")]
     pub dynamic_memory_guard_size: Option<u64>,
 
+    /// Bytes to reserve at the end of linear memory for growth for dynamic
+    /// memories.
+    #[clap(long, value_name = "SIZE")]
+    pub dynamic_memory_reserved_for_growth: Option<u64>,
+
     /// Enable Cranelift's internal debug verifier (expensive)
     #[clap(long)]
     pub enable_cranelift_debug_verifier: bool,
@@ -323,6 +328,9 @@ impl CommonOptions {
         if let Some(size) = self.dynamic_memory_guard_size {
             config.dynamic_memory_guard_size(size);
         }
+        if let Some(size) = self.dynamic_memory_reserved_for_growth {
+            config.dynamic_memory_reserved_for_growth(size);
+        }
 
         // If fuel has been configured, set the `consume fuel` flag on the config.
         if self.fuel.is_some() {


Test patch:
diff --git a/cranelift/filetests/src/concurrent.rs b/cranelift/filetests/src/concurrent.rs
index a3d7543f8..c85f50fa5 100644
--- a/cranelift/filetests/src/concurrent.rs
+++ b/cranelift/filetests/src/concurrent.rs
@@ -82,14 +82,16 @@ impl ConcurrentRunner {
 
     /// Join all the worker threads.
     /// Transfer pass timings from the worker threads to the current thread.
-    pub fn join(&mut self) {
+    pub fn join(&mut self) -> timing::PassTimes {
         assert!(self.request_tx.is_none(), "must shutdown before join");
+        let mut pass_times = timing::PassTimes::default();
         for h in self.handles.drain(..) {
             match h.join() {
-                Ok(t) => timing::add_to_current(&t),
+                Ok(t) => pass_times.add(&t),
                 Err(e) => println!("worker panicked: {:?}", e),
             }
         }
+        pass_times
     }
 
     /// Add a new job to the queues.
diff --git a/cranelift/filetests/src/lib.rs b/cranelift/filetests/src/lib.rs
index 7fb7d44b3..c82e45280 100644
--- a/cranelift/filetests/src/lib.rs
+++ b/cranelift/filetests/src/lib.rs
@@ -24,10 +24,8 @@
 
 pub use crate::function_runner::TestFileCompiler;
 use crate::runner::TestRunner;
-use cranelift_codegen::timing;
 use cranelift_reader::TestCommand;
 use std::path::Path;
-use std::time;
 
 mod concurrent;
 pub mod function_runner;
@@ -63,7 +61,7 @@ mod test_wasm;
 /// Directories are scanned recursively for test cases ending in `.clif`. These test cases are
 /// executed on background threads.
 ///
-pub fn run(verbose: bool, report_times: bool, files: &[String]) -> anyhow::Result<time::Duration> {
+pub fn run(verbose: bool, report_times: bool, files: &[String]) -> anyhow::Result<()> {
     let mut runner = TestRunner::new(verbose, report_times);
 
     for path in files.iter().map(Path::new) {
@@ -89,8 +87,8 @@ pub fn run_passes(
     passes: &[String],
     target: &str,
     file: &str,
-) -> anyhow::Result<time::Duration> {
-    let mut runner = TestRunner::new(verbose, /* report_times */ false);
+) -> anyhow::Result<()> {
+    let mut runner = TestRunner::new(verbose, report_times);
 
     let path = Path::new(file);
     if path == Path::new("-") || path.is_file() {
@@ -99,11 +97,8 @@ pub fn run_passes(
         runner.push_dir(path);
     }
 
-    let result = runner.run_passes(passes, target);
-    if report_times {
-        println!("{}", timing::take_current());
-    }
-    result
+    runner.start_threads();
+    runner.run_passes(passes, target)
 }
 
 /// Create a new subcommand trait object to match `parsed.command`.
diff --git a/cranelift/filetests/src/runner.rs b/cranelift/filetests/src/runner.rs
index 5b86f020e..a1517b4dc 100644
--- a/cranelift/filetests/src/runner.rs
+++ b/cranelift/filetests/src/runner.rs
@@ -5,7 +5,6 @@
 
 use crate::concurrent::{ConcurrentRunner, Reply};
 use crate::runone;
-use cranelift_codegen::timing;
 use std::error::Error;
 use std::ffi::OsStr;
 use std::fmt::{self, Display};
@@ -298,9 +297,9 @@ impl TestRunner {
                     None => break,
                 }
             }
-            conc.join();
+            let pass_times = conc.join();
             if self.report_times {
-                println!("{}", timing::take_current());
+                println!("{}", pass_times);
             }
         }
     }
@@ -355,8 +354,7 @@ impl TestRunner {
     }
 
     /// Scan pushed directories for tests and run them.
-    pub fn run(&mut self) -> anyhow::Result<time::Duration> {
-        let started = time::Instant::now();
+    pub fn run(&mut self) -> anyhow::Result<()> {
         self.scan_dirs(IsPass::NotPass);
         self.schedule_jobs();
         self.report_slow_tests();
@@ -364,26 +362,22 @@ impl TestRunner {
 
         println!("{} tests", self.tests.len());
         match self.errors {
-            0 => Ok(started.elapsed()),
+            0 => Ok(()),
             1 => anyhow::bail!("1 failure"),
             n => anyhow::bail!("{} failures", n),
         }
     }
 
     /// Scan pushed directories for tests and run specified passes from commandline on them.
-    pub fn run_passes(
-        &mut self,
-        passes: &[String],
-        target: &str,
-    ) -> anyhow::Result<time::Duration> {
-        let started = time::Instant::now();
+    pub fn run_passes(&mut self, passes: &[String], target: &str) -> anyhow::Result<()> {
         self.scan_dirs(IsPass::Pass);
         self.schedule_pass_job(passes, target);
         self.report_slow_tests();
+        self.drain_threads();
 
         println!("{} tests", self.tests.len());
         match self.errors {
-            0 => Ok(started.elapsed()),
+            0 => Ok(()),
             1 => anyhow::bail!("1 failure"),
             n => anyhow::bail!("{} failures", n),
         }
diff --git a/tests/all/wast.rs b/tests/all/wast.rs
index 5da3e4cbd..260e3bc00 100644
--- a/tests/all/wast.rs
+++ b/tests/all/wast.rs
@@ -85,6 +85,8 @@ fn run_wast(wast: &str, strategy: Strategy, pooling: bool) -> anyhow::Result<()>
             cfg.static_memory_maximum_size(0);
         }
         cfg.dynamic_memory_reserved_for_growth(0);
+        cfg.static_memory_guard_size(0);
+        cfg.dynamic_memory_guard_size(0);
     }
 
     let _pooling_lock = if pooling {
@@ -101,7 +103,7 @@ fn run_wast(wast: &str, strategy: Strategy, pooling: bool) -> anyhow::Result<()>
         // fails to grow, the values here will need to be adjusted.
         let mut pool = PoolingAllocationConfig::default();
         pool.instance_count(450)
-            .instance_memories(2)
+            .instance_memories(if multi_memory { 9 } else { 1 })
             .instance_tables(4)
             .instance_memory_pages(805);
         cfg.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
diff --git a/tests/misc_testsuite/relaxed-simd/i16x8_relaxed_q15mulr_s.wast b/tests/misc_testsuite/relaxed-simd/i16x8_relaxed_q15mulr_s.wast
deleted file mode 100644
index 265d99160..000000000
--- a/tests/misc_testsuite/relaxed-simd/i16x8_relaxed_q15mulr_s.wast
+++ /dev/null
@@ -1,26 +0,0 @@
-;; Tests for i16x8.relaxed_q15mulr_s.
-(module
-    (func (export "i16x8.relaxed_q15mulr_s") (param v128 v128) (result v128) (i16x8.relaxed_q15mulr_s (local.get 0) (local.get 1)))
-
-    (func (export "i16x8.relaxed_q15mulr_s_cmp") (param v128 v128) (result v128)
-          (i16x8.eq
-            (i16x8.relaxed_q15mulr_s (local.get 0) (local.get 1))
-            (i16x8.relaxed_q15mulr_s (local.get 0) (local.get 1))))
-)
-
-;; INT16_MIN = -32768
-(assert_return (invoke "i16x8.relaxed_q15mulr_s"
-                       (v128.const i16x8 -32768 -32767 32767 0 0 0 0 0)
-                       (v128.const i16x8 -32768 -32768 32767 0 0 0 0 0))
-               ;; overflows, return either INT16_MIN or INT16_MAX
-               (either (v128.const i16x8 -32768 32767 32766 0 0 0 0 0)
-                       (v128.const i16x8 32767 32767 32766 0 0 0 0 0)))
-
-;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
-
-(assert_return (invoke "i16x8.relaxed_q15mulr_s_cmp"
-                       (v128.const i16x8 -32768 -32767 32767 0 0 0 0 0)
-                       (v128.const i16x8 -32768 -32768 32767 0 0 0 0 0))
-               ;; overflows, return either INT16_MIN or INT16_MAX
-               (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1))
-
diff --git a/tests/misc_testsuite/relaxed-simd/i32x4_relaxed_trunc.wast b/tests/misc_testsuite/relaxed-simd/i32x4_relaxed_trunc.wast
deleted file mode 100644
index 889542c6a..000000000
--- a/tests/misc_testsuite/relaxed-simd/i32x4_relaxed_trunc.wast
+++ /dev/null
@@ -1,123 +0,0 @@
-;; Tests for i32x4.relaxed_trunc_f32x4_s, i32x4.relaxed_trunc_f32x4_u, i32x4.relaxed_trunc_f64x2_s_zero, and i32x4.relaxed_trunc_f64x2_u_zero.
-
-(module
-    (func (export "i32x4.relaxed_trunc_f32x4_s") (param v128) (result v128) (i32x4.relaxed_trunc_f32x4_s (local.get 0)))
-    (func (export "i32x4.relaxed_trunc_f32x4_u") (param v128) (result v128) (i32x4.relaxed_trunc_f32x4_u (local.get 0)))
-    (func (export "i32x4.relaxed_trunc_f64x2_s_zero") (param v128) (result v128) (i32x4.relaxed_trunc_f64x2_s_zero (local.get 0)))
-    (func (export "i32x4.relaxed_trunc_f64x2_u_zero") (param v128) (result v128) (i32x4.relaxed_trunc_f64x2_u_zero (local.get 0)))
-
-    (func (export "i32x4.relaxed_trunc_f32x4_s_cmp") (param v128) (result v128)
-          (i32x4.eq
-            (i32x4.relaxed_trunc_f32x4_s (local.get 0))
-            (i32x4.relaxed_trunc_f32x4_s (local.get 0))))
-    (func (export "i32x4.relaxed_trunc_f32x4_u_cmp") (param v128) (result v128)
-          (i32x4.eq
-            (i32x4.relaxed_trunc_f32x4_u (local.get 0))
-            (i32x4.relaxed_trunc_f32x4_u (local.get 0))))
-    (func (export "i32x4.relaxed_trunc_f64x2_s_zero_cmp") (param v128) (result v128)
-          (i32x4.eq
-            (i32x4.relaxed_trunc_f64x2_s_zero (local.get 0))
-            (i32x4.relaxed_trunc_f64x2_s_zero (local.get 0))))
-    (func (export "i32x4.relaxed_trunc_f64x2_u_zero_cmp") (param v128) (result v128)
-          (i32x4.eq
-            (i32x4.relaxed_trunc_f64x2_u_zero (local.get 0))
-            (i32x4.relaxed_trunc_f64x2_u_zero (local.get 0))))
-)
-
-;; Test some edge cases around min/max to ensure that the instruction either
-;; saturates correctly or returns INT_MIN.
-;;
-;; Note, though, that INT_MAX itself is not tested. The value for INT_MAX is
-;; 2147483647 but that is not representable in a `f32` since it requires 31 bits
-;; when a f32 has only 24 bits available. This means that the closest integers
-;; to INT_MAX which can be represented are 2147483520 and 2147483648, meaning
-;; that the INT_MAX test case cannot be tested.
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s"
-                       ;;                INT32_MIN     <INT32_MIN        >INT32_MAX
-                       (v128.const f32x4 -2147483648.0 -2147483904.0 2.0 2147483904.0))
-               ;; out of range -> saturate or INT32_MIN
-               (either (v128.const i32x4 -2147483648 -2147483648 2 2147483647)
-                       (v128.const i32x4 -2147483648 -2147483648 2 -2147483648)))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s"
-                       (v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
-               ;; nans -> 0 or INT32_MIN
-               (either (v128.const i32x4 0 0 0 0)
-                       (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u"
-                       ;; UINT32_MIN UINT32_MIN-1 <UINT32_MAX UINT32_MAX+1
-                       (v128.const f32x4 0 -1.0 4294967040.0 4294967296.0))
-               ;; out of range -> saturate or UINT32_MAX
-               (either (v128.const i32x4 0 0 4294967040 0xffffffff)
-                       (v128.const i32x4 0 0xffffffff 4294967040 0xffffffff)))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u"
-                       (v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
-               ;; nans -> 0 or UINT32_MAX
-               (either (v128.const i32x4 0 0 0 0)
-                       (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero"
-                       (v128.const f64x2 -2147483904.0 2147483904.0))
-               ;; out of range -> saturate or INT32_MIN
-               (either (v128.const i32x4 -2147483648 2147483647 0 0)
-                       (v128.const i32x4 -2147483648 -2147483648 0 0)))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero"
-                       (v128.const f64x2 nan -nan))
-               (either (v128.const i32x4 0 0 0 0)
-                       (v128.const i32x4 0x80000000 0x80000000 0 0)))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero"
-                       (v128.const f64x2 -1.0 4294967296.0))
-               ;; out of range -> saturate or UINT32_MAX
-               (either (v128.const i32x4 0 0xffffffff 0 0)
-                       (v128.const i32x4 0xffffffff 0xffffffff 0 0)))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero"
-                       (v128.const f64x2 nan -nan))
-               (either (v128.const i32x4 0 0 0 0)
-                       (v128.const i32x4 0 0 0xffffffff 0xffffffff)))
-
-;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
-
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s_cmp"
-                       ;; INT32_MIN <INT32_MIN INT32_MAX >INT32_MAX
-                       (v128.const f32x4 -2147483648.0 -2147483904.0 2147483647.0 2147483904.0))
-               ;; out of range -> saturate or INT32_MIN
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s_cmp"
-                       (v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
-               ;; nans -> 0 or INT32_MIN
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u_cmp"
-                       ;; UINT32_MIN UINT32_MIN-1 <UINT32_MAX UINT32_MAX+1
-                       (v128.const f32x4 0 -1.0 4294967040.0 4294967296.0))
-               ;; out of range -> saturate or UINT32_MAX
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u_cmp"
-                       (v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
-               ;; nans -> 0 or UINT32_MAX
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero_cmp"
-                       (v128.const f64x2 -2147483904.0 2147483904.0))
-               ;; out of range -> saturate or INT32_MIN
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero_cmp"
-                       (v128.const f64x2 nan -nan))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero_cmp"
-                       (v128.const f64x2 -1.0 4294967296.0))
-               ;; out of range -> saturate or UINT32_MAX
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero_cmp"
-                       (v128.const f64x2 nan -nan))
-               (v128.const i32x4 -1 -1 -1 -1))
diff --git a/tests/misc_testsuite/relaxed-simd/i8x16_relaxed_swizzle.wast b/tests/misc_testsuite/relaxed-simd/i8x16_relaxed_swizzle.wast
deleted file mode 100644
index 1b20668d3..000000000
--- a/tests/misc_testsuite/relaxed-simd/i8x16_relaxed_swizzle.wast
+++ /dev/null
@@ -1,44 +0,0 @@
-;; Tests for relaxed i8x16 swizzle.
-
-(module
-    (func (export "i8x16.relaxed_swizzle") (param v128 v128) (result v128) (i8x16.relaxed_swizzle (local.get 0) (local.get 1)))
-
-    (func (export "i8x16.relaxed_swizzle_cmp") (param v128 v128) (result v128)
-          (i8x16.eq
-            (i8x16.relaxed_swizzle (local.get 0) (local.get 1))
-            (i8x16.relaxed_swizzle (local.get 0) (local.get 1))))
-)
-
-(assert_return (invoke "i8x16.relaxed_swizzle"
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15))
-               (either (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))
-
-;; out of range, returns 0 or modulo 15 if < 128
-(assert_return (invoke "i8x16.relaxed_swizzle"
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31))
-               (either (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))
-
-;; out of range, returns 0 if >= 128
-(assert_return (invoke "i8x16.relaxed_swizzle"
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 128 129 130 131 132 133 134 135 248 249 250 251 252 253 254 255))
-               (either (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))
-
-;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
-
-;; out of range, returns 0 or modulo 15 if < 128
-(assert_return (invoke "i8x16.relaxed_swizzle_cmp"
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31))
-               (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1))
-
-;; out of range, returns 0 if >= 128
-(assert_return (invoke "i8x16.relaxed_swizzle_cmp"
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 128 129 130 131 132 133 134 135 248 249 250 251 252 253 254 255))
-               (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1))
diff --git a/tests/misc_testsuite/relaxed-simd/relaxed_dot_product.wast b/tests/misc_testsuite/relaxed-simd/relaxed_dot_product.wast
deleted file mode 100644
index 41dee0afc..000000000
--- a/tests/misc_testsuite/relaxed-simd/relaxed_dot_product.wast
+++ /dev/null
@@ -1,106 +0,0 @@
-;; Tests for relaxed dot products.
-
-(module
-    (func (export "i16x8.relaxed_dot_i8x16_i7x16_s") (param v128 v128) (result v128) (i16x8.relaxed_dot_i8x16_i7x16_s (local.get 0) (local.get 1)))
-    (func (export "i32x4.relaxed_dot_i8x16_i7x16_add_s") (param v128 v128 v128) (result v128) (i32x4.relaxed_dot_i8x16_i7x16_add_s (local.get 0) (local.get 1) (local.get 2)))
-
-    (func (export "i16x8.relaxed_dot_i8x16_i7x16_s_cmp") (param v128 v128) (result v128)
-          (i16x8.eq
-            (i16x8.relaxed_dot_i8x16_i7x16_s (local.get 0) (local.get 1))
-            (i16x8.relaxed_dot_i8x16_i7x16_s (local.get 0) (local.get 1))))
-    (func (export "i32x4.relaxed_dot_i8x16_i7x16_add_s_cmp") (param v128 v128 v128) (result v128)
-          (i16x8.eq
-            (i32x4.relaxed_dot_i8x16_i7x16_add_s (local.get 0) (local.get 1) (local.get 2))
-            (i32x4.relaxed_dot_i8x16_i7x16_add_s (local.get 0) (local.get 1) (local.get 2))))
-)
-
-;; Simple values to ensure things are functional.
-(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s"
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15))
-               (v128.const i16x8 1 13 41 85 145 221 313 421))
-
-;; Test max and min i8 values;
-(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s"
-                       (v128.const i8x16 -128 -128 127 127 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 127 127 127 127 0 0 0 0 0 0 0 0 0 0 0 0))
-               (v128.const i16x8 -32512 32258 0 0 0 0 0 0))
-
-;; signed * unsigned   : -128 *  129 * 2 = -33,024 saturated to -32,768
-;; signed * signed     : -128 * -127 * 2 =  32,512
-;; unsigned * unsigned :  128 *  129 * 2 =  33,024
-(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s"
-                       (v128.const i8x16 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0 0 0))
-               (either
-                 (v128.const i16x8 -32768 0 0 0 0 0 0 0)
-                 (v128.const i16x8  32512 0 0 0 0 0 0 0)
-                 (v128.const i16x8  33024 0 0 0 0 0 0 0)))
-
-;; Simple values to ensure things are functional.
-(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s"
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i32x4 0 1 2 3))
-               ;; intermediate result is [14, 126, 366, 734]
-               (v128.const i32x4 14 127 368 737))
-
-;; Test max and min i8 values;
-(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s"
-                       (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 127 127 127 127 127 127 127 127 0 0 0 0 0 0 0 0)
-                       (v128.const i32x4 1 2 3 4))
-               ;; intermediate result is [-65024, 64516, 0, 0]
-               (v128.const i32x4 -65023 64518 3 4))
-
-;; signed * unsigned   : -128 *  129 * 4 = -66,048 (+ 1) VPDPBUSD AVX2-VNNI or AVX512-VNNI
-;; signed * unsigned with intermediate saturation :
-;;   (-128 * 129) + (-128 * 129) = -33024 saturated to -32768 (PMADDUBSW)
-;;   -32768 + -32768 = -65536 (+ 1)
-;; signed * signed     : -128 * -127 * 4 =  65,024 (+ 1)
-;; unsigned * unsigned :  128 *  129 * 2 =  66,048 (+ 1)
-(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s"
-                       (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 -127 -127 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i32x4 1 2 3 4))
-               (either
-                 (v128.const i32x4 -66047 2 3 4)
-                 (v128.const i32x4 -65535 2 3 4)
-                 (v128.const i32x4  65025 2 3 4)
-                 (v128.const i32x4  66049 2 3 4)))
-
-;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
-
-;; Test max and min i8 values;
-(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s_cmp"
-                       (v128.const i8x16 -128 -128 127 127 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 127 127 127 127 0 0 0 0 0 0 0 0 0 0 0 0))
-               (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1))
-
-;; Test max and min i8 values;
-(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s_cmp"
-                       (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 127 127 127 127 127 127 127 127 0 0 0 0 0 0 0 0)
-                       (v128.const i32x4 1 2 3 4))
-               ;; intermediate result is [-65024, 64516, 0, 0]
-               (v128.const i32x4 -1 -1 -1 -1))
-
-;; signed * unsigned   : -128 *  129 * 2 = -33,024 saturated to -32,768
-;; signed * signed     : -128 * -127 * 2 =  32,512
-;; unsigned * unsigned :  128 *  129 * 2 =  33,024
-(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s_cmp"
-                       (v128.const i8x16 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0 0 0))
-               (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1))
-
-;; signed * unsigned   : -128 *  129 * 4 = -66,048 (+ 1) VPDPBUSD AVX2-VNNI or AVX512-VNNI
-;; signed * unsigned with intermediate saturation :
-;;   (-128 * 129) + (-128 * 129) = -33024 saturated to -32768 (PMADDUBSW)
-;;   -32768 + -32768 = -65536 (+ 1)
-;; signed * signed     : -128 * -127 * 4 =  65,024 (+ 1)
-;; unsigned * unsigned :  128 *  129 * 2 =  66,048 (+ 1)
-(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s_cmp"
-                       (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 -127 -127 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i32x4 1 2 3 4))
-               (v128.const i32x4 -1 -1 -1 -1))
diff --git a/tests/misc_testsuite/relaxed-simd/relaxed_laneselect.wast b/tests/misc_testsuite/relaxed-simd/relaxed_laneselect.wast
deleted file mode 100644
index 4ea6eb818..000000000
--- a/tests/misc_testsuite/relaxed-simd/relaxed_laneselect.wast
+++ /dev/null
@@ -1,92 +0,0 @@
-;; Tests for i8x16.relaxed_laneselect, i16x8.relaxed_laneselect, i32x4.relaxed_laneselect, and i64x2.relaxed_laneselect.
-
-(module
-    (func (export "i8x16.relaxed_laneselect") (param v128 v128 v128) (result v128) (i8x16.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))
-    (func (export "i16x8.relaxed_laneselect") (param v128 v128 v128) (result v128) (i16x8.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))
-    (func (export "i32x4.relaxed_laneselect") (param v128 v128 v128) (result v128) (i32x4.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))
-    (func (export "i64x2.relaxed_laneselect") (param v128 v128 v128) (result v128) (i64x2.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))
-
-    (func (export "i8x16.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128)
-          (i8x16.eq
-            (i8x16.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))
-            (i8x16.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))))
-    (func (export "i16x8.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128)
-          (i16x8.eq
-            (i16x8.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))
-            (i16x8.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))))
-    (func (export "i32x4.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128)
-          (i32x4.eq
-            (i32x4.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))
-            (i32x4.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))))
-    (func (export "i64x2.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128)
-          (i64x2.eq
-            (i64x2.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))
-            (i64x2.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))))
-)
-
-(assert_return (invoke "i8x16.relaxed_laneselect"
-                       (v128.const i8x16 0    1  0x12 0x12 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 16   17 0x34 0x34 20 21 22 23 24 25 26 27 28 29 30 31)
-                       (v128.const i8x16 0xff 0  0xf0 0x0f 0 0 0 0 0 0 0 0 0 0 0 0))
-               (either (v128.const i8x16 0    17 0x14 0x32 20 21 22 23 24 25 26 27 28 29 30 31)
-                       (v128.const i8x16 0    17 0x12 0x34 20 21 22 23 24 25 26 27 28 29 30 31)))
-
-(assert_return (invoke "i16x8.relaxed_laneselect"
-                       (v128.const i16x8 0      1 0x1234 0x1234 4 5 6 7)
-                       (v128.const i16x8 8      9 0x5678 0x5678 12 13 14 15)
-                       (v128.const i16x8 0xffff 0 0xff00 0x00ff 0 0 0 0))
-               (either (v128.const i16x8 0      9 0x1278 0x5634 12 13 14 15)
-                       (v128.const i16x8 0      9 0x1234 0x5678 12 13 14 15)))
-
-(assert_return (invoke "i32x4.relaxed_laneselect"
-                       (v128.const i32x4 0          1 0x12341234 0x12341234)
-                       (v128.const i32x4 4          5 0x56785678 0x56785678)
-                       (v128.const i32x4 0xffffffff 0 0xffff0000 0x0000ffff))
-               (either (v128.const i32x4 0          5 0x12345678 0x56781234)
-                       (v128.const i32x4 0          5 0x12341234 0x56785678)))
-
-(assert_return (invoke "i64x2.relaxed_laneselect"
-                       (v128.const i64x2 0                  1)
-                       (v128.const i64x2 2                  3)
-                       (v128.const i64x2 0xffffffffffffffff 0))
-               (either (v128.const i64x2 0                  3)
-                       (v128.const i64x2 0                  3)))
-
-(assert_return (invoke "i64x2.relaxed_laneselect"
-                       (v128.const i64x2 0x1234123412341234 0x1234123412341234)
-                       (v128.const i64x2 0x5678567856785678 0x5678567856785678)
-                       (v128.const i64x2 0xffffffff00000000 0x00000000ffffffff))
-               (either (v128.const i64x2 0x1234123456785678 0x5678567812341234)
-                       (v128.const i64x2 0x1234123412341234 0x5678567856785678)))
-
-;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
-
-(assert_return (invoke "i8x16.relaxed_laneselect_cmp"
-                       (v128.const i8x16 0    1  0x12 0x12 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 16   17 0x34 0x34 20 21 22 23 24 25 26 27 28 29 30 31)
-                       (v128.const i8x16 0xff 0  0xf0 0x0f 0 0 0 0 0 0 0 0 0 0 0 0))
-               (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1))
-
-(assert_return (invoke "i16x8.relaxed_laneselect_cmp"
-                       (v128.const i16x8 0      1 0x1234 0x1234 4 5 6 7)
-                       (v128.const i16x8 8      9 0x5678 0x5678 12 13 14 15)
-                       (v128.const i16x8 0xffff 0 0xff00 0x00ff 0 0 0 0))
-               (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_laneselect_cmp"
-                       (v128.const i32x4 0          1 0x12341234 0x12341234)
-                       (v128.const i32x4 4          5 0x56785678 0x56785678)
-                       (v128.const i32x4 0xffffffff 0 0xffff0000 0x0000ffff))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i64x2.relaxed_laneselect_cmp"
-                       (v128.const i64x2 0                  1)
-                       (v128.const i64x2 2                  3)
-                       (v128.const i64x2 0xffffffffffffffff 0))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "i64x2.relaxed_laneselect_cmp"
-                       (v128.const i64x2 0x1234123412341234 0x1234123412341234)
-                       (v128.const i64x2 0x5678567856785678 0x5678567856785678)
-                       (v128.const i64x2 0xffffffff00000000 0x00000000ffffffff))
-               (v128.const i64x2 -1 -1))
diff --git a/tests/misc_testsuite/relaxed-simd/relaxed_madd_nmadd.wast b/tests/misc_testsuite/relaxed-simd/relaxed_madd_nmadd.wast
deleted file mode 100644
index 0e0e0c2bf..000000000
--- a/tests/misc_testsuite/relaxed-simd/relaxed_madd_nmadd.wast
+++ /dev/null
@@ -1,190 +0,0 @@
-;; Tests for f32x4.relaxed_madd, f32x4.relaxed_nmadd, f64x2.relaxed_madd, and f64x2.relaxed_nmadd.
-
-(module
-    (func (export "f32x4.relaxed_madd") (param v128 v128 v128) (result v128) (f32x4.relaxed_madd (local.get 0) (local.get 1) (local.get 2)))
-    (func (export "f32x4.relaxed_nmadd") (param v128 v128 v128) (result v128) (f32x4.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2)))
-    (func (export "f64x2.relaxed_nmadd") (param v128 v128 v128) (result v128) (f64x2.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2)))
-    (func (export "f64x2.relaxed_madd") (param v128 v128 v128) (result v128) (f64x2.relaxed_madd (local.get 0) (local.get 1) (local.get 2)))
-
-    (func (export "f32x4.relaxed_madd_cmp") (param v128 v128 v128) (result v128)
-          (f32x4.eq
-            (f32x4.relaxed_madd (local.get 0) (local.get 1) (local.get 2))
-            (f32x4.relaxed_madd (local.get 0) (local.get 1) (local.get 2))))
-    (func (export "f32x4.relaxed_nmadd_cmp") (param v128 v128 v128) (result v128)
-          (f32x4.eq
-            (f32x4.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2))
-            (f32x4.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2))))
-    (func (export "f64x2.relaxed_nmadd_cmp") (param v128 v128 v128) (result v128)
-          (f64x2.eq
-            (f64x2.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2))
-            (f64x2.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2))))
-    (func (export "f64x2.relaxed_madd_cmp") (param v128 v128 v128) (result v128)
-          (f64x2.eq
-            (f64x2.relaxed_madd (local.get 0) (local.get 1) (local.get 2))
-            (f64x2.relaxed_madd (local.get 0) (local.get 1) (local.get 2))))
-)
-
-
-;; FLT_MAX == 0x1.fffffep+127
-;; FLT_MAX * 2 - FLT_MAX ==
-;;   FLT_MAX (if fma)
-;;   0       (if no fma)
-;; from https://www.vinc17.net/software/fma-tests.c
-(assert_return (invoke "f32x4.relaxed_madd"
-                       (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 )
-                       (v128.const f32x4 2.0 2.0 2.0 2.0)
-                       (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127))
-               (either (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)
-                       (v128.const f32x4 inf inf inf inf)))
-
-;; Special values for float:
-;; x            = 0x1.000004p+0 (1 + 2^-22)
-;; y            = 0x1.0002p+0   (1 + 2^-15)
-;; z            = -(1.0 + 0x0.0002p+0 + 0x0.000004p+0)
-;;              = -0x1.000204p+0
-;; x.y          = 1.0 + 0x0.0002p+0 + 0x0.000004p+0 + 0x1p-37 (round bit)
-;; x.y+z        = 0 (2 roundings)
-;; fma(x, y, z) = (0x1p-37) 2^-37
-;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information
-(assert_return (invoke "f32x4.relaxed_madd"
-                       (v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0)
-                       (v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0)
-                       (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
-               (either (v128.const f32x4 0x1p-37 0x1p-37 0x1p-37 0x1p-37)
-                       (v128.const f32x4 0 0 0 0)))
-;; fnma tests with negated x, same answers are expected.
-(assert_return (invoke "f32x4.relaxed_nmadd"
-                       (v128.const f32x4 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0)
-                       (v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0)
-                       (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
-               (either (v128.const f32x4 0x1p-37 0x1p-37 0x1p-37 0x1p-37)
-                       (v128.const f32x4 0 0 0 0)))
-;; fnma tests with negated y, same answers are expected.
-(assert_return (invoke "f32x4.relaxed_nmadd"
-                       (v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0)
-                       (v128.const f32x4 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0)
-                       (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
-               (either (v128.const f32x4 0x1p-37 0x1p-37 0x1p-37 0x1p-37)
-                       (v128.const f32x4 0 0 0 0)))
-
-;; DBL_MAX = 0x1.fffffffffffffp+1023
-;; DLB_MAX * 2 - DLB_MAX ==
-;;   DLB_MAX (if fma)
-;;   0       (if no fma)
-;; form https://www.vinc17.net/software/fma-tests.c
-;; from https://www.vinc17.net/software/fma-tests.c
-(assert_return (invoke "f64x2.relaxed_madd"
-                       (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)
-                       (v128.const f64x2 2.0 2.0)
-                       (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023))
-               (either (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)
-                       (v128.const f64x2 inf inf)))
-
-;; Special values for double:
-;; x            = 0x1.00000004p+0 (1 + 2^-30)
-;; y            = 0x1.000002p+0   (1 + 2^-23)
-;; z            = -(1.0 + 0x0.000002p+0 + 0x0.00000004p+0)
-;;              = -0x1.00000204p+0
-;; x.y          = 1.0 + 0x0.000002p+0 + 0x0.00000004p+0 + 0x1p-53 (round bit)
-;; x.y+z        = 0 (2 roundings)
-;; fma(x, y, z) = 0x1p-53
-;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information
-(assert_return (invoke "f64x2.relaxed_madd"
-                       (v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0)
-                       (v128.const f64x2 0x1.000002p+0 0x1.000002p+0)
-                       (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
-               (either (v128.const f64x2 0x1p-53 0x1p-53)
-                       (v128.const f64x2 0 0)))
-;; fnma tests with negated x, same answers are expected.
-(assert_return (invoke "f64x2.relaxed_nmadd"
-                       (v128.const f64x2 -0x1.00000004p+0 -0x1.00000004p+0)
-                       (v128.const f64x2 0x1.000002p+0 0x1.000002p+0)
-                       (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
-               (either (v128.const f64x2 0x1p-53 0x1p-53)
-                       (v128.const f64x2 0 0)))
-;; fnma tests with negated y, same answers are expected.
-(assert_return (invoke "f64x2.relaxed_nmadd"
-                       (v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0)
-                       (v128.const f64x2 -0x1.000002p+0 -0x1.000002p+0)
-                       (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
-               (either (v128.const f64x2 0x1p-53 0x1p-53)
-                       (v128.const f64x2 0 0)))
-
-;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
-
-;; FLT_MAX == 0x1.fffffep+127
-;; FLT_MAX * 2 - FLT_MAX ==
-;;   FLT_MAX (if fma)
-;;   0       (if no fma)
-;; from https://www.vinc17.net/software/fma-tests.c
-(assert_return (invoke "f32x4.relaxed_madd_cmp"
-                       (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 )
-                       (v128.const f32x4 2.0 2.0 2.0 2.0)
-                       (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-;; Special values for float:
-;; x            = 0x1.000004p+0 (1 + 2^-22)
-;; y            = 0x1.0002p+0   (1 + 2^-15)
-;; z            = -(1.0 + 0x0.0002p+0 + 0x0.000004p+0)
-;;              = -0x1.000204p+0
-;; x.y          = 1.0 + 0x0.0002p+0 + 0x0.000004p+0 + 0x1p-37 (round bit)
-;; x.y+z        = 0 (2 roundings)
-;; fma(x, y, z) = (0x1p-37) 2^-37
-;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information
-(assert_return (invoke "f32x4.relaxed_madd_cmp"
-                       (v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0)
-                       (v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0)
-                       (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
-               (v128.const i32x4 -1 -1 -1 -1))
-;; fnma tests with negated x, same answers are expected.
-(assert_return (invoke "f32x4.relaxed_nmadd_cmp"
-                       (v128.const f32x4 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0)
-                       (v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0)
-                       (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
-               (v128.const i32x4 -1 -1 -1 -1))
-;; fnma tests with negated y, same answers are expected.
-(assert_return (invoke "f32x4.relaxed_nmadd_cmp"
-                       (v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0)
-                       (v128.const f32x4 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0)
-                       (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-;; DBL_MAX = 0x1.fffffffffffffp+1023
-;; DLB_MAX * 2 - DLB_MAX ==
-;;   DLB_MAX (if fma)
-;;   0       (if no fma)
-;; form https://www.vinc17.net/software/fma-tests.c
-;; from https://www.vinc17.net/software/fma-tests.c
-(assert_return (invoke "f64x2.relaxed_madd_cmp"
-                       (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)
-                       (v128.const f64x2 2.0 2.0)
-                       (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023))
-               (v128.const i64x2 -1 -1))
-
-;; Special values for double:
-;; x            = 0x1.00000004p+0 (1 + 2^-30)
-;; y            = 0x1.000002p+0   (1 + 2^-23)
-;; z            = -(1.0 + 0x0.000002p+0 + 0x0.00000004p+0)
-;;              = -0x1.00000204p+0
-;; x.y          = 1.0 + 0x0.000002p+0 + 0x0.00000004p+0 + 0x1p-53 (round bit)
-;; x.y+z        = 0 (2 roundings)
-;; fma(x, y, z) = 0x1p-53
-;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information
-(assert_return (invoke "f64x2.relaxed_madd_cmp"
-                       (v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0)
-                       (v128.const f64x2 0x1.000002p+0 0x1.000002p+0)
-                       (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
-               (v128.const i64x2 -1 -1))
-;; fnma tests with negated x, same answers are expected.
-(assert_return (invoke "f64x2.relaxed_nmadd_cmp"
-                       (v128.const f64x2 -0x1.00000004p+0 -0x1.00000004p+0)
-                       (v128.const f64x2 0x1.000002p+0 0x1.000002p+0)
-                       (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
-               (v128.const i64x2 -1 -1))
-;; fnma tests with negated y, same answers are expected.
-(assert_return (invoke "f64x2.relaxed_nmadd_cmp"
-                       (v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0)
-                       (v128.const f64x2 -0x1.000002p+0 -0x1.000002p+0)
-                       (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
-               (v128.const i64x2 -1 -1))
diff --git a/tests/misc_testsuite/relaxed-simd/relaxed_min_max.wast b/tests/misc_testsuite/relaxed-simd/relaxed_min_max.wast
deleted file mode 100644
index d8a04ba4d..000000000
--- a/tests/misc_testsuite/relaxed-simd/relaxed_min_max.wast
+++ /dev/null
@@ -1,183 +0,0 @@
-;; Tests for f32x4.min, f32x4.max, f64x2.min, and f64x2.max.
-
-(module
-    (func (export "f32x4.relaxed_min") (param v128 v128) (result v128) (f32x4.relaxed_min (local.get 0) (local.get 1)))
-    (func (export "f32x4.relaxed_max") (param v128 v128) (result v128) (f32x4.relaxed_max (local.get 0) (local.get 1)))
-    (func (export "f64x2.relaxed_min") (param v128 v128) (result v128) (f64x2.relaxed_min (local.get 0) (local.get 1)))
-    (func (export "f64x2.relaxed_max") (param v128 v128) (result v128) (f64x2.relaxed_max (local.get 0) (local.get 1)))
-
-    (func (export "f32x4.relaxed_min_cmp") (param v128 v128) (result v128)
-          (i32x4.eq
-            (f32x4.relaxed_min (local.get 0) (local.get 1))
-            (f32x4.relaxed_min (local.get 0) (local.get 1))))
-    (func (export "f32x4.relaxed_max_cmp") (param v128 v128) (result v128)
-          (i32x4.eq
-            (f32x4.relaxed_max (local.get 0) (local.get 1))
-            (f32x4.relaxed_max (local.get 0) (local.get 1))))
-    (func (export "f64x2.relaxed_min_cmp") (param v128 v128) (result v128)
-          (i64x2.eq
-            (f64x2.relaxed_min (local.get 0) (local.get 1))
-            (f64x2.relaxed_min (local.get 0) (local.get 1))))
-    (func (export "f64x2.relaxed_max_cmp") (param v128 v128) (result v128)
-          (i64x2.eq
-            (f64x2.relaxed_max (local.get 0) (local.get 1))
-            (f64x2.relaxed_max (local.get 0) (local.get 1))))
-)
-
-(assert_return (invoke "f32x4.relaxed_min"
-                       (v128.const f32x4 -nan nan 0 0)
-                       (v128.const f32x4 0 0 -nan nan))
-               (either (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)
-                       (v128.const f32x4 nan:canonical nan:canonical 0 0)
-                       (v128.const f32x4 0 0 nan:canonical nan:canonical)
-                       (v128.const f32x4 0 0 0 0)))
-
-(assert_return (invoke "f32x4.relaxed_min"
-                       (v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 +0.0 +0.0 -0.0))
-               (either (v128.const f32x4 -0.0 -0.0 +0.0 -0.0)
-                       (v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 +0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 -0.0 +0.0 -0.0)))
-
-(assert_return (invoke "f32x4.relaxed_max"
-                       (v128.const f32x4 -nan nan 0 0)
-                       (v128.const f32x4 0 0 -nan nan))
-               (either (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)
-                       (v128.const f32x4 nan:canonical nan:canonical 0 0)
-                       (v128.const f32x4 0 0 nan:canonical nan:canonical)
-                       (v128.const f32x4 0 0 0 0)))
-
-(assert_return (invoke "f32x4.relaxed_max"
-                       (v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 +0.0 +0.0 -0.0))
-               (either (v128.const f32x4 +0.0 +0.0 +0.0 -0.0)
-                       (v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 +0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 -0.0 +0.0 -0.0)))
-
-(assert_return (invoke "f64x2.relaxed_min"
-                       (v128.const f64x2 -nan nan)
-                       (v128.const f64x2 0 0))
-               (either (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 0 0)))
-
-(assert_return (invoke "f64x2.relaxed_min"
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 -nan nan))
-               (either (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 0 0)))
-
-(assert_return (invoke "f64x2.relaxed_min"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 -0.0 +0.0))
-               (either (v128.const f64x2 -0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 -0.0 +0.0)
-                       (v128.const f64x2 -0.0 -0.0)))
-
-(assert_return (invoke "f64x2.relaxed_min"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0))
-               (either (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0)))
-
-(assert_return (invoke "f64x2.relaxed_max"
-                       (v128.const f64x2 -nan nan)
-                       (v128.const f64x2 0 0))
-               (either (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 0 0)))
-
-(assert_return (invoke "f64x2.relaxed_max"
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 -nan nan))
-               (either (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 0 0)))
-
-(assert_return (invoke "f64x2.relaxed_max"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 -0.0 +0.0))
-               (either (v128.const f64x2 +0.0 +0.0)
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 -0.0 +0.0)
-                       (v128.const f64x2 -0.0 -0.0)))
-
-(assert_return (invoke "f64x2.relaxed_max"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0))
-               (either (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0)))
-
-;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
-
-(assert_return (invoke "f32x4.relaxed_min_cmp"
-                       (v128.const f32x4 -nan nan 0 0)
-                       (v128.const f32x4 0 0 -nan nan))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "f32x4.relaxed_min_cmp"
-                       (v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 +0.0 +0.0 -0.0))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "f32x4.relaxed_max_cmp"
-                       (v128.const f32x4 -nan nan 0 0)
-                       (v128.const f32x4 0 0 -nan nan))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "f32x4.relaxed_max_cmp"
-                       (v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 +0.0 +0.0 -0.0))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_min_cmp"
-                       (v128.const f64x2 -nan nan)
-                       (v128.const f64x2 0 0))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_min_cmp"
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 -nan nan))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_min_cmp"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 -0.0 +0.0))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_min_cmp"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_max_cmp"
-                       (v128.const f64x2 -nan nan)
-                       (v128.const f64x2 0 0))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_max_cmp"
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 -nan nan))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_max_cmp"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 -0.0 +0.0))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_max_cmp"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0))
-               (v128.const i64x2 -1 -1))
diff --git a/tests/spec_testsuite b/tests/spec_testsuite
index 4f77306bb..7ef86ddee 160000
--- a/tests/spec_testsuite
+++ b/tests/spec_testsuite
@@ -1,1 +1,1 @@
-Subproject commit 4f77306bb63151631d84f58dedf67958eb9911b9
+Subproject commit 7ef86ddeed81458f9031a49a40b3a3f99c1c6a8a