alexcrichton added the cranelift label to Issue #14939.
alexcrichton added the cranelift:area:aarch64 label to Issue #14939.
alexcrichton added the bug label to Issue #14939.
alexcrichton opened issue #14939:
This test:
test unwind set unwind_info target aarch64-pc-windows-msvc function %f() preserve_all { fn0 = %g() block0: call fn0() return }fails with:
$ cargo run -p cranelift-tools -- test preserve-all.clif thread 'worker #N' panicked at cranelift/codegen/src/isa/unwind/winarm64.rs:263:33: Can't save registers before X19 FAIL preserve-all.clif: panicked in worker #N: Can't save registers before X19 1 tests Error: 1 failureThis module:
(module (type $t (func (param i32) (result i32))) (table funcref (elem $g)) (func $g (type $t) local.get 0 i32.const 1 i32.add) (func (export "f") (param i32) (result i32) local.get 0 i32.const 0 return_call_indirect (type $t)))fails with guest debugging enabled:
$ cargo run --features all-arch -- compile --target aarch64-pc-windows-msvc -D guest-debug=y retcall_ind.wat thread '<unnamed>' panicked at cranelift/codegen/src/isa/unwind/winarm64.rs:263:33: Can't save registers before X19Without guest debugging it compiles, but its unwind info is wrong:
$ cargo run --features all-arch -- compile --target aarch64-pc-windows-msvc retcall_ind.wat $ aarch64-linux-gnu-objdump -d retcall_ind.cwasm 0000000000000020 <wasm[0]::function[1]>: 20: a9bf7bfd stp x29, x30, [sp, #-16]! 24: 910003fd mov x29, sp 28: f8408050 ldur x16, [x2, #8] 2c: f8418210 ldur x16, [x16, #24] 30: 91008210 add x16, x16, #0x20 34: eb3063ff cmp sp, x16 38: 54000363 b.cc a4 <wasm[0]::function[1]+0x84> 3c: a9bf6bf3 stp x19, x26, [sp, #-16]! ... $ aarch64-linux-gnu-objcopy -O binary --only-section=.xdata retcall_ind.cwasm retcall_ind.xdata $ xxd -g1 retcall_ind.xdata | head -2 00000000: 05 00 00 08 e1 81 00 00 23 00 00 08 cc 01 e1 81 ........#....... 00000010: 3a 00 00 30 01 da 01 da 81 db 01 db 81 cc 01 cc :..0............The
.xdataentry forfunction[1]is at offset 0x8, and its unwind codes
arecc 01 e1 81:
cc 01issave_regp_x x19, x20, [sp, #-16]!, but the prologue savesx19, x26.- There's no
end(e4) code. The next function's header (3a 00 00 30) follows immediately.- The stack-limit check at 0x28-0x38 has no codes, so the codes don't map one-to-one to prologue instructions.
<details>
<summary>LLM summary, if helpful</summary>All three are in
cranelift/codegen/src/isa/unwind/winarm64.rs:
preserve_allpanic: Windows arm64 unwind codes can only describe
saves of x19-x28 and d8-d15 (short ofsave_any_xreg), and
create_unwind_infopanics on anything else.preserve_allsaves
x0-x17 too, and Wasmtime uses it for the guest-debug breakpoint
trampoline.Non-consecutive pair: the prologue saves callee-saved registers two
at a time in sorted order, and regalloc2 doesn't hand them out
consecutively, sostp x19, x26happens. The twoSaveRegrecords are
merged into onesave_regp_x, which can only express x(N), x(N+1), and
the second register is dropped. An SEH unwinder would restore x20 from
x26's slot and leave x26 alone.No
endcode / not one-to-one:emitwrites only the prologue
codes, neverend, and nonopcodes for prologue instructions that
don't change unwind state (bti c, the stack-limit check, the tail-call
frame move, multi-instruction SP adjustments). The format requires both
for unwinding from inside a prologue. Wasmtime's own unwinding uses
frame pointers, so this affects only SEH-based tools (debuggers, ETW,
RtlVirtualUnwind). None of this was tested on Windows.</details>
Last updated: Oct 31 2026 at 23:03 UTC