On RV32, musttail calls where arguments are passed indirectly (CCValAssign::Indirect) produce dangling pointers. The caller loads data from the incoming indirect pointer, creates a new stack temporary, stores the data there, and passes a pointer to the new slot. The tail call then deallocates the caller's stack frame before the callee executes, leaving the pointer dangling.
Example: fp128 on RV32 is passed indirectly (16 bytes, pointer in a0). i128 on RV32 also triggers this path.
declare i32 @callee(fp128 %a)
define i32 @caller(fp128 %a) {
entry:
%v = musttail call i32 @callee(fp128 %a)
ret i32 %v
}
On RV32, the generated code:
- Loads fp128 data from incoming pointer (caller's caller's frame)
- Creates a new stack slot via
DAG.CreateStackTemporary
- Stores data to the new stack slot
- Passes pointer to new stack slot in
a0
- Tail-jumps to callee -- stack frame is deallocated, pointer is now dangling
The correct behavior for musttail is to forward the original incoming pointer directly, since musttail guarantees matching prototypes. The incoming pointer points to the caller's caller's frame, which remains valid after the tail call.
Note: PR #184972 fixed the non-musttail case by rejecting tail calls with indirect args entirely. This issue is specifically about musttail, which must be a tail call and therefore needs a different fix (forwarding the pointer rather than rejecting the optimization).
On RV64, fp128 and i128 fit in 2 registers (direct), so this issue only manifests on RV32 (or with types larger than 2xXLEN on RV64).
On RV32,
musttailcalls where arguments are passed indirectly (CCValAssign::Indirect) produce dangling pointers. The caller loads data from the incoming indirect pointer, creates a new stack temporary, stores the data there, and passes a pointer to the new slot. The tail call then deallocates the caller's stack frame before the callee executes, leaving the pointer dangling.Example:
fp128on RV32 is passed indirectly (16 bytes, pointer ina0).i128on RV32 also triggers this path.On RV32, the generated code:
DAG.CreateStackTemporarya0The correct behavior for
musttailis to forward the original incoming pointer directly, sincemusttailguarantees matching prototypes. The incoming pointer points to the caller's caller's frame, which remains valid after the tail call.Note: PR #184972 fixed the non-
musttailcase by rejecting tail calls with indirect args entirely. This issue is specifically aboutmusttail, which must be a tail call and therefore needs a different fix (forwarding the pointer rather than rejecting the optimization).On RV64,
fp128andi128fit in 2 registers (direct), so this issue only manifests on RV32 (or with types larger than 2xXLEN on RV64).