Shoryamishra61 opened PR #14947 from Shoryamishra61:fix/interrupt-poll-14926 to bytecodealliance:main:
Fixes #14926
Why this change is needed
When inlining functions that contain
interrupt_pollor compiling functions where aninterrupt_pollpossesses user stack maps, Cranelift encountered several issues:
- Inliner Crash / Unknown Call Opcode:
interrupt_pollhasOpcode::is_call() == truebecause it transfers control to the runtime interpreter, but it lacks aFuncRef/ external callee body. The inliner previously hit the unhandled call assertion.- Calling Convention Invariant: The IR verifier mandates that
interrupt_pollonly appears in functions with thetailcalling convention. Inlining atailcallee containing aninterrupt_pollinto a caller with a different convention (e.g.system_v) resulted in invalid CLIF.- Exception Table Fixups: When inlining into a
try_callsite, exception table fixups attempted to inspectinterrupt_pollas a callable target.- Backend Safepoints & Stack Map PC: Neither the x64 nor AArch64 backends classified
InterruptPollas a safepoint (is_safepoint()), and stack maps were not attached to the faulting load instruction PC where runtime signal handlers inspect frame state.- Consecutive Stack Map PC Collision: In rare cases where a
preserve_allcall immediately precedes aninterrupt_polland both have stack maps, the call return PC and the faulting load PC coincide, violating exact-PC stack map uniqueness.Implementation Highlights
- Inliner (
cranelift/codegen/src/inline.rs):
- Allowed
interrupt_pollto pass through the instruction-copying loop without tripping the unhandled call assertion.- Added
can_inlinecheck requiring callers to haveCallConv::Tailif the callee containsinterrupt_poll; non-tail callers preserve the out-of-line call.- Restricted exception table fixup candidates to
Call,CallIndirect,TryCall, andTryCallIndirect.- ISA Backends (
cranelift/codegen/src/isa/{x64, aarch64}/inst/):
- Updated
is_safepoint()to returntrueforInst::InterruptPoll.- Emitted user stack maps at the address of the faulting load (
sink.cur_offset()).- Emitted an architecture-specific NOP (x64:
0x90, AArch64:0xd503201f) when a preceding call stack map shares the same PC with the poll's stack map.- MachBuffer & Disassembly (
buffer.rs,write.rs):
- Added
MachBuffer::last_user_stack_map_pc()to detect PC collisions.- Generalized
write.rsto print user stack maps for any safepoint instruction in textual CLIF output.- Regression Tests (
cranelift/filetests/):
- Added 5 new CLIF fixtures covering inliner behavior, x64/AArch64 stack map emission, and consecutive call/poll PC collision handling.
Verification Done
The following test suites were executed locally against current upstream
main(257ecb40e665):
issue_repro_1.clifandissue_repro_2.cliffrom #14926 (PASSED)cargo run -p cranelift-tools -- test cranelift/filetests/filetests(1,387 tests PASSED)cargo test -p cranelift-codegen -j 1(204 unit tests, 22 doctests PASSED)cargo test -p cranelift-filetests -j 1(3 unit tests, 1 doctest PASSED)cargo fmt -p cranelift-codegen -- --check&cargo clippy -p cranelift-codegen --all-targets -- -D warnings(PASSED, 0 warnings)cargo run --release -p cranelift-tools -j 1 -- test <5 fixtures>(release build compiled and 5 regression tests PASSED)AI Tool Usage Disclosure
In compliance with the Bytecode Alliance AI Tool Use Policy: An AI assistant (Antigravity) was used locally to assist with forward-porting the patch to current upstream
main, running test harness commands, resolving trailing whitespace on test fixtures, and formatting this submission text. All code logic, test results, and diffs were inspected and reviewed.
Shoryamishra61 removed the review request for wasmtime-compiler-reviewers from PR #14947.
Shoryamishra61 requested alexcrichton for a review on PR #14947.
Shoryamishra61 requested wasmtime-compiler-reviewers for a review on PR #14947.
github-actions[bot] added the label cranelift on PR #14947.
github-actions[bot] added the label cranelift:area:machinst on PR #14947.
github-actions[bot] added the label cranelift:area:x64 on PR #14947.
github-actions[bot] added the label cranelift:area:aarch64 on PR #14947.
:memo: alexcrichton submitted PR review.
:speech_balloon: alexcrichton created PR review comment:
It's ok to trim down verbose LLM comments like this. Basically this just needs to convey "
InterruptPollreturnsis_call(), but it doesn't participate in inlining"
:speech_balloon: alexcrichton created PR review comment:
IIRC we've already got inlining heuristics elsewhere in Wasmtime which would cover things like this. Subjectively I'd say it's ok to drop this and leave this up to callers, and if the heuristics from Wasmtime are ever extracted we could perhaps consolidate with this.
:speech_balloon: alexcrichton created PR review comment:
IIRC we've already got infrastructure in other places for whether or not metadata applies to after an instruction or that instruction itself (or something like that). For example I think
MachDebugTagPosserves that purpose. Personally I think it would be better to improve stack map lookup to handle that as opposed to injecting otherwise-not-usefulnops.
cfallin commented on PR #14947:
interrupt_pollhasOpcode::is_call() == trueMy intuition here is that this is probably the wrong decision, in hindsight: yes it can "do a call" in a sense, if a signal handler runs, but that's completely transparent. The "can call" opcode characteristic is really more relevant for things like inlining, as we see here, and in most ways the opcode is not a call (with args, with a callee, etc). So we should probably flip the condition here and then work through the implications, IMHO.
(I will take a more detailed look next week when I am fully back at the keyboard; I just wanted to leave this thought now as the discussion is open)
alexcrichton requested cfallin for a review on PR #14947.
cfallin commented on PR #14947:
Looking at this a little more, it remains my opinion that
interrupt_pollshould not be a "call". Some of the other fixes here are good though. @Shoryamishra61 can you address that concern by removing the "callness" of the opcode and then working through the implications?(Also cc @erikrose)
erikrose commented on PR #14947:
Yes, we should hold loosely my choice to put
.call()on there; it may be less transgressive without. This might also be an opportunity to enrich the documentation ofcall(), beyond "a call instruction". I struggled with what the right choice was. Thanks for having a second look!
cfallin commented on PR #14947:
@erikrose would you mind taking on the fix here, since the original creator of this PR hasn't responded for a week?
Shoryamishra61 commented on PR #14947:
Apologies for the delayed response, @cfallin and @alexcrichton . I've been occupied with other commitments over the past week and wasn't able to follow up on the review discussions.
I'm still interested in seeing this through and addressing the feedback.
I understand the concern about treating
interrupt_pollas a call, and I agree that removing its call classification is worth investigating. I'll revisit the affected inliner paths and verifier assumptions, simplify the comments highlighted in review, and look into the existing metadata infrastructure for handling stack map PCs without introducing unnecessary NOPs.If the work hasn't already been handed over, I'd appreciate the opportunity to make these revisions. If @erikrose has already started, I'm happy to coordinate to avoid duplicating effort.
Thanks for the detailed feedback and your patience.
cfallin commented on PR #14947:
Ah, no worries @Shoryamishra61, I just wanted to make sure things were moving forward and didn't want to put any pressure on an external contributor (you don't owe us anything!) -- if you want to keep going on this, please do. I guess if you feel like you need to hand it off at any point, it would make sense to pass off to Erik. Either way we (as Cranelift maintainers) do want to see it resolved before declaring the MMU-interruption work done, since this is a bug, but that's not on you to ensure :-)
Last updated: Oct 31 2026 at 23:03 UTC