[Mips] R3000A load-delay scheduling: post-RA at -O2/-Os, LWL/LWR pairs (PS2-193) #22

Merged
techwritescode merged 6 commits from r3000a-load-delay into r5900-ee 2026-10-16 19:55:29 +00:00

Two R3000A-gated changes from PS2-193 (IOP code size and timing against GCC). Both rest on how the R3000A's load delay works.

Commit Change
Run the post-RA scheduler for the R3000A at -O2 and -Os getOptLevelToEnablePostRAScheduler returned Default only for the R5900. The R3000A has a scheduling model (LoadLatency 2), so the post-RA scheduler now also runs at -O2/-Os, where IOP modules are built, and fills load delay slots with independent work.
Let an R3000A LWL/LWR pair share the load delay The R3000A merges an LWL and an LWR to the same register through the load delay, so the second load needs no nop in front of it. Gated on the R3000A: the upstream unalignedload.ll PSX (-mcpu=mips1) checks keep their nop.

New tests: CodeGen/Mips/r3000a-postra-sched.ll (with and without -post-RA-scheduler=false) and CodeGen/Mips/r3000a-lwl-lwr-delay.ll (R3000A without the nop, mips1 with it).

Effect, measured on the 904 sdk-core IOP objects (PS2-193 comments): nops 33,130 down to 31,622; IRX total from +8.7% to +8.3% against GCC. Timing on the console is not measured yet.

Verified: llvm-lit CodeGen/Mips MC/Mips, 1552 tests, 0 failures. That run was on the local branch r3000a-postra-sched, which also carries two more R3000A commits that are not in this PR (see below). These two changes do not depend on them.

Held back for review against the fork rule (UPDATING.md, "What belongs in the fork"), still local on r3000a-postra-sched:

  • -Os inlining threshold for the R3000A lowered to the -Oz value (-2.9% IRX size). A threshold change.
  • Tail merging for the R3000A, with a sink-hi16 pass added before it so HI16/LO16 pairs stay together (-1.5%). Adds a target pass to the pipeline.

🤖 Generated with Claude Code

Two R3000A-gated changes from PS2-193 (IOP code size and timing against GCC). Both rest on how the R3000A's load delay works. | Commit | Change | |---|---| | Run the post-RA scheduler for the R3000A at -O2 and -Os | `getOptLevelToEnablePostRAScheduler` returned Default only for the R5900. The R3000A has a scheduling model (LoadLatency 2), so the post-RA scheduler now also runs at -O2/-Os, where IOP modules are built, and fills load delay slots with independent work. | | Let an R3000A LWL/LWR pair share the load delay | The R3000A merges an LWL and an LWR to the same register through the load delay, so the second load needs no nop in front of it. Gated on the R3000A: the upstream `unalignedload.ll` PSX (`-mcpu=mips1`) checks keep their nop. | New tests: `CodeGen/Mips/r3000a-postra-sched.ll` (with and without `-post-RA-scheduler=false`) and `CodeGen/Mips/r3000a-lwl-lwr-delay.ll` (R3000A without the nop, mips1 with it). Effect, measured on the 904 sdk-core IOP objects (PS2-193 comments): nops 33,130 down to 31,622; IRX total from +8.7% to +8.3% against GCC. Timing on the console is not measured yet. Verified: `llvm-lit CodeGen/Mips MC/Mips`, 1552 tests, 0 failures. That run was on the local branch `r3000a-postra-sched`, which also carries two more R3000A commits that are not in this PR (see below). These two changes do not depend on them. Held back for review against the fork rule (UPDATING.md, "What belongs in the fork"), still local on `r3000a-postra-sched`: - -Os inlining threshold for the R3000A lowered to the -Oz value (-2.9% IRX size). A threshold change. - Tail merging for the R3000A, with a sink-hi16 pass added before it so HI16/LO16 pairs stay together (-1.5%). Adds a target pass to the pipeline. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The R3000A scheduling model separates loads from their users, but the
post-RA scheduler only ran at -O3 for every CPU but the R5900. On the sdk-core
IOP modules at -Os this removes about 1,500 of the load-delay nops and shrinks
the IRX total from +8.7% to +8.3% over GCC.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
[Mips] Let an R3000A LWL/LWR pair share the load delay
Some checks failed
Check for private emails used in PRs / validate_email (pull_request) Has been skipped
action.yml / [Mips] Let an R3000A LWL/LWR pair share the load delay (pull_request) Failing after 0s
Check LLVM ABI annotations / Check LLVM_ABI annotations with ids (pull_request) Has been skipped
CI Checks / Build and Test Linux (pull_request) Has been skipped
CI Checks / Build and Test Windows (pull_request) Has been skipped
CI Checks / Compute macOS Projects (pull_request) Has been skipped
CI Checks / Build and Test macOS arm64 (pull_request) Has been skipped
release-binaries-all.yml / [Mips] Let an R3000A LWL/LWR pair share the load delay (pull_request) Failing after 0s
release-binaries.yml / [Mips] Let an R3000A LWL/LWR pair share the load delay (pull_request) Failing after 0s
release-documentation.yml / [Mips] Let an R3000A LWL/LWR pair share the load delay (pull_request) Failing after 0s
release-sources.yml / [Mips] Let an R3000A LWL/LWR pair share the load delay (pull_request) Failing after 0s
action.yml / [Mips] Let an R3000A LWL/LWR pair share the load delay (push) Failing after 0s
release-binaries-all.yml / [Mips] Let an R3000A LWL/LWR pair share the load delay (push) Failing after 0s
release-binaries.yml / [Mips] Let an R3000A LWL/LWR pair share the load delay (push) Failing after 0s
release-documentation.yml / [Mips] Let an R3000A LWL/LWR pair share the load delay (push) Failing after 0s
release-sources.yml / [Mips] Let an R3000A LWL/LWR pair share the load delay (push) Failing after 0s
Labelling new pull requests / greeter (pull_request_target) Has been cancelled
Labelling new pull requests / automate-prs-labels (pull_request_target) Has been cancelled
e4fc85dd91
The R3000A merges an LWL and an LWR to the same register through the load
delay, so the second load needs no nop in front of it. Gated on the R3000A;
other MIPS I CPUs keep the nop. Tests cover this and the post-RA scheduler
change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
[Mips] Limit the R3000A post-RA scheduler to one instruction per cycle
Some checks failed
action.yml / [Mips] Limit the R3000A post-RA scheduler to one instruction per cycle (push) Failing after 0s
release-binaries-all.yml / [Mips] Limit the R3000A post-RA scheduler to one instruction per cycle (push) Failing after 0s
release-binaries.yml / [Mips] Limit the R3000A post-RA scheduler to one instruction per cycle (push) Failing after 0s
release-documentation.yml / [Mips] Limit the R3000A post-RA scheduler to one instruction per cycle (push) Failing after 0s
release-sources.yml / [Mips] Limit the R3000A post-RA scheduler to one instruction per cycle (push) Failing after 0s
Labelling new pull requests / greeter (pull_request_target) Has been skipped
Labelling new pull requests / automate-prs-labels (pull_request_target) Has been skipped
action.yml / [Mips] Limit the R3000A post-RA scheduler to one instruction per cycle (pull_request) Failing after 0s
Check LLVM ABI annotations / Check LLVM_ABI annotations with ids (pull_request) Has been skipped
CI Checks / Build and Test Linux (pull_request) Has been skipped
CI Checks / Build and Test Windows (pull_request) Has been skipped
CI Checks / Compute macOS Projects (pull_request) Has been skipped
CI Checks / Build and Test macOS arm64 (pull_request) Has been skipped
release-binaries-all.yml / [Mips] Limit the R3000A post-RA scheduler to one instruction per cycle (pull_request) Failing after 0s
release-binaries.yml / [Mips] Limit the R3000A post-RA scheduler to one instruction per cycle (pull_request) Failing after 0s
release-documentation.yml / [Mips] Limit the R3000A post-RA scheduler to one instruction per cycle (pull_request) Failing after 0s
release-sources.yml / [Mips] Limit the R3000A post-RA scheduler to one instruction per cycle (pull_request) Failing after 0s
fe782f0019
The R3000A has no itinerary, so the post-RA list scheduler got a hazard
recognizer with no issue limit and put several instructions in one cycle.
It then used up independent instructions before a jump-table load was
ready and left the load right before the jump, which costs a load-delay
nop. A single-issue hazard recognizer for the R3000A fixes the order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Author
Owner

New commit fe782f0: limit the R3000A post-RA scheduler to one instruction per cycle.

The R3000A has no itinerary, so the post-RA list scheduler got the default hazard recognizer, which never limits issue. It put several instructions in one "cycle", spent the independent ones before a jump-table load was ready, and left the load directly before jr, which costs a load-delay nop. On the console the IOP state-machine kernel got 6% slower with the post-RA change alone. A single-issue hazard recognizer, gated on the R3000A, fixes it. Test: CodeGen/Mips/r3000a-postra-single-issue.ll, which fails without the fix. r3000a-postra-sched.ll is updated for the new order.

Lit on this branch (separate build of r3000a-load-delay): CodeGen/Mips + MC/Mips, 1553 tests, 0 failures.

Results for this branch against r5900-ee 5c05831:

Baseline This PR
89 sdk-core IRXs, total bytes 1,608,028 (+8.73% vs GCC) 1,595,372 (+7.88% vs GCC), -0.79%

IOP kernel timing on the console, IOP system clock ticks, minimum over 3 boots:

Kernel GCC Baseline This PR PR/baseline
crc 88417 83347 79375 0.95
sort 133145 150384 150831 1.00
decode 76324 86092 84061 0.98
div 53144 39148 38650 0.99
state 66266 69062 69062 1.00
str 147678 142430 134828 0.95

No kernel is slower than the baseline. sort is 1.13x GCC with or without this PR: GCC ends the inner loop with bne against a hoisted -1, while LLVM's loop strength reduction uses a separate counter and needs slti + beqz. That choice is made in a generic pass, so it is left as a documented exception.

New commit fe782f0: limit the R3000A post-RA scheduler to one instruction per cycle. The R3000A has no itinerary, so the post-RA list scheduler got the default hazard recognizer, which never limits issue. It put several instructions in one "cycle", spent the independent ones before a jump-table load was ready, and left the load directly before `jr`, which costs a load-delay nop. On the console the IOP state-machine kernel got 6% slower with the post-RA change alone. A single-issue hazard recognizer, gated on the R3000A, fixes it. Test: `CodeGen/Mips/r3000a-postra-single-issue.ll`, which fails without the fix. `r3000a-postra-sched.ll` is updated for the new order. Lit on this branch (separate build of `r3000a-load-delay`): CodeGen/Mips + MC/Mips, 1553 tests, 0 failures. Results for this branch against r5900-ee 5c05831: | | Baseline | This PR | |---|---|---| | 89 sdk-core IRXs, total bytes | 1,608,028 (+8.73% vs GCC) | 1,595,372 (+7.88% vs GCC), -0.79% | IOP kernel timing on the console, IOP system clock ticks, minimum over 3 boots: | Kernel | GCC | Baseline | This PR | PR/baseline | |---|---|---|---|---| | crc | 88417 | 83347 | 79375 | 0.95 | | sort | 133145 | 150384 | 150831 | 1.00 | | decode | 76324 | 86092 | 84061 | 0.98 | | div | 53144 | 39148 | 38650 | 0.99 | | state | 66266 | 69062 | 69062 | 1.00 | | str | 147678 | 142430 | 134828 | 0.95 | No kernel is slower than the baseline. sort is 1.13x GCC with or without this PR: GCC ends the inner loop with `bne` against a hoisted -1, while LLVM's loop strength reduction uses a separate counter and needs `slti` + `beqz`. That choice is made in a generic pass, so it is left as a documented exception.
Without conditional moves each select becomes a branch diamond. Consecutive
selects on one condition, such as the halves of an i64 select, now use a
single diamond with several PHIs.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
[Mips] Lower R3000A i64 orderings without branches
Some checks failed
action.yml / [Mips] Lower R3000A i64 orderings without branches (push) Failing after 0s
release-binaries-all.yml / [Mips] Lower R3000A i64 orderings without branches (push) Failing after 0s
release-binaries.yml / [Mips] Lower R3000A i64 orderings without branches (push) Failing after 0s
release-documentation.yml / [Mips] Lower R3000A i64 orderings without branches (push) Failing after 0s
release-sources.yml / [Mips] Lower R3000A i64 orderings without branches (push) Failing after 0s
Check for private emails used in PRs / validate_email (pull_request) Has been skipped
action.yml / [Mips] Lower R3000A i64 orderings without branches (pull_request) Failing after 0s
Check LLVM ABI annotations / Check LLVM_ABI annotations with ids (pull_request) Has been skipped
CI Checks / Build and Test Linux (pull_request) Has been skipped
CI Checks / Build and Test Windows (pull_request) Has been skipped
CI Checks / Compute macOS Projects (pull_request) Has been skipped
CI Checks / Build and Test macOS arm64 (pull_request) Has been skipped
release-binaries-all.yml / [Mips] Lower R3000A i64 orderings without branches (pull_request) Failing after 0s
release-binaries.yml / [Mips] Lower R3000A i64 orderings without branches (pull_request) Failing after 0s
release-documentation.yml / [Mips] Lower R3000A i64 orderings without branches (pull_request) Failing after 0s
release-sources.yml / [Mips] Lower R3000A i64 orderings without branches (pull_request) Failing after 0s
Labelling new pull requests / greeter (pull_request_target) Has been cancelled
Labelling new pull requests / automate-prs-labels (pull_request_target) Has been cancelled
90831b04b5
The generic expansion compares the high words and selects between the low and
high results, which costs two branches. Expand through SETCCCARRY instead:
(Hi1 < Hi2) | ((Hi1 - Hi2) <u LoBorrow), with the low borrow as a plain sltu.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
[Mips] Drop explanatory comments from the R3000A select and compare lowering
Some checks failed
action.yml / [Mips] Drop explanatory comments from the R3000A select and compare lowering (push) Failing after 0s
release-binaries-all.yml / [Mips] Drop explanatory comments from the R3000A select and compare lowering (push) Failing after 0s
Labelling new pull requests / greeter (pull_request_target) Has been skipped
Labelling new pull requests / automate-prs-labels (pull_request_target) Has been skipped
action.yml / [Mips] Drop explanatory comments from the R3000A select and compare lowering (pull_request) Failing after 0s
Check LLVM ABI annotations / Check LLVM_ABI annotations with ids (pull_request) Has been skipped
CI Checks / Build and Test Linux (pull_request) Has been skipped
CI Checks / Build and Test Windows (pull_request) Has been skipped
CI Checks / Compute macOS Projects (pull_request) Has been skipped
CI Checks / Build and Test macOS arm64 (pull_request) Has been skipped
release-binaries-all.yml / [Mips] Drop explanatory comments from the R3000A select and compare lowering (pull_request) Failing after 0s
release-binaries.yml / [Mips] Drop explanatory comments from the R3000A select and compare lowering (pull_request) Failing after 0s
release-documentation.yml / [Mips] Drop explanatory comments from the R3000A select and compare lowering (pull_request) Failing after 0s
release-sources.yml / [Mips] Drop explanatory comments from the R3000A select and compare lowering (pull_request) Failing after 0s
release-binaries.yml / [Mips] Drop explanatory comments from the R3000A select and compare lowering (push) Failing after 0s
release-documentation.yml / [Mips] Drop explanatory comments from the R3000A select and compare lowering (push) Failing after 0s
release-sources.yml / [Mips] Drop explanatory comments from the R3000A select and compare lowering (push) Failing after 0s
Add buildbot information to first PRs from new contributors / buildbot_comment (pull_request_target) Has been skipped
3d1f4b5e16
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
ps2/llvm!22
No description provided.