> On 11/12/26 1:47 AM, Jin Ma wrote: > > Implement TARGET_CALLEE_SAVE_COST and TARGET_FRAME_ALLOCATION_COST so > > IRA weighs callee saves against spills at their marginal execution > > cost: reusing an already-required save is free, completing an odd > > save count is discounted by half the memory cost (the fusion-aware > > scheduling keeps the pair adjacent), and the first spill of a > > frameless leaf is charged the separate SP adjustment. > > > > This is a first, deliberately simple attempt, posted as a draft to > > collect suggestions on the approach. The model is unweighted and > > knows nothing about emission; with more precise modeling (call and > > spill frequency weighting, awareness of the final save layout) the > > fusion characteristics of the target should translate into more > > reliable gains. > > > > Assisted-by: qwen3.8-max > > > > gcc/ChangeLog: > > > > * config/riscv/riscv.cc (riscv_ira_save_mask): New. > > (riscv_frame_allocation_cost): New. > > (riscv_callee_save_cost): New. > > (TARGET_FRAME_ALLOCATION_COST, TARGET_CALLEE_SAVE_COST): Define. > > > > gcc/testsuite/ChangeLog: > > > > * gcc.target/riscv/callee-save-cost-mempair.c: New test. > > * gcc.target/riscv/callee-save-cost-fusion.c: New test. > Interesting. I like the basic idea. Obviously it needs to test that > load/store pair fusion is supported. > > Does aarch64 do something similar and if so are we roughly following > what they did?
Yes. aarch64 already implements both hooks (aarch64_callee_save_cost and aarch64_frame_allocation_cost), and i386 likewise implements callee_save_cost (ix86_callee_save_cost, modeling its push/pop: a push is much shorter than a typical spill and does its own stack allocation and deallocation). The middle-end wiring (the callee-save cost query in ira-color.cc with entry/exit frequency weighting) came in with PR117477. I follow the same structure: mask-based marginal costs, free reuse of registers that must be saved anyway, and frame costs charged symmetrically on the spill and callee-save sides. RISC-V also has single-instruction pairing extensions in the same family as aarch64's stp/ldp and i386's push/pop: XTheadMemPair (th.sdd/th.ldd, already covered by the discount condition in this RFC), Zcmp (cm.push/cm.pop), and XTheadInt (th.int.push/th.int.pop). For extensions of this kind the aarch64/i386 style of modeling applies directly; both that path and the macro-fusion path need more thorough experimentation, which is why this went out as an RFC. For fusion the key difference is that aarch64's ldp/stp are real single instructions, so a pair can be folded at full value, whereas a RISC-V macro-fused pair is still two instructions, but decodes to a single uop: it saves decode bandwidth and backend resources such as issue-queue and ROB entries, and executes as one merged memory operation. However, the pair only forms when the two instructions are emitted adjacently (-fshrink-wrap-separate, for example, can split and relocate individual saves), which IRA cannot guarantee at decision time. So I conservatively discount the completing save by half of mem_cost: when the pair materializes the benefit is close to full value, when it does not there is none, and the half is a hedge on the formation probability. Admittedly this is a very preliminary idea and may well not be the right tradeoff; call and spill frequencies are not weighted either. I would also like to ask whether you think fusion-aware cost modeling in IRA is a viable direction and worth pursuing, and whether there are risks I have not considered or a better way to model this. BR, Jin
