Repository: bytecodealliance/wasmtime
Pull request #8018 (2024-02-28 19:19:02)

Refactor runtime table management for more type-safety
This commit is an attempt to make the mistake fixed in #8016 harder to happen again. This removes the `set_raw` helper entirely from tables and instead forces all callers to view the table as either a table of funcrefs or a table of externefs. By forcing that choice outwards instead of handling it inwards it enables dealing with a typed table in more contexts which should help naturally do the right thing.

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 545d0d943..4338a4f23 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -653,7 +653,7 @@ dependencies = [
  "target-lexicon",
  "thiserror",
  "toml",
- "wasmparser",
+ "wasmparser 0.201.0",
  "wat",
 ]
 
@@ -832,7 +832,7 @@ dependencies = [
  "serde_derive",
  "smallvec",
  "target-lexicon",
- "wasmparser",
+ "wasmparser 0.201.0",
  "wasmtime-types",
  "wat",
 ]
@@ -2620,7 +2620,7 @@ version = "0.0.0"
 dependencies = [
  "cargo_metadata",
  "heck",
- "wit-component",
+ "wit-component 0.201.0",
 ]
 
 [[package]]
@@ -2930,7 +2930,7 @@ name = "verify-component-adapter"
 version = "19.0.0"
 dependencies = [
  "anyhow",
- "wasmparser",
+ "wasmparser 0.201.0",
  "wat",
 ]
 
@@ -3021,7 +3021,7 @@ dependencies = [
  "byte-array-literals",
  "object",
  "wasi",
- "wasm-encoder",
+ "wasm-encoder 0.201.0",
  "wit-bindgen",
 ]
 
@@ -3088,6 +3088,15 @@ dependencies = [
  "leb128",
 ]
 
+[[package]]
+name = "wasm-encoder"
+version = "0.201.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a"
+dependencies = [
+ "leb128",
+]
+
 [[package]]
 name = "wasm-metadata"
 version = "0.200.0"
@@ -3100,36 +3109,52 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "spdx",
- "wasm-encoder",
- "wasmparser",
+ "wasm-encoder 0.200.0",
+ "wasmparser 0.200.0",
+]
+
+[[package]]
+name = "wasm-metadata"
+version = "0.201.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2"
+dependencies = [
+ "anyhow",
+ "indexmap 2.0.0",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "spdx",
+ "wasm-encoder 0.201.0",
+ "wasmparser 0.201.0",
 ]
 
 [[package]]
 name = "wasm-mutate"
-version = "0.200.0"
+version = "0.201.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "940164dfefc300dbe5e2c310d7b67e9a502a03083bcec9fcbc8e852b79214135"
+checksum = "e4998a6d2c774d9ead7fd7e4d4c932b78c665ac893fd067ae7e18a62c3c4af71"
 dependencies = [
  "egg",
  "log",
  "rand",
  "thiserror",
- "wasm-encoder",
- "wasmparser",
+ "wasm-encoder 0.201.0",
+ "wasmparser 0.201.0",
 ]
 
 [[package]]
 name = "wasm-smith"
-version = "0.200.0"
+version = "0.201.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c94f72d4693c627c5d0b73b95f24f73566a610fd748b57216ea90f400d09f55"
+checksum = "61ff53a54a853f174b0df74cdb1553f1451e7bcdc23b26b1379f664ee1913d1a"
 dependencies = [
  "anyhow",
  "arbitrary",
  "flagset",
  "indexmap 2.0.0",
  "leb128",
- "wasm-encoder",
+ "wasm-encoder 0.201.0",
 ]
 
 [[package]]
@@ -3183,6 +3208,17 @@ dependencies = [
  "semver",
 ]
 
+[[package]]
+name = "wasmparser"
+version = "0.201.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708"
+dependencies = [
+ "bitflags 2.4.1",
+ "indexmap 2.0.0",
+ "semver",
+]
+
 [[package]]
 name = "wasmparser-nostd"
 version = "0.100.1"
@@ -3194,12 +3230,12 @@ dependencies = [
 
 [[package]]
 name = "wasmprinter"
-version = "0.200.0"
+version = "0.201.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bd153b0dce4e727565ccb3bd41b0bd2b62b2b9b6b1057d1cdb7fe8e0f3e06ab"
+checksum = "a67e66da702706ba08729a78e3c0079085f6bfcb1a62e4799e97bbf728c2c265"
 dependencies = [
  "anyhow",
- "wasmparser",
+ "wasmparser 0.201.0",
 ]
 
 [[package]]
@@ -3231,8 +3267,8 @@ dependencies = [
  "target-lexicon",
  "tempfile",
  "wasi-common",
- "wasm-encoder",
- "wasmparser",
+ "wasm-encoder 0.201.0",
+ "wasmparser 0.201.0",
  "wasmtime-cache",
  "wasmtime-component-macro",
  "wasmtime-component-util",
@@ -3363,7 +3399,7 @@ dependencies = [
  "tracing",
  "walkdir",
  "wasi-common",
- "wasmparser",
+ "wasmparser 0.201.0",
  "wasmtime",
  "wasmtime-cache",
  "wasmtime-cli-flags",
@@ -3377,10 +3413,10 @@ dependencies = [
  "wasmtime-wasi-nn",
  "wasmtime-wasi-threads",
  "wasmtime-wast",
- "wast 200.0.0",
+ "wast 201.0.0",
  "wat",
  "windows-sys 0.52.0",
- "wit-component",
+ "wit-component 0.201.0",
 ]
 
 [[package]]
@@ -3409,7 +3445,7 @@ dependencies = [
  "wasmtime",
  "wasmtime-component-util",
  "wasmtime-wit-bindgen",
- "wit-parser",
+ "wit-parser 0.201.0",
 ]
 
 [[package]]
@@ -3433,7 +3469,7 @@ dependencies = [
  "object",
  "target-lexicon",
  "thiserror",
- "wasmparser",
+ "wasmparser 0.201.0",
  "wasmtime-cranelift-shared",
  "wasmtime-environ",
  "wasmtime-versioned-export-macros",
@@ -3472,8 +3508,8 @@ dependencies = [
  "serde_derive",
  "target-lexicon",
  "thiserror",
- "wasm-encoder",
- "wasmparser",
+ "wasm-encoder 0.201.0",
+ "wasmparser 0.201.0",
  "wasmprinter",
  "wasmtime-component-util",
  "wasmtime-types",
@@ -3488,7 +3524,7 @@ dependencies = [
  "component-fuzz-util",
  "env_logger",
  "libfuzzer-sys",
- "wasmparser",
+ "wasmparser 0.201.0",
  "wasmprinter",
  "wasmtime-environ",
  "wat",
@@ -3545,7 +3581,7 @@ dependencies = [
  "rand",
  "smallvec",
  "target-lexicon",
- "wasmparser",
+ "wasmparser 0.201.0",
  "wasmtime",
  "wasmtime-fuzzing",
 ]
@@ -3565,12 +3601,12 @@ dependencies = [
  "target-lexicon",
  "tempfile",
  "v8",
- "wasm-encoder",
+ "wasm-encoder 0.201.0",
  "wasm-mutate",
  "wasm-smith",
  "wasm-spec-interpreter",
  "wasmi",
- "wasmparser",
+ "wasmparser 0.201.0",
  "wasmprinter",
  "wasmtime",
  "wasmtime-wast",
@@ -3617,7 +3653,7 @@ dependencies = [
  "rand",
  "rustix",
  "sptr",
- "wasm-encoder",
+ "wasm-encoder 0.201.0",
  "wasmtime-asm-macros",
  "wasmtime-environ",
  "wasmtime-fiber",
@@ -3639,7 +3675,7 @@ dependencies = [
  "serde",
  "serde_derive",
  "thiserror",
- "wasmparser",
+ "wasmparser 0.201.0",
 ]
 
 [[package]]
@@ -3744,7 +3780,7 @@ dependencies = [
  "anyhow",
  "log",
  "wasmtime",
- "wast 200.0.0",
+ "wast 201.0.0",
 ]
 
 [[package]]
@@ -3756,7 +3792,7 @@ dependencies = [
  "gimli",
  "object",
  "target-lexicon",
- "wasmparser",
+ "wasmparser 0.201.0",
  "wasmtime-cranelift-shared",
  "wasmtime-environ",
  "winch-codegen",
@@ -3769,7 +3805,7 @@ dependencies = [
  "anyhow",
  "heck",
  "indexmap 2.0.0",
- "wit-parser",
+ "wit-parser 0.201.0",
 ]
 
 [[package]]
@@ -3787,24 +3823,24 @@ dependencies = [
 
 [[package]]
 name = "wast"
-version = "200.0.0"
+version = "201.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1810d14e6b03ebb8fb05eef4009ad5749c989b65197d83bce7de7172ed91366"
+checksum = "1ef6e1ef34d7da3e2b374fd2b1a9c0227aff6cad596e1b24df9b58d0f6222faa"
 dependencies = [
  "bumpalo",
  "leb128",
  "memchr",
  "unicode-width",
- "wasm-encoder",
+ "wasm-encoder 0.201.0",
 ]
 
 [[package]]
 name = "wat"
-version = "1.200.0"
+version = "1.201.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "776cbd10e217f83869beaa3f40e312bb9e91d5eee29bbf6f560db1261b6a4c3d"
+checksum = "453d5b37a45b98dee4f4cb68015fc73634d7883bbef1c65e6e9c78d454cf3f32"
 dependencies = [
- "wast 200.0.0",
+ "wast 201.0.0",
 ]
 
 [[package]]
@@ -3929,7 +3965,7 @@ dependencies = [
  "regalloc2",
  "smallvec",
  "target-lexicon",
- "wasmparser",
+ "wasmparser 0.201.0",
  "wasmtime-environ",
 ]
 
@@ -3945,7 +3981,7 @@ dependencies = [
  "similar",
  "target-lexicon",
  "toml",
- "wasmparser",
+ "wasmparser 0.201.0",
  "wasmtime-environ",
  "wat",
  "winch-codegen",
@@ -3975,7 +4011,7 @@ dependencies = [
  "similar",
  "target-lexicon",
  "toml",
- "wasmparser",
+ "wasmparser 0.201.0",
  "wasmtime-environ",
  "wat",
  "winch-codegen",
@@ -4145,9 +4181,9 @@ dependencies = [
 
 [[package]]
 name = "wit-bindgen"
-version = "0.19.1"
+version = "0.19.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4ee004d9efcb9931de5eb7ac424d6f32c176ad81297636302b66e4fedf7bd02"
+checksum = "b37d270da94012e0ac490ac633ad5bdd76a10a3fb15069edb033c1b771ce931f"
 dependencies = [
  "bitflags 2.4.1",
  "wit-bindgen-rust-macro",
@@ -4155,32 +4191,32 @@ dependencies = [
 
 [[package]]
 name = "wit-bindgen-core"
-version = "0.19.1"
+version = "0.19.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81b9b74b29ab2b8652cd8fbefcf93616bd5415088b539f74edb277d94a3d9765"
+checksum = "9121070bebf9dda946f7ecffc9ec23a890b46c79be84ff6359f0c588223b20e7"
 dependencies = [
  "anyhow",
- "wit-parser",
+ "wit-parser 0.200.0",
 ]
 
 [[package]]
 name = "wit-bindgen-rust"
-version = "0.19.1"
+version = "0.19.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bafb494693e7380aafc96a4911c6f4e57abc507b5f9694b7c5fbe88b07101f7a"
+checksum = "a1753606a880b4c7701da8870d3935e550e667261a9b570f709cfefc22136ea5"
 dependencies = [
  "anyhow",
  "heck",
- "wasm-metadata",
+ "wasm-metadata 0.200.0",
  "wit-bindgen-core",
- "wit-component",
+ "wit-component 0.200.0",
 ]
 
 [[package]]
 name = "wit-bindgen-rust-macro"
-version = "0.19.1"
+version = "0.19.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b62c1deb957177fb2f072e33ce24016d68162bc3863661dec85e9b81ae91900f"
+checksum = "171482ae1eb3c417b61e7e1b487e4dded906a136e1ac5da35eab38bdff5554c5"
 dependencies = [
  "anyhow",
  "proc-macro2",
@@ -4203,10 +4239,29 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "wasm-encoder",
- "wasm-metadata",
- "wasmparser",
- "wit-parser",
+ "wasm-encoder 0.200.0",
+ "wasm-metadata 0.200.0",
+ "wasmparser 0.200.0",
+ "wit-parser 0.200.0",
+]
+
+[[package]]
+name = "wit-component"
+version = "0.201.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825"
+dependencies = [
+ "anyhow",
+ "bitflags 2.4.1",
+ "indexmap 2.0.0",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "wasm-encoder 0.201.0",
+ "wasm-metadata 0.201.0",
+ "wasmparser 0.201.0",
+ "wit-parser 0.201.0",
 ]
 
 [[package]]
@@ -4224,7 +4279,25 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "unicode-xid",
- "wasmparser",
+ "wasmparser 0.200.0",
+]
+
+[[package]]
+name = "wit-parser"
+version = "0.201.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "196d3ecfc4b759a8573bf86a9b3f8996b304b3732e4c7de81655f875f6efdca6"
+dependencies = [
+ "anyhow",
+ "id-arena",
+ "indexmap 2.0.0",
+ "log",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "unicode-xid",
+ "wasmparser 0.201.0",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 76cc5a55c..f73baa8a0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -226,18 +226,18 @@ io-lifetimes = { version = "2.0.3", default-features = false }
 io-extras = "0.18.1"
 rustix = "0.38.31"
 # wit-bindgen:
-wit-bindgen = { version = "0.19.1", default-features = false }
+wit-bindgen = { version = "0.19.2", default-features = false }
 
 # wasm-tools family:
-wasmparser = "0.200.0"
-wat = "1.200.0"
-wast = "200.0.0"
-wasmprinter = "0.200.0"
-wasm-encoder = "0.200.0"
-wasm-smith = "0.200.0"
-wasm-mutate = "0.200.0"
-wit-parser = "0.200.0"
-wit-component = "0.200.0"
+wasmparser = "0.201.0"
+wat = "1.201.0"
+wast = "201.0.0"
+wasmprinter = "0.201.0"
+wasm-encoder = "0.201.0"
+wasm-smith = "0.201.0"
+wasm-mutate = "0.201.0"
+wit-parser = "0.201.0"
+wit-component = "0.201.0"
 
 # Non-Bytecode Alliance maintained dependencies:
 # --------------------------
diff --git a/cranelift/codegen/src/opts/README.md b/cranelift/codegen/src/opts/README.md
index d7b09e4ad..4f586a1ad 100644
--- a/cranelift/codegen/src/opts/README.md
+++ b/cranelift/codegen/src/opts/README.md
@@ -85,3 +85,41 @@ of it boils down to the fact that, unlike traditional e-graphs, our rules are
    One day we intend to extend ISLE with built-in support for commutativity, so
    we don't need to author the redundant commutations ourselves:
    https://github.com/bytecodealliance/wasmtime/issues/6128
+
+4. Be careful with (ideally avoid) multiple matches on the same `Value`, as
+   they can result in surprising multi-matching behavior. Be skeptical of
+   helpers that can inadvertently create this behavior.
+
+   In our mid-end ISLE environment, a `Value` corresponds to an eclass, with
+   multiple possible representations. A rule that matches on a `Value` will
+   traverse all enodes in the eclass, looking for a match. This is usually
+   exactly what we want: it is what allows a pattern like `(iadd (iconst k) x)`
+   to find the `iconst` amongst multiple possibilities for the argument.
+
+   However, this can also result in surprising behavior. If one has a helper
+   and a simplify rule like
+
+       (decl suitable_for_rewrite (Value) Value)
+       (rule (suitable_for_rewrite x @ (iadd ...)) x)
+       (rule (suitable_for_rewrite x @ (isub ...)) x)
+
+       (rule (simplify (ireduce _ x))
+         (if-let _ (suitable_for_rewrite x))
+         x)
+
+    Then this can result in the extremely surprising behavior that `(ireduce
+    (other_op ...))` matches, if `(other_op ...)` is in the same eclass as an
+    `iadd` or `isub`. This happens because the left-hand side binds `x`, which
+    describes the entire eclass; and `suitable_for_rewrite` matches if *any*
+    representation of `x` matches.
+
+    This resulted in a real bug in #7999. The best guidance is to keep rules
+    simple and direct: rather than attempting to abstract out helpers and
+    perform multiple, separate, matches on a `Value`, write patterns directly.
+    This has the additional benefit that the rewrites are more clearly visible
+    to the casual reader. For example:
+
+        (rule (simplify (ireduce _ (iadd ...)))
+              (iadd ...))
+        (rule (simplify (ireduce _ (isub ...)))
+              (isub ...))
diff --git a/crates/fuzzing/src/generators/single_inst_module.rs b/crates/fuzzing/src/generators/single_inst_module.rs
index 9a66fcdf1..e368686ce 100644
--- a/crates/fuzzing/src/generators/single_inst_module.rs
+++ b/crates/fuzzing/src/generators/single_inst_module.rs
@@ -595,7 +595,8 @@ mod test {
     i32.add
   )
   (export "test" (func 0))
-)"#
+)
+"#
         )
     }
 
diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs
index 515b0acd4..9ad701432 100644
--- a/crates/runtime/src/instance.rs
+++ b/crates/runtime/src/instance.rs
@@ -835,13 +835,11 @@ impl Instance {
                     .get(src..)
                     .and_then(|s| s.get(..len))
                     .ok_or(Trap::TableOutOfBounds)?;
-                table.init(
+                table.init_func(
                     dst,
-                    elements.iter().map(|idx| {
-                        TableElement::FuncRef(
-                            self.get_func_ref(*idx).unwrap_or(std::ptr::null_mut()),
-                        )
-                    }),
+                    elements
+                        .iter()
+                        .map(|idx| self.get_func_ref(*idx).unwrap_or(std::ptr::null_mut())),
                 )?;
             }
             TableSegmentElements::Expressions(exprs) => {
@@ -850,11 +848,11 @@ impl Instance {
                     .get(src..)
                     .and_then(|s| s.get(..len))
                     .ok_or(Trap::TableOutOfBounds)?;
-                table.init(
-                    dst,
-                    exprs.iter().map(|expr| match ty {
-                        TableElementType::Func => {
-                            let funcref = match expr {
+                match ty {
+                    TableElementType::Func => {
+                        table.init_func(
+                            dst,
+                            exprs.iter().map(|expr| match expr {
                                 TableElementExpression::Null => std::ptr::null_mut(),
                                 TableElementExpression::Function(idx) => {
                                     self.get_func_ref(*idx).unwrap()
@@ -863,22 +861,23 @@ impl Instance {
                                     let global = self.defined_or_imported_global_ptr(*idx);
                                     unsafe { (*global).as_func_ref() }
                                 }
-                            };
-                            TableElement::FuncRef(funcref)
-                        }
-                        TableElementType::Extern => {
-                            let externref = match expr {
+                            }),
+                        )?;
+                    }
+                    TableElementType::Extern => {
+                        table.init_extern(
+                            dst,
+                            exprs.iter().map(|expr| match expr {
                                 TableElementExpression::Null => None,
                                 TableElementExpression::Function(_) => unreachable!(),
                                 TableElementExpression::GlobalGet(idx) => {
                                     let global = self.defined_or_imported_global_ptr(*idx);
                                     unsafe { (*global).as_externref().clone() }
                                 }
-                            };
-                            TableElement::ExternRef(externref)
-                        }
-                    }),
-                )?;
+                            }),
+                        )?;
+                    }
+                }
             }
         }
 
diff --git a/crates/runtime/src/instance/allocator.rs b/crates/runtime/src/instance/allocator.rs
index 071b28a41..f6683a35b 100644
--- a/crates/runtime/src/instance/allocator.rs
+++ b/crates/runtime/src/instance/allocator.rs
@@ -2,7 +2,7 @@ use crate::imports::Imports;
 use crate::instance::{Instance, InstanceHandle};
 use crate::memory::Memory;
 use crate::mpk::ProtectionKey;
-use crate::table::Table;
+use crate::table::{Table, TableElementType};
 use crate::{CompiledModuleId, ModuleRuntimeInfo, Store};
 use anyhow::{anyhow, bail, Result};
 use std::{alloc, any::Any, mem, ptr, sync::Arc};
@@ -531,14 +531,25 @@ fn initialize_tables(instance: &mut Instance, module: &Module) -> Result<()> {
             TableInitialValue::FuncRef(idx) => {
                 let funcref = instance.get_func_ref(*idx).unwrap();
                 let table = unsafe { &mut *instance.get_defined_table(table) };
-                table.init_func(funcref)?;
+                let init = (0..table.size()).map(|_| funcref);
+                table.init_func(0, init)?;
             }
 
             TableInitialValue::GlobalGet(idx) => unsafe {
                 let global = instance.defined_or_imported_global_ptr(*idx);
-                let funcref = (*global).as_func_ref();
                 let table = &mut *instance.get_defined_table(table);
-                table.init_func(funcref)?;
+                match table.element_type() {
+                    TableElementType::Func => {
+                        let funcref = (*global).as_func_ref();
+                        let init = (0..table.size()).map(|_| funcref);
+                        table.init_func(0, init)?;
+                    }
+                    TableElementType::Extern => {
+                        let externref = (*global).as_externref();
+                        let init = (0..table.size()).map(|_| externref.clone());
+                        table.init_extern(0, init)?;
+                    }
+                }
             },
         }
     }
diff --git a/crates/runtime/src/table.rs b/crates/runtime/src/table.rs
index 804a2e174..b07670739 100644
--- a/crates/runtime/src/table.rs
+++ b/crates/runtime/src/table.rs
@@ -55,64 +55,6 @@ unsafe impl Send for TableElement where VMExternRef: Send {}
 unsafe impl Sync for TableElement where VMExternRef: Sync {}
 
 impl TableElement {
-    /// Consumes the given raw table element value into a table element.
-    ///
-    /// # Safety
-    ///
-    /// This is unsafe as it will *not* clone any externref, leaving the reference count unchanged.
-    ///
-    /// This should only be used if the raw pointer is no longer in use.
-    unsafe fn from_table_value(ty: TableElementType, ptr: TableValue) -> Self {
-        match (ty, ptr) {
-            (TableElementType::Func, None) => Self::UninitFunc,
-            (TableElementType::Func, Some(ptr)) => {
-                let ptr = ptr.as_ptr();
-                let masked = Strict::map_addr(ptr, |a| a & FUNCREF_MASK);
-                Self::FuncRef(masked.cast())
-            }
-            (TableElementType::Extern, None) => Self::ExternRef(None),
-            (TableElementType::Extern, Some(ptr)) => {
-                Self::ExternRef(VMExternRef::from_raw(ptr.as_ptr()))
-            }
-        }
-    }
-
-    /// Clones a table element from the underlying table element.
-    ///
-    /// # Safety
-    ///
-    /// This is unsafe as it will clone any externref, incrementing the reference count.
-    unsafe fn clone_from_table_value(ty: TableElementType, ptr: TableValue) -> Self {
-        match ty {
-            // Functions have no ownership, so defer to the prior method.
-            TableElementType::Func => TableElement::from_table_value(ty, ptr),
-
-            TableElementType::Extern => {
-                Self::ExternRef(ptr.and_then(|p| VMExternRef::clone_from_raw(p.as_ptr())))
-            }
-        }
-    }
-
-    /// Consumes a table element into a raw table element value. This
-    /// includes any tag bits or other storage details that we
-    /// maintain in the table slot.
-    ///
-    /// # Safety
-    ///
-    /// This is unsafe as it will consume any underlying externref into a raw pointer without modifying
-    /// the reference count.
-    ///
-    unsafe fn into_table_value(self) -> TableValue {
-        match self {
-            Self::UninitFunc => None,
-            Self::FuncRef(e) => {
-                let tagged = Strict::map_addr(e, |e| e | FUNCREF_INIT_BIT);
-                Some(NonNull::new(tagged.cast()).unwrap().into())
-            }
-            Self::ExternRef(e) => e.map(|e| NonNull::new(e.into_raw()).unwrap().into()),
-        }
-    }
-
     /// Consumes a table element into a pointer/reference, as it
     /// exists outside the table itself. This strips off any tag bits
     /// or other information that only lives inside the table.
@@ -160,6 +102,33 @@ impl From<VMExternRef> for TableElement {
     }
 }
 
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+struct TaggedFuncRef(*mut VMFuncRef);
+
+impl TaggedFuncRef {
+    const UNINIT: TaggedFuncRef = TaggedFuncRef(ptr::null_mut());
+
+    /// Converts the given `ptr`, a valid funcref pointer, into a tagged pointer
+    /// by adding in the `FUNCREF_INIT_BIT`.
+    fn from(ptr: *mut VMFuncRef) -> Self {
+        let masked = Strict::map_addr(ptr, |a| a | FUNCREF_INIT_BIT);
+        TaggedFuncRef(masked)
+    }
+
+    /// Converts a tagged pointer into a `TableElement`, returning `UninitFunc`
+    /// for null (not a tagged value) or `FuncRef` for otherwise tagged values.
+    fn into_table_element(self) -> TableElement {
+        let ptr = self.0;
+        if ptr.is_null() {
+            TableElement::UninitFunc
+        } else {
+            let unmasked = Strict::map_addr(ptr, |a| a & FUNCREF_MASK);
+            TableElement::FuncRef(unmasked)
+        }
+    }
+}
+
 /// Represents an instance's table.
 pub enum Table {
     /// A "static" table where storage space is managed externally, currently
@@ -290,12 +259,25 @@ impl Table {
     }
 
     /// Initializes the contents of this table to the specified function
-    pub fn init_func(&mut self, init: *mut VMFuncRef) -> Result<(), Trap> {
-        assert!(self.element_type() == TableElementType::Func);
-        for slot in self.elements_mut().iter_mut() {
-            unsafe {
-                *slot = TableElement::FuncRef(init).into_table_value();
-            }
+    ///
+    /// # Panics
+    ///
+    /// Panics if the table is not a function table.
+    pub fn init_func(
+        &mut self,
+        dst: u32,
+        items: impl ExactSizeIterator<Item = *mut VMFuncRef>,
+    ) -> Result<(), Trap> {
+        let dst = usize::try_from(dst).map_err(|_| Trap::TableOutOfBounds)?;
+
+        let elements = self
+            .funcrefs_mut()
+            .get_mut(dst..)
+            .and_then(|s| s.get_mut(..items.len()))
+            .ok_or(Trap::TableOutOfBounds)?;
+
+        for (item, slot) in items.zip(elements) {
+            *slot = TaggedFuncRef::from(item);
         }
         Ok(())
     }
@@ -303,27 +285,21 @@ impl Table {
     /// Fill `table[dst..]` with values from `items`
     ///
     /// Returns a trap error on out-of-bounds accesses.
-    pub fn init(
+    pub fn init_extern(
         &mut self,
         dst: u32,
-        items: impl ExactSizeIterator<Item = TableElement>,
+        items: impl ExactSizeIterator<Item = Option<VMExternRef>>,
     ) -> Result<(), Trap> {
-        let ty = self.element_type();
+        let dst = usize::try_from(dst).map_err(|_| Trap::TableOutOfBounds)?;
 
-        let elements = match self
-            .elements_mut()
-            .get_mut(usize::try_from(dst).unwrap()..)
+        let elements = self
+            .externrefs_mut()
+            .get_mut(dst..)
             .and_then(|s| s.get_mut(..items.len()))
-        {
-            Some(elements) => elements,
-            None => return Err(Trap::TableOutOfBounds),
-        };
+            .ok_or(Trap::TableOutOfBounds)?;
 
         for (item, slot) in items.zip(elements) {
-            debug_assert!(ty.matches(&item));
-            unsafe {
-                *slot = item.into_table_value();
-            }
+            *slot = item;
         }
         Ok(())
     }
@@ -331,6 +307,10 @@ impl Table {
     /// Fill `table[dst..dst + len]` with `val`.
     ///
     /// Returns a trap error on out-of-bounds accesses.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `val` does not have a type that matches this table.
     pub fn fill(&mut self, dst: u32, val: TableElement, len: u32) -> Result<(), Trap> {
         let start = dst as usize;
         let end = start
@@ -341,15 +321,16 @@ impl Table {
             return Err(Trap::TableOutOfBounds);
         }
 
-        debug_assert!(self.type_matches(&val));
-
-        let ty = self.element_type();
-        if let Some((last, elements)) = self.elements_mut()[start..end].split_last_mut() {
-            for e in elements {
-                Self::set_raw(ty, e, val.clone());
+        match val {
+            TableElement::FuncRef(f) => {
+                self.funcrefs_mut()[start..end].fill(TaggedFuncRef::from(f));
+            }
+            TableElement::ExternRef(e) => {
+                self.externrefs_mut()[start..end].fill(e);
+            }
+            TableElement::UninitFunc => {
+                self.funcrefs_mut()[start..end].fill(TaggedFuncRef::UNINIT);
             }
-
-            Self::set_raw(ty, last, val);
         }
 
         Ok(())
@@ -362,6 +343,10 @@ impl Table {
     /// Returns `None` if table can't be grown by the specified amount of
     /// elements, or if the `init_value` is the wrong kind of table element.
     ///
+    /// # Panics
+    ///
+    /// Panics if `init_value` does not have a type that matches this table.
+    ///
     /// # Unsafety
     ///
     /// Resizing the table can reallocate its internal elements buffer. This
@@ -424,9 +409,19 @@ impl Table {
     ///
     /// Returns `None` if the index is out of bounds.
     pub fn get(&self, index: u32) -> Option<TableElement> {
-        self.elements()
-            .get(index as usize)
-            .map(|p| unsafe { TableElement::clone_from_table_value(self.element_type(), *p) })
+        let index = usize::try_from(index).ok()?;
+        match self.element_type() {
+            TableElementType::Func => self
+                .funcrefs()
+                .get(index)
+                .copied()
+                .map(|e| e.into_table_element()),
+            TableElementType::Extern => self
+                .externrefs()
+                .get(index)
+                .cloned()
+                .map(TableElement::ExternRef),
+        }
     }
 
     /// Set reference to the specified element.
@@ -435,14 +430,23 @@ impl Table {
     ///
     /// Returns an error if `index` is out of bounds or if this table type does
     /// not match the element type.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `elem` is not of the right type for this table.
     pub fn set(&mut self, index: u32, elem: TableElement) -> Result<(), ()> {
-        if !self.type_matches(&elem) {
-            return Err(());
+        let index = usize::try_from(index).map_err(|_| ())?;
+        match elem {
+            TableElement::FuncRef(f) => {
+                *self.funcrefs_mut().get_mut(index).ok_or(())? = TaggedFuncRef::from(f);
+            }
+            TableElement::UninitFunc => {
+                *self.funcrefs_mut().get_mut(index).ok_or(())? = TaggedFuncRef::UNINIT;
+            }
+            TableElement::ExternRef(e) => {
+                *self.externrefs_mut().get_mut(index).ok_or(())? = e;
+            }
         }
-
-        let ty = self.element_type();
-        let e = self.elements_mut().get_mut(index as usize).ok_or(())?;
-        Self::set_raw(ty, e, elem);
         Ok(())
     }
 
@@ -507,27 +511,57 @@ impl Table {
         self.element_type().matches(val)
     }
 
-    fn elements(&self) -> &[TableValue] {
+    fn raw_elements(&self) -> &[TableValue] {
         match self {
             Table::Static { data, size, .. } => unsafe { &data.as_ref()[..*size as usize] },
             Table::Dynamic { elements, .. } => &elements[..],
         }
     }
 
-    fn elements_mut(&mut self) -> &mut [TableValue] {
+    fn raw_elements_mut(&mut self) -> &mut [TableValue] {
         match self {
             Table::Static { data, size, .. } => unsafe { &mut data.as_mut()[..*size as usize] },
             Table::Dynamic { elements, .. } => &mut elements[..],
         }
     }
 
-    fn set_raw(ty: TableElementType, elem: &mut TableValue, val: TableElement) {
+    fn funcrefs(&self) -> &[TaggedFuncRef] {
+        assert_eq!(self.element_type(), TableElementType::Func);
         unsafe {
-            let old = *elem;
-            *elem = val.into_table_value();
+            let (a, b, c) = self.raw_elements().align_to();
+            assert!(a.is_empty());
+            assert!(c.is_empty());
+            b
+        }
+    }
 
-            // Drop the old element
-            let _ = TableElement::from_table_value(ty, old);
+    fn funcrefs_mut(&mut self) -> &mut [TaggedFuncRef] {
+        assert_eq!(self.element_type(), TableElementType::Func);
+        unsafe {
+            let (a, b, c) = self.raw_elements_mut().align_to_mut();
+            assert!(a.is_empty());
+            assert!(c.is_empty());
+            b
+        }
+    }
+
+    fn externrefs(&self) -> &[Option<VMExternRef>] {
+        assert_eq!(self.element_type(), TableElementType::Extern);
+        unsafe {
+            let (a, b, c) = self.raw_elements().align_to();
+            assert!(a.is_empty());
+            assert!(c.is_empty());
+            b
+        }
+    }
+
+    fn externrefs_mut(&mut self) -> &mut [Option<VMExternRef>] {
+        assert_eq!(self.element_type(), TableElementType::Extern);
+        unsafe {
+            let (a, b, c) = self.raw_elements_mut().align_to_mut();
+            assert!(a.is_empty());
+            assert!(c.is_empty());
+            b
         }
     }
 
@@ -545,41 +579,34 @@ impl Table {
         match ty {
             TableElementType::Func => {
                 // `funcref` are `Copy`, so just do a mempcy
-                dst_table.elements_mut()[dst_range]
-                    .copy_from_slice(&src_table.elements()[src_range]);
+                dst_table.funcrefs_mut()[dst_range]
+                    .copy_from_slice(&src_table.funcrefs()[src_range]);
             }
             TableElementType::Extern => {
-                // We need to clone each `externref`
-                let dst = dst_table.elements_mut();
-                let src = src_table.elements();
-                for (s, d) in src_range.zip(dst_range) {
-                    let elem = unsafe { TableElement::clone_from_table_value(ty, src[s]) };
-                    Self::set_raw(ty, &mut dst[d], elem);
-                }
+                dst_table.externrefs_mut()[dst_range]
+                    .clone_from_slice(&src_table.externrefs()[src_range]);
             }
         }
     }
 
     fn copy_elements_within(&mut self, dst_range: Range<usize>, src_range: Range<usize>) {
         let ty = self.element_type();
-        let dst = self.elements_mut();
         match ty {
             TableElementType::Func => {
                 // `funcref` are `Copy`, so just do a memmove
-                dst.copy_within(src_range, dst_range.start);
+                self.funcrefs_mut().copy_within(src_range, dst_range.start);
             }
             TableElementType::Extern => {
                 // We need to clone each `externref` while handling overlapping
                 // ranges
+                let elements = self.externrefs_mut();
                 if dst_range.start <= src_range.start {
                     for (s, d) in src_range.zip(dst_range) {
-                        let elem = unsafe { TableElement::clone_from_table_value(ty, dst[s]) };
-                        Self::set_raw(ty, &mut dst[d], elem);
+                        elements[d] = elements[s].clone();
                     }
                 } else {
                     for (s, d) in src_range.rev().zip(dst_range.rev()) {
-                        let elem = unsafe { TableElement::clone_from_table_value(ty, dst[s]) };
-                        Self::set_raw(ty, &mut dst[d], elem);
+                        elements[d] = elements[s].clone();
                     }
                 }
             }
@@ -589,16 +616,17 @@ impl Table {
 
 impl Drop for Table {
     fn drop(&mut self) {
-        let ty = self.element_type();
+        match self.element_type() {
+            // `funcref` tables don't need drops.
+            TableElementType::Func => {}
 
-        // `funcref` tables don't need drops.
-        if let TableElementType::Func = ty {
-            return;
-        }
-
-        // Properly drop any table elements stored in the table
-        for element in self.elements() {
-            drop(unsafe { TableElement::from_table_value(ty, *element) });
+            // `externref` tables are null'd out to ensure that no strong
+            // references are preserved.
+            TableElementType::Extern => {
+                for e in self.externrefs_mut() {
+                    let _ = e.take();
+                }
+            }
         }
     }
 }
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index 505b56aa4..21f6652b7 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -313,6 +313,22 @@ start = "2022-08-22"
 end = "2024-06-26"
 notes = "The Bytecode Alliance is the author of this crate."
 
+[[wildcard-audits.wasmtime-c-api-impl]]
+who = "Trevor Elliott <telliott@fastly.com>"
+criteria = "safe-to-deploy"
+user-id = 73222 # wasmtime-publish
+start = "2024-02-20"
+end = "2025-02-28"
+notes = "The Bytecode Alliance is the author of this crate."
+
+[[wildcard-audits.wasmtime-c-api-macros]]
+who = "Trevor Elliott <telliott@fastly.com>"
+criteria = "safe-to-deploy"
+user-id = 73222 # wasmtime-publish
+start = "2024-02-20"
+end = "2025-02-28"
+notes = "The Bytecode Alliance is the author of this crate."
+
 [[wildcard-audits.wasmtime-cache]]
 who = "Bobby Holley <bobbyholley@gmail.com>"
 criteria = "safe-to-deploy"
diff --git a/supply-chain/config.toml b/supply-chain/config.toml
index 91df64277..f66e4fcdb 100644
--- a/supply-chain/config.toml
+++ b/supply-chain/config.toml
@@ -85,6 +85,12 @@ audit-as-crates-io = true
 [policy.wasmtime-asm-macros]
 audit-as-crates-io = true
 
+[policy.wasmtime-c-api-impl]
+audit-as-crates-io = true
+
+[policy.wasmtime-c-api-macros]
+audit-as-crates-io = true
+
 [policy.wasmtime-cache]
 audit-as-crates-io = true
 
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index 5ad0c81a8..fecb5021d 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -81,6 +81,14 @@ audited_as = "17.0.1"
 version = "19.0.0"
 audited_as = "17.0.1"
 
+[[unpublished.wasmtime-c-api-impl]]
+version = "19.0.0"
+audited_as = "18.0.1"
+
+[[unpublished.wasmtime-c-api-macros]]
+version = "19.0.0"
+audited_as = "18.0.1"
+
 [[unpublished.wasmtime-cache]]
 version = "19.0.0"
 audited_as = "17.0.1"
@@ -848,6 +856,12 @@ when = "2024-02-15"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-encoder]]
+version = "0.201.0"
+when = "2024-02-27"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-metadata]]
 version = "0.10.20"
 when = "2024-02-12"
@@ -861,6 +875,12 @@ when = "2024-02-15"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-metadata]]
+version = "0.201.0"
+when = "2024-02-27"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-mutate]]
 version = "0.2.48"
 when = "2024-02-12"
@@ -874,6 +894,12 @@ when = "2024-02-15"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-mutate]]
+version = "0.201.0"
+when = "2024-02-27"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-smith]]
 version = "0.16.1"
 when = "2024-02-12"
@@ -887,6 +913,12 @@ when = "2024-02-15"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-smith]]
+version = "0.201.0"
+when = "2024-02-27"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmparser]]
 version = "0.121.2"
 when = "2024-02-12"
@@ -900,6 +932,12 @@ when = "2024-02-15"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmparser]]
+version = "0.201.0"
+when = "2024-02-27"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmprinter]]
 version = "0.2.80"
 when = "2024-02-12"
@@ -913,6 +951,12 @@ when = "2024-02-15"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmprinter]]
+version = "0.201.0"
+when = "2024-02-27"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime]]
 version = "17.0.1"
 when = "2024-02-07"
@@ -925,6 +969,18 @@ when = "2024-02-07"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-c-api-impl]]
+version = "18.0.1"
+when = "2024-02-20"
+user-id = 73222
+user-login = "wasmtime-publish"
+
+[[publisher.wasmtime-c-api-macros]]
+version = "18.0.1"
+when = "2024-02-20"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-cache]]
 version = "17.0.1"
 when = "2024-02-07"
@@ -1070,6 +1126,12 @@ when = "2024-02-15"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wast]]
+version = "201.0.0"
+when = "2024-02-27"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wat]]
 version = "1.0.88"
 when = "2024-02-12"
@@ -1083,6 +1145,12 @@ when = "2024-02-15"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wat]]
+version = "1.201.0"
+when = "2024-02-27"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wiggle]]
 version = "17.0.1"
 when = "2024-02-07"
@@ -1281,6 +1349,12 @@ when = "2024-02-23"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen]]
+version = "0.19.2"
+when = "2024-02-26"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-bindgen-core]]
 version = "0.17.0"
 when = "2024-02-05"
@@ -1294,6 +1368,12 @@ when = "2024-02-23"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen-core]]
+version = "0.19.2"
+when = "2024-02-26"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-bindgen-rust]]
 version = "0.17.0"
 when = "2024-02-05"
@@ -1307,6 +1387,12 @@ when = "2024-02-23"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen-rust]]
+version = "0.19.2"
+when = "2024-02-26"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-bindgen-rust-macro]]
 version = "0.17.0"
 when = "2024-02-05"
@@ -1320,6 +1406,12 @@ when = "2024-02-23"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen-rust-macro]]
+version = "0.19.2"
+when = "2024-02-26"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-component]]
 version = "0.20.1"
 when = "2024-01-29"
@@ -1340,6 +1432,12 @@ when = "2024-02-15"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-component]]
+version = "0.201.0"
+when = "2024-02-27"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-parser]]
 version = "0.13.1"
 when = "2024-01-09"
@@ -1360,6 +1458,12 @@ when = "2024-02-15"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-parser]]
+version = "0.201.0"
+when = "2024-02-27"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.witx]]
 version = "0.9.1"
 when = "2021-06-22"


Test patch:
diff --git a/tests/all/gc.rs b/tests/all/gc.rs
index 2f7efad07..7910e5739 100644
--- a/tests/all/gc.rs
+++ b/tests/all/gc.rs
@@ -651,3 +651,81 @@ fn gc_and_tail_calls_and_stack_arguments() -> anyhow::Result<()> {
 
     Ok(())
 }
+
+#[test]
+#[cfg_attr(miri, ignore)]
+fn no_leak_with_global_get_elem_segment() -> anyhow::Result<()> {
+    let dropped = Arc::new(AtomicBool::new(false));
+
+    let engine = Engine::default();
+    let mut store = Store::new(&engine, ());
+    let module = Module::new(
+        &engine,
+        r#"
+            (module
+                (import "" "" (global $init externref))
+                (start $f)
+                (table $t 1 externref)
+                (elem $e externref (global.get $init))
+
+                (func $f
+                    i32.const 0
+                    i32.const 0
+                    i32.const 1
+                    table.init $t $e
+
+                    i32.const 0
+                    i32.const 0
+                    i32.const 1
+                    table.init $t $e
+                )
+            )
+        "#,
+    )?;
+
+    let global = Global::new(
+        &mut store,
+        GlobalType::new(ValType::EXTERNREF, Mutability::Const),
+        Val::ExternRef(Some(ExternRef::new(SetFlagOnDrop(dropped.clone())))),
+    )?;
+
+    Instance::new(&mut store, &module, &[global.into()])?;
+
+    drop(store);
+
+    assert!(dropped.load(SeqCst));
+    Ok(())
+}
+
+#[test]
+#[cfg_attr(miri, ignore)]
+fn table_init_with_externref_global_get() -> anyhow::Result<()> {
+    let dropped = Arc::new(AtomicBool::new(false));
+
+    let mut config = Config::new();
+    config.wasm_function_references(true);
+    let engine = Engine::new(&config)?;
+    let mut store = Store::new(&engine, ());
+    let module = Module::new(
+        &engine,
+        r#"
+            (module
+                (import "" "" (global $init externref))
+                (table $t 1 externref (global.get $init))
+            )
+        "#,
+    )?;
+
+    let global = Global::new(
+        &mut store,
+        GlobalType::new(ValType::EXTERNREF, Mutability::Const),
+        Val::ExternRef(Some(ExternRef::new(SetFlagOnDrop(dropped.clone())))),
+    )?;
+
+    Instance::new(&mut store, &module, &[global.into()])?;
+
+    drop(store);
+
+    assert!(dropped.load(SeqCst));
+    Ok(())
+}