From d10d3b9fc60dbf6a5a656c6f69c5950456ac26a2 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Tue, 30 Jun 2026 20:34:32 -0500 Subject: [PATCH] More deferred issues resolved. --- STATUS.md | 43 ++++- demos/rsrcProbe.apl | Bin 46685 -> 46685 bytes src/llvm/lib/Target/W65816/CMakeLists.txt | 1 - src/llvm/lib/Target/W65816/W65816.h | 7 - .../lib/Target/W65816/W65816FrameLowering.cpp | 39 ++++- .../lib/Target/W65816/W65816ISelLowering.cpp | 29 ++-- .../Target/W65816/W65816PreSpillCrossCall.cpp | 160 ------------------ .../lib/Target/W65816/W65816TargetMachine.cpp | 25 ++- 8 files changed, 103 insertions(+), 201 deletions(-) delete mode 100644 src/llvm/lib/Target/W65816/W65816PreSpillCrossCall.cpp diff --git a/STATUS.md b/STATUS.md index d3ac62f..f310dda 100644 --- a/STATUS.md +++ b/STATUS.md @@ -347,11 +347,41 @@ which runs correctly under MAME (apple2gs). The ~6% gap is the per-call defensive `REP #$30` mode-set our FrameLowering emits at every function entry (Calypsi omits it via a global M/X=16 invariant) plus a marginally larger stack frame. Both - are deliberate correctness choices; eliding the entry REP safely needs - the deferred per-region REP/SEP scheduling pass (design §3.3) -- a - naive global elision risks the silent i16-immediate-in-M=1 miscompile - documented in W65816FrameLowering.cpp. Speed is the optimization - priority, not size. + are deliberate correctness choices; eliding the entry REP safely by + default needs the deferred per-region REP/SEP scheduling pass (design + §3.3) -- a naive global elision risks the silent i16-immediate-in-M=1 + miscompile documented in W65816FrameLowering.cpp. Speed is the + optimization priority, not size. + + An opt-in `-mllvm -w65816-omit-entry-rep` flag (default off, hidden) + drops the per-function entry `REP #$30` for whole-program builds that + are known to maintain the M=16/X=16 invariant everywhere (crt0, all + compiled code, and every hand-written asm / interrupt entry point -- + our runtime does; libgcc's `__mulhi3`/`__umulhisi3`/etc. already rely + on it and carry no entry REP). It has NO per-function safety net -- it + trusts the invariant globally, so it is unsound if any linked code + (including an interrupt handler) can enter a function in 8-bit mode. + Use only for deliberate whole-program builds; the safe-by-default + version remains the §3.3 dataflow pass above. + + Measured whole-program win (runtime + demo both built with the flag, + final linked `.bin`; `--gc-sections` on so the saving scales with live + function count): + + | demo | base | omit-rep | saved | % | + |-------------|-------:|---------:|------:|-------:| + | helloBeep | 3611 | 3607 | 4 | 0.11% | + | helloWindow | 5493 | 5487 | 6 | 0.11% | + | frame | 10492 | 10460 | 32 | 0.30% | + | minicad | 15311 | 15275 | 36 | 0.24% | + | reversi | 19425 | 19371 | 54 | 0.28% | + | printfProbe | 36791 | 36771 | 20 | 0.05% | + | **9 demos** | 108588 | 108404 | 184 | 0.17% | + + So ~0.17% overall (2 bytes per live function + ~3 cyc/call): a small, + uniform win, largest where function count is high (reversi/minicad) and + smallest where a few big functions dominate (printfProbe). Confirms + the gain does not justify making elision the default. - `compare/` holds three side-by-side C tests with our asm and Calypsi's listing for static-size comparison: @@ -429,7 +459,8 @@ for the common-case C / minimal-C++ workload. Priority is speed - **Layer 1 ptr32 deref-fold (always on)** — Constant offset on a ptr32 deref folds into the `[dp],Y` Y register instead of a CLC/ADC - carry-chain pre-add. Plus consecutive-deref CSE that shares the + carry-chain pre-add, for both stores (ST_PTR_OFF / STB_PTR_OFF) and + loads (LD_PTR_OFF). Plus consecutive-deref CSE that shares the `$E0/$E2` staging across `s->a`, `s->b`, ... accesses with the same base. Always on; saves ~3 instructions per struct-field access. See `feedback_ptr32_deref_fold_layer1_landed.md`. diff --git a/demos/rsrcProbe.apl b/demos/rsrcProbe.apl index 8329fe5ccfcccde915dedf7fdc81610c290fc293..2c569d4b9613ec6501d7d105eaf0a2170debbc6f 100644 GIT binary patch delta 12781 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 delta 13151 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 diff --git a/src/llvm/lib/Target/W65816/CMakeLists.txt b/src/llvm/lib/Target/W65816/CMakeLists.txt index 838f4d7..6356e11 100644 --- a/src/llvm/lib/Target/W65816/CMakeLists.txt +++ b/src/llvm/lib/Target/W65816/CMakeLists.txt @@ -32,7 +32,6 @@ add_llvm_target(W65816CodeGen W65816WidenAcc16.cpp W65816SpillToX.cpp W65816NegYIndY.cpp - W65816PreSpillCrossCall.cpp W65816SjLjFinalize.cpp W65816LowerWide32.cpp W65816I32IncFold.cpp diff --git a/src/llvm/lib/Target/W65816/W65816.h b/src/llvm/lib/Target/W65816/W65816.h index 1af1083..e3b4efa 100644 --- a/src/llvm/lib/Target/W65816/W65816.h +++ b/src/llvm/lib/Target/W65816/W65816.h @@ -104,12 +104,6 @@ FunctionPass *createW65816SpillToX(); // so signed-negative Y crosses bank boundaries. See W65816NegYIndY.cpp. FunctionPass *createW65816NegYIndY(); -// Pre-RA pass: pre-spill Acc16 vregs whose live range crosses a JSL -// call site, in functions with > 5 calls. Drops greedy regalloc -// pressure for high-call-count functions that would otherwise hit -// "ran out of registers". See W65816PreSpillCrossCall.cpp. -FunctionPass *createW65816PreSpillCrossCall(); - // IR pass: finishes the SjLjEHPrepare lowering by inserting a setjmp // at function entry and a dispatch block, then erasing the eh.sjlj.* // intrinsics our backend doesn't lower natively. Runs after @@ -208,7 +202,6 @@ void initializeW65816UnLSRPass(PassRegistry &); void initializeW65816WidenAcc16Pass(PassRegistry &); void initializeW65816SpillToXPass(PassRegistry &); void initializeW65816NegYIndYPass(PassRegistry &); -void initializeW65816PreSpillCrossCallPass(PassRegistry &); void initializeW65816SjLjFinalizePass(PassRegistry &); void initializeW65816LowerWide32Pass(PassRegistry &); void initializeW65816ImgCalleeSavePass(PassRegistry &); diff --git a/src/llvm/lib/Target/W65816/W65816FrameLowering.cpp b/src/llvm/lib/Target/W65816/W65816FrameLowering.cpp index f85ea6d..f330bff 100644 --- a/src/llvm/lib/Target/W65816/W65816FrameLowering.cpp +++ b/src/llvm/lib/Target/W65816/W65816FrameLowering.cpp @@ -21,10 +21,42 @@ #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/IR/Function.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" using namespace llvm; +// Omit the per-function-entry `REP #$30`. Every function is required by the +// C ABI to be entered in M=16/X=16, and the whole toolchain already upholds +// that invariant: crt0 sets it at startup, compiled code never makes a call +// or a return while in 8-bit mode (8-bit ops are short, block-local +// SEP/REP-wrapped windows that contain no calls), and the hand-written +// runtime relies on it too — libgcc's __mulhi3 / __umulhisi3 / __udivhi3 / +// __lshrsi3 carry NO entry REP and would already miscompile if any caller +// were in 8-bit mode. Given that, the entry REP is redundant; dropping it +// saves 2 bytes + 3 cycles per function. Default OFF: it is only sound if +// the ENTIRE linked program (including every hand-written asm entry point and +// any interrupt handler) maintains the invariant, so it must be a deliberate +// whole-program choice. +// +// Investigation (2026-06-30): measured gain is ~0.15% .text and ~3 cyc/call +// (this is the fib 1.06x-Calypsi case; the win is broad but small since it is +// only the per-call entry REP). A whole-toolchain smoke build with this forced +// on was blocked only by asm-grep regression guards that assert the REP's +// presence as a correctness proxy (e.g. the sgnlt i8-signed-compare guard) -- +// no runtime miscompile was reached, consistent with the invariant holding. +// This flag has NO per-function safety net: it trusts the invariant globally. +// Making the elision safe by default needs the design-doc 3.3 REP/SEP dataflow +// pass, which would prove per-function that mode is 16-bit at every call and +// return and keep the REP for anything it cannot prove -- that is the real fix +// and remains deferred (multi-week; the gain does not justify it while the +// geomean already beats Calypsi 0.62x). +static cl::opt OmitEntryRep( + "w65816-omit-entry-rep", + cl::desc("Omit the per-function-entry REP #$30 (relies on the whole-program " + "M=16/X=16 ABI invariant; saves 2 bytes / 3 cycles per function)."), + cl::init(false), cl::Hidden); + // (The pure-i8-detection helpers were removed when the prologue went // to "always 16-bit M". See emitPrologue comment.) // @@ -97,7 +129,12 @@ void W65816FrameLowering::emitPrologue(MachineFunction &MF, // Caught by tracing inc_g for `char inc_g(void) { g++; return g; }`. (void)MRI; MF.getInfo()->setUsesAcc8(false); - BuildMI(MBB, MBBI, DL, TII.get(W65816::REP)).addImm(0x30); + // The entry REP guarantees this function runs in 16-bit M/X even if a + // caller somehow left 8-bit mode set. Under -w65816-omit-entry-rep the + // whole program is asserted to already maintain the M=16/X=16 invariant, + // so this becomes redundant (see the OmitEntryRep comment above). + if (!OmitEntryRep) + BuildMI(MBB, MBBI, DL, TII.get(W65816::REP)).addImm(0x30); // Reserve stack space for locals/spills. // diff --git a/src/llvm/lib/Target/W65816/W65816ISelLowering.cpp b/src/llvm/lib/Target/W65816/W65816ISelLowering.cpp index 8e14854..f9c79a5 100644 --- a/src/llvm/lib/Target/W65816/W65816ISelLowering.cpp +++ b/src/llvm/lib/Target/W65816/W65816ISelLowering.cpp @@ -1188,19 +1188,24 @@ SDValue W65816TargetLowering::LowerLoad(SDValue Op, // asserts memvt must be supported; i1 isn't. if (MemVT == MVT::i1) MemVT = MVT::i8; SDVTList VTs = DAG.getVTList(MVT::i16, MVT::Other); - // Try to peel a constant offset from Ptr and route through - // LD_PTR_OFF — folds `(ptr + K)` into the Y-register of `[E0],Y`, - // saving the i32 ADD's CLC/ADC carry chain. ~3 instr per access. + // Peel a constant offset from Ptr and route through LD_PTR_OFF — folds + // `(ptr + K)` into the Y-register of the `[E0],Y` deref, saving the i32 + // ADD's CLC/ADC carry chain (~3 instr per access). Mirrors the + // LowerStore ST_PTR_OFF / STB_PTR_OFF peel; the `[dp],Y` inserter and + // the W65816ldPtrOff tablegen pattern already handle the result. // See feedback_ptr32_deref_fold_layer1_mi.md. - // LD_PTR_OFF: deferred — the peel fires correctly but the resulting - // SDAG breaks the JSON-tokenizer + snprintf smoke tests in ways - // bisection didn't isolate. Stick with LD_PTR (no peel) here; the - // LowerStore peel for ST_PTR_OFF / STB_PTR_OFF keeps the store-side - // optimization. Future: route loads through a SDAG combine that - // runs post-LegalizeOps so we see the final REG_SEQUENCE shape. - SDValue Ops[] = { Chain, Ptr }; - SDValue LdNode = DAG.getMemIntrinsicNode(W65816ISD::LD_PTR, DL, VTs, Ops, - MemVT, Ld->getMemOperand()); + SDValue LdNode; + SDValue Base; uint16_t Off = 0; + if (peelPtr32Offset(DAG, DL, Ptr, Base, Off)) { + SDValue OffN = DAG.getTargetConstant(Off, DL, MVT::i16); + SDValue OpsOff[] = { Chain, Base, OffN }; + LdNode = DAG.getMemIntrinsicNode(W65816ISD::LD_PTR_OFF, DL, VTs, OpsOff, + MemVT, Ld->getMemOperand()); + } else { + SDValue Ops[] = { Chain, Ptr }; + LdNode = DAG.getMemIntrinsicNode(W65816ISD::LD_PTR, DL, VTs, Ops, + MemVT, Ld->getMemOperand()); + } SDValue Val = LdNode; // Byte memory access: mask the high byte for zextload, leave anyext. // i1 memVT was widened to i8 above; the mask path is the same. diff --git a/src/llvm/lib/Target/W65816/W65816PreSpillCrossCall.cpp b/src/llvm/lib/Target/W65816/W65816PreSpillCrossCall.cpp deleted file mode 100644 index 60c21e3..0000000 --- a/src/llvm/lib/Target/W65816/W65816PreSpillCrossCall.cpp +++ /dev/null @@ -1,160 +0,0 @@ -//===-- W65816PreSpillCrossCall.cpp - Pre-spill cross-call Acc16 vregs ---===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Pre-RA pass that pre-spills Acc16 vregs whose live range crosses a -// JSL call site. Greedy regalloc has only one register in the Acc16 -// class (A), and JSL clobbers A — so any Acc16 vreg live across a -// call MUST be spilled. Greedy normally figures this out, but for -// functions with many such vregs (the "ok |= bit" bitmask pattern -// repeated across N if-blocks each calling a helper) greedy can run -// out of registers during spill placement, aborting compilation with -// "ran out of registers". -// -// We pre-empt the failure: walk the MBB, find cross-call Acc16 -// vregs, and explicitly STAfi their value after the def + LDAfi at -// each use. This converts the cross-call live ranges into stack- -// resident loads, dropping greedy's pressure to the point it can -// always succeed. -// -// Cost: an extra STAfi+LDAfi (~6 cyc each) per cross-call vreg. -// This is the same cost greedy would emit if it succeeded (a spill -// + reload), so we're not pessimising — just making the spill -// explicit BEFORE greedy gets confused. -// -// Heuristic: only pre-spill if the function has > 5 call sites OR a -// cross-call Acc16 vreg with > 2 uses after the call. Below that, -// let greedy do its thing (it usually picks better placements). -// -//===----------------------------------------------------------------------===// - -#include "W65816.h" -#include "W65816InstrInfo.h" -#include "W65816Subtarget.h" -#include "llvm/CodeGen/MachineFrameInfo.h" -#include "llvm/CodeGen/MachineFunction.h" -#include "llvm/CodeGen/MachineFunctionPass.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" - -using namespace llvm; - -#define DEBUG_TYPE "w65816-pre-spill-cross-call" - -namespace { - -class W65816PreSpillCrossCall : public MachineFunctionPass { -public: - static char ID; - W65816PreSpillCrossCall() : MachineFunctionPass(ID) {} - - StringRef getPassName() const override { - return "W65816 pre-spill Acc16 vregs across calls"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; -}; - -} // namespace - -char W65816PreSpillCrossCall::ID = 0; - -INITIALIZE_PASS(W65816PreSpillCrossCall, DEBUG_TYPE, - "W65816 pre-spill Acc16 vregs across calls", false, false) - -FunctionPass *llvm::createW65816PreSpillCrossCall() { - return new W65816PreSpillCrossCall(); -} - -bool W65816PreSpillCrossCall::runOnMachineFunction(MachineFunction &MF) { - if (MF.getFunction().hasOptNone()) return false; - MachineRegisterInfo &MRI = MF.getRegInfo(); - if (!MRI.getNumVirtRegs()) return false; - const W65816InstrInfo *TII = - MF.getSubtarget().getInstrInfo(); - MachineFrameInfo &MFI = MF.getFrameInfo(); - - // First pass: count call sites in the function. Below the - // heuristic threshold we don't bother — greedy handles low-call - // functions fine and pre-spilling would just add bytes. - constexpr unsigned kCallCountThreshold = 4u; - unsigned callCount = 0; - for (MachineBasicBlock &MBB : MF) { - for (MachineInstr &MI : MBB) { - if (MI.isCall()) { - callCount++; - } - } - } - if (callCount < kCallCountThreshold) return false; - - bool Changed = false; - - // Walk every Acc16 vreg in the function. For each, find its def - // (allowing multi-def vregs like SELECT_CC results — pick the - // first by MachineInstr iteration), then check if any use is - // separated from the def by a JSL call (in the same MBB). If - // so, pre-spill via STAfi at def + LDAfi at each post-call use. - unsigned NumVRegs = MRI.getNumVirtRegs(); - for (unsigned i = 0; i < NumVRegs; ++i) { - Register VReg = Register::index2VirtReg(i); - if (MRI.def_empty(VReg)) continue; - if (MRI.getRegClass(VReg) != &W65816::Acc16RegClass) continue; - // Find the first def. For PHIs we skip — pre-spilling a PHI - // result is complex and rarely helpful for the high-pressure - // pattern we target (which is sequential bitmask updates). - MachineInstr *DefMI = nullptr; - for (MachineInstr &D : MRI.def_instructions(VReg)) { - if (D.isPHI()) { DefMI = nullptr; break; } - if (!DefMI) DefMI = &D; - } - if (!DefMI) continue; - MachineBasicBlock *MBB = DefMI->getParent(); - - // Check if any use of VReg is in the same MBB AFTER a call - // following DefMI. - bool sawCallAfterDef = false; - SmallVector postCallUses; - auto Walker = std::next(DefMI->getIterator()); - while (Walker != MBB->end()) { - MachineInstr &W = *Walker++; - if (W.isCall()) sawCallAfterDef = true; - if (sawCallAfterDef && W.readsRegister(VReg, /*TRI=*/nullptr)) - postCallUses.push_back(&W); - } - if (postCallUses.empty()) continue; - - // Pre-spill. Fresh slot per vreg — StackSlotColoring may merge - // slots later if their lifetimes don't overlap. - int FI = MFI.CreateStackObject(2, Align(2), /*isSpillSlot=*/true); - DebugLoc DL = DefMI->getDebugLoc(); - auto AfterDef = std::next(DefMI->getIterator()); - BuildMI(*MBB, AfterDef, DL, TII->get(W65816::STAfi)) - .addReg(VReg).addFrameIndex(FI).addImm(0); - for (MachineInstr *UseMI : postCallUses) { - Register Reload = MRI.createVirtualRegister(&W65816::Acc16RegClass); - BuildMI(*UseMI->getParent(), UseMI->getIterator(), UseMI->getDebugLoc(), - TII->get(W65816::LDAfi), Reload) - .addFrameIndex(FI).addImm(0); - // Rewrite this use's references of VReg to Reload. - for (auto &MO : UseMI->uses()) { - if (MO.isReg() && MO.getReg() == VReg) { - MO.setReg(Reload); - MO.setIsKill(false); - } - } - } - Changed = true; - } - - return Changed; -} diff --git a/src/llvm/lib/Target/W65816/W65816TargetMachine.cpp b/src/llvm/lib/Target/W65816/W65816TargetMachine.cpp index c57339b..087a115 100644 --- a/src/llvm/lib/Target/W65816/W65816TargetMachine.cpp +++ b/src/llvm/lib/Target/W65816/W65816TargetMachine.cpp @@ -56,7 +56,6 @@ LLVMInitializeW65816Target() { initializeW65816WidenAcc16Pass(PR); initializeW65816SpillToXPass(PR); initializeW65816NegYIndYPass(PR); - initializeW65816PreSpillCrossCallPass(PR); initializeW65816SjLjFinalizePass(PR); initializeW65816LowerWide32Pass(PR); initializeW65816I32IncFoldPass(PR); @@ -234,19 +233,17 @@ void W65816PassConfig::addPreRegAlloc() { addPass(createW65816ABridgeViaX()); addPass(createW65816TiedDefSpill()); addPass(createW65816WidenAcc16()); - // Pre-spill cross-call Acc16 vregs in high-call functions to - // relieve greedy regalloc pressure. Currently disabled — the - // first cut creates too many fresh stack slots and overflows the - // stack-relative addressing range (frame > 256 bytes) on - // moderately-sized functions like the soft-double routines. - // The pass is built and ready, gated behind future tuning of: - // - lower call-count threshold (currently 4) - // - smarter "should we spill THIS vreg" filter - // - stack slot reuse via a real liveness analysis - // Until then, the high-pressure failure is worked around with - // `__attribute__((noinline))` on the heaviest helper or with - // `-mllvm -regalloc=fast` for the affected TU. - // addPass(createW65816PreSpillCrossCall()); + // Note: there is intentionally no cross-call Acc16 pre-spill pass here. + // An earlier W65816PreSpillCrossCall pass tried to relieve greedy's + // single-accumulator pressure by pre-spilling cross-call vregs, but it + // was never enabled (it bloated frames) and, as of 2026-06-30, the + // "ran out of registers" deadlock it targeted no longer reproduces on + // any TU in the lua + coremark + runtime corpus (the i32 / SepRep / + // regalloc codegen fixes resolved it). Measured cost of enabling it + // was +0.17% .text for zero deadlocks fixed, so the pass was removed. + // The rare residual greedy failure (if any ever returns) is handled by + // scripts/ccRegallocFallback.sh, which retries the one affected TU with + // -regalloc=basic. } void W65816PassConfig::addPostRegAlloc() {