[WebAssembly] const local of __funcref type miscompiled at -O0: table.set fed an uninitialized i32
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
## Summary
On wasm32 at `-O0`, a `const`-qualified local of a `__funcref` function pointer type is miscompiled. Clang folds the initializer into a `ptrtoint (ptr @f to target("wasm.funcref"))` constant expression instead of emitting `llvm.wasm.ptr.to_funcref`, and the backend then feeds an uninitialized `i32` local to `table.set`. The resulting module fails validation. Dropping the `const` produces correct code.
## Reproducer
```cpp
typedef void (*__funcref Fn)();
__attribute__((import_module("env"), import_name("f"))) void f();
extern "C" void go() {
const Fn t = (Fn)f;
t();
}
```
```
clang++ --target=wasm32 -O0 -mreference-types -nostdlib -c repro.cpp -o repro.o
wasm-ld repro.o -o repro.wasm --no-entry --export=go
```
Validation (V8 via Node 24, Chrome 153 behaves the same):
```
CompileError: WebAssembly.compile(): Compiling function #1:"go" failed: table.set[1] expected type funcref, found local.get of type i32 @+104
```
## Generated code
`llvm-objdump -d repro.wasm`:
```
0000004b :
.local funcref, i32
51: 41 81 80 80 80 00 i32.const 1
57: 25 81 80 80 80 00 table.get 1
5d: 21 00 local.set 0
5f: 41 81 80 80 80 00 i32.const 1
65: 1a drop
66: 41 00 i32.const 0
68: 20 01 local.get 1
6a: 26 80 80 80 80 00 table.set 0
70: 41 00 i32.const 0
72: 11 80 80 80 80 00 80 80 80 80 00 call_indirect 0
```
Local 1 is an `i32` that is never written; the `funcref` in local 0 is never read.
`clang++ ... -S -emit-llvm`:
```llvm
define hidden void @go() #0 {
%1 = alloca target("wasm.funcref"), align 4
%2 = call target("wasm.funcref") @llvm.wasm.ptr.to_funcref(ptr @_Z1fv)
store target("wasm.funcref") %2, ptr %1, align 4
%3 = call ptr @llvm.wasm.funcref.to_ptr(target("wasm.funcref") ptrtoint (ptr @_Z1fv to target("wasm.funcref")))
call void %3()
ret void
}
```
The store to `%1` is dead; the load of `t` for the call was replaced by the constant `ptrtoint (ptr @_Z1fv to target("wasm.funcref"))`, which is not how the initializer was emitted.
## Expected
The same code as without `const`, which validates and runs:
```
0000004b :
.local funcref, funcref
4f: 41 81 80 80 80 00 i32.const 1
55: 25 81 80 80 80 00 table.get 1
5b: 21 00 local.set 0
5d: 20 00 local.get 0
5f: 21 01 local.set 1
61: 41 00 i32.const 0
63: 20 01 local.get 1
65: 26 80 80 80 80 00 table.set 0
```
## Version
```
clang version 23.1.0 (https://github.com/llvm/llvm-project ea7d852a70e8bdfaf601d6626a760f9771b2c4b4)
Target: x86_64-pc-windows-msvc (host), compiling for wasm32
```
Contributor guide
Research direction
Start by running the provided clang++ and wasm-ld reproducer, then compare its LLVM IR and llvm-objdump output with the non-const version. Trace how the const funcref initializer becomes the shown ptrtoint expression and reaches the WebAssembly backend. Done means the generated module validates and the const and non-const cases produce equivalent valid calls.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, wasm
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 52/100