Repository: bytecodealliance/wasmtime
Pull request #8130 (2024-03-14 07:31:18)

Reorder book-based documentation
This PR suggests to reorder the book-based documentation. The main change is moving the CLI chapter before the API (embedding) chapter like was proposed in #7987 by @alexcrichton. The rest is mostly cleaning up of duplicate or outdated information.

With these changes, this PR fixes some confusion that I had with the current layout. Currently, it's not so clear how the Subsection "2.3 Embedding in Rust" relates to Chapter "3. Using WebAssembly from your language" with Subsection "3.1 Rust". With these changes, these two examples are merged and now all fall under Chapter "2. Using the Wasmtime API".

More specifically, this is what this PR changed:

1. Move the CLI chapter before the API chapter (like proposed in #7987).
1. Move some Rust and C examples from the "Examples" chapter into the API chapter.
1. Remove the original main C and Rust chapter introduction files. They contained mostly outdated or duplicate information.
1. Rename the "Examples" chapter to "Further Examples". (For lack of a better name.)
1. Rename "Embedding" to "API" at some places since API is a more generally known term. The term "embedding" is also slightly more confusing, I think, because the term nowadays also used a lot in LLM-related communication.
1. Fix some outdated links and documentation by reading through the entire book.
1. Fix a missing subheading for Elixir (it was mentioned on the lang page, but not in the menu).


cfallin: Reassigning review to @alexcrichton as it's not totally clear to me how this should relate with the still-pending #7987. Thanks @rikhuijzer for the careful thought around all of these changes though; they do seem like reasonable cleanups to me!

Patch:
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/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/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/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/step.rs b/cranelift/interpreter/src/step.rs
index 0ffff70e2..74cf6b8d1 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, .. }
@@ -550,28 +549,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/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/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/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)