fitzgen commented on issue #14569:
<details><summary>Full LLM report</summary>
Cranelift inliner gives inlined dynamic stack slots the wrong dynamic type
Date 2026-10-05 Wasmtime commit 73b04cff3317d1e308866eb24359483ac6116669(main)Host macOS 15.8.1 (Darwin 24.6.0), aarch64-apple-darwinModel Claude Opus 5.5 ( claude-opus-5-5)Component cranelift/codegen/src/inline.rs(create_dynamic_types)Class Wrong entity remapping: compile panic, or a silently wrong slot type Severity Low. Wasmtime never emits dynamic types; this affects Cranelift embedders that use Context::inlinewith dynamic vectors.Summary
create_dynamic_types(cranelift/codegen/src/inline.rs:1559-1565) computes
the offset that it adds to every calleeDynamicTypeindex from the wrong
table:fn create_dynamic_types(func: &mut ir::Function, callee: &ir::Function, ...) -> u32 { let offset = func.dynamic_stack_slots.len(); // should be func.dfg.dynamic_types.len() ... for ... in callee.dfg.dynamic_types.values() { func.dfg.dynamic_types.push(...); // appended at dynamic_types.len()The callee's dynamic types are appended at
func.dfg.dynamic_types.len(). But
create_dynamic_stack_slotsand every inlined instruction that names a
dynamic type remap the index using the dynamic-stack-slot count. When the
caller has a different number of dynamic stack slots than dynamic types, every
inlined reference names the wrongdtN, and the verifier accepts it:
More slots than types: the remapped index points past the end of the
table.Context::compilethen panics with
Undeclared dynamic vector type: dt2(ir/function.rs:276, reached from
machinst/abi.rs:1294).More types than slots: the remapped index lands on one of the caller's
own types. In the repro, the inlined slot becomes ani8x16slot instead
of ani32x4one. Frame layout happens to survive today only because every
backend'sdynamic_vector_bytesis independent of the type.This has been present since the inliner landed (
968952abe5, #11210). The
existingcranelift/filetests/filetests/inline/dynamic-stack-slots.clifmisses
it because its caller has equal numbers of slots and types.Reproduction
Run from the repository root.
$ target/debug/clif-util test reports/018-inline-dynamic-type-offset/repro-inline.clif Missed #0: \bdss2 = explicit_dynamic_slot dt1\b # caller: 1 type, 2 slots Error: 1 failure $ target/debug/clif-util test reports/018-inline-dynamic-type-offset/repro-wrong-type.clif Missed #0: \bdss1 = explicit_dynamic_slot dt2\b # caller: 2 types, 1 slot Error: 1 failure # (gets the caller's i8x16 dt1) $ cargo run --manifest-path reports/018-inline-dynamic-type-offset/harness/Cargo.toml -- \ reports/018-inline-dynamic-type-offset/repro.clif verifier OK for %caller thread 'main' panicked at cranelift/codegen/src/ir/function.rs:276:32: Undeclared dynamic vector type: dt2
harness/is a roughly 60-line program that drives Cranelift's public API on
a.cliffile:
- Parse the file with
cranelift-reader.- Inline every call with
Context::inline.- Run the verifier.
Context::compilefor aarch64, then print the relocations.Suggested fix
Use
func.dfg.dynamic_types.len()for the offset. Addtest inlinefiletests
whose caller has unequal numbers of dynamic types and dynamic stack slots, in
both directions.Separately, the verifier should check that each dynamic stack slot's
dyn_ty
is declared, so that this class of bug fails verification instead of panicking
in the ABI code.</details>
fitzgen opened issue #14569:
create_dynamic_typesincranelift/codegen/src/inline.rscomputes its
offset fromfunc.dynamic_stack_slots.len(), but it should use
func.dfg.dynamic_types.len(). Whenever the caller has a different number
of dynamic stack slots than dynamic types, inlined slots name the wrong
dynamic type. The verifier accepts the result, and then:
Context::compilepanics withUndeclared dynamic vector type, if the
index is out of range; orthe inlined slot silently gets one of the caller's unrelated types.
.clifTest Casetest inline target aarch64 function %callee(i32) { gv0 = dyn_scale_target_const.i32x4 dt0 = i32x4*gv0 dss0 = explicit_dynamic_slot dt0 block0(v0: i32): v1 = splat.dt0 v0 v2 = dynamic_stack_addr.i64 dss0 store.dt0 notrap aligned v1, v2 return } function %caller() { gv0 = dyn_scale_target_const.i64x2 dt0 = i64x2*gv0 dss0 = explicit_dynamic_slot dt0 dss1 = explicit_dynamic_slot dt0 fn0 = %callee(i32) block0: v0 = iconst.i32 1 call fn0(v0) return } ; check: dss2 = explicit_dynamic_slot dt1Steps to Reproduce
clif-util test test.clifExpected Results
The inlined slot uses the copied callee type:
dss2 = explicit_dynamic_slot dt1.Actual Results
Missed #0: \bdss2 = explicit_dynamic_slot dt1\b > dss2 = explicit_dynamic_slot dt2The caller has no
dt2. Compiling the inlined function panics with
Undeclared dynamic vector type: dt2.Versions and Environment
Cranelift version or commit:
73b04cff33Operating system: macOS 15.8.1
Architecture: aarch64
fitzgen added the bug label to Issue #14569.
fitzgen added the cranelift label to Issue #14569.
Last updated: Oct 11 2026 at 04:10 UTC