rv64: Preserve FP only for dynamic frames
7c9a7f91c60daea6af9b563519c84fd42b9d1d9bc888b890b0248247fe48d81b
Static frames do not modify FP, and the register allocator does not assign it. Limit FP preservation to dynamic frames while keeping frame sizes and stack offsets unchanged. Assisted-by: Codex:gpt-6-astra
1 parent
2c74c0a9
lib/std/arch/rv64/emit.rad
+8 -8
| 621 | 621 | ////////////////////////// |
|
| 622 | 622 | // Prologue / Epilogue // |
|
| 623 | 623 | ////////////////////////// |
|
| 624 | 624 | ||
| 625 | 625 | /// Emit function prologue. |
|
| 626 | - | /// Allocates stack frame, saves RA/FP, saves callee-saved registers. |
|
| 626 | + | /// Allocate the frame and save registers. Save FP only for dynamic frames. |
|
| 627 | 627 | export fn emitPrologue(e: *mut Emitter, frame: *Frame) { |
|
| 628 | 628 | // Fast path: leaf function with no locals. |
|
| 629 | 629 | if frame.totalSize == 0 { |
|
| 630 | 630 | return; |
|
| 631 | 631 | } |
| 641 | 641 | } |
|
| 642 | 642 | // Save return address. |
|
| 643 | 643 | if not frame.isLeaf { |
|
| 644 | 644 | emitSd(e, super::RA, super::SP, totalSize - super::DWORD_SIZE); |
|
| 645 | 645 | } |
|
| 646 | - | // Save frame pointer. |
|
| 647 | - | emitSd(e, super::FP, super::SP, totalSize - super::DWORD_SIZE * 2); |
|
| 648 | - | ||
| 649 | - | // Set up frame pointer, only needed when dynamic allocs may move SP. |
|
| 646 | + | // Save and set FP only when dynamic allocations can move SP. |
|
| 650 | 647 | if frame.isDynamic { |
|
| 648 | + | emitSd(e, super::FP, super::SP, totalSize - super::DWORD_SIZE * 2); |
|
| 651 | 649 | emitAddImm(e, super::FP, super::SP, totalSize); |
|
| 652 | 650 | } |
|
| 653 | 651 | // Save callee-saved registers. |
|
| 654 | 652 | for i in 0..frame.savedRegsLen { |
|
| 655 | 653 | let sr = frame.savedRegs[i]; |
| 666 | 664 | } |
|
| 667 | 665 | recordBranch(e, frame.epilogueBlock, BranchKind::Jump); |
|
| 668 | 666 | } |
|
| 669 | 667 | ||
| 670 | 668 | /// Emit function epilogue. |
|
| 671 | - | /// Restores callee-saved registers, `RA/FP`, deallocates frame, returns. |
|
| 669 | + | /// Restore saved registers and release the frame. Restore FP only for dynamic frames. |
|
| 672 | 670 | export fn emitEpilogue(e: *mut Emitter, frame: *Frame) { |
|
| 673 | 671 | // Record epilogue block address for return jumps. |
|
| 674 | 672 | recordBlock(e, frame.epilogueBlock); |
|
| 675 | 673 | ||
| 676 | 674 | // Fast path: leaf function with no locals. |
| 688 | 686 | // Restore callee-saved registers. |
|
| 689 | 687 | for i in 0..frame.savedRegsLen { |
|
| 690 | 688 | let sr = frame.savedRegs[i]; |
|
| 691 | 689 | emitLd(e, sr.reg, super::SP, sr.offset); |
|
| 692 | 690 | } |
|
| 693 | - | // Restore frame pointer. |
|
| 694 | - | emitLd(e, super::FP, super::SP, totalSize - super::DWORD_SIZE * 2); |
|
| 691 | + | // Restore FP only if the prologue saved and changed it. |
|
| 692 | + | if frame.isDynamic { |
|
| 693 | + | emitLd(e, super::FP, super::SP, totalSize - super::DWORD_SIZE * 2); |
|
| 694 | + | } |
|
| 695 | 695 | // Restore return address. |
|
| 696 | 696 | if not frame.isLeaf { |
|
| 697 | 697 | emitLd(e, super::RA, super::SP, totalSize - super::DWORD_SIZE); |
|
| 698 | 698 | } |
|
| 699 | 699 | // Deallocate stack frame. |