Repository: bytecodealliance/wasmtime
Pull request #8129 (2024-03-14 03:04:31)

Add a `disas` test suite for Wasmtime
This commit adds a suite of tests at `tests/disas/*.wat` which is intended to replace the tests in `cranelift/filetests/filetests/wasm`. Tests are configured differently than before using Wasmtime CLI flags rather than a custom TOML-based configuration scheme. Otherwise though the same shape of tests is supported.

This commit migrates a small handful of tests as a showcase and bulk migration is left for a follow-up.

<!--
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
-->


alexcrichton: This was inspired by https://github.com/bytecodealliance/wasmtime/pull/8125 and the discussion there

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 607126412..afd7eada2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3471,6 +3471,9 @@ dependencies = [
  "clap",
  "component-macro-test",
  "component-test-util",
+ "cranelift-codegen",
+ "cranelift-filetests",
+ "cranelift-reader",
  "criterion",
  "env_logger",
  "filecheck",
diff --git a/Cargo.toml b/Cargo.toml
index c7431f823..563ea9a68 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -65,7 +65,7 @@ rustix = { workspace = true, features = ["mm", "param", "process"] }
 
 [dev-dependencies]
 # depend again on wasmtime to activate its default features for tests
-wasmtime = { workspace = true, features = ['component-model', 'async', 'default', 'winch', 'debug-builtins'] }
+wasmtime = { workspace = true, features = ['component-model', 'async', 'default', 'winch', 'debug-builtins', 'all-arch'] }
 env_logger = { workspace = true }
 log = { workspace = true }
 filecheck = { workspace = true }
@@ -91,6 +91,9 @@ walkdir = { workspace = true }
 test-programs-artifacts = { workspace = true }
 bytesize = "1.3.0"
 wit-component = { workspace = true }
+cranelift-filetests = { workspace = true }
+cranelift-codegen = { workspace = true }
+cranelift-reader = { workspace = true }
 
 [target.'cfg(windows)'.dev-dependencies]
 windows-sys = { workspace = true, features = ["Win32_System_Memory"] }
@@ -391,6 +394,10 @@ run = ["dep:wasmtime-wasi", "wasmtime/runtime", "wasmtime-runtime", "dep:listenf
 name = "host_segfault"
 harness = false
 
+[[test]]
+name = "disas"
+harness = false
+
 [[example]]
 name = "tokio"
 required-features = ["wasi-common/tokio"]
diff --git a/cranelift/codegen/meta/src/shared/entities.rs b/cranelift/codegen/meta/src/shared/entities.rs
index 374e61f41..3df9ecaa1 100644
--- a/cranelift/codegen/meta/src/shared/entities.rs
+++ b/cranelift/codegen/meta/src/shared/entities.rs
@@ -43,9 +43,6 @@ pub(crate) struct EntityRefs {
     /// A reference to a jump table declared in the function preamble.
     pub(crate) jump_table: OperandKind,
 
-    /// A reference to a table declared in the function preamble.
-    pub(crate) table: OperandKind,
-
     /// A variable-sized list of value operands. Use for Block and function call arguments.
     pub(crate) varargs: OperandKind,
 }
@@ -87,8 +84,6 @@ impl EntityRefs {
 
             jump_table: new("table", "ir::JumpTable", "A jump table."),
 
-            table: new("table", "ir::Table", "A table."),
-
             varargs: OperandKind::new(
                 "",
                 "&[Value]",
diff --git a/cranelift/codegen/meta/src/shared/formats.rs b/cranelift/codegen/meta/src/shared/formats.rs
index 35ea2e7f8..b3ee5b2c2 100644
--- a/cranelift/codegen/meta/src/shared/formats.rs
+++ b/cranelift/codegen/meta/src/shared/formats.rs
@@ -30,7 +30,6 @@ pub(crate) struct Formats {
     pub(crate) dynamic_stack_store: Rc<InstructionFormat>,
     pub(crate) store: Rc<InstructionFormat>,
     pub(crate) store_no_offset: Rc<InstructionFormat>,
-    pub(crate) table_addr: Rc<InstructionFormat>,
     pub(crate) ternary: Rc<InstructionFormat>,
     pub(crate) ternary_imm8: Rc<InstructionFormat>,
     pub(crate) trap: Rc<InstructionFormat>,
@@ -192,13 +191,6 @@ impl Formats {
                 .imm(&entities.dynamic_stack_slot)
                 .build(),
 
-            // Accessing a WebAssembly table.
-            table_addr: Builder::new("TableAddr")
-                .imm(&entities.table)
-                .value()
-                .imm(&imm.offset32)
-                .build(),
-
             trap: Builder::new("Trap").imm(&imm.trapcode).build(),
 
             cond_trap: Builder::new("CondTrap").value().imm(&imm.trapcode).build(),
diff --git a/cranelift/codegen/meta/src/shared/instructions.rs b/cranelift/codegen/meta/src/shared/instructions.rs
index 6727d9c8c..5e75ac3a4 100644
--- a/cranelift/codegen/meta/src/shared/instructions.rs
+++ b/cranelift/codegen/meta/src/shared/instructions.rs
@@ -1299,38 +1299,6 @@ pub(crate) fn define(
         .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 
-    let TableOffset = &TypeVar::new(
-        "TableOffset",
-        "An unsigned table offset",
-        TypeSetBuilder::new().ints(32..64).build(),
-    );
-
-    ig.push(
-        Inst::new(
-            "table_addr",
-            r#"
-        Bounds check and compute absolute address of a table entry.
-
-        Verify that the offset ``p`` is in bounds for the table T, and generate
-        an absolute address that is safe to dereference.
-
-        ``Offset`` must be less than the size of a table element.
-
-        1. If ``p`` is not greater than the table bound, return an absolute
-           address corresponding to a byte offset of ``p`` from the table's
-           base address.
-        2. If ``p`` is greater than the table bound, generate a trap.
-        "#,
-            &formats.table_addr,
-        )
-        .operands_in(vec![
-            Operand::new("T", &entities.table),
-            Operand::new("p", TableOffset),
-            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from element address"),
-        ])
-        .operands_out(vec![Operand::new("addr", iAddr)]),
-    );
-
     ig.push(
         Inst::new(
             "iconst",
diff --git a/cranelift/codegen/src/ir/entities.rs b/cranelift/codegen/src/ir/entities.rs
index 993860101..428468252 100644
--- a/cranelift/codegen/src/ir/entities.rs
+++ b/cranelift/codegen/src/ir/entities.rs
@@ -388,33 +388,6 @@ impl SigRef {
     }
 }
 
-/// An opaque reference to a [WebAssembly
-/// table](https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format#WebAssembly_tables).
-///
-/// `Table`s are used to store a list of function references.
-/// They can be created with [`FuncEnvironment::make_table`](https://docs.rs/cranelift-wasm/*/cranelift_wasm/trait.FuncEnvironment.html#tymethod.make_table).
-/// They can be used with
-/// [`FuncEnvironment::translate_call_indirect`](https://docs.rs/cranelift-wasm/*/cranelift_wasm/trait.FuncEnvironment.html#tymethod.translate_call_indirect).
-///
-/// While the order is stable, it is arbitrary.
-#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
-#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
-pub struct Table(u32);
-entity_impl!(Table, "table");
-
-impl Table {
-    /// Create a new table reference from its number.
-    ///
-    /// This method is for use by the parser.
-    pub fn with_number(n: u32) -> Option<Self> {
-        if n < u32::MAX {
-            Some(Self(n))
-        } else {
-            None
-        }
-    }
-}
-
 /// An opaque reference to any of the entities defined in this module that can appear in CLIF IR.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
@@ -445,8 +418,6 @@ pub enum AnyEntity {
     FuncRef(FuncRef),
     /// A function call signature.
     SigRef(SigRef),
-    /// A table.
-    Table(Table),
     /// A function's stack limit
     StackLimit,
 }
@@ -467,7 +438,6 @@ impl fmt::Display for AnyEntity {
             Self::Constant(r) => r.fmt(f),
             Self::FuncRef(r) => r.fmt(f),
             Self::SigRef(r) => r.fmt(f),
-            Self::Table(r) => r.fmt(f),
             Self::StackLimit => write!(f, "stack_limit"),
         }
     }
@@ -551,12 +521,6 @@ impl From<SigRef> for AnyEntity {
     }
 }
 
-impl From<Table> for AnyEntity {
-    fn from(r: Table) -> Self {
-        Self::Table(r)
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/cranelift/codegen/src/ir/function.rs b/cranelift/codegen/src/ir/function.rs
index 5d91e705e..f5bccf19f 100644
--- a/cranelift/codegen/src/ir/function.rs
+++ b/cranelift/codegen/src/ir/function.rs
@@ -8,7 +8,7 @@ use crate::ir::{
     self, pcc::Fact, Block, DataFlowGraph, DynamicStackSlot, DynamicStackSlotData,
     DynamicStackSlots, DynamicType, ExtFuncData, FuncRef, GlobalValue, GlobalValueData, Inst,
     JumpTable, JumpTableData, Layout, MemoryType, MemoryTypeData, Opcode, SigRef, Signature,
-    SourceLocs, StackSlot, StackSlotData, StackSlots, Table, TableData, Type,
+    SourceLocs, StackSlot, StackSlotData, StackSlots, Type,
 };
 use crate::isa::CallConv;
 use crate::write::write_function;
@@ -178,9 +178,6 @@ pub struct FunctionStencil {
     /// Memory types for proof-carrying code.
     pub memory_types: PrimaryMap<ir::MemoryType, ir::MemoryTypeData>,
 
-    /// Tables referenced.
-    pub tables: PrimaryMap<ir::Table, ir::TableData>,
-
     /// Data flow graph containing the primary definition of all instructions, blocks and values.
     pub dfg: DataFlowGraph,
 
@@ -209,7 +206,6 @@ impl FunctionStencil {
         self.global_values.clear();
         self.global_value_facts.clear();
         self.memory_types.clear();
-        self.tables.clear();
         self.dfg.clear();
         self.layout.clear();
         self.srclocs.clear();
@@ -268,11 +264,6 @@ impl FunctionStencil {
             .concrete()
     }
 
-    /// Declares a table accessible to the function.
-    pub fn create_table(&mut self, data: TableData) -> Table {
-        self.tables.push(data)
-    }
-
     /// Find a presumed unique special-purpose function parameter value.
     ///
     /// Returns the value of the last `purpose` parameter, or `None` if no such parameter exists.
@@ -423,7 +414,6 @@ impl Function {
                 global_values: PrimaryMap::new(),
                 global_value_facts: SecondaryMap::new(),
                 memory_types: PrimaryMap::new(),
-                tables: PrimaryMap::new(),
                 dfg: DataFlowGraph::new(),
                 layout: Layout::new(),
                 srclocs: SecondaryMap::new(),
diff --git a/cranelift/codegen/src/ir/memflags.rs b/cranelift/codegen/src/ir/memflags.rs
index 72d09dae3..8a5626d74 100644
--- a/cranelift/codegen/src/ir/memflags.rs
+++ b/cranelift/codegen/src/ir/memflags.rs
@@ -1,5 +1,6 @@
 //! Memory operation flags.
 
+use super::TrapCode;
 use core::fmt;
 
 #[cfg(feature = "enable-serde")]
@@ -9,6 +10,9 @@ enum FlagBit {
     /// Guaranteed not to trap. This may enable additional
     /// optimizations to be performed.
     Notrap,
+    /// If this memory access traps, use [TrapCode::TableOutOfBounds]
+    /// instead of [TrapCode::HeapOutOfBounds].
+    TableTrap,
     /// Guaranteed to use "natural alignment" for the given type. This
     /// may enable better instruction selection.
     Aligned,
@@ -36,8 +40,17 @@ enum FlagBit {
     Checked,
 }
 
-const NAMES: [&str; 9] = [
-    "notrap", "aligned", "readonly", "little", "big", "heap", "table", "vmctx", "checked",
+const NAMES: [&str; 10] = [
+    "notrap",
+    "tabletrap",
+    "aligned",
+    "readonly",
+    "little",
+    "big",
+    "heap",
+    "table",
+    "vmctx",
+    "checked",
 ];
 
 /// Endianness of a memory access.
@@ -162,6 +175,25 @@ impl MemFlags {
         self.with(FlagBit::Notrap)
     }
 
+    /// Test if the `tabletrap` bit is set.
+    ///
+    /// By default, if this memory access traps, the associated trap
+    /// code will be `HeapOutOfBounds`. With this flag set, the trap
+    /// code will instead be `TableOutOfBounds`.
+    pub const fn tabletrap(self) -> bool {
+        self.read(FlagBit::TableTrap)
+    }
+
+    /// Set the `tabletrap` bit.
+    pub fn set_tabletrap(&mut self) {
+        *self = self.with_tabletrap();
+    }
+
+    /// Set the `tabletrap` bit, returning new flags.
+    pub const fn with_tabletrap(self) -> Self {
+        self.with(FlagBit::TableTrap)
+    }
+
     /// Test if the `aligned` flag is set.
     ///
     /// By default, Cranelift memory instructions work with any unaligned effective address. If the
@@ -296,6 +328,21 @@ impl MemFlags {
     pub const fn with_checked(self) -> Self {
         self.with(FlagBit::Checked)
     }
+
+    /// Get the trap code to report if this memory access traps. If the
+    /// `notrap` flag is set, then return `None`. Otherwise, the choice
+    /// of trap code depends on the `tabletrap` flag. The default trap
+    /// code is `HeapOutOfBounds`, but with `tabletrap` set, the trap
+    /// code is instead `TableOutOfBounds`.
+    pub const fn trap_code(self) -> Option<TrapCode> {
+        if self.notrap() {
+            None
+        } else if self.tabletrap() {
+            Some(TrapCode::TableOutOfBounds)
+        } else {
+            Some(TrapCode::HeapOutOfBounds)
+        }
+    }
 }
 
 impl fmt::Display for MemFlags {
diff --git a/cranelift/codegen/src/ir/mod.rs b/cranelift/codegen/src/ir/mod.rs
index 396f245a2..aff5078a7 100644
--- a/cranelift/codegen/src/ir/mod.rs
+++ b/cranelift/codegen/src/ir/mod.rs
@@ -23,7 +23,6 @@ pub mod pcc;
 mod progpoint;
 mod sourceloc;
 pub mod stackslot;
-mod table;
 mod trapcode;
 pub mod types;
 
@@ -39,7 +38,7 @@ pub use crate::ir::dfg::{BlockData, DataFlowGraph, ValueDef};
 pub use crate::ir::dynamic_type::{dynamic_to_fixed, DynamicTypeData, DynamicTypes};
 pub use crate::ir::entities::{
     Block, Constant, DynamicStackSlot, DynamicType, FuncRef, GlobalValue, Immediate, Inst,
-    JumpTable, MemoryType, SigRef, StackSlot, Table, UserExternalNameRef, Value,
+    JumpTable, MemoryType, SigRef, StackSlot, UserExternalNameRef, Value,
 };
 pub use crate::ir::extfunc::{
     AbiParam, ArgumentExtension, ArgumentPurpose, ExtFuncData, Signature,
@@ -63,7 +62,6 @@ pub use crate::ir::sourceloc::SourceLoc;
 pub use crate::ir::stackslot::{
     DynamicStackSlotData, DynamicStackSlots, StackSlotData, StackSlotKind, StackSlots,
 };
-pub use crate::ir::table::TableData;
 pub use crate::ir::trapcode::TrapCode;
 pub use crate::ir::types::Type;
 
diff --git a/cranelift/codegen/src/ir/table.rs b/cranelift/codegen/src/ir/table.rs
deleted file mode 100644
index 6c6b32a14..000000000
--- a/cranelift/codegen/src/ir/table.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-//! Tables.
-
-use crate::ir::immediates::Uimm64;
-use crate::ir::{GlobalValue, Type};
-use core::fmt;
-
-#[cfg(feature = "enable-serde")]
-use serde_derive::{Deserialize, Serialize};
-
-/// Information about a table declaration.
-#[derive(Clone, PartialEq, Hash)]
-#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
-pub struct TableData {
-    /// Global value giving the address of the start of the table.
-    pub base_gv: GlobalValue,
-
-    /// Guaranteed minimum table size in elements. Table accesses before `min_size` don't need
-    /// bounds checking.
-    pub min_size: Uimm64,
-
-    /// Global value giving the current bound of the table, in elements.
-    pub bound_gv: GlobalValue,
-
-    /// The size of a table element, in bytes.
-    pub element_size: Uimm64,
-
-    /// The index type for the table.
-    pub index_type: Type,
-}
-
-impl fmt::Display for TableData {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        f.write_str("dynamic")?;
-        write!(
-            f,
-            " {}, min {}, bound {}, element_size {}, index_type {}",
-            self.base_gv, self.min_size, self.bound_gv, self.element_size, self.index_type
-        )
-    }
-}
diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
index 880a804a5..b02fe200e 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
@@ -4,7 +4,7 @@ use cranelift_control::ControlPlane;
 use regalloc2::Allocation;
 
 use crate::binemit::StackMap;
-use crate::ir::{self, types::*, TrapCode};
+use crate::ir::{self, types::*};
 use crate::isa::aarch64::inst::*;
 use crate::trace;
 
@@ -1005,9 +1005,9 @@ impl MachInstEmit for Inst {
                     _ => unreachable!(),
                 };
 
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual load instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
 
                 match &mem {
@@ -1140,9 +1140,9 @@ impl MachInstEmit for Inst {
                     _ => unreachable!(),
                 };
 
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual store instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
 
                 match &mem {
@@ -1219,9 +1219,9 @@ impl MachInstEmit for Inst {
                 let rt = allocs.next(rt);
                 let rt2 = allocs.next(rt2);
                 let mem = mem.with_allocs(&mut allocs);
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual store instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
                 match &mem {
                     &PairAMode::SignedOffset { reg, simm7 } => {
@@ -1250,9 +1250,9 @@ impl MachInstEmit for Inst {
                 let rt = allocs.next(rt.to_reg());
                 let rt2 = allocs.next(rt2.to_reg());
                 let mem = mem.with_allocs(&mut allocs);
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual load instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
 
                 match &mem {
@@ -1289,9 +1289,9 @@ impl MachInstEmit for Inst {
                 let rt2 = allocs.next(rt2.to_reg());
                 let mem = mem.with_allocs(&mut allocs);
 
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual load instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
 
                 let opc = match self {
@@ -1334,9 +1334,9 @@ impl MachInstEmit for Inst {
                 let rt2 = allocs.next(rt2);
                 let mem = mem.with_allocs(&mut allocs);
 
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual store instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
 
                 let opc = match self {
@@ -1494,8 +1494,8 @@ impl MachInstEmit for Inst {
                 let rt = allocs.next_writable(rt);
                 let rn = allocs.next(rn);
 
-                if !flags.notrap() {
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                if let Some(trap_code) = flags.trap_code() {
+                    sink.add_trap(trap_code);
                 }
 
                 sink.put4(enc_acq_rel(ty, op, rs, rt, rn));
@@ -1534,8 +1534,8 @@ impl MachInstEmit for Inst {
                 // again:
                 sink.bind_label(again_label, &mut state.ctrl_plane);
 
-                if !flags.notrap() {
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                if let Some(trap_code) = flags.trap_code() {
+                    sink.add_trap(trap_code);
                 }
 
                 sink.put4(enc_ldaxr(ty, x27wr, x25)); // ldaxr x27, [x25]
@@ -1658,8 +1658,8 @@ impl MachInstEmit for Inst {
                     }
                 }
 
-                if !flags.notrap() {
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                if let Some(trap_code) = flags.trap_code() {
+                    sink.add_trap(trap_code);
                 }
                 if op == AtomicRMWLoopOp::Xchg {
                     sink.put4(enc_stlxr(ty, x24wr, x26, x25)); // stlxr w24, x26, [x25]
@@ -1699,8 +1699,8 @@ impl MachInstEmit for Inst {
                     _ => panic!("Unsupported type: {}", ty),
                 };
 
-                if !flags.notrap() {
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                if let Some(trap_code) = flags.trap_code() {
+                    sink.add_trap(trap_code);
                 }
 
                 sink.put4(enc_cas(size, rd, rt, rn));
@@ -1733,8 +1733,8 @@ impl MachInstEmit for Inst {
                 // again:
                 sink.bind_label(again_label, &mut state.ctrl_plane);
 
-                if !flags.notrap() {
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                if let Some(trap_code) = flags.trap_code() {
+                    sink.add_trap(trap_code);
                 }
 
                 // ldaxr x27, [x25]
@@ -1760,8 +1760,8 @@ impl MachInstEmit for Inst {
                 ));
                 sink.use_label_at_offset(br_out_offset, out_label, LabelUse::Branch19);
 
-                if !flags.notrap() {
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                if let Some(trap_code) = flags.trap_code() {
+                    sink.add_trap(trap_code);
                 }
 
                 sink.put4(enc_stlxr(ty, x24wr, x28, x25)); // stlxr w24, x28, [x25]
@@ -1789,8 +1789,8 @@ impl MachInstEmit for Inst {
                 let rn = allocs.next(rn);
                 let rt = allocs.next_writable(rt);
 
-                if !flags.notrap() {
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                if let Some(trap_code) = flags.trap_code() {
+                    sink.add_trap(trap_code);
                 }
 
                 sink.put4(enc_ldar(access_ty, rt, rn));
@@ -1804,8 +1804,8 @@ impl MachInstEmit for Inst {
                 let rn = allocs.next(rn);
                 let rt = allocs.next(rt);
 
-                if !flags.notrap() {
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                if let Some(trap_code) = flags.trap_code() {
+                    sink.add_trap(trap_code);
                 }
 
                 sink.put4(enc_stlr(access_ty, rt, rn));
@@ -2977,9 +2977,9 @@ impl MachInstEmit for Inst {
                 let rn = allocs.next(rn);
                 let (q, size) = size.enc_size();
 
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual load instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
 
                 sink.put4(enc_ldst_vec(q, size, rn, rd));
diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit.rs b/cranelift/codegen/src/isa/riscv64/inst/emit.rs
index aa86021c4..c4f32c58b 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/emit.rs
@@ -630,9 +630,9 @@ impl Inst {
                     _ => return None,
                 };
 
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual load instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
                 sink.put2(encode_ci_sp_load(op, rd, imm6));
             }
@@ -701,9 +701,9 @@ impl Inst {
                     encode_cl_type(op, rd, base, imm5)
                 };
 
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual load instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
                 sink.put2(encoded);
             }
@@ -732,9 +732,9 @@ impl Inst {
                     _ => return None,
                 };
 
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual load instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
                 sink.put2(encode_css_type(op, src, imm6));
             }
@@ -799,9 +799,9 @@ impl Inst {
                     encode_cs_type(op, src, base, imm5)
                 };
 
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual load instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
                 sink.put2(encoded);
             }
@@ -1077,9 +1077,9 @@ impl Inst {
                     }
                 };
 
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual load instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
 
                 sink.put4(encode_i_type(op.op_code(), rd, op.funct3(), addr, imm12));
@@ -1100,9 +1100,9 @@ impl Inst {
                     }
                 };
 
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual load instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
 
                 sink.put4(encode_s_type(op.op_code(), op.funct3(), addr, src, imm12));
@@ -1492,7 +1492,11 @@ impl Inst {
                 src,
                 amo,
             } => {
-                sink.add_trap(TrapCode::HeapOutOfBounds);
+                // TODO: get flags from original CLIF atomic instruction
+                let flags = MemFlags::new();
+                if let Some(trap_code) = flags.trap_code() {
+                    sink.add_trap(trap_code);
+                }
                 let x = op.op_code()
                     | reg_to_gpr_num(rd.to_reg()) << 7
                     | op.funct3() << 12
@@ -2718,9 +2722,9 @@ impl Inst {
                     }
                 };
 
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual load instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
 
                 sink.put4(encode_vmem_load(
@@ -2765,9 +2769,9 @@ impl Inst {
                     }
                 };
 
-                if !flags.notrap() {
+                if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual load instruction starts.
-                    sink.add_trap(TrapCode::HeapOutOfBounds);
+                    sink.add_trap(trap_code);
                 }
 
                 sink.put4(encode_vmem_store(
diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle
index df9ebf694..5891cbbe0 100644
--- a/cranelift/codegen/src/isa/riscv64/lower.isle
+++ b/cranelift/codegen/src/isa/riscv64/lower.isle
@@ -1838,21 +1838,29 @@
 ;;
 ;; This allows us to skip the mask expansion step and use the more efficient
 ;; vmerge.vvm instruction.
-(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (bitselect (icmp cc a @ (value_type (ty_vec_fits_in_register cmp_ty)) b) x y)))
+;;
+;; We should be careful to ensure that the mask and the vmerge have the
+;; same type. So that we don't generate a mask with length 16 (i.e. for i8x16), and then
+;; only copy the first few lanes of the result to the destination register because
+;; the bitselect has a different length (i.e. i64x2).
+;;
+;; See: https://github.com/bytecodealliance/wasmtime/issues/8131
+
+(rule 2 (lower (has_type (ty_vec_fits_in_register _ty) (bitselect (icmp cc a @ (value_type (ty_vec_fits_in_register cmp_ty)) b) x y)))
   (let ((mask VReg (gen_icmp_mask cmp_ty cc a b)))
-    (rv_vmerge_vvm y x mask ty)))
+    (rv_vmerge_vvm y x mask cmp_ty)))
 
-(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (bitselect (fcmp cc a @ (value_type (ty_vec_fits_in_register cmp_ty)) b) x y)))
+(rule 2 (lower (has_type (ty_vec_fits_in_register _ty) (bitselect (fcmp cc a @ (value_type (ty_vec_fits_in_register cmp_ty)) b) x y)))
   (let ((mask VReg (gen_fcmp_mask cmp_ty cc a b)))
-    (rv_vmerge_vvm y x mask ty)))
+    (rv_vmerge_vvm y x mask cmp_ty)))
 
-(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (bitselect (bitcast _ (fcmp cc a @ (value_type (ty_vec_fits_in_register cmp_ty)) b)) x y)))
+(rule 2 (lower (has_type (ty_vec_fits_in_register _ty) (bitselect (bitcast _ (fcmp cc a @ (value_type (ty_vec_fits_in_register cmp_ty)) b)) x y)))
   (let ((mask VReg (gen_fcmp_mask cmp_ty cc a b)))
-    (rv_vmerge_vvm y x mask ty)))
+    (rv_vmerge_vvm y x mask cmp_ty)))
 
-(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (bitselect (bitcast _ (icmp cc a @ (value_type (ty_vec_fits_in_register cmp_ty)) b)) x y)))
+(rule 2 (lower (has_type (ty_vec_fits_in_register _ty) (bitselect (bitcast _ (icmp cc a @ (value_type (ty_vec_fits_in_register cmp_ty)) b)) x y)))
   (let ((mask VReg (gen_icmp_mask cmp_ty cc a b)))
-    (rv_vmerge_vvm y x mask ty)))
+    (rv_vmerge_vvm y x mask cmp_ty)))
 
 
 ;;;;;  Rules for `isplit`;;;;;;;;;
diff --git a/cranelift/codegen/src/isa/s390x/inst.isle b/cranelift/codegen/src/isa/s390x/inst.isle
index 54b3649da..74e9b0e3c 100644
--- a/cranelift/codegen/src/isa/s390x/inst.isle
+++ b/cranelift/codegen/src/isa/s390x/inst.isle
@@ -2872,8 +2872,8 @@
       (let ((rev Reg (vec_permute_dw_imm $I64X2 reg 1 reg 0)))
         (vec_rot_imm $I16X8 (vec_rot_imm $I32X4 (vec_rot_imm $I64X2 rev 32) 16) 8)))
 
-;; When passing a vector value in register to a function whose ABI uses
-;; a different lane order than the current function, we need to swap lanes.
+;; When passing a vector value to a function whose ABI uses a different
+;; lane order than the current function, we need to swap lanes.
 ;; The first operand is the lane order used by the callee.
 (decl abi_vec_elt_rev (LaneOrder Type Reg) Reg)
 (rule 4 (abi_vec_elt_rev _ (gpr32_ty ty) reg) reg)
@@ -2948,15 +2948,17 @@
 (rule (copy_reg_to_arg_slot uses lo _ (ABIArgSlot.Reg reg ty ext) src)
       (let ((_ Unit (args_builder_push uses (abi_vec_elt_rev lo ty src) reg)))
         (output_none)))
-(rule (copy_reg_to_arg_slot _ _ base (ABIArgSlot.Stack offset ty ext) src)
-      (side_effect (arg_store (abi_ext_ty ext ty) src (memarg_stack_off base offset))))
+(rule (copy_reg_to_arg_slot _ lo base (ABIArgSlot.Stack offset ty ext) src)
+      (side_effect (arg_store (abi_ext_ty ext ty) (abi_vec_elt_rev lo ty src)
+                              (memarg_stack_off base offset))))
 
 ;; Copy one component of an argument/return value from its slot.
 (decl copy_reg_from_arg_slot (CallRetList LaneOrder i64 ABIArgSlot) Reg)
 (rule (copy_reg_from_arg_slot defs lo _ (ABIArgSlot.Reg reg ty ext))
       (abi_vec_elt_rev lo ty (defs_lookup defs reg)))
-(rule (copy_reg_from_arg_slot _ _ base (ABIArgSlot.Stack offset ty ext))
-      (arg_load (abi_ext_ty ext ty) (memarg_stack_off base offset)))
+(rule (copy_reg_from_arg_slot _ lo base (ABIArgSlot.Stack offset ty ext))
+      (abi_vec_elt_rev lo ty (arg_load (abi_ext_ty ext ty)
+                                       (memarg_stack_off base offset))))
 
 ;; Helper to compute the type of an implicitly extended argument/return value.
 (decl abi_ext_ty (ArgumentExtension Type) Type)
diff --git a/cranelift/codegen/src/isa/s390x/inst/args.rs b/cranelift/codegen/src/isa/s390x/inst/args.rs
index 553ca8660..0f1389c80 100644
--- a/cranelift/codegen/src/isa/s390x/inst/args.rs
+++ b/cranelift/codegen/src/isa/s390x/inst/args.rs
@@ -102,10 +102,6 @@ impl MemArg {
         }
     }
 
-    pub(crate) fn can_trap(&self) -> bool {
-        !self.get_flags().notrap()
-    }
-
     /// Edit registers with allocations.
     pub fn with_allocs(&self, allocs: &mut AllocationConsumer<'_>) -> Self {
         match self {
@@ -187,10 +183,6 @@ impl MemArgPair {
         }
     }
 
-    pub(crate) fn can_trap(&self) -> bool {
-        !self.flags.notrap()
-    }
-
     /// Edit registers with allocations.
     pub fn with_allocs(&self, allocs: &mut AllocationConsumer<'_>) -> Self {
         MemArgPair {
diff --git a/cranelift/codegen/src/isa/s390x/inst/emit.rs b/cranelift/codegen/src/isa/s390x/inst/emit.rs
index 6531d7df6..b929957b5 100644
--- a/cranelift/codegen/src/isa/s390x/inst/emit.rs
+++ b/cranelift/codegen/src/isa/s390x/inst/emit.rs
@@ -184,8 +184,10 @@ pub fn mem_emit(
         inst.emit(&[], sink, emit_info, state);
     }
 
-    if add_trap && mem.can_trap() {
-        sink.add_trap(TrapCode::HeapOutOfBounds);
+    if add_trap {
+        if let Some(trap_code) = mem.get_flags().trap_code() {
+            sink.add_trap(trap_code);
+        }
     }
 
     match &mem {
@@ -245,8 +247,10 @@ pub fn mem_rs_emit(
         inst.emit(&[], sink, emit_info, state);
     }
 
-    if add_trap && mem.can_trap() {
-        sink.add_trap(TrapCode::HeapOutOfBounds);
+    if add_trap {
+        if let Some(trap_code) = mem.get_flags().trap_code() {
+            sink.add_trap(trap_code);
+        }
     }
 
     match &mem {
@@ -294,8 +298,10 @@ pub fn mem_imm8_emit(
         inst.emit(&[], sink, emit_info, state);
     }
 
-    if add_trap && mem.can_trap() {
-        sink.add_trap(TrapCode::HeapOutOfBounds);
+    if add_trap {
+        if let Some(trap_code) = mem.get_flags().trap_code() {
+            sink.add_trap(trap_code);
+        }
     }
 
     match &mem {
@@ -339,8 +345,10 @@ pub fn mem_imm16_emit(
         inst.emit(&[], sink, emit_info, state);
     }
 
-    if add_trap && mem.can_trap() {
-        sink.add_trap(TrapCode::HeapOutOfBounds);
+    if add_trap {
+        if let Some(trap_code) = mem.get_flags().trap_code() {
+            sink.add_trap(trap_code);
+        }
     }
 
     match &mem {
@@ -363,8 +371,10 @@ pub fn mem_mem_emit(
     sink: &mut MachBuffer<Inst>,
     _state: &mut EmitState,
 ) {
-    if add_trap && (dst.can_trap() || src.can_trap()) {
-        sink.add_trap(TrapCode::HeapOutOfBounds);
+    if add_trap {
+        if let Some(trap_code) = dst.flags.trap_code().or(src.flags.trap_code()) {
+            sink.add_trap(trap_code);
+        }
     }
 
     put(
@@ -405,8 +415,10 @@ pub fn mem_vrx_emit(
         inst.emit(&[], sink, emit_info, state);
     }
 
-    if add_trap && mem.can_trap() {
-        sink.add_trap(TrapCode::HeapOutOfBounds);
+    if add_trap {
+        if let Some(trap_code) = mem.get_flags().trap_code() {
+            sink.add_trap(trap_code);
+        }
     }
 
     match &mem {
diff --git a/cranelift/codegen/src/isa/x64/encoding/evex.rs b/cranelift/codegen/src/isa/x64/encoding/evex.rs
index 03a3ac6c7..3805e7215 100644
--- a/cranelift/codegen/src/isa/x64/encoding/evex.rs
+++ b/cranelift/codegen/src/isa/x64/encoding/evex.rs
@@ -15,7 +15,6 @@
 //! Software Development Manual, volume 2A).
 
 use super::rex::{self, LegacyPrefixes, OpcodeMap};
-use crate::ir::TrapCode;
 use crate::isa::x64::args::{Amode, Avx512TupleType};
 use crate::isa::x64::inst::Inst;
 use crate::MachBuffer;
@@ -202,8 +201,8 @@ impl EvexInstruction {
     /// - an optional immediate, if necessary (not currently implemented)
     pub fn encode(&self, sink: &mut MachBuffer<Inst>) {
         if let RegisterOrAmode::Amode(amode) = &self.rm {
-            if amode.can_trap() {
-                sink.add_trap(TrapCode::HeapOutOfBounds);
+            if let Some(trap_code) = amode.get_flags().trap_code() {
+                sink.add_trap(trap_code);
             }
         }
         sink.put4(self.bits);
diff --git a/cranelift/codegen/src/isa/x64/encoding/rex.rs b/cranelift/codegen/src/isa/x64/encoding/rex.rs
index 88b6eca2d..ba7b3b19d 100644
--- a/cranelift/codegen/src/isa/x64/encoding/rex.rs
+++ b/cranelift/codegen/src/isa/x64/encoding/rex.rs
@@ -10,7 +10,6 @@
 
 use crate::machinst::{Reg, RegClass};
 use crate::{
-    ir::TrapCode,
     isa::x64::inst::{
         args::{Amode, OperandSize},
         regs, Inst, LabelUse,
@@ -314,9 +313,8 @@ pub(crate) fn emit_std_enc_mem(
     // 64-bit integer registers, because they are part of an address
     // expression.  But `enc_g` can be derived from a register of any class.
 
-    let can_trap = mem_e.can_trap();
-    if can_trap {
-        sink.add_trap(TrapCode::HeapOutOfBounds);
+    if let Some(trap_code) = mem_e.get_flags().trap_code() {
+        sink.add_trap(trap_code);
     }
 
     prefixes.emit(sink);
diff --git a/cranelift/codegen/src/isa/x64/encoding/vex.rs b/cranelift/codegen/src/isa/x64/encoding/vex.rs
index 7e853c336..1124a6b5b 100644
--- a/cranelift/codegen/src/isa/x64/encoding/vex.rs
+++ b/cranelift/codegen/src/isa/x64/encoding/vex.rs
@@ -4,7 +4,6 @@
 use super::evex::{Register, RegisterOrAmode};
 use super::rex::{LegacyPrefixes, OpcodeMap};
 use super::ByteSink;
-use crate::ir::TrapCode;
 use crate::isa::x64::args::Amode;
 use crate::isa::x64::encoding::rex;
 use crate::isa::x64::inst::Inst;
@@ -251,8 +250,8 @@ impl VexInstruction {
     /// Emit the VEX-encoded instruction to the provided buffer.
     pub fn encode(&self, sink: &mut MachBuffer<Inst>) {
         if let RegisterOrAmode::Amode(amode) = &self.rm {
-            if amode.can_trap() {
-                sink.add_trap(TrapCode::HeapOutOfBounds);
+            if let Some(trap_code) = amode.get_flags().trap_code() {
+                sink.add_trap(trap_code);
             }
         }
 
diff --git a/cranelift/codegen/src/isa/x64/inst/args.rs b/cranelift/codegen/src/isa/x64/inst/args.rs
index 85c70b6b3..45ab8e6e8 100644
--- a/cranelift/codegen/src/isa/x64/inst/args.rs
+++ b/cranelift/codegen/src/isa/x64/inst/args.rs
@@ -405,10 +405,6 @@ impl Amode {
         }
     }
 
-    pub(crate) fn can_trap(&self) -> bool {
-        !self.get_flags().notrap()
-    }
-
     pub(crate) fn with_allocs(&self, allocs: &mut AllocationConsumer<'_>) -> Self {
         // The order in which we consume allocs here must match the
         // order in which we produce operands in get_operands() above.
diff --git a/cranelift/codegen/src/legalizer/mod.rs b/cranelift/codegen/src/legalizer/mod.rs
index e186b1530..b8de7bcd7 100644
--- a/cranelift/codegen/src/legalizer/mod.rs
+++ b/cranelift/codegen/src/legalizer/mod.rs
@@ -22,10 +22,8 @@ use crate::isa::TargetIsa;
 use crate::trace;
 
 mod globalvalue;
-mod table;
 
 use self::globalvalue::expand_global_value;
-use self::table::expand_table_addr;
 
 fn imm_const(pos: &mut FuncCursor, arg: Value, imm: Imm64, is_signed: bool) -> Value {
     let ty = pos.func.dfg.value_type(arg);
@@ -152,12 +150,6 @@ pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa:
                     mflags.set_aligned();
                     pos.func.dfg.replace(inst).store(mflags, arg, addr, 0);
                 }
-                InstructionData::TableAddr {
-                    opcode: ir::Opcode::TableAddr,
-                    table,
-                    arg,
-                    offset,
-                } => expand_table_addr(isa, inst, &mut pos.func, table, arg, offset),
 
                 InstructionData::BinaryImm64 { opcode, arg, imm } => {
                     let is_signed = match opcode {
diff --git a/cranelift/codegen/src/legalizer/table.rs b/cranelift/codegen/src/legalizer/table.rs
deleted file mode 100644
index db72508a2..000000000
--- a/cranelift/codegen/src/legalizer/table.rs
+++ /dev/null
@@ -1,114 +0,0 @@
-//! Legalization of tables.
-//!
-//! This module exports the `expand_table_addr` function which transforms a `table_addr`
-//! instruction into code that depends on the kind of table referenced.
-
-use crate::cursor::{Cursor, FuncCursor};
-use crate::ir::condcodes::IntCC;
-use crate::ir::immediates::Offset32;
-use crate::ir::{self, InstBuilder};
-use crate::isa::TargetIsa;
-
-/// Expand a `table_addr` instruction according to the definition of the table.
-pub fn expand_table_addr(
-    isa: &dyn TargetIsa,
-    inst: ir::Inst,
-    func: &mut ir::Function,
-    table: ir::Table,
-    index: ir::Value,
-    element_offset: Offset32,
-) {
-    let bound_gv = func.tables[table].bound_gv;
-    let index_ty = func.dfg.value_type(index);
-    let addr_ty = func.dfg.value_type(func.dfg.first_result(inst));
-    let mut pos = FuncCursor::new(func).at_inst(inst);
-    pos.use_srcloc(inst);
-
-    // Start with the bounds check. Trap if `index + 1 > bound`.
-    let bound = pos.ins().global_value(index_ty, bound_gv);
-
-    // `index > bound - 1` is the same as `index >= bound`.
-    let oob = pos
-        .ins()
-        .icmp(IntCC::UnsignedGreaterThanOrEqual, index, bound);
-    pos.ins().trapnz(oob, ir::TrapCode::TableOutOfBounds);
-
-    // If Spectre mitigations are enabled, we will use a comparison to
-    // short-circuit the computed table element address to the start
-    // of the table on the misspeculation path when out-of-bounds.
-    let spectre_oob_cmp = if isa.flags().enable_table_access_spectre_mitigation() {
-        Some((index, bound))
-    } else {
-        None
-    };
-
-    compute_addr(
-        inst,
-        table,
-        addr_ty,
-        index,
-        index_ty,
-        element_offset,
-        pos.func,
-        spectre_oob_cmp,
-    );
-}
-
-/// Emit code for the base address computation of a `table_addr` instruction.
-fn compute_addr(
-    inst: ir::Inst,
-    table: ir::Table,
-    addr_ty: ir::Type,
-    mut index: ir::Value,
-    index_ty: ir::Type,
-    element_offset: Offset32,
-    func: &mut ir::Function,
-    spectre_oob_cmp: Option<(ir::Value, ir::Value)>,
-) {
-    let mut pos = FuncCursor::new(func).at_inst(inst);
-    pos.use_srcloc(inst);
-
-    // Convert `index` to `addr_ty`.
-    if index_ty != addr_ty {
-        index = pos.ins().uextend(addr_ty, index);
-    }
-
-    // Add the table base address base
-    let base_gv = pos.func.tables[table].base_gv;
-    let base = pos.ins().global_value(addr_ty, base_gv);
-
-    let element_size = pos.func.tables[table].element_size;
-    let mut offset;
-    let element_size: u64 = element_size.into();
-    if element_size == 1 {
-        offset = index;
-    } else if element_size.is_power_of_two() {
-        offset = pos
-            .ins()
-            .ishl_imm(index, i64::from(element_size.trailing_zeros()));
-    } else {
-        offset = pos.ins().imul_imm(index, element_size as i64);
-    }
-
-    let element_addr = if element_offset == Offset32::new(0) {
-        pos.ins().iadd(base, offset)
-    } else {
-        let imm: i64 = element_offset.into();
-        offset = pos.ins().iadd(base, offset);
-        pos.ins().iadd_imm(offset, imm)
-    };
-
-    let element_addr = if let Some((index, bound)) = spectre_oob_cmp {
-        let cond = pos
-            .ins()
-            .icmp(IntCC::UnsignedGreaterThanOrEqual, index, bound);
-        // If out-of-bounds, choose the table base on the misspeculation path.
-        pos.ins().select_spectre_guard(cond, base, element_addr)
-    } else {
-        element_addr
-    };
-    let new_inst = pos.func.dfg.value_def(element_addr).inst().unwrap();
-
-    pos.func.dfg.replace_with_aliases(inst, new_inst);
-    pos.remove_inst();
-}
diff --git a/cranelift/codegen/src/opts.rs b/cranelift/codegen/src/opts.rs
index a99458704..4204c6ba3 100644
--- a/cranelift/codegen/src/opts.rs
+++ b/cranelift/codegen/src/opts.rs
@@ -8,7 +8,7 @@ use crate::ir::instructions::InstructionFormat;
 pub use crate::ir::types::*;
 pub use crate::ir::{
     AtomicRmwOp, BlockCall, Constant, DynamicStackSlot, FuncRef, GlobalValue, Immediate,
-    InstructionData, MemFlags, Opcode, StackSlot, Table, TrapCode, Type, Value,
+    InstructionData, MemFlags, Opcode, StackSlot, TrapCode, Type, Value,
 };
 use crate::isle_common_prelude_methods;
 use crate::machinst::isle::*;
diff --git a/cranelift/codegen/src/verifier/mod.rs b/cranelift/codegen/src/verifier/mod.rs
index d80724570..3e5981eef 100644
--- a/cranelift/codegen/src/verifier/mod.rs
+++ b/cranelift/codegen/src/verifier/mod.rs
@@ -457,49 +457,6 @@ impl<'a> Verifier<'a> {
         Ok(())
     }
 
-    fn verify_tables(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
-        if let Some(isa) = self.isa {
-            for (table, table_data) in &self.func.tables {
-                let base = table_data.base_gv;
-                if !self.func.global_values.is_valid(base) {
-                    return errors.nonfatal((table, format!("invalid base global value {}", base)));
-                }
-
-                let pointer_type = isa.pointer_type();
-                let base_type = self.func.global_values[base].global_type(isa);
-                if base_type != pointer_type {
-                    errors.report((
-                        table,
-                        format!(
-                            "table base has type {}, which is not the pointer type {}",
-                            base_type, pointer_type
-                        ),
-                    ));
-                }
-
-                let bound_gv = table_data.bound_gv;
-                if !self.func.global_values.is_valid(bound_gv) {
-                    return errors
-                        .nonfatal((table, format!("invalid bound global value {}", bound_gv)));
-                }
-
-                let index_type = table_data.index_type;
-                let bound_type = self.func.global_values[bound_gv].global_type(isa);
-                if index_type != bound_type {
-                    errors.report((
-                        table,
-                        format!(
-                            "table index type {} differs from the type of its bound, {}",
-                            index_type, bound_type
-                        ),
-                    ));
-                }
-            }
-        }
-
-        Ok(())
-    }
-
     /// Check that the given block can be encoded as a BB, by checking that only
     /// branching instructions are ending the block.
     fn encodable_as_bb(&self, block: Block, errors: &mut VerifierErrors) -> VerifierStepResult {
@@ -661,9 +618,6 @@ impl<'a> Verifier<'a> {
             UnaryGlobalValue { global_value, .. } => {
                 self.verify_global_value(inst, global_value, errors)?;
             }
-            TableAddr { table, .. } => {
-                self.verify_table(inst, table, errors)?;
-            }
             NullAry {
                 opcode: Opcode::GetPinnedReg,
             }
@@ -856,19 +810,6 @@ impl<'a> Verifier<'a> {
         }
     }
 
-    fn verify_table(
-        &self,
-        inst: Inst,
-        table: ir::Table,
-        errors: &mut VerifierErrors,
-    ) -> VerifierStepResult {
-        if !self.func.tables.is_valid(table) {
-            errors.nonfatal((inst, self.context(inst), format!("invalid table {}", table)))
-        } else {
-            Ok(())
-        }
-    }
-
     fn verify_value_list(
         &self,
         inst: Inst,
@@ -1533,20 +1474,6 @@ impl<'a> Verifier<'a> {
     // constraints.
     fn typecheck_special(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
         match self.func.dfg.insts[inst] {
-            ir::InstructionData::TableAddr { table, arg, .. } => {
-                let index_type = self.func.dfg.value_type(arg);
-                let table_index_type = self.func.tables[table].index_type;
-                if index_type != table_index_type {
-                    return errors.nonfatal((
-                        inst,
-                        self.context(inst),
-                        format!(
-                            "index type {} differs from table index type {}",
-                            index_type, table_index_type,
-                        ),
-                    ));
-                }
-            }
             ir::InstructionData::UnaryGlobalValue { global_value, .. } => {
                 if let Some(isa) = self.isa {
                     let inst_type = self.func.dfg.value_type(self.func.dfg.first_result(inst));
@@ -1801,7 +1728,6 @@ impl<'a> Verifier<'a> {
     pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
         self.verify_global_values(errors)?;
         self.verify_memory_types(errors)?;
-        self.verify_tables(errors)?;
         self.typecheck_entry_block_params(errors)?;
         self.check_entry_not_cold(errors)?;
         self.typecheck_function_signature(errors)?;
diff --git a/cranelift/codegen/src/write.rs b/cranelift/codegen/src/write.rs
index 3bfc4d770..190e1c20e 100644
--- a/cranelift/codegen/src/write.rs
+++ b/cranelift/codegen/src/write.rs
@@ -63,13 +63,6 @@ pub trait FuncWriter {
             self.write_entity_definition(w, func, mt.into(), mt_data, None)?;
         }
 
-        for (table, table_data) in &func.tables {
-            if !table_data.index_type.is_invalid() {
-                any = true;
-                self.write_entity_definition(w, func, table.into(), table_data, None)?;
-            }
-        }
-
         // Write out all signatures before functions since function declarations can refer to
         // signatures.
         for (sig, sig_data) in &func.dfg.signatures {
@@ -479,15 +472,6 @@ pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt
             dynamic_stack_slot,
             ..
         } => write!(w, " {}, {}", arg, dynamic_stack_slot),
-        TableAddr {
-            table, arg, offset, ..
-        } => {
-            if i32::from(offset) == 0 {
-                write!(w, " {}, {}", table, arg)
-            } else {
-                write!(w, " {}, {}{}", table, arg, offset)
-            }
-        }
         Load {
             flags, arg, offset, ..
         } => write!(w, "{} {}{}", flags, arg, offset),
diff --git a/cranelift/docs/ir.md b/cranelift/docs/ir.md
index f2b48627f..701cc0cce 100644
--- a/cranelift/docs/ir.md
+++ b/cranelift/docs/ir.md
@@ -553,36 +553,6 @@ GV = [colocated] symbol Name
     :arg Name: External name.
     :result GV: Global value.
 
-### Tables
-
-Code compiled from WebAssembly often needs access to objects outside of its
-linear memory. WebAssembly uses *tables* to allow programs to refer to opaque
-values through integer indices.
-
-A table is declared in the function preamble and can be accessed with the
-`table_addr` instruction that [traps] on out-of-bounds accesses.
-Table addresses can be smaller than the native pointer size, for example
-unsigned `i32` offsets on a 64-bit architecture.
-
-A table appears as a consecutive range of address space, conceptually
-divided into elements of fixed sizes, which are identified by their index.
-The memory is [accessible].
-
-The *table bound* is the number of elements currently in the table. This is
-the bound that `table_addr` checks against.
-
-A table can be relocated to a different base address when it is resized, and
-its bound can move dynamically. The bound of a table is stored in a global
-value.
-
-T = dynamic Base, min MinElements, bound BoundGV, element_size ElementSize
-    Declare a table in the preamble.
-
-    :arg Base: Global value holding the table's base address.
-    :arg MinElements: Guaranteed minimum table size in elements.
-    :arg BoundGV: Global value containing the current table bound in elements.
-    :arg ElementSize: Size of each element.
-
 ### Constant materialization
 
 A few instructions have variants that take immediate operands, but in general
diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs
index 0652e3536..6dc17eac1 100644
--- a/cranelift/fuzzgen/src/function_generator.rs
+++ b/cranelift/fuzzgen/src/function_generator.rs
@@ -916,7 +916,6 @@ static OPCODE_SIGNATURES: Lazy<Vec<OpcodeSignature>> = Lazy::new(|| {
                 (Opcode::GetFramePointer),
                 (Opcode::GetStackPointer),
                 (Opcode::GetReturnAddress),
-                (Opcode::TableAddr),
                 (Opcode::Null),
                 (Opcode::X86Blendv),
                 (Opcode::IcmpImm),
@@ -1091,7 +1090,6 @@ fn inserter_for_format(fmt: InstructionFormat) -> OpcodeInserter {
         InstructionFormat::StackStore => insert_stack_store,
         InstructionFormat::Store => insert_load_store,
         InstructionFormat::StoreNoOffset => insert_load_store,
-        InstructionFormat::TableAddr => todo!(),
         InstructionFormat::Ternary => insert_opcode,
         InstructionFormat::TernaryImm8 => insert_ins_ext_lane,
         InstructionFormat::Trap => todo!(),
diff --git a/cranelift/interpreter/src/interpreter.rs b/cranelift/interpreter/src/interpreter.rs
index 7d0bbe3d0..6737494e6 100644
--- a/cranelift/interpreter/src/interpreter.rs
+++ b/cranelift/interpreter/src/interpreter.rs
@@ -331,7 +331,11 @@ impl<'a> State<'a> for InterpreterState<'a> {
         let src = match addr.region {
             AddressRegion::Stack => {
                 if addr_end > self.stack.len() {
-                    return Err(MemoryError::OutOfBoundsLoad { addr, load_size });
+                    return Err(MemoryError::OutOfBoundsLoad {
+                        addr,
+                        load_size,
+                        mem_flags,
+                    });
                 }
 
                 &self.stack[addr_start..addr_end]
@@ -363,7 +367,11 @@ impl<'a> State<'a> for InterpreterState<'a> {
         let dst = match addr.region {
             AddressRegion::Stack => {
                 if addr_end > self.stack.len() {
-                    return Err(MemoryError::OutOfBoundsStore { addr, store_size });
+                    return Err(MemoryError::OutOfBoundsStore {
+                        addr,
+                        store_size,
+                        mem_flags,
+                    });
                 }
 
                 &mut self.stack[addr_start..addr_end]
diff --git a/cranelift/interpreter/src/state.rs b/cranelift/interpreter/src/state.rs
index 5c5d7a685..9f3bfdddd 100644
--- a/cranelift/interpreter/src/state.rs
+++ b/cranelift/interpreter/src/state.rs
@@ -125,9 +125,17 @@ pub enum MemoryError {
     #[error("Requested an offset of {offset} but max was {max}")]
     InvalidOffset { offset: u64, max: u64 },
     #[error("Load of {load_size} bytes is larger than available size at address {addr:?}")]
-    OutOfBoundsLoad { addr: Address, load_size: usize },
+    OutOfBoundsLoad {
+        addr: Address,
+        load_size: usize,
+        mem_flags: MemFlags,
+    },
     #[error("Store of {store_size} bytes is larger than available size at address {addr:?}")]
-    OutOfBoundsStore { addr: Address, store_size: usize },
+    OutOfBoundsStore {
+        addr: Address,
+        store_size: usize,
+        mem_flags: MemFlags,
+    },
     #[error("Load of {load_size} bytes is misaligned at address {addr:?}")]
     MisalignedLoad { addr: Address, load_size: usize },
     #[error("Store of {store_size} bytes is misaligned at address {addr:?}")]
diff --git a/cranelift/interpreter/src/step.rs b/cranelift/interpreter/src/step.rs
index 0ffff70e2..96d465966 100644
--- a/cranelift/interpreter/src/step.rs
+++ b/cranelift/interpreter/src/step.rs
@@ -120,8 +120,7 @@ where
             InstructionData::Load { offset, .. }
             | InstructionData::Store { offset, .. }
             | InstructionData::StackLoad { offset, .. }
-            | InstructionData::StackStore { offset, .. }
-            | InstructionData::TableAddr { offset, .. } => DataValue::from(offset),
+            | InstructionData::StackStore { offset, .. } => DataValue::from(offset),
             // 64-bit.
             InstructionData::UnaryImm { imm, .. }
             | InstructionData::BinaryImm64 { imm, .. }
@@ -161,8 +160,12 @@ where
         MemoryError::InvalidAddressType(_) => TrapCode::HeapOutOfBounds,
         MemoryError::InvalidOffset { .. } => TrapCode::HeapOutOfBounds,
         MemoryError::InvalidEntry { .. } => TrapCode::HeapOutOfBounds,
-        MemoryError::OutOfBoundsStore { .. } => TrapCode::HeapOutOfBounds,
-        MemoryError::OutOfBoundsLoad { .. } => TrapCode::HeapOutOfBounds,
+        MemoryError::OutOfBoundsStore { mem_flags, .. } => mem_flags
+            .trap_code()
+            .expect("store with notrap flag should not trap"),
+        MemoryError::OutOfBoundsLoad { mem_flags, .. } => mem_flags
+            .trap_code()
+            .expect("load with notrap flag should not trap"),
         MemoryError::MisalignedLoad { .. } => TrapCode::HeapMisaligned,
         MemoryError::MisalignedStore { .. } => TrapCode::HeapMisaligned,
     };
@@ -550,28 +553,6 @@ where
             state.set_pinned_reg(arg0);
             ControlFlow::Continue
         }
-        Opcode::TableAddr => {
-            if let InstructionData::TableAddr { table, offset, .. } = inst {
-                let table = &state.get_current_function().tables[table];
-                let base = state.resolve_global_value(table.base_gv)?;
-                let bound = state.resolve_global_value(table.bound_gv)?;
-                let index_ty = table.index_type;
-                let element_size = DataValue::int(u64::from(table.element_size) as i128, index_ty)?;
-                let inst_offset = DataValue::int(i32::from(offset) as i128, index_ty)?;
-
-                let byte_offset = arg(0).mul(element_size.clone())?.add(inst_offset)?;
-                let bound_bytes = bound.mul(element_size)?;
-                if byte_offset > bound_bytes {
-                    return Ok(ControlFlow::Trap(CraneliftTrap::User(
-                        TrapCode::HeapOutOfBounds,
-                    )));
-                }
-
-                assign(base.add(byte_offset)?)
-            } else {
-                unreachable!()
-            }
-        }
         Opcode::Iconst => assign(DataValueExt::int(imm().into_int_signed()?, ctrl_ty)?),
         Opcode::F32const => assign(imm()),
         Opcode::F64const => assign(imm()),
diff --git a/cranelift/reader/src/lexer.rs b/cranelift/reader/src/lexer.rs
index 28ef8fb79..2899e008e 100644
--- a/cranelift/reader/src/lexer.rs
+++ b/cranelift/reader/src/lexer.rs
@@ -41,7 +41,6 @@ pub enum Token<'a> {
     DynamicStackSlot(u32), // dss4
     GlobalValue(u32),      // gv3
     MemoryType(u32),       // mt0
-    Table(u32),            // table2
     Constant(u32),         // const2
     FuncRef(u32),          // fn2
     SigRef(u32),           // sig2
@@ -346,7 +345,6 @@ impl<'a> Lexer<'a> {
             "dt" => Some(Token::DynamicType(number)),
             "gv" => Some(Token::GlobalValue(number)),
             "mt" => Some(Token::MemoryType(number)),
-            "table" => Some(Token::Table(number)),
             "const" => Some(Token::Constant(number)),
             "fn" => Some(Token::FuncRef(number)),
             "sig" => Some(Token::SigRef(number)),
diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs
index 6b2be439e..03c29fa4a 100644
--- a/cranelift/reader/src/parser.rs
+++ b/cranelift/reader/src/parser.rs
@@ -20,8 +20,7 @@ use cranelift_codegen::ir::{
     AbiParam, ArgumentExtension, ArgumentPurpose, Block, Constant, ConstantData, DynamicStackSlot,
     DynamicStackSlotData, DynamicTypeData, ExtFuncData, ExternalName, FuncRef, Function,
     GlobalValue, GlobalValueData, JumpTableData, MemFlags, MemoryTypeData, MemoryTypeField, Opcode,
-    SigRef, Signature, StackSlot, StackSlotData, StackSlotKind, Table, TableData, UserFuncName,
-    Value,
+    SigRef, Signature, StackSlot, StackSlotData, StackSlotKind, UserFuncName, Value,
 };
 use cranelift_codegen::isa::{self, CallConv};
 use cranelift_codegen::packed_option::ReservedValue;
@@ -346,30 +345,6 @@ impl Context {
         }
     }
 
-    // Allocate a table slot.
-    fn add_table(&mut self, table: Table, data: TableData, loc: Location) -> ParseResult<()> {
-        while self.function.tables.next_key().index() <= table.index() {
-            self.function.create_table(TableData {
-                base_gv: GlobalValue::reserved_value(),
-                min_size: Uimm64::new(0),
-                bound_gv: GlobalValue::reserved_value(),
-                element_size: Uimm64::new(0),
-                index_type: INVALID,
-            });
-        }
-        self.function.tables[table] = data;
-        self.map.def_table(table, loc)
-    }
-
-    // Resolve a reference to a table.
-    fn check_table(&self, table: Table, loc: Location) -> ParseResult<()> {
-        if !self.map.contains_table(table) {
-            err!(loc, "undefined table {}", table)
-        } else {
-            Ok(())
-        }
-    }
-
     // Allocate a new signature.
     fn add_sig(
         &mut self,
@@ -664,17 +639,6 @@ impl<'a> Parser<'a> {
         err!(self.loc, err_msg)
     }
 
-    // Match and consume a table reference.
-    fn match_table(&mut self, err_msg: &str) -> ParseResult<Table> {
-        if let Some(Token::Table(table)) = self.token() {
-            self.consume();
-            if let Some(table) = Table::with_number(table) {
-                return Ok(table);
-            }
-        }
-        err!(self.loc, err_msg)
-    }
-
     // Match and consume a memory-type reference.
     fn match_mt(&mut self, err_msg: &str) -> ParseResult<MemoryType> {
         if let Some(Token::MemoryType(mt)) = self.token() {
@@ -1483,11 +1447,6 @@ impl<'a> Parser<'a> {
                     self.parse_memory_type_decl()
                         .and_then(|(mt, dat)| ctx.add_mt(mt, dat, self.loc))
                 }
-                Some(Token::Table(..)) => {
-                    self.start_gathering_comments();
-                    self.parse_table_decl()
-                        .and_then(|(table, dat)| ctx.add_table(table, dat, self.loc))
-                }
                 Some(Token::SigRef(..)) => {
                     self.start_gathering_comments();
                     self.parse_signature_decl().and_then(|(sig, dat)| {
@@ -1781,71 +1740,6 @@ impl<'a> Parser<'a> {
         Ok((mt, data))
     }
 
-    // Parse a table decl.
-    //
-    // table-decl ::= * Table(table) "=" table-desc
-    // table-desc ::= table-style table-base { "," table-attr }
-    // table-style ::= "dynamic"
-    // table-base ::= GlobalValue(base)
-    // table-attr ::= "min" Imm64(bytes)
-    //              | "bound" Imm64(bytes)
-    //              | "element_size" Imm64(bytes)
-    //              | "index_type" type
-    //
-    fn parse_table_decl(&mut self) -> ParseResult<(Table, TableData)> {
-        let table = self.match_table("expected table number: table«n»")?;
-        self.match_token(Token::Equal, "expected '=' in table declaration")?;
-
-        let style_name = self.match_any_identifier("expected 'static' or 'dynamic'")?;
-
-        // table-desc ::= table-style * table-base { "," table-attr }
-        // table-base ::= * GlobalValue(base)
-        let base = match self.token() {
-            Some(Token::GlobalValue(base_num)) => match GlobalValue::with_number(base_num) {
-                Some(gv) => gv,
-                None => return err!(self.loc, "invalid global value number for table base"),
-            },
-            _ => return err!(self.loc, "expected table base"),
-        };
-        self.consume();
-
-        let mut data = TableData {
-            base_gv: base,
-            min_size: 0.into(),
-            bound_gv: GlobalValue::reserved_value(),
-            element_size: 0.into(),
-            index_type: ir::types::I32,
-        };
-
-        // table-desc ::= * { "," table-attr }
-        while self.optional(Token::Comma) {
-            match self.match_any_identifier("expected table attribute name")? {
-                "min" => {
-                    data.min_size = self.match_uimm64("expected integer min size")?;
-                }
-                "bound" => {
-                    data.bound_gv = match style_name {
-                        "dynamic" => self.match_gv("expected gv bound")?,
-                        t => return err!(self.loc, "unknown table style '{}'", t),
-                    };
-                }
-                "element_size" => {
-                    data.element_size = self.match_uimm64("expected integer element size")?;
-                }
-                "index_type" => {
-                    data.index_type = self.match_type("expected index type")?;
-                }
-                t => return err!(self.loc, "unknown table attribute '{}'", t),
-            }
-        }
-
-        // Collect any trailing comments.
-        self.token();
-        self.claim_gathered_comments(table);
-
-        Ok((table, data))
-    }
-
     // Parse a signature decl.
     //
     // signature-decl ::= SigRef(sigref) "=" signature
@@ -3141,19 +3035,6 @@ impl<'a> Parser<'a> {
                     dynamic_stack_slot: dss,
                 }
             }
-            InstructionFormat::TableAddr => {
-                let table = self.match_table("expected table identifier")?;
-                ctx.check_table(table, self.loc)?;
-                self.match_token(Token::Comma, "expected ',' between operands")?;
-                let arg = self.match_value("expected SSA value table address")?;
-                let offset = self.optional_offset32()?;
-                InstructionData::TableAddr {
-                    opcode,
-                    table,
-                    arg,
-                    offset,
-                }
-            }
             InstructionFormat::Load => {
                 let flags = self.optional_memflags();
                 let addr = self.match_value("expected SSA value address")?;
diff --git a/cranelift/reader/src/sourcemap.rs b/cranelift/reader/src/sourcemap.rs
index 99b55b9cb..36b10f1c8 100644
--- a/cranelift/reader/src/sourcemap.rs
+++ b/cranelift/reader/src/sourcemap.rs
@@ -11,7 +11,7 @@ use crate::lexer::split_entity_name;
 use cranelift_codegen::ir::entities::{AnyEntity, DynamicType};
 use cranelift_codegen::ir::{
     Block, Constant, DynamicStackSlot, FuncRef, GlobalValue, JumpTable, MemoryType, SigRef,
-    StackSlot, Table, Value,
+    StackSlot, Value,
 };
 use std::collections::HashMap;
 
@@ -49,11 +49,6 @@ impl SourceMap {
         self.locations.contains_key(&gv.into())
     }
 
-    /// Look up a table entity.
-    pub fn contains_table(&self, table: Table) -> bool {
-        self.locations.contains_key(&table.into())
-    }
-
     /// Look up a signature entity.
     pub fn contains_sig(&self, sig: SigRef) -> bool {
         self.locations.contains_key(&sig.into())
@@ -106,13 +101,6 @@ impl SourceMap {
                     Some(gv.into())
                 }
             }),
-            "table" => Table::with_number(num).and_then(|table| {
-                if !self.contains_table(table) {
-                    None
-                } else {
-                    Some(table.into())
-                }
-            }),
             "sig" => SigRef::with_number(num).and_then(|sig| {
                 if !self.contains_sig(sig) {
                     None
@@ -187,11 +175,6 @@ impl SourceMap {
         self.def_entity(entity.into(), loc)
     }
 
-    /// Define the table `entity`.
-    pub fn def_table(&mut self, entity: Table, loc: Location) -> ParseResult<()> {
-        self.def_entity(entity.into(), loc)
-    }
-
     /// Define the signature `entity`.
     pub fn def_sig(&mut self, entity: SigRef, loc: Location) -> ParseResult<()> {
         self.def_entity(entity.into(), loc)
diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs
index 03ad76c4e..b093e666f 100644
--- a/crates/cranelift/src/func_environ.rs
+++ b/crates/cranelift/src/func_environ.rs
@@ -1579,10 +1579,10 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
                 // pseudocode:
                 //
                 // ```
-                // if value != null:
-                //     value.ref_count += 1
                 // let current_elem = table[index]
                 // table[index] = value
+                // if value != null:
+                //     value.ref_count += 1
                 // if current_elem != null:
                 //     current_elem.ref_count -= 1
                 //     if current_elem.ref_count == 0:
@@ -1615,16 +1615,27 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
                 let continue_block = builder.create_block();
                 builder.insert_block_after(continue_block, drop_block);
 
-                // Calculate the table address of the current element and do
-                // bounds checks. This is the first thing we do, because we
-                // don't want to modify any ref counts if this `table.set` is
-                // going to trap.
+                // Grab the current element from the table if it's in-bounds,
+                // and store the new `value` into the table. This is the first
+                // thing we do, because we don't want to modify the ref count
+                // on `value` if this `table.set` is going to trap due to an
+                // out-of-bounds index.
+                //
+                // Note that we load the current element as a pointer, not a
+                // reference. This is so that if we call out-of-line to run its
+                // destructor, and its destructor triggers GC, this reference is
+                // not recorded in the stack map (which would lead to the GC
+                // saving a reference to a deallocated object, and then using it
+                // after its been freed).
                 let table_entry_addr = table_data.prepare_table_addr(
                     builder,
                     index,
                     pointer_type,
                     self.isa.flags().enable_table_access_spectre_mitigation(),
                 );
+                let flags = ir::MemFlags::trusted().with_table();
+                let current_elem = builder.ins().load(pointer_type, flags, table_entry_addr, 0);
+                builder.ins().store(flags, value, table_entry_addr, 0);
 
                 // If value is not null, increment `value`'s ref count.
                 //
@@ -1647,23 +1658,10 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
                 self.mutate_externref_ref_count(builder, value, 1);
                 builder.ins().jump(check_current_elem_block, &[]);
 
-                // Grab the current element from the table, and store the new
-                // `value` into the table.
-                //
-                // Note that we load the current element as a pointer, not a
-                // reference. This is so that if we call out-of-line to run its
-                // destructor, and its destructor triggers GC, this reference is
-                // not recorded in the stack map (which would lead to the GC
-                // saving a reference to a deallocated object, and then using it
-                // after its been freed).
-                builder.switch_to_block(check_current_elem_block);
-                let flags = ir::MemFlags::trusted().with_table();
-                let current_elem = builder.ins().load(pointer_type, flags, table_entry_addr, 0);
-                builder.ins().store(flags, value, table_entry_addr, 0);
-
                 // If the current element is non-null, decrement its reference
                 // count. And if its reference count has reached zero, then make
                 // an out-of-line call to deallocate it.
+                builder.switch_to_block(check_current_elem_block);
                 let current_elem_is_null =
                     builder
                         .ins()
diff --git a/crates/wit-bindgen/src/types.rs b/crates/wit-bindgen/src/types.rs
index 208b2f865..b41360495 100644
--- a/crates/wit-bindgen/src/types.rs
+++ b/crates/wit-bindgen/src/types.rs
@@ -40,6 +40,14 @@ impl std::ops::BitOrAssign for TypeInfo {
 
 impl Types {
     pub fn analyze(&mut self, resolve: &Resolve, world: WorldId) {
+        // Build up all type information first which is inherited through types,
+        // such as properties of borrows/lists/etc.
+        for (t, _) in resolve.types.iter() {
+            self.type_id_info(resolve, t);
+        }
+
+        // ... next handle borrowed/owned flags which aren't inherited through
+        // types.
         let world = &resolve.worlds[world];
         for (import, (_, item)) in world
             .imports
diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md
index f4758b7ef..d6ce4a5d9 100644
--- a/docs/SUMMARY.md
+++ b/docs/SUMMARY.md
@@ -1,16 +1,13 @@
 # Summary
 
 - [Introduction](./introduction.md)
-- [Examples](./examples.md)
-  - [Debugging WebAssembly](./examples-debugging.md)
-    - [Debugging with `gdb` and `lldb`](./examples-debugging-native-debugger.md)
-    - [Debugging with Core Dumps](./examples-debugging-core-dumps.md)
-  - [Profiling WebAssembly](./examples-profiling.md)
-    - [Profiling with Perf](./examples-profiling-perf.md)
-    - [Profiling with VTune](./examples-profiling-vtune.md)
-    - [Profiling with samply](./examples-profiling-samply.md)
-    - [Cross-platform Profiling](./examples-profiling-guest.md)
-  - [Embedding in Rust](./examples-rust-embed.md)
+- [Using the Wasmtime CLI](./cli.md)
+  - [Installation](./cli-install.md)
+  - [CLI Options](./cli-options.md)
+  - [CLI Logging](./cli-logging.md)
+  - [Cache Configuration](./cli-cache.md)
+- [Using the Wasmtime API](./lang.md)
+  - [Rust](./lang-rust.md)
     - [Hello, world!](./examples-rust-hello-world.md)
     - [Calculating the GCD](./examples-rust-gcd.md)
     - [Using Linear Memory](./examples-rust-memory.md)
@@ -19,7 +16,7 @@
     - [Debugging](./examples-rust-debugging.md)
     - [Core Dumps](./examples-rust-core-dumps.md)
     - [Using Multi-Value](./examples-rust-multi-value.md)
-  - [Embedding in C](./examples-c-embed.md)
+  - [C](./lang-c.md)
     - [Hello, World!](./examples-c-hello-world.md)
     - [Calculating the GCD](./examples-c-gcd.md)
     - [Using Linear Memory](./examples-c-memory.md)
@@ -27,21 +24,23 @@
     - [Linking Modules](./examples-c-linking.md)
     - [Debugging](./examples-c-debugging.md)
     - [Using Multi-Value](./examples-c-multi-value.md)
-  - [Checking Guests' Memory Accesses](./wmemcheck.md)
-  - [Building a minimal embedding](./examples-minimal.md)
-- [Using WebAssembly from your language](./lang.md)
-  - [Rust](./lang-rust.md)
-  - [C](./lang-c.md)
   - [Python](./lang-python.md)
   - [.NET](./lang-dotnet.md)
   - [Go](./lang-go.md)
   - [Bash](./lang-bash.md)
   - [Ruby](./lang-ruby.md)
-- [Using the `wasmtime` CLI](./cli.md)
-  - [Installation](./cli-install.md)
-  - [CLI Options](./cli-options.md)
-  - [CLI Logging](./cli-logging.md)
-  - [Cache Configuration](./cli-cache.md)
+  - [Elixir](./lang-elixir.md)
+- [Further Examples](./examples.md)
+  - [Debugging WebAssembly](./examples-debugging.md)
+    - [Debugging with `gdb` and `lldb`](./examples-debugging-native-debugger.md)
+    - [Debugging with Core Dumps](./examples-debugging-core-dumps.md)
+  - [Profiling WebAssembly](./examples-profiling.md)
+    - [Profiling with Perf](./examples-profiling-perf.md)
+    - [Profiling with VTune](./examples-profiling-vtune.md)
+    - [Profiling with samply](./examples-profiling-samply.md)
+    - [Cross-platform Profiling](./examples-profiling-guest.md)
+  - [Checking Guests' Memory Accesses](./wmemcheck.md)
+  - [Building a minimal embedding](./examples-minimal.md)
 - [Stability](stability.md)
   - [Release Process](./stability-release.md)
   - [Tiers of support](./stability-tiers.md)
diff --git a/docs/cli-install.md b/docs/cli-install.md
index b6f98b42b..ffd370f62 100644
--- a/docs/cli-install.md
+++ b/docs/cli-install.md
@@ -2,7 +2,7 @@
 
 Here we'll show you how to install the `wasmtime` command line tool. Note that
 this is distinct from embedding the Wasmtime project into another, for that
-you'll want to consult the [embedding documentation](embed.md).
+you'll want to consult the [embedding documentation](lang.md).
 
 The easiest way to install the `wasmtime` CLI tool is through our installation
 script. Linux and macOS users can execute the following:
diff --git a/docs/examples-c-embed.md b/docs/examples-c-embed.md
index c6dc5b678..57b88955a 100644
--- a/docs/examples-c-embed.md
+++ b/docs/examples-c-embed.md
@@ -1,6 +1,1 @@
-# Embedding in C
-
-This section is intended to showcase the C embedding API for Wasmtime. Full
-reference documentation for the C API [can be found online][online]
-
-[online]: https://bytecodealliance.github.io/wasmtime/c-api/
+# C
diff --git a/docs/examples-profiling.md b/docs/examples-profiling.md
index 348979c03..d1b0ab0c7 100644
--- a/docs/examples-profiling.md
+++ b/docs/examples-profiling.md
@@ -20,14 +20,16 @@ platforms:
 - For Intel's x86 CPUs on Linux or Windows, we support
   [VTune](./examples-profiling-vtune.md).
 
-- For everything else, see the cross-platform profiler below.
+- For Linux and macOS, we support [samply](./examples-profiling-samply.md).
+
+- For everything else, see the [cross-platform
+  profiler](./examples-profiling-guest.md).
 
 The native profilers can measure time spent in WebAssembly guest code as well as
 time spent in the Wasmtime host and potentially even time spent in the kernel.
 This provides a comprehensive view of performance.
 
-If the native profiling tools don't work for you, Wasmtime also provides a
-[cross-platform profiler](./examples-profiling-guest.md). This profiler can only
-measure time spent in WebAssembly guest code, and its timing measurements are
-not as precise as the native profilers. However, it works on every platform that
-Wasmtime supports.
+The cross-platform-profiler can only measure time spent in WebAssembly guest
+code, and its timing measurements are not as precise as the native profilers.
+However, it works on every platform that Wasmtime supports.
+
diff --git a/docs/examples-rust-embed.md b/docs/examples-rust-embed.md
deleted file mode 100644
index 14b4b1ebb..000000000
--- a/docs/examples-rust-embed.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Embedding in Rust
-
-This section is intended to showcase the Rust embedding API for Wasmtime. This
-is done through the [`wasmtime` crate](https://crates.io/crates/wasmtime). In
-addition to browsing the following examples you can also browse the [full API
-documentation](https://docs.rs/wasmtime).
diff --git a/docs/examples.md b/docs/examples.md
index f2cc544ef..1ab9078d1 100644
--- a/docs/examples.md
+++ b/docs/examples.md
@@ -1,4 +1,4 @@
-# Examples
+# Further Examples
 
 The examples contained in this section explain how to use Wasmtime in several
 common scenarios.
diff --git a/docs/lang-c.md b/docs/lang-c.md
index a40cd10d1..5db21c927 100644
--- a/docs/lang-c.md
+++ b/docs/lang-c.md
@@ -1,7 +1,6 @@
-# C
+# Embedding in C
 
-... more coming soon
+This section is intended to showcase the C embedding API for Wasmtime. Full
+reference documentation for the C API [can be found online][online].
 
-In the meantime, have a look at the [C API docs].
-
-[C API docs]: https://bytecodealliance.github.io/wasmtime/c-api/
+[online]: https://bytecodealliance.github.io/wasmtime/c-api/
diff --git a/docs/lang-rust.md b/docs/lang-rust.md
index cc5d57248..9199605a1 100644
--- a/docs/lang-rust.md
+++ b/docs/lang-rust.md
@@ -3,18 +3,16 @@
 This document shows an example of how to embed Wasmtime using the [Rust
 API][apidoc] to execute a simple wasm program. Be sure to also check out the
 [full API documentation][apidoc] for a full listing of what the [`wasmtime`
-crate][wasmtime] has to offer and the [book examples for
-Rust](./examples-rust-embed.md) for more information.
+crate][wasmtime] has to offer.
 
 [apidoc]: https://bytecodealliance.github.io/wasmtime/api/wasmtime/
 [wasmtime]: https://crates.io/crates/wasmtime
 
 ## Creating the WebAssembly to execute
 
-Creation of a WebAssembly file is generally covered by the [Writing
-WebAssembly chapter](./wasm.md), so we'll just assume that you've already got a
-wasm file on hand for the rest of this tutorial. To make things simple we'll
-also just assume you've got a `hello.wat` file which looks like this:
+We'll just assume that you've already got a wasm file on hand for the rest of
+this tutorial. To make things simple we'll also just assume you've got a
+`hello.wat` file which looks like this:
 
 ```wat
 (module
diff --git a/docs/lang.md b/docs/lang.md
index 94e38d1b0..be222bf6e 100644
--- a/docs/lang.md
+++ b/docs/lang.md
@@ -1,4 +1,4 @@
-# Using WebAssembly from your Language
+# Using the Wasmtime API
 
 Wasmtime can be used as a library to embed WebAssembly execution support
 within applications. Wasmtime is written in Rust, but bindings are available


Test patch:
diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-bitselect.clif b/cranelift/filetests/filetests/isa/riscv64/simd-bitselect.clif
index 28c0092ac..eb9186895 100644
--- a/cranelift/filetests/filetests/isa/riscv64/simd-bitselect.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/simd-bitselect.clif
@@ -410,3 +410,115 @@ block0(v0: i64x2, v1: i64x2, v2: f64x2, v3: f64x2):
 ;   addi sp, sp, 0x10
 ;   ret
 
+function %bitselect_i8x16_fcmp_f64x2(i8x16) -> i8x16 fast {
+    const0 = 0x00000000000000000000000000000000
+
+block0(v0: i8x16):
+    v1 = bitcast.f64x2 little v0
+    v2 = fcmp eq v1, v1
+    v3 = bitcast.i8x16 little v2
+    v4 = vconst.i8x16 const0
+    v5 = bitselect.i8x16 v3, v0, v4
+    return v5
+}
+
+; VCode:
+;   addi sp,sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v9,16(fp) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vle8.v v14,[const(0)] #avl=16, #vtype=(e8, m1, ta, ma)
+;   vmfeq.vv v0,v9,v9 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vmerge.vvm v15,v14,v9,v0.t #avl=2, #vtype=(e64, m1, ta, ma)
+;   vse8.v v15,0(a0) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   addi sp,sp,16
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   mv s0, sp
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, s0, 0x10
+;   .byte 0x87, 0x84, 0x0f, 0x02
+;   auipc t6, 0
+;   addi t6, t6, 0x34
+;   .byte 0x07, 0x87, 0x0f, 0x02
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0x57, 0x90, 0x94, 0x62
+;   .byte 0xd7, 0x87, 0xe4, 0x5c
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0xa7, 0x07, 0x05, 0x02
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   .byte 0x00, 0x00, 0x00, 0x00
+
+function %bitselect_i8x16_icmp_i64x2(i8x16) -> i8x16 fast {
+    const0 = 0x00000000000000000000000000000000
+
+block0(v0: i8x16):
+    v1 = bitcast.i64x2 little v0
+    v2 = icmp eq v1, v1
+    v3 = bitcast.i8x16 little v2
+    v4 = vconst.i8x16 const0
+    v5 = bitselect.i8x16 v3, v0, v4
+    return v5
+}
+
+; VCode:
+;   addi sp,sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v9,16(fp) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vle8.v v14,[const(0)] #avl=16, #vtype=(e8, m1, ta, ma)
+;   vmseq.vv v0,v9,v9 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vmerge.vvm v15,v14,v9,v0.t #avl=2, #vtype=(e64, m1, ta, ma)
+;   vse8.v v15,0(a0) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   addi sp,sp,16
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   mv s0, sp
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, s0, 0x10
+;   .byte 0x87, 0x84, 0x0f, 0x02
+;   auipc t6, 0
+;   addi t6, t6, 0x34
+;   .byte 0x07, 0x87, 0x0f, 0x02
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0x57, 0x80, 0x94, 0x62
+;   .byte 0xd7, 0x87, 0xe4, 0x5c
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0xa7, 0x07, 0x05, 0x02
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   .byte 0x00, 0x00, 0x00, 0x00
+
diff --git a/cranelift/filetests/filetests/isa/s390x/vec-abi.clif b/cranelift/filetests/filetests/isa/s390x/vec-abi.clif
index 76130443c..f05272f67 100644
--- a/cranelift/filetests/filetests/isa/s390x/vec-abi.clif
+++ b/cranelift/filetests/filetests/isa/s390x/vec-abi.clif
@@ -1,247 +1,391 @@
 test compile precise-output
 target s390x
 
-function %caller_be_to_be(i64x2, i32x4, i16x8, i8x16) -> i32x4 {
-    fn0 = %callee_be(i64x2, i32x4, i16x8, i8x16) -> i32x4
+function %caller_be_to_be(i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16) -> i32x4 {
+    fn0 = %callee_be(i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16) -> i32x4
 
-block0(v0: i64x2, v1: i32x4, v2: i16x8, v3: i8x16):
-    v4 = call fn0(v0, v1, v2, v3)
-    return v4
+block0(v0: i64x2, v1: i32x4, v2: i16x8, v3: i8x16, v4: i64x2, v5: i32x4, v6: i16x8, v7: i8x16, v8: i64x2, v9: i32x4, v10: i16x8, v11: i8x16):
+    v12 = call fn0(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
+    return v12
 }
 
 ; VCode:
 ;   stmg %r14, %r15, 112(%r15)
-;   aghi %r15, -160
-;   virtual_sp_offset_adjust 160
+;   aghi %r15, -224
+;   virtual_sp_offset_adjust 224
 ; block0:
+;   vl %v17, 384(%r15)
+;   vl %v19, 400(%r15)
+;   vl %v21, 416(%r15)
+;   vl %v23, 432(%r15)
+;   vst %v17, 160(%r15)
+;   vst %v19, 176(%r15)
+;   vst %v21, 192(%r15)
+;   vst %v23, 208(%r15)
 ;   bras %r1, 12 ; data %callee_be + 0 ; lg %r4, 0(%r1)
 ;   basr %r14, %r4
-;   lmg %r14, %r15, 272(%r15)
+;   lmg %r14, %r15, 336(%r15)
 ;   br %r14
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   stmg %r14, %r15, 0x70(%r15)
-;   aghi %r15, -0xa0
+;   aghi %r15, -0xe0
 ; block1: ; offset 0xa
-;   bras %r1, 0x16
+;   vl %v17, 0x180(%r15)
+;   vl %v19, 0x190(%r15)
+;   vl %v21, 0x1a0(%r15)
+;   vl %v23, 0x1b0(%r15)
+;   vst %v17, 0xa0(%r15)
+;   vst %v19, 0xb0(%r15)
+;   vst %v21, 0xc0(%r15)
+;   vst %v23, 0xd0(%r15)
+;   bras %r1, 0x46
 ;   .byte 0x00, 0x00 ; reloc_external Abs8 %callee_be 0
 ;   .byte 0x00, 0x00
 ;   .byte 0x00, 0x00
 ;   .byte 0x00, 0x00
 ;   lg %r4, 0(%r1)
 ;   basr %r14, %r4
-;   lmg %r14, %r15, 0x110(%r15)
+;   lmg %r14, %r15, 0x150(%r15)
 ;   br %r14
 
-function %caller_be_to_le(i64x2, i32x4, i16x8, i8x16) -> i32x4 {
-    fn0 = %callee_le(i64x2, i32x4, i16x8, i8x16) -> i32x4 wasmtime_system_v
+function %caller_be_to_le(i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16) -> i32x4 {
+    fn0 = %callee_le(i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16) -> i32x4 wasmtime_system_v
 
-block0(v0: i64x2, v1: i32x4, v2: i16x8, v3: i8x16):
-    v4 = call fn0(v0, v1, v2, v3)
-    return v4
+block0(v0: i64x2, v1: i32x4, v2: i16x8, v3: i8x16, v4: i64x2, v5: i32x4, v6: i16x8, v7: i8x16, v8: i64x2, v9: i32x4, v10: i16x8, v11: i8x16):
+    v12 = call fn0(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
+    return v12
 }
 
 ; VCode:
 ;   stmg %r14, %r15, 112(%r15)
-;   aghi %r15, -224
-;   virtual_sp_offset_adjust 160
-;   std %f8, 160(%r15)
-;   std %f9, 168(%r15)
-;   std %f10, 176(%r15)
-;   std %f11, 184(%r15)
-;   std %f12, 192(%r15)
-;   std %f13, 200(%r15)
-;   std %f14, 208(%r15)
-;   std %f15, 216(%r15)
+;   aghi %r15, -288
+;   virtual_sp_offset_adjust 224
+;   std %f8, 224(%r15)
+;   std %f9, 232(%r15)
+;   std %f10, 240(%r15)
+;   std %f11, 248(%r15)
+;   std %f12, 256(%r15)
+;   std %f13, 264(%r15)
+;   std %f14, 272(%r15)
+;   std %f15, 280(%r15)
 ; block0:
+;   vl %v17, 448(%r15)
+;   vl %v19, 464(%r15)
+;   vl %v21, 480(%r15)
+;   vl %v23, 496(%r15)
 ;   vpdi %v24, %v24, %v24, 4
-;   vpdi %v7, %v25, %v25, 4
-;   verllg %v25, %v7, 32
-;   vpdi %v19, %v26, %v26, 4
-;   verllg %v21, %v19, 32
-;   verllf %v26, %v21, 16
-;   vpdi %v27, %v27, %v27, 4
-;   verllg %v27, %v27, 32
-;   verllf %v29, %v27, 16
-;   verllh %v27, %v29, 8
+;   vpdi %v0, %v25, %v25, 4
+;   verllg %v25, %v0, 32
+;   vpdi %v0, %v26, %v26, 4
+;   verllg %v1, %v0, 32
+;   verllf %v26, %v1, 16
+;   vpdi %v5, %v27, %v27, 4
+;   verllg %v7, %v5, 32
+;   verllf %v18, %v7, 16
+;   verllh %v27, %v18, 8
+;   vpdi %v28, %v28, %v28, 4
+;   vpdi %v29, %v29, %v29, 4
+;   verllg %v29, %v29, 32
+;   vpdi %v30, %v30, %v30, 4
+;   verllg %v30, %v30, 32
+;   verllf %v30, %v30, 16
+;   vpdi %v1, %v31, %v31, 4
+;   verllg %v3, %v1, 32
+;   verllf %v5, %v3, 16
+;   verllh %v31, %v5, 8
+;   vpdi %v17, %v17, %v17, 4
+;   vst %v17, 160(%r15)
+;   vpdi %v20, %v19, %v19, 4
+;   verllg %v22, %v20, 32
+;   vst %v22, 176(%r15)
+;   vpdi %v0, %v21, %v21, 4
+;   verllg %v0, %v0, 32
+;   verllf %v0, %v0, 16
+;   vst %v0, 192(%r15)
+;   vpdi %v0, %v23, %v23, 4
+;   verllg %v2, %v0, 32
+;   verllf %v4, %v2, 16
+;   verllh %v6, %v4, 8
+;   vst %v6, 208(%r15)
 ;   bras %r1, 12 ; data %callee_le + 0 ; lg %r4, 0(%r1)
 ;   basr %r14, %r4
-;   vpdi %v5, %v24, %v24, 4
-;   verllg %v24, %v5, 32
-;   ld %f8, 160(%r15)
-;   ld %f9, 168(%r15)
-;   ld %f10, 176(%r15)
-;   ld %f11, 184(%r15)
-;   ld %f12, 192(%r15)
-;   ld %f13, 200(%r15)
-;   ld %f14, 208(%r15)
-;   ld %f15, 216(%r15)
-;   lmg %r14, %r15, 336(%r15)
+;   vpdi %v21, %v24, %v24, 4
+;   verllg %v24, %v21, 32
+;   ld %f8, 224(%r15)
+;   ld %f9, 232(%r15)
+;   ld %f10, 240(%r15)
+;   ld %f11, 248(%r15)
+;   ld %f12, 256(%r15)
+;   ld %f13, 264(%r15)
+;   ld %f14, 272(%r15)
+;   ld %f15, 280(%r15)
+;   lmg %r14, %r15, 400(%r15)
 ;   br %r14
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   stmg %r14, %r15, 0x70(%r15)
-;   aghi %r15, -0xe0
-;   std %f8, 0xa0(%r15)
-;   std %f9, 0xa8(%r15)
-;   std %f10, 0xb0(%r15)
-;   std %f11, 0xb8(%r15)
-;   std %f12, 0xc0(%r15)
-;   std %f13, 0xc8(%r15)
-;   std %f14, 0xd0(%r15)
-;   std %f15, 0xd8(%r15)
+;   aghi %r15, -0x120
+;   std %f8, 0xe0(%r15)
+;   std %f9, 0xe8(%r15)
+;   std %f10, 0xf0(%r15)
+;   std %f11, 0xf8(%r15)
+;   std %f12, 0x100(%r15)
+;   std %f13, 0x108(%r15)
+;   std %f14, 0x110(%r15)
+;   std %f15, 0x118(%r15)
 ; block1: ; offset 0x2a
+;   vl %v17, 0x1c0(%r15)
+;   vl %v19, 0x1d0(%r15)
+;   vl %v21, 0x1e0(%r15)
+;   vl %v23, 0x1f0(%r15)
 ;   vpdi %v24, %v24, %v24, 4
-;   vpdi %v7, %v25, %v25, 4
-;   verllg %v25, %v7, 0x20
-;   vpdi %v19, %v26, %v26, 4
-;   verllg %v21, %v19, 0x20
-;   verllf %v26, %v21, 0x10
-;   vpdi %v27, %v27, %v27, 4
-;   verllg %v27, %v27, 0x20
-;   verllf %v29, %v27, 0x10
-;   verllh %v27, %v29, 8
-;   bras %r1, 0x72
+;   vpdi %v0, %v25, %v25, 4
+;   verllg %v25, %v0, 0x20
+;   vpdi %v0, %v26, %v26, 4
+;   verllg %v1, %v0, 0x20
+;   verllf %v26, %v1, 0x10
+;   vpdi %v5, %v27, %v27, 4
+;   verllg %v7, %v5, 0x20
+;   verllf %v18, %v7, 0x10
+;   verllh %v27, %v18, 8
+;   vpdi %v28, %v28, %v28, 4
+;   vpdi %v29, %v29, %v29, 4
+;   verllg %v29, %v29, 0x20
+;   vpdi %v30, %v30, %v30, 4
+;   verllg %v30, %v30, 0x20
+;   verllf %v30, %v30, 0x10
+;   vpdi %v1, %v31, %v31, 4
+;   verllg %v3, %v1, 0x20
+;   verllf %v5, %v3, 0x10
+;   verllh %v31, %v5, 8
+;   vpdi %v17, %v17, %v17, 4
+;   vst %v17, 0xa0(%r15)
+;   vpdi %v20, %v19, %v19, 4
+;   verllg %v22, %v20, 0x20
+;   vst %v22, 0xb0(%r15)
+;   vpdi %v0, %v21, %v21, 4
+;   verllg %v0, %v0, 0x20
+;   verllf %v0, %v0, 0x10
+;   vst %v0, 0xc0(%r15)
+;   vpdi %v0, %v23, %v23, 4
+;   verllg %v2, %v0, 0x20
+;   verllf %v4, %v2, 0x10
+;   verllh %v6, %v4, 8
+;   vst %v6, 0xd0(%r15)
+;   bras %r1, 0x11a
 ;   .byte 0x00, 0x00 ; reloc_external Abs8 %callee_le 0
 ;   .byte 0x00, 0x00
 ;   .byte 0x00, 0x00
 ;   .byte 0x00, 0x00
 ;   lg %r4, 0(%r1)
 ;   basr %r14, %r4
-;   vpdi %v5, %v24, %v24, 4
-;   verllg %v24, %v5, 0x20
-;   ld %f8, 0xa0(%r15)
-;   ld %f9, 0xa8(%r15)
-;   ld %f10, 0xb0(%r15)
-;   ld %f11, 0xb8(%r15)
-;   ld %f12, 0xc0(%r15)
-;   ld %f13, 0xc8(%r15)
-;   ld %f14, 0xd0(%r15)
-;   ld %f15, 0xd8(%r15)
-;   lmg %r14, %r15, 0x150(%r15)
+;   vpdi %v21, %v24, %v24, 4
+;   verllg %v24, %v21, 0x20
+;   ld %f8, 0xe0(%r15)
+;   ld %f9, 0xe8(%r15)
+;   ld %f10, 0xf0(%r15)
+;   ld %f11, 0xf8(%r15)
+;   ld %f12, 0x100(%r15)
+;   ld %f13, 0x108(%r15)
+;   ld %f14, 0x110(%r15)
+;   ld %f15, 0x118(%r15)
+;   lmg %r14, %r15, 0x190(%r15)
 ;   br %r14
 
-function %caller_le_to_be(i64x2, i32x4, i16x8, i8x16) -> i32x4 wasmtime_system_v {
-    fn0 = %callee_be(i64x2, i32x4, i16x8, i8x16) -> i32x4
+function %caller_le_to_be(i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16) -> i32x4 wasmtime_system_v {
+    fn0 = %callee_be(i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16) -> i32x4
 
-block0(v0: i64x2, v1: i32x4, v2: i16x8, v3: i8x16):
-    v4 = call fn0(v0, v1, v2, v3)
-    return v4
+block0(v0: i64x2, v1: i32x4, v2: i16x8, v3: i8x16, v4: i64x2, v5: i32x4, v6: i16x8, v7: i8x16, v8: i64x2, v9: i32x4, v10: i16x8, v11: i8x16):
+    v12 = call fn0(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
+    return v12
 }
 
 ; VCode:
 ;   stmg %r14, %r15, 112(%r15)
-;   aghi %r15, -224
-;   virtual_sp_offset_adjust 160
-;   std %f8, 160(%r15)
-;   std %f9, 168(%r15)
-;   std %f10, 176(%r15)
-;   std %f11, 184(%r15)
-;   std %f12, 192(%r15)
-;   std %f13, 200(%r15)
-;   std %f14, 208(%r15)
-;   std %f15, 216(%r15)
+;   aghi %r15, -288
+;   virtual_sp_offset_adjust 224
+;   std %f8, 224(%r15)
+;   std %f9, 232(%r15)
+;   std %f10, 240(%r15)
+;   std %f11, 248(%r15)
+;   std %f12, 256(%r15)
+;   std %f13, 264(%r15)
+;   std %f14, 272(%r15)
+;   std %f15, 280(%r15)
 ; block0:
+;   vl %v17, 448(%r15)
+;   vl %v19, 464(%r15)
+;   vl %v21, 480(%r15)
+;   vl %v23, 496(%r15)
 ;   vpdi %v24, %v24, %v24, 4
-;   vpdi %v7, %v25, %v25, 4
-;   verllg %v25, %v7, 32
-;   vpdi %v19, %v26, %v26, 4
-;   verllg %v21, %v19, 32
-;   verllf %v26, %v21, 16
-;   vpdi %v27, %v27, %v27, 4
-;   verllg %v27, %v27, 32
-;   verllf %v29, %v27, 16
-;   verllh %v27, %v29, 8
+;   vpdi %v0, %v25, %v25, 4
+;   verllg %v25, %v0, 32
+;   vpdi %v0, %v26, %v26, 4
+;   verllg %v1, %v0, 32
+;   verllf %v26, %v1, 16
+;   vpdi %v5, %v27, %v27, 4
+;   verllg %v7, %v5, 32
+;   verllf %v18, %v7, 16
+;   verllh %v27, %v18, 8
+;   vpdi %v28, %v28, %v28, 4
+;   vpdi %v29, %v29, %v29, 4
+;   verllg %v29, %v29, 32
+;   vpdi %v30, %v30, %v30, 4
+;   verllg %v30, %v30, 32
+;   verllf %v30, %v30, 16
+;   vpdi %v1, %v31, %v31, 4
+;   verllg %v3, %v1, 32
+;   verllf %v5, %v3, 16
+;   verllh %v31, %v5, 8
+;   vpdi %v17, %v17, %v17, 4
+;   vst %v17, 160(%r15)
+;   vpdi %v20, %v19, %v19, 4
+;   verllg %v22, %v20, 32
+;   vst %v22, 176(%r15)
+;   vpdi %v0, %v21, %v21, 4
+;   verllg %v0, %v0, 32
+;   verllf %v0, %v0, 16
+;   vst %v0, 192(%r15)
+;   vpdi %v0, %v23, %v23, 4
+;   verllg %v2, %v0, 32
+;   verllf %v4, %v2, 16
+;   verllh %v6, %v4, 8
+;   vst %v6, 208(%r15)
 ;   bras %r1, 12 ; data %callee_be + 0 ; lg %r4, 0(%r1)
 ;   basr %r14, %r4
-;   vpdi %v5, %v24, %v24, 4
-;   verllg %v24, %v5, 32
-;   ld %f8, 160(%r15)
-;   ld %f9, 168(%r15)
-;   ld %f10, 176(%r15)
-;   ld %f11, 184(%r15)
-;   ld %f12, 192(%r15)
-;   ld %f13, 200(%r15)
-;   ld %f14, 208(%r15)
-;   ld %f15, 216(%r15)
-;   lmg %r14, %r15, 336(%r15)
+;   vpdi %v21, %v24, %v24, 4
+;   verllg %v24, %v21, 32
+;   ld %f8, 224(%r15)
+;   ld %f9, 232(%r15)
+;   ld %f10, 240(%r15)
+;   ld %f11, 248(%r15)
+;   ld %f12, 256(%r15)
+;   ld %f13, 264(%r15)
+;   ld %f14, 272(%r15)
+;   ld %f15, 280(%r15)
+;   lmg %r14, %r15, 400(%r15)
 ;   br %r14
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   stmg %r14, %r15, 0x70(%r15)
-;   aghi %r15, -0xe0
-;   std %f8, 0xa0(%r15)
-;   std %f9, 0xa8(%r15)
-;   std %f10, 0xb0(%r15)
-;   std %f11, 0xb8(%r15)
-;   std %f12, 0xc0(%r15)
-;   std %f13, 0xc8(%r15)
-;   std %f14, 0xd0(%r15)
-;   std %f15, 0xd8(%r15)
+;   aghi %r15, -0x120
+;   std %f8, 0xe0(%r15)
+;   std %f9, 0xe8(%r15)
+;   std %f10, 0xf0(%r15)
+;   std %f11, 0xf8(%r15)
+;   std %f12, 0x100(%r15)
+;   std %f13, 0x108(%r15)
+;   std %f14, 0x110(%r15)
+;   std %f15, 0x118(%r15)
 ; block1: ; offset 0x2a
+;   vl %v17, 0x1c0(%r15)
+;   vl %v19, 0x1d0(%r15)
+;   vl %v21, 0x1e0(%r15)
+;   vl %v23, 0x1f0(%r15)
 ;   vpdi %v24, %v24, %v24, 4
-;   vpdi %v7, %v25, %v25, 4
-;   verllg %v25, %v7, 0x20
-;   vpdi %v19, %v26, %v26, 4
-;   verllg %v21, %v19, 0x20
-;   verllf %v26, %v21, 0x10
-;   vpdi %v27, %v27, %v27, 4
-;   verllg %v27, %v27, 0x20
-;   verllf %v29, %v27, 0x10
-;   verllh %v27, %v29, 8
-;   bras %r1, 0x72
+;   vpdi %v0, %v25, %v25, 4
+;   verllg %v25, %v0, 0x20
+;   vpdi %v0, %v26, %v26, 4
+;   verllg %v1, %v0, 0x20
+;   verllf %v26, %v1, 0x10
+;   vpdi %v5, %v27, %v27, 4
+;   verllg %v7, %v5, 0x20
+;   verllf %v18, %v7, 0x10
+;   verllh %v27, %v18, 8
+;   vpdi %v28, %v28, %v28, 4
+;   vpdi %v29, %v29, %v29, 4
+;   verllg %v29, %v29, 0x20
+;   vpdi %v30, %v30, %v30, 4
+;   verllg %v30, %v30, 0x20
+;   verllf %v30, %v30, 0x10
+;   vpdi %v1, %v31, %v31, 4
+;   verllg %v3, %v1, 0x20
+;   verllf %v5, %v3, 0x10
+;   verllh %v31, %v5, 8
+;   vpdi %v17, %v17, %v17, 4
+;   vst %v17, 0xa0(%r15)
+;   vpdi %v20, %v19, %v19, 4
+;   verllg %v22, %v20, 0x20
+;   vst %v22, 0xb0(%r15)
+;   vpdi %v0, %v21, %v21, 4
+;   verllg %v0, %v0, 0x20
+;   verllf %v0, %v0, 0x10
+;   vst %v0, 0xc0(%r15)
+;   vpdi %v0, %v23, %v23, 4
+;   verllg %v2, %v0, 0x20
+;   verllf %v4, %v2, 0x10
+;   verllh %v6, %v4, 8
+;   vst %v6, 0xd0(%r15)
+;   bras %r1, 0x11a
 ;   .byte 0x00, 0x00 ; reloc_external Abs8 %callee_be 0
 ;   .byte 0x00, 0x00
 ;   .byte 0x00, 0x00
 ;   .byte 0x00, 0x00
 ;   lg %r4, 0(%r1)
 ;   basr %r14, %r4
-;   vpdi %v5, %v24, %v24, 4
-;   verllg %v24, %v5, 0x20
-;   ld %f8, 0xa0(%r15)
-;   ld %f9, 0xa8(%r15)
-;   ld %f10, 0xb0(%r15)
-;   ld %f11, 0xb8(%r15)
-;   ld %f12, 0xc0(%r15)
-;   ld %f13, 0xc8(%r15)
-;   ld %f14, 0xd0(%r15)
-;   ld %f15, 0xd8(%r15)
-;   lmg %r14, %r15, 0x150(%r15)
+;   vpdi %v21, %v24, %v24, 4
+;   verllg %v24, %v21, 0x20
+;   ld %f8, 0xe0(%r15)
+;   ld %f9, 0xe8(%r15)
+;   ld %f10, 0xf0(%r15)
+;   ld %f11, 0xf8(%r15)
+;   ld %f12, 0x100(%r15)
+;   ld %f13, 0x108(%r15)
+;   ld %f14, 0x110(%r15)
+;   ld %f15, 0x118(%r15)
+;   lmg %r14, %r15, 0x190(%r15)
 ;   br %r14
 
-function %caller_le_to_le(i64x2, i32x4, i16x8, i8x16) -> i32x4 wasmtime_system_v {
-    fn0 = %callee_le(i64x2, i32x4, i16x8, i8x16) -> i32x4 wasmtime_system_v
+function %caller_le_to_le(i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16) -> i32x4 wasmtime_system_v {
+    fn0 = %callee_le(i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16, i64x2, i32x4, i16x8, i8x16) -> i32x4 wasmtime_system_v
 
-block0(v0: i64x2, v1: i32x4, v2: i16x8, v3: i8x16):
-    v4 = call fn0(v0, v1, v2, v3)
-    return v4
+block0(v0: i64x2, v1: i32x4, v2: i16x8, v3: i8x16, v4: i64x2, v5: i32x4, v6: i16x8, v7: i8x16, v8: i64x2, v9: i32x4, v10: i16x8, v11: i8x16):
+    v12 = call fn0(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
+    return v12
 }
 
 ; VCode:
 ;   stmg %r14, %r15, 112(%r15)
-;   aghi %r15, -160
-;   virtual_sp_offset_adjust 160
+;   aghi %r15, -224
+;   virtual_sp_offset_adjust 224
 ; block0:
+;   vl %v17, 384(%r15)
+;   vl %v19, 400(%r15)
+;   vl %v21, 416(%r15)
+;   vl %v23, 432(%r15)
+;   vst %v17, 160(%r15)
+;   vst %v19, 176(%r15)
+;   vst %v21, 192(%r15)
+;   vst %v23, 208(%r15)
 ;   bras %r1, 12 ; data %callee_le + 0 ; lg %r4, 0(%r1)
 ;   basr %r14, %r4
-;   lmg %r14, %r15, 272(%r15)
+;   lmg %r14, %r15, 336(%r15)
 ;   br %r14
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   stmg %r14, %r15, 0x70(%r15)
-;   aghi %r15, -0xa0
+;   aghi %r15, -0xe0
 ; block1: ; offset 0xa
-;   bras %r1, 0x16
+;   vl %v17, 0x180(%r15)
+;   vl %v19, 0x190(%r15)
+;   vl %v21, 0x1a0(%r15)
+;   vl %v23, 0x1b0(%r15)
+;   vst %v17, 0xa0(%r15)
+;   vst %v19, 0xb0(%r15)
+;   vst %v21, 0xc0(%r15)
+;   vst %v23, 0xd0(%r15)
+;   bras %r1, 0x46
 ;   .byte 0x00, 0x00 ; reloc_external Abs8 %callee_le 0
 ;   .byte 0x00, 0x00
 ;   .byte 0x00, 0x00
 ;   .byte 0x00, 0x00
 ;   lg %r4, 0(%r1)
 ;   basr %r14, %r4
-;   lmg %r14, %r15, 0x110(%r15)
+;   lmg %r14, %r15, 0x150(%r15)
 ;   br %r14
 
diff --git a/cranelift/filetests/filetests/isa/x64/table.clif b/cranelift/filetests/filetests/isa/x64/table.clif
deleted file mode 100644
index c3c217fc9..000000000
--- a/cranelift/filetests/filetests/isa/x64/table.clif
+++ /dev/null
@@ -1,59 +0,0 @@
-test compile precise-output
-set enable_safepoints=true
-set enable_table_access_spectre_mitigation=true
-
-target x86_64
-
-function %table_set(i32, r64, i64 vmctx) {
-    gv0 = vmctx
-    gv1 = load.i64 notrap aligned gv0
-    gv2 = load.i32 notrap aligned gv0 +8
-    table0 = dynamic gv1, element_size 1, bound gv2, index_type i32
-
-block0(v0: i32, v1: r64, v2: i64):
-    v3 = table_addr.i64 table0, v0+0
-    store.r64 notrap aligned v1, v3
-    return
-}
-
-; VCode:
-;   pushq   %rbp
-;   movq    %rsp, %rbp
-; block0:
-;   movl    8(%rdx), %r11d
-;   cmpl    %r11d, %edi
-;   jnb     label2; j label1
-; block1:
-;   movl    %edi, %ecx
-;   movq    0(%rdx), %rax
-;   lea     0(%rax,%rcx,1), %rcx
-;   cmpl    %r11d, %edi
-;   cmovnbq %rax, %rcx, %rcx
-;   movq    %rsi, 0(%rcx)
-;   movq    %rbp, %rsp
-;   popq    %rbp
-;   ret
-; block2:
-;   ud2 table_oob
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   pushq %rbp
-;   movq %rsp, %rbp
-; block1: ; offset 0x4
-;   movl 8(%rdx), %r11d
-;   cmpl %r11d, %edi
-;   jae 0x28
-; block2: ; offset 0x11
-;   movl %edi, %ecx
-;   movq (%rdx), %rax
-;   addq %rax, %rcx
-;   cmpl %r11d, %edi
-;   cmovaeq %rax, %rcx
-;   movq %rsi, (%rcx)
-;   movq %rbp, %rsp
-;   popq %rbp
-;   retq
-; block3: ; offset 0x28
-;   ud2 ; trap: table_oob
-
diff --git a/cranelift/filetests/filetests/parser/memory.clif b/cranelift/filetests/filetests/parser/memory.clif
index 8358ab04f..7ef85c07f 100644
--- a/cranelift/filetests/filetests/parser/memory.clif
+++ b/cranelift/filetests/filetests/parser/memory.clif
@@ -49,23 +49,3 @@ block0:
     v2 = bxor v0, v1
     return v2
 }
-
-function %table_addr(i32) {
-    table0 = dynamic gv1, element_size 1, bound gv2, index_type i32
-
-block0(v0: i32):
-    v1 = table_addr.i32 table0, v0
-    v2 = table_addr.i32 table0, v0+1
-    v3 = table_addr.i32 table0, v0+0
-    return
-}
-
-; sameln: function %table_addr(i32) fast {
-; nextln:     table0 = dynamic gv1, min 0, bound gv2, element_size 1, index_type i32
-; nextln: 
-; nextln: block0(v0: i32):
-; nextln:     v1 = table_addr.i32 table0, v0
-; nextln:     v2 = table_addr.i32 table0, v0+1
-; nextln:     v3 = table_addr.i32 table0, v0
-; nextln:     return
-; nextln: }
diff --git a/cranelift/filetests/filetests/runtests/simd-bitselect.clif b/cranelift/filetests/filetests/runtests/simd-bitselect.clif
index 7d250c779..6c399ee03 100644
--- a/cranelift/filetests/filetests/runtests/simd-bitselect.clif
+++ b/cranelift/filetests/filetests/runtests/simd-bitselect.clif
@@ -94,3 +94,37 @@ block0(v0: i64x2, v1: i64x2, v2: i64x2):
 ; run: %bitwise_bitselect_i64x2(0x11111111111111111111111111111111, 0x11111111111111111111111111111111, 0x00000000000000000000000000000000) == 0x11111111111111111111111111111111
 ; run: %bitwise_bitselect_i64x2(0x01010011000011110000000011111111, 0x11111111111111111111111111111111, 0x00000000000000000000000000000000) == 0x01010011000011110000000011111111
 ; run: %bitwise_bitselect_i64x2(0x00000000000000001111111111111111, 0x00000000000000000000000000000000, 0x11111111111111111111111111111111) == 0x11111111111111110000000000000000
+
+
+;; See issue #8131
+;;
+;; These tests test the fusion of `bitselect+bitcast+{f,i}cmp` that
+;; some backends perform. Importantly the `fcmp` and the `bitselect`
+;; have both different type sizes as well as a different number of
+;; lanes.
+
+function %bitselect_i8x16_fcmp_f64x2(i8x16) -> i8x16 fast {
+    const0 = 0x00000000000000000000000000000000
+
+block0(v0: i8x16):
+    v1 = bitcast.f64x2 little v0
+    v2 = fcmp eq v1, v1
+    v3 = bitcast.i8x16 little v2
+    v4 = vconst.i8x16 const0
+    v5 = bitselect.i8x16 v3, v0, v4  ; v3 = const0
+    return v5
+}
+; run: %bitselect_i8x16_fcmp_f64x2(0x80808080808080808080808080808080) == 0x80808080808080808080808080808080
+
+function %bitselect_i8x16_icmp_i64x2(i8x16) -> i8x16 fast {
+    const0 = 0x00000000000000000000000000000000
+
+block0(v0: i8x16):
+    v1 = bitcast.i64x2 little v0
+    v2 = icmp eq v1, v1
+    v3 = bitcast.i8x16 little v2
+    v4 = vconst.i8x16 const0
+    v5 = bitselect.i8x16 v3, v0, v4  ; v3 = const0
+    return v5
+}
+; run: %bitselect_i8x16_icmp_i64x2(0x80808080808080808080808080808080) == 0x80808080808080808080808080808080
diff --git a/cranelift/filetests/filetests/verifier/table.clif b/cranelift/filetests/filetests/verifier/table.clif
deleted file mode 100644
index 4cbfa79ce..000000000
--- a/cranelift/filetests/filetests/verifier/table.clif
+++ /dev/null
@@ -1,46 +0,0 @@
-test verifier
-target x86_64
-
-function %table_base_type(i64 vmctx) {
-    gv0 = vmctx
-    gv1 = load.i32 notrap aligned gv0
-    table0 = dynamic gv1, element_size 1, bound gv1, index_type i32 ; error: table base has type i32, which is not the pointer type i64
-
-block0(v0: i64):
-    return
-}
-
-function %invalid_base(i64 vmctx) {
-    gv0 = vmctx
-    table0 = dynamic gv1, bound gv0, element_size 1, index_type i64 ; error: invalid base global value gv1
-
-block0(v0: i64):
-    return
-}
-
-function %invalid_bound(i64 vmctx) {
-    gv0 = vmctx
-    table0 = dynamic gv0, bound gv1, element_size 1, index_type i64 ; error: invalid bound global value gv1
-
-block0(v0: i64):
-    return
-}
-
-function %table_bound_type(i64 vmctx) {
-    gv0 = vmctx
-    gv1 = load.i16 notrap aligned gv0
-    table0 = dynamic gv0, bound gv1, element_size 1, index_type i32 ; error: table index type i32 differs from the type of its bound, i16
-
-block0(v0: i64):
-    return
-}
-
-function %table_addr_index_type(i64 vmctx, i64) {
-    gv0 = vmctx
-    gv1 = load.i32 notrap aligned gv0
-    table0 = dynamic gv0, element_size 1, bound gv1, index_type i32
-
-block0(v0: i64, v1: i64):
-    v2 = table_addr.i64 table0, v1+0; error: index type i64 differs from table index type i32
-    return
-}
diff --git a/cranelift/filetests/filetests/wasm/aarch64-relaxed-simd.wat b/cranelift/filetests/filetests/wasm/aarch64-relaxed-simd.wat
deleted file mode 100644
index b1a0bcd4c..000000000
--- a/cranelift/filetests/filetests/wasm/aarch64-relaxed-simd.wat
+++ /dev/null
@@ -1,87 +0,0 @@
-;;! target = "aarch64"
-;;! compile = true
-
-(module
-  (func (param v128) (result v128)
-    local.get 0
-    i32x4.relaxed_trunc_f32x4_s
-  )
-
-  (func (param v128) (result v128)
-    local.get 0
-    i32x4.relaxed_trunc_f32x4_u
-  )
-
-  (func (param v128) (result v128)
-    local.get 0
-    i32x4.relaxed_trunc_f64x2_s_zero
-  )
-
-  (func (param v128) (result v128)
-    local.get 0
-    i32x4.relaxed_trunc_f64x2_u_zero
-  )
-
-  (func (param v128 v128) (result v128)
-    local.get 0
-    local.get 1
-    i16x8.relaxed_dot_i8x16_i7x16_s
-  )
-
-  (func (param v128 v128 v128) (result v128)
-    local.get 0
-    local.get 1
-    local.get 2
-    i32x4.relaxed_dot_i8x16_i7x16_add_s
-  )
-)
-
-;; function u0:0:
-;; block0:
-;;   fcvtzs v0.4s, v0.4s
-;;   b label1
-;; block1:
-;;   ret
-;;
-;; function u0:1:
-;; block0:
-;;   fcvtzu v0.4s, v0.4s
-;;   b label1
-;; block1:
-;;   ret
-;;
-;; function u0:2:
-;; block0:
-;;   fcvtzs v4.2d, v0.2d
-;;   sqxtn v0.2s, v4.2d
-;;   b label1
-;; block1:
-;;   ret
-;;
-;; function u0:3:
-;; block0:
-;;   fcvtzu v4.2d, v0.2d
-;;   uqxtn v0.2s, v4.2d
-;;   b label1
-;; block1:
-;;   ret
-;;
-;; function u0:4:
-;; block0:
-;;   smull v6.8h, v0.8b, v1.8b
-;;   smull2 v7.8h, v0.16b, v1.16b
-;;   addp v0.8h, v6.8h, v7.8h
-;;   b label1
-;; block1:
-;;   ret
-;;
-;; function u0:5:
-;; block0:
-;;   smull v17.8h, v0.8b, v1.8b
-;;   smull2 v18.8h, v0.16b, v1.16b
-;;   addp v17.8h, v17.8h, v18.8h
-;;   saddlp v17.4s, v17.8h
-;;   add v0.4s, v17.4s, v2.4s
-;;   b label1
-;; block1:
-;;   ret
diff --git a/cranelift/filetests/filetests/wasm/basic-wat-test.wat b/cranelift/filetests/filetests/wasm/basic-wat-test.wat
deleted file mode 100644
index f42bc37bf..000000000
--- a/cranelift/filetests/filetests/wasm/basic-wat-test.wat
+++ /dev/null
@@ -1,45 +0,0 @@
-;;! target = "x86_64"
-;;!
-;;! [globals.vmctx]
-;;! type = "i64"
-;;! vmctx = true
-;;!
-;;! [globals.heap_base]
-;;! type = "i64"
-;;! load = { base = "vmctx", offset = 0, readonly = true }
-;;!
-;;! [[heaps]]
-;;! base = "heap_base"
-;;! min_size = 0
-;;! offset_guard_size = 0xFFFFFFFF
-;;! index_type = "i32"
-;;! style = { kind = "static", bound = 0x1000 }
-
-(module
-  (memory 0)
-  (func (param i32 i32) (result i32)
-    local.get 0
-    i32.load
-    local.get 1
-    i32.load
-    i32.add))
-
-;; function u0:0(i32, i32, i64 vmctx) -> i32 fast {
-;;     gv0 = vmctx
-;;     gv1 = load.i64 notrap aligned readonly gv0
-;;
-;;                                 block0(v0: i32, v1: i32, v2: i64):
-;; @0021                               v4 = uextend.i64 v0
-;; @0021                               v5 = global_value.i64 gv1
-;; @0021                               v6 = iadd v5, v4
-;; @0021                               v7 = load.i32 little heap v6
-;; @0026                               v8 = uextend.i64 v1
-;; @0026                               v9 = global_value.i64 gv1
-;; @0026                               v10 = iadd v9, v8
-;; @0026                               v11 = load.i32 little heap v10
-;; @0029                               v12 = iadd v7, v11
-;; @002a                               jump block1(v12)
-;;
-;;                                 block1(v3: i32):
-;; @002a                               return v3
-;; }
\ No newline at end of file
diff --git a/cranelift/filetests/filetests/wasm/duplicate-loads-dynamic-memory.wat b/cranelift/filetests/filetests/wasm/duplicate-loads-dynamic-memory.wat
deleted file mode 100644
index 524f7eb83..000000000
--- a/cranelift/filetests/filetests/wasm/duplicate-loads-dynamic-memory.wat
+++ /dev/null
@@ -1,95 +0,0 @@
-;;! target = "x86_64"
-;;!
-;;! optimize = true
-;;!
-;;! settings = [
-;;!   "enable_heap_access_spectre_mitigation=true",
-;;!   "opt_level=speed_and_size",
-;;! ]
-;;!
-;;! [globals.vmctx]
-;;! type = "i64"
-;;! vmctx = true
-;;!
-;;! [globals.heap_base]
-;;! type = "i64"
-;;! load = { base = "vmctx", offset = 0 }
-;;!
-;;! [globals.heap_bound]
-;;! type = "i64"
-;;! load = { base = "vmctx", offset = 8 }
-;;!
-;;! [[heaps]]
-;;! base = "heap_base"
-;;! min_size = 0
-;;! offset_guard_size = 0xffffffff
-;;! index_type = "i32"
-;;! style = { kind = "dynamic", bound = "heap_bound" }
-
-(module
-  (memory (export "memory") 0)
-  (func (export "load-without-offset") (param i32) (result i32 i32)
-    local.get 0
-    i32.load
-    local.get 0
-    i32.load
-  )
-  (func (export "load-with-offset") (param i32) (result i32 i32)
-    local.get 0
-    i32.load offset=1234
-    local.get 0
-    i32.load offset=1234
-  )
-)
-
-;; function u0:0(i32, i64 vmctx) -> i32, i32 fast {
-;;     gv0 = vmctx
-;;     gv1 = load.i64 notrap aligned gv0+8
-;;     gv2 = load.i64 notrap aligned gv0
-;;
-;;                                 block0(v0: i32, v1: i64):
-;;                                     v20 -> v1
-;;                                     v21 -> v1
-;;                                     v22 -> v1
-;;                                     v23 -> v1
-;; @0057                               v5 = load.i64 notrap aligned v1+8
-;; @0057                               v7 = load.i64 notrap aligned v1
-;; @0057                               v4 = uextend.i64 v0
-;; @0057                               v6 = icmp ugt v4, v5
-;; @0057                               v9 = iconst.i64 0
-;; @0057                               v8 = iadd v7, v4
-;; @0057                               v10 = select_spectre_guard v6, v9, v8  ; v9 = 0
-;; @0057                               v11 = load.i32 little heap v10
-;;                                     v2 -> v11
-;; @005f                               jump block1
-;;
-;;                                 block1:
-;; @005f                               return v11, v11
-;; }
-;;
-;; function u0:1(i32, i64 vmctx) -> i32, i32 fast {
-;;     gv0 = vmctx
-;;     gv1 = load.i64 notrap aligned gv0+8
-;;     gv2 = load.i64 notrap aligned gv0
-;;
-;;                                 block0(v0: i32, v1: i64):
-;;                                     v24 -> v1
-;;                                     v25 -> v1
-;;                                     v26 -> v1
-;;                                     v27 -> v1
-;; @0064                               v5 = load.i64 notrap aligned v1+8
-;; @0064                               v7 = load.i64 notrap aligned v1
-;; @0064                               v4 = uextend.i64 v0
-;; @0064                               v6 = icmp ugt v4, v5
-;; @0064                               v11 = iconst.i64 0
-;; @0064                               v8 = iadd v7, v4
-;; @0064                               v9 = iconst.i64 1234
-;; @0064                               v10 = iadd v8, v9  ; v9 = 1234
-;; @0064                               v12 = select_spectre_guard v6, v11, v10  ; v11 = 0
-;; @0064                               v13 = load.i32 little heap v12
-;;                                     v2 -> v13
-;; @006e                               jump block1
-;;
-;;                                 block1:
-;; @006e                               return v13, v13
-;; }
diff --git a/cranelift/filetests/filetests/wasm/r32.clif b/cranelift/filetests/filetests/wasm/r32.clif
index 4ac3f3701..f9525a818 100644
--- a/cranelift/filetests/filetests/wasm/r32.clif
+++ b/cranelift/filetests/filetests/wasm/r32.clif
@@ -15,30 +15,6 @@ block1(v3: r32):
     return v3
 }
 
-function %table_set(i32, r32, i32 vmctx) {
-    gv0 = vmctx
-    gv1 = load.i32 notrap aligned gv0
-    gv2 = load.i32 notrap aligned gv0 +4
-    table0 = dynamic gv1, element_size 1, bound gv2, index_type i32
-
-block0(v0: i32, v1: r32, v2: i32):
-    v3 = table_addr.i32 table0, v0, +0;
-    store.r32 notrap aligned v1, v3
-    return
-}
-
-function %table_get(i32, i32 vmctx) -> r32 {
-    gv0 = vmctx
-    gv1 = load.i32 notrap aligned gv0
-    gv2 = load.i32 notrap aligned gv0 +4
-    table0 = dynamic gv1, element_size 1, bound gv2, index_type i32
-
-block0(v0: i32, v1: i32):
-    v2 = table_addr.i32 table0, v0, +0;
-    v3 = load.r32 notrap aligned v2
-    return v3
-}
-
 function %test_refs(r32, r32, r32, i32 vmctx) {
     fn0 = %select_ref(i32, r32, r32) -> r32
     fn1 = %table_set(i32, r32, i32 vmctx)
diff --git a/cranelift/filetests/filetests/wasm/r64.clif b/cranelift/filetests/filetests/wasm/r64.clif
index 74f6c7764..f448f9db0 100644
--- a/cranelift/filetests/filetests/wasm/r64.clif
+++ b/cranelift/filetests/filetests/wasm/r64.clif
@@ -15,30 +15,6 @@ block1(v3: r64):
     return v3
 }
 
-function %table_set(i32, r64, i64 vmctx) {
-    gv0 = vmctx
-    gv1 = load.i64 notrap aligned gv0
-    gv2 = load.i32 notrap aligned gv0 +8
-    table0 = dynamic gv1, element_size 1, bound gv2, index_type i32
-
-block0(v0: i32, v1: r64, v2: i64):
-    v3 = table_addr.i64 table0, v0+0;
-    store.r64 notrap aligned v1, v3
-    return
-}
-
-function %table_get(i32, i64 vmctx) -> r64 {
-    gv0 = vmctx
-    gv1 = load.i64 notrap aligned gv0
-    gv2 = load.i32 notrap aligned gv0 +8
-    table0 = dynamic gv1, element_size 1, bound gv2, index_type i32
-
-block0(v0: i32, v1: i64):
-    v2 = table_addr.i64 table0, v0+0;
-    v3 = load.r64 notrap aligned v2
-    return v3
-}
-
 function %test_refs(r64, r64, r64, i64 vmctx) {
     fn0 = %select_ref(i32, r64, r64) -> r64
     fn1 = %table_set(i32, r64, i64 vmctx)
diff --git a/cranelift/filetests/src/lib.rs b/cranelift/filetests/src/lib.rs
index 9357f57ba..1f33370f5 100644
--- a/cranelift/filetests/src/lib.rs
+++ b/cranelift/filetests/src/lib.rs
@@ -30,7 +30,7 @@ mod test_run;
 mod test_safepoint;
 mod test_unwind;
 mod test_verifier;
-mod test_wasm;
+pub mod test_wasm;
 
 /// Main entry point for `clif-util test`.
 ///
diff --git a/cranelift/filetests/src/test_wasm.rs b/cranelift/filetests/src/test_wasm.rs
index fe3b0b082..e5d6c031b 100644
--- a/cranelift/filetests/src/test_wasm.rs
+++ b/cranelift/filetests/src/test_wasm.rs
@@ -5,8 +5,12 @@ mod env;
 
 use anyhow::{bail, ensure, Context, Result};
 use config::TestConfig;
+use cranelift_codegen::ir::Function;
+use cranelift_codegen::isa::TargetIsa;
 use cranelift_control::ControlPlane;
 use env::ModuleEnv;
+use serde::de::DeserializeOwned;
+use serde_derive::Deserialize;
 use similar::TextDiff;
 use std::{fmt::Write, path::Path};
 
@@ -53,23 +57,82 @@ pub fn run(path: &Path, wat: &str) -> Result<()> {
     cranelift_wasm::translate_module(&wasm, &mut env)
         .context("failed to translate the test case into CLIF")?;
 
+    let kind = if config.compile {
+        TestKind::Compile
+    } else if config.optimize {
+        TestKind::Optimize
+    } else {
+        TestKind::Clif
+    };
+    run_functions(
+        path,
+        wat,
+        isa,
+        kind,
+        env.inner.info.function_bodies.values().as_slice(),
+    )
+}
+
+/// Parse test configuration from the specified test, comments starting with
+/// `;;!`.
+pub fn parse_test_config<T>(wat: &str) -> Result<T>
+where
+    T: DeserializeOwned,
+{
+    // The test config source is the leading lines of the WAT file that are
+    // prefixed with `;;!`.
+    let config_lines: Vec<_> = wat
+        .lines()
+        .take_while(|l| l.starts_with(";;!"))
+        .map(|l| &l[3..])
+        .collect();
+    let config_text = config_lines.join("\n");
+
+    toml::from_str(&config_text).context("failed to parse the test configuration")
+}
+
+/// Which kind of test is being performed.
+#[derive(Default, Debug, Deserialize)]
+#[serde(rename_all = "lowercase")]
+pub enum TestKind {
+    /// Test the CLIF output, raw from translation.
+    #[default]
+    Clif,
+    /// Compile output to machine code.
+    Compile,
+    /// Test the CLIF output, optimized.
+    Optimize,
+}
+
+/// Assert that `wat` contains the test expectations necessary for `funcs`.
+pub fn run_functions(
+    path: &Path,
+    wat: &str,
+    isa: &dyn TargetIsa,
+    kind: TestKind,
+    funcs: &[Function],
+) -> Result<()> {
     let mut actual = String::new();
-    for (_index, func) in env.inner.info.function_bodies.iter() {
-        if config.compile {
-            let mut ctx = cranelift_codegen::Context::for_function(func.clone());
-            ctx.set_disasm(true);
-            let code = ctx
-                .compile(isa, &mut Default::default())
-                .map_err(|e| crate::pretty_anyhow_error(&e.func, e.inner))?;
-            writeln!(&mut actual, "function {}:", func.name).unwrap();
-            writeln!(&mut actual, "{}", code.vcode.as_ref().unwrap()).unwrap();
-        } else if config.optimize {
-            let mut ctx = cranelift_codegen::Context::for_function(func.clone());
-            ctx.optimize(isa, &mut ControlPlane::default())
-                .map_err(|e| crate::pretty_anyhow_error(&ctx.func, e))?;
-            writeln!(&mut actual, "{}", ctx.func.display()).unwrap();
-        } else {
-            writeln!(&mut actual, "{}", func.display()).unwrap();
+    for func in funcs {
+        match kind {
+            TestKind::Compile => {
+                let mut ctx = cranelift_codegen::Context::for_function(func.clone());
+                ctx.set_disasm(true);
+                let code = ctx
+                    .compile(isa, &mut Default::default())
+                    .map_err(|e| crate::pretty_anyhow_error(&e.func, e.inner))?;
+                writeln!(&mut actual, "function {}:", func.name).unwrap();
+                writeln!(&mut actual, "{}", code.vcode.as_ref().unwrap()).unwrap();
+            }
+            TestKind::Optimize => {
+                let mut ctx = cranelift_codegen::Context::for_function(func.clone());
+                ctx.optimize(isa, &mut ControlPlane::default())
+                    .map_err(|e| crate::pretty_anyhow_error(&ctx.func, e))?;
+                writeln!(&mut actual, "{}", ctx.func.display()).unwrap();
+            }
+            TestKind::Clif => {
+                writeln!(&mut actual, "{}", func.display()).unwrap();
+            }
         }
     }
     let actual = actual.trim();
diff --git a/crates/component-macro/tests/codegen/dead-code.wit b/crates/component-macro/tests/codegen/dead-code.wit
new file mode 100644
index 000000000..1190c31a0
--- /dev/null
+++ b/crates/component-macro/tests/codegen/dead-code.wit
@@ -0,0 +1,27 @@
+package a:b;
+
+world imports {
+  import interface-with-live-type;
+  import interface-with-dead-type;
+}
+
+interface interface-with-live-type {
+  record live-type {
+    a: u32,
+  }
+  f: func() -> live-type;
+}
+
+
+interface interface-with-dead-type {
+  use interface-with-live-type.{live-type};
+
+  record dead-type {
+    a: u32,
+  }
+
+  variant v {
+    a(live-type),
+    b(dead-type),
+  }
+}
diff --git a/tests/disas.rs b/tests/disas.rs
new file mode 100644
index 000000000..26372b5ee
--- /dev/null
+++ b/tests/disas.rs
@@ -0,0 +1,209 @@
+//! A filetest-lookalike test suite using Cranelift tooling but built on
+//! Wasmtime's code generator.
+//!
+//! This test will read the `tests/disas/*` directory and interpret all files in
+//! that directory as a test. Each test must be in the wasm text format and
+//! start with directives that look like:
+//!
+//! ```wasm
+//! ;;! target = "x86_64"
+//! ;;! compile = true
+//!
+//! (module
+//!     ;; ...
+//! )
+//! ```
+//!
+//! Tests must configure a `target` and then can optionally specify a kind of
+//! test:
+//!
+//! * No specifier - the output CLIF from translation is inspected.
+//! * `optimize = true` - CLIF is emitted, then optimized, then inspected.
+//! * `compile = true` - backends are run to produce machine code and that's inspected.
+//!
+//! Tests may also have a `flags` directive which are CLI flags to Wasmtime
+//! itself:
+//!
+//! ```wasm
+//! ;;! target = "x86_64"
+//! ;;! flags = "-O opt-level=s"
+//!
+//! (module
+//!     ;; ...
+//! )
+//! ```
+//!
+//! Flags are parsed by the `wasmtime_cli_flags` crate to build a `Config`.
+//!
+//! Configuration of tests is prefixed with `;;!` comments and must be present
+//! at the start of the file. These comments are then parsed as TOML and
+//! deserialized into `TestConfig` in this crate.
+
+use anyhow::{bail, Context, Result};
+use clap::Parser;
+use cranelift_codegen::isa::{lookup_by_name, TargetIsa};
+use cranelift_codegen::settings::{Configurable, Flags, SetError};
+use cranelift_filetests::test_wasm::{parse_test_config, run_functions, TestKind};
+use serde_derive::Deserialize;
+use std::path::{Path, PathBuf};
+use std::sync::Arc;
+use tempfile::TempDir;
+use wasmtime::{Engine, OptLevel};
+use wasmtime_cli_flags::CommonOptions;
+
+fn main() {
+    if cfg!(miri) {
+        return;
+    }
+    // First discover all tests ...
+    let mut tests = Vec::new();
+    for file in std::fs::read_dir("./tests/disas").unwrap() {
+        tests.push(file.unwrap().path());
+    }
+
+    // ... then run all tests!
+    for test in tests.iter() {
+        run_test(&test)
+            .with_context(|| format!("failed to run tests {test:?}"))
+            .unwrap();
+    }
+}
+
+fn run_test(path: &Path) -> Result<()> {
+    let mut test = Test::new(path)?;
+    let clifs = test.generate_clif()?;
+    let isa = test.build_target_isa()?;
+
+    // Parse the text format CLIF which is emitted by Wasmtime back into
+    // in-memory data structures.
+    let functions = clifs
+        .iter()
+        .map(|clif| {
+            let mut funcs = cranelift_reader::parse_functions(clif)?;
+            if funcs.len() != 1 {
+                bail!("expected one function per clif");
+            }
+            Ok(funcs.remove(0))
+        })
+        .collect::<Result<Vec<_>>>()?;
+
+    // And finally, use `cranelift_filetests` to perform the rest of the test.
+    run_functions(
+        &test.path,
+        &test.contents,
+        &*isa,
+        test.config.test,
+        &functions,
+    )?;
+
+    Ok(())
+}
+#[derive(Debug, Deserialize)]
+struct TestConfig {
+    target: String,
+    #[serde(default)]
+    test: TestKind,
+    flags: Option<TestConfigFlags>,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(untagged)]
+enum TestConfigFlags {
+    SpaceSeparated(String),
+    List(Vec<String>),
+}
+
+struct Test {
+    path: PathBuf,
+    contents: String,
+    opts: CommonOptions,
+    config: TestConfig,
+}
+
+impl Test {
+    /// Parse the contents of `path` looking for directive-based comments
+    /// starting with `;;!` near the top of the file.
+    fn new(path: &Path) -> Result<Test> {
+        let contents =
+            std::fs::read_to_string(path).with_context(|| format!("failed to read {path:?}"))?;
+        let config: TestConfig =
+            parse_test_config(&contents).context("failed to parse test configuration as TOML")?;
+        let mut flags = vec!["wasmtime"];
+        match &config.flags {
+            Some(TestConfigFlags::SpaceSeparated(s)) => flags.extend(s.split_whitespace()),
+            Some(TestConfigFlags::List(s)) => flags.extend(s.iter().map(|s| s.as_str())),
+            None => {}
+        }
+        let opts = wasmtime_cli_flags::CommonOptions::try_parse_from(&flags)?;
+
+        Ok(Test {
+            path: path.to_path_buf(),
+            config,
+            opts,
+            contents,
+        })
+    }
+
+    /// Generates CLIF for all the wasm functions in this test.
+    fn generate_clif(&mut self) -> Result<Vec<String>> {
+        // Use wasmtime::Config with its `emit_clif` option to get Wasmtime's
+        // code generator to jettison CLIF out the back.
+        let tempdir = TempDir::new().context("failed to make a tempdir")?;
+        let mut config = self.opts.config(Some(&self.config.target))?;
+        config.emit_clif(tempdir.path());
+        let engine = Engine::new(&config).context("failed to create engine")?;
+        let module = wat::parse_file(&self.path)?;
+        engine
+            .precompile_module(&module)
+            .context("failed to compile module")?;
+
+        // Read all `*.clif` files from the clif directory that the compilation
+        // process just emitted.
+        let mut clifs = Vec::new();
+        for entry in tempdir
+            .path()
+            .read_dir()
+            .context("failed to read tempdir")?
+        {
+            let entry = entry.context("failed to iterate over tempdir")?;
+            let path = entry.path();
+            let clif = std::fs::read_to_string(&path)
+                .with_context(|| format!("failed to read clif file {path:?}"))?;
+            clifs.push(clif);
+        }
+        clifs.sort();
+        Ok(clifs)
+    }
+
+    /// Use the test configuration present with CLI flags to build a
+    /// `TargetIsa` to compile/optimize the CLIF.
+    fn build_target_isa(&self) -> Result<Arc<dyn TargetIsa>> {
+        let mut builder = lookup_by_name(&self.config.target)?;
+        let mut flags = cranelift_codegen::settings::builder();
+        let opt_level = match self.opts.opts.opt_level {
+            None | Some(OptLevel::Speed) => "speed",
+            Some(OptLevel::SpeedAndSize) => "speed_and_size",
+            Some(OptLevel::None) => "none",
+            _ => unreachable!(),
+        };
+        flags.set("opt_level", opt_level)?;
+        for (key, val) in self.opts.codegen.cranelift.iter() {
+            let key = &key.replace("-", "_");
+            let target_res = match val {
+                Some(val) => builder.set(key, val),
+                None => builder.enable(key),
+            };
+            match target_res {
+                Ok(()) => continue,
+                Err(SetError::BadName(_)) => {}
+                Err(e) => bail!(e),
+            }
+            match val {
+                Some(val) => flags.set(key, val)?,
+                None => flags.enable(key)?,
+            }
+        }
+        let isa = builder.finish(Flags::new(flags))?;
+        Ok(isa)
+    }
+}
diff --git a/tests/disas/aarch64-relaxed-simd.wat b/tests/disas/aarch64-relaxed-simd.wat
new file mode 100644
index 000000000..5276f36c7
--- /dev/null
+++ b/tests/disas/aarch64-relaxed-simd.wat
@@ -0,0 +1,141 @@
+;;! target = "aarch64"
+;;! test = "compile"
+
+(module
+  (func (param v128) (result v128)
+    local.get 0
+    i32x4.relaxed_trunc_f32x4_s
+  )
+
+  (func (param v128) (result v128)
+    local.get 0
+    i32x4.relaxed_trunc_f32x4_u
+  )
+
+  (func (param v128) (result v128)
+    local.get 0
+    i32x4.relaxed_trunc_f64x2_s_zero
+  )
+
+  (func (param v128) (result v128)
+    local.get 0
+    i32x4.relaxed_trunc_f64x2_u_zero
+  )
+
+  (func (param v128 v128) (result v128)
+    local.get 0
+    local.get 1
+    i16x8.relaxed_dot_i8x16_i7x16_s
+  )
+
+  (func (param v128 v128 v128) (result v128)
+    local.get 0
+    local.get 1
+    local.get 2
+    i32x4.relaxed_dot_i8x16_i7x16_add_s
+  )
+)
+
+;; function u0:0:
+;;   stp fp, lr, [sp, #-16]!
+;;   unwind PushFrameRegs { offset_upward_to_caller_sp: 16 }
+;;   mov fp, sp
+;;   ldr x16, [x0, #8]
+;;   ldr x16, [x16]
+;;   subs xzr, sp, x16, UXTX
+;;   b.lo #trap=stk_ovf
+;;   unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 }
+;; block0:
+;;   b label1
+;; block1:
+;;   fcvtzs v0.4s, v0.4s
+;;   ldp fp, lr, [sp], #16
+;;   ret
+;;
+;; function u0:1:
+;;   stp fp, lr, [sp, #-16]!
+;;   unwind PushFrameRegs { offset_upward_to_caller_sp: 16 }
+;;   mov fp, sp
+;;   ldr x16, [x0, #8]
+;;   ldr x16, [x16]
+;;   subs xzr, sp, x16, UXTX
+;;   b.lo #trap=stk_ovf
+;;   unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 }
+;; block0:
+;;   b label1
+;; block1:
+;;   fcvtzu v0.4s, v0.4s
+;;   ldp fp, lr, [sp], #16
+;;   ret
+;;
+;; function u0:2:
+;;   stp fp, lr, [sp, #-16]!
+;;   unwind PushFrameRegs { offset_upward_to_caller_sp: 16 }
+;;   mov fp, sp
+;;   ldr x16, [x0, #8]
+;;   ldr x16, [x16]
+;;   subs xzr, sp, x16, UXTX
+;;   b.lo #trap=stk_ovf
+;;   unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 }
+;; block0:
+;;   b label1
+;; block1:
+;;   fcvtzs v6.2d, v0.2d
+;;   sqxtn v0.2s, v6.2d
+;;   ldp fp, lr, [sp], #16
+;;   ret
+;;
+;; function u0:3:
+;;   stp fp, lr, [sp, #-16]!
+;;   unwind PushFrameRegs { offset_upward_to_caller_sp: 16 }
+;;   mov fp, sp
+;;   ldr x16, [x0, #8]
+;;   ldr x16, [x16]
+;;   subs xzr, sp, x16, UXTX
+;;   b.lo #trap=stk_ovf
+;;   unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 }
+;; block0:
+;;   b label1
+;; block1:
+;;   fcvtzu v6.2d, v0.2d
+;;   uqxtn v0.2s, v6.2d
+;;   ldp fp, lr, [sp], #16
+;;   ret
+;;
+;; function u0:4:
+;;   stp fp, lr, [sp, #-16]!
+;;   unwind PushFrameRegs { offset_upward_to_caller_sp: 16 }
+;;   mov fp, sp
+;;   ldr x16, [x0, #8]
+;;   ldr x16, [x16]
+;;   subs xzr, sp, x16, UXTX
+;;   b.lo #trap=stk_ovf
+;;   unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 }
+;; block0:
+;;   b label1
+;; block1:
+;;   smull v16.8h, v0.8b, v1.8b
+;;   smull2 v17.8h, v0.16b, v1.16b
+;;   addp v0.8h, v16.8h, v17.8h
+;;   ldp fp, lr, [sp], #16
+;;   ret
+;;
+;; function u0:5:
+;;   stp fp, lr, [sp, #-16]!
+;;   unwind PushFrameRegs { offset_upward_to_caller_sp: 16 }
+;;   mov fp, sp
+;;   ldr x16, [x0, #8]
+;;   ldr x16, [x16]
+;;   subs xzr, sp, x16, UXTX
+;;   b.lo #trap=stk_ovf
+;;   unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 }
+;; block0:
+;;   b label1
+;; block1:
+;;   smull v19.8h, v0.8b, v1.8b
+;;   smull2 v20.8h, v0.16b, v1.16b
+;;   addp v19.8h, v19.8h, v20.8h
+;;   saddlp v19.4s, v19.8h
+;;   add v0.4s, v19.4s, v2.4s
+;;   ldp fp, lr, [sp], #16
+;;   ret
diff --git a/tests/disas/basic-wat-test.wat b/tests/disas/basic-wat-test.wat
new file mode 100644
index 000000000..35663b6d6
--- /dev/null
+++ b/tests/disas/basic-wat-test.wat
@@ -0,0 +1,37 @@
+;;! target = "x86_64"
+
+(module
+  (memory 0)
+  (func (param i32 i32) (result i32)
+    local.get 0
+    i32.load
+    local.get 1
+    i32.load
+    i32.add))
+
+;; function u0:0(i64 vmctx, i64, i32, i32) -> i32 fast {
+;;     gv0 = vmctx
+;;     gv1 = load.i64 notrap aligned readonly gv0+8
+;;     gv2 = load.i64 notrap aligned gv1
+;;     gv3 = vmctx
+;;     gv4 = load.i64 notrap aligned readonly checked gv3+80
+;;     sig0 = (i64 vmctx, i32 uext, i32 uext, i32 uext) -> i32 uext system_v
+;;     sig1 = (i64 vmctx, i32 uext, i32 uext) -> i32 uext system_v
+;;     sig2 = (i64 vmctx, i32 uext) -> i32 uext system_v
+;;     stack_limit = gv2
+;;
+;;                                 block0(v0: i64, v1: i64, v2: i32, v3: i32):
+;; @0021                               v5 = uextend.i64 v2
+;; @0021                               v6 = global_value.i64 gv4
+;; @0021                               v7 = iadd v6, v5
+;; @0021                               v8 = load.i32 little heap v7
+;; @0026                               v9 = uextend.i64 v3
+;; @0026                               v10 = global_value.i64 gv4
+;; @0026                               v11 = iadd v10, v9
+;; @0026                               v12 = load.i32 little heap v11
+;; @0029                               v13 = iadd v8, v12
+;; @002a                               jump block1(v13)
+;;
+;;                                 block1(v4: i32):
+;; @002a                               return v4
+;; }
diff --git a/cranelift/filetests/filetests/wasm/byteswap.wat b/tests/disas/byteswap.wat
similarity index 55%
rename from cranelift/filetests/filetests/wasm/byteswap.wat
rename to tests/disas/byteswap.wat
index 81ccba004..a2f559de0 100644
--- a/cranelift/filetests/filetests/wasm/byteswap.wat
+++ b/tests/disas/byteswap.wat
@@ -1,6 +1,5 @@
 ;;! target = "x86_64"
-;;! optimize = true
-;;! settings = ["opt_level=speed"]
+;;! test = "optimize"
 
 (module
   (func (export "bswap32") (param i32) (result i32)
@@ -72,20 +71,34 @@
   )
 )
 
-;; function u0:0(i32, i64 vmctx) -> i32 fast {
-;;                                 block0(v0: i32, v1: i64):
+;; function u0:0(i64 vmctx, i64, i32) -> i32 fast {
+;;     gv0 = vmctx
+;;     gv1 = load.i64 notrap aligned readonly gv0+8
+;;     gv2 = load.i64 notrap aligned gv1
+;;     sig0 = (i64 vmctx, i32 uext, i32 uext) -> i32 uext system_v
+;;     sig1 = (i64 vmctx, i32 uext) -> i32 uext system_v
+;;     stack_limit = gv2
+;;
+;;                                 block0(v0: i64, v1: i64, v2: i32):
 ;; @0057                               jump block1
 ;;
 ;;                                 block1:
-;;                                     v18 = bswap.i32 v0
-;; @0057                               return v18
+;;                                     v19 = bswap.i32 v2
+;; @0057                               return v19
 ;; }
 ;;
-;; function u0:1(i64, i64 vmctx) -> i64 fast {
-;;                                 block0(v0: i64, v1: i64):
+;; function u0:1(i64 vmctx, i64, i64) -> i64 fast {
+;;     gv0 = vmctx
+;;     gv1 = load.i64 notrap aligned readonly gv0+8
+;;     gv2 = load.i64 notrap aligned gv1
+;;     sig0 = (i64 vmctx, i32 uext, i32 uext) -> i32 uext system_v
+;;     sig1 = (i64 vmctx, i32 uext) -> i32 uext system_v
+;;     stack_limit = gv2
+;;
+;;                                 block0(v0: i64, v1: i64, v2: i64):
 ;; @00ad                               jump block1
 ;;
 ;;                                 block1:
-;;                                     v38 = bswap.i64 v0
-;; @00ad                               return v38
+;;                                     v39 = bswap.i64 v2
+;; @00ad                               return v39
 ;; }
diff --git a/tests/disas/duplicate-loads-dynamic-memory.wat b/tests/disas/duplicate-loads-dynamic-memory.wat
new file mode 100644
index 000000000..3fb25eb4d
--- /dev/null
+++ b/tests/disas/duplicate-loads-dynamic-memory.wat
@@ -0,0 +1,89 @@
+;;! target = "x86_64"
+;;! test = "optimize"
+;;! flags = [
+;;!   "-Ccranelift-enable-heap-access-spectre-mitigation",
+;;!   "-Oopt-level=s",
+;;!   "-Ostatic-memory-maximum-size=0",
+;;! ]
+
+(module
+  (memory (export "memory") 0)
+  (func (export "load-without-offset") (param i32) (result i32 i32)
+    local.get 0
+    i32.load
+    local.get 0
+    i32.load
+  )
+  (func (export "load-with-offset") (param i32) (result i32 i32)
+    local.get 0
+    i32.load offset=1234
+    local.get 0
+    i32.load offset=1234
+  )
+)
+
+;; function u0:0(i64 vmctx, i64, i32) -> i32, i32 fast {
+;;     gv0 = vmctx
+;;     gv1 = load.i64 notrap aligned readonly gv0+8
+;;     gv2 = load.i64 notrap aligned gv1
+;;     gv3 = vmctx
+;;     gv4 = load.i64 notrap aligned gv3+88
+;;     gv5 = load.i64 notrap aligned checked gv3+80
+;;     sig0 = (i64 vmctx, i32 uext, i32 uext, i32 uext) -> i32 uext system_v
+;;     sig1 = (i64 vmctx, i32 uext, i32 uext) -> i32 uext system_v
+;;     sig2 = (i64 vmctx, i32 uext) -> i32 uext system_v
+;;     stack_limit = gv2
+;;
+;;                                 block0(v0: i64, v1: i64, v2: i32):
+;;                                     v21 -> v0
+;;                                     v22 -> v0
+;;                                     v23 -> v0
+;;                                     v24 -> v0
+;; @0057                               v6 = load.i64 notrap aligned v0+88
+;; @0057                               v8 = load.i64 notrap aligned checked v0+80
+;; @0057                               v5 = uextend.i64 v2
+;; @0057                               v7 = icmp ugt v5, v6
+;; @0057                               v10 = iconst.i64 0
+;; @0057                               v9 = iadd v8, v5
+;; @0057                               v11 = select_spectre_guard v7, v10, v9  ; v10 = 0
+;; @0057                               v12 = load.i32 little heap v11
+;;                                     v3 -> v12
+;; @005f                               jump block1
+;;
+;;                                 block1:
+;; @005f                               return v12, v12
+;; }
+;;
+;; function u0:1(i64 vmctx, i64, i32) -> i32, i32 fast {
+;;     gv0 = vmctx
+;;     gv1 = load.i64 notrap aligned readonly gv0+8
+;;     gv2 = load.i64 notrap aligned gv1
+;;     gv3 = vmctx
+;;     gv4 = load.i64 notrap aligned gv3+88
+;;     gv5 = load.i64 notrap aligned checked gv3+80
+;;     sig0 = (i64 vmctx, i32 uext, i32 uext, i32 uext) -> i32 uext system_v
+;;     sig1 = (i64 vmctx, i32 uext, i32 uext) -> i32 uext system_v
+;;     sig2 = (i64 vmctx, i32 uext) -> i32 uext system_v
+;;     stack_limit = gv2
+;;
+;;                                 block0(v0: i64, v1: i64, v2: i32):
+;;                                     v25 -> v0
+;;                                     v26 -> v0
+;;                                     v27 -> v0
+;;                                     v28 -> v0
+;; @0064                               v6 = load.i64 notrap aligned v0+88
+;; @0064                               v8 = load.i64 notrap aligned checked v0+80
+;; @0064                               v5 = uextend.i64 v2
+;; @0064                               v7 = icmp ugt v5, v6
+;; @0064                               v12 = iconst.i64 0
+;; @0064                               v9 = iadd v8, v5
+;; @0064                               v10 = iconst.i64 1234
+;; @0064                               v11 = iadd v9, v10  ; v10 = 1234
+;; @0064                               v13 = select_spectre_guard v7, v12, v11  ; v12 = 0
+;; @0064                               v14 = load.i32 little heap v13
+;;                                     v3 -> v14
+;; @006e                               jump block1
+;;
+;;                                 block1:
+;; @006e                               return v14, v14
+;; }