diff --git a/.github/ISSUE_TEMPLATE/help-wanted.md b/.github/ISSUE_TEMPLATE/help-wanted.md index 99aeff3d0..86eda9aaa 100644 --- a/.github/ISSUE_TEMPLATE/help-wanted.md +++ b/.github/ISSUE_TEMPLATE/help-wanted.md @@ -14,5 +14,5 @@ assignees: '' **Your setup** -- Disc: USA v1.01 (`SCUS_972.68`), SHA-256 matching docs/BUILDING.md? +- Disc: USA v1.01 (`SCUS_972.68`), SHA-256 matching docs/build/building.md? - Host: (macOS / Linux / Windows + WSL), Docker or native diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f1ed499c1..74f168a4a 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,4 +5,4 @@ ## Checked +how many functions this adds (leave `progress/report.json` out: it is regenerated on master). Say what you did not run. --> diff --git a/.github/workflows/progress.yml b/.github/workflows/progress.yml index db3981708..b402cf9d7 100644 --- a/.github/workflows/progress.yml +++ b/.github/workflows/progress.yml @@ -1,5 +1,5 @@ # ByteMatch progress reports (https://bytematch.dev/OpenRAC/rac2-decomp), as rac1-decomp -# publishes its own. One per release (docs/VERSIONS.md): +# publishes its own. One per release (docs/build/versions.md): # # SCUS_972.68 USA v1.01 (the primary target) progress/report.json -> SCUS_972.68_report # SCES_516.07 Europe/Australia v1.00 progress/pal/report.json -> SCES_516.07_report diff --git a/.gitignore b/.gitignore index 28773c9ad..5664e6496 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ .venv/ reference/ assets/ +!docs/assets/ asm/ asm_pp/ build/ diff --git a/AGENTS.md b/AGENTS.md index 3116a64dd..58ee02224 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,8 +1,8 @@ # Instructions for agents This file is for AI agents (Claude, Codex, Qwen or others) working in this repository. Read it -completely before changing anything; then read README.md, CONTRIBUTING.md, docs/BUILDING.md and -docs/LEVELS.md. The repository follows the same structure as rac1-decomp and the other OpenRAC +completely before changing anything; then read README.md, CONTRIBUTING.md, docs/build/building.md and +docs/build/levels.md. The repository follows the same structure as rac1-decomp and the other OpenRAC decompilations: keep it that way. Also read `CLAUDE.md` and `.claude/CLAUDE.md` when present, and follow their applicable @@ -24,7 +24,7 @@ equals the retail programs. | `config/cflags.txt` | compiler flags per file | by hand, when a whole file needs other flags | | `include/` | headers shared with rac1-decomp | by hand | | `nonmatching/` | near misses, never linked | you | -| `progress/report.json`, the README table | progress | `tools/progress.py` only, on `master` by a maintainer | +| `progress/report.json` | progress | `tools/progress.py` only, on `master` by a maintainer | | `tools/` | disc reader, build, check, progress, compiler recipes | by hand, with tests | | `docs/` | how to build, the level format, the compiler, game research | by hand | | `baserom/`, `build/`, `toolchain/`, `work/` | game data, build output, proprietary tools, scratch | never committed | @@ -111,13 +111,13 @@ attempt; OpenRAC's `docs/port/BACKFILL.md` has the measurements and the full pro - **Out of budget.** About ten compiles without getting closer. Then stop: put the marker back, leave the attempt in `nonmatching//` with what differs and -what would unblock it, and let it go up one model tier (docs/AGENT_WORKFLOW.md). Stopping is a +what would unblock it, and let it go up one model tier (docs/agents/agent-workflow.md). Stopping is a normal result. C that behaves like the function without matching it is useful to the native port, never to `src/` or the progress report. ## Shared blocker knowledge -[`docs/MATCHING-BLOCKERS.md`](docs/MATCHING-BLOCKERS.md) is the shared reference for +[`docs/contributing/matching-blockers.md`](docs/contributing/matching-blockers.md) is the shared reference for blockers, measured attempts and verified remedies. **Every contributor may edit any entry through a normal fork/topic branch and pull request**, including entries originally written by someone else. Maintainers review merges; they do not own all knowledge edits. @@ -129,7 +129,7 @@ read the relevant entry and retained source, and check cited fixes against curre `origin/master`. Do not repeat recorded attempts without a new hypothesis or changed input. When you find a blocker, useful evidence or a solution, update the existing entry in -`docs/MATCHING-BLOCKERS.md` in your branch and include it in the relevant PR. Keep it concise: +`docs/contributing/matching-blockers.md` in your branch and include it in the relevant PR. Keep it concise: - Identify the target/family, release, compiler/assembler profile and placement coverage. - Record the hypothesis, measured result, remaining difference and next useful experiment. @@ -161,7 +161,7 @@ side by side instead of one after another: - Start long steps in the background and keep working; do not sit on a build. The exception: never run two builds of the same program in the same checkout at once. Workers -each have their own worktree for that reason (docs/AGENT_WORKFLOW.md). +each have their own worktree for that reason (docs/agents/agent-workflow.md). ## Rules @@ -207,7 +207,7 @@ each have their own worktree for that reason (docs/AGENT_WORKFLOW.md). permitted when the EE ABI preserves each register bank's argument order, count and widths, stack arguments, return contract and side effects. Do not invent accesses or change the callee's physical interface to influence allocation. See - [the examples and limits](docs/COMPILER-NOTES.md#source-methods-shared-with-rac1). + [the examples and limits](docs/build/compiler-notes.md#source-methods-shared-with-rac1). - **English.** Everything that lands here is in English: code, comments, commit messages, issues, pull requests, reviews, and the notes kept in `nonmatching/`. Translate before it is published. @@ -268,22 +268,22 @@ work in. It was removed on purpose. Do not recreate any of it: for an isolated fix, an independent change that needs separate review, or the final remainder of a batch. Do not combine unrelated work or delay a necessary fix just to reach five. Every batch must still pass the required checks before publication. -- Leave `progress/report.json` and the README table out of your commits (see "The progress +- Leave `progress/report.json` out of your commits (see "The progress report"). ## The progress report -`progress/report.json` and the README table are generated, and every pull request that -regenerates them conflicts with every other one. So pull requests do not touch them: +`progress/report.json` is generated, and every pull request that regenerates it conflicts +with every other one. So pull requests do not touch it: - In a pull request, say what `tools/build.py all` printed and how many functions you added. `tools/progress.py --check` passes with the report behind; it only fails if the report counts something that is no longer C. - After merging, a maintainer runs `tools/build.py all` and `tools/progress.py` on `master` and - commits the two files alone (`chore(progress): N of 9,101 functions`). Several merged pull + commits it alone (`chore(progress): N of 9,101 functions`). Several merged pull requests can share one regeneration. -- If your branch already has the two files changed, restore them before pushing: - `git checkout origin/master -- progress/report.json README.md`. +- If your branch already has it changed, restore it before pushing: + `git checkout origin/master -- progress/report.json`. ## Pushing changes @@ -306,7 +306,7 @@ reservation files. ## Which model does what -docs/AGENT_WORKFLOW.md is the whole procedure. In short, sort by size with `python3 tools/todo.py`: +docs/agents/agent-workflow.md is the whole procedure. In short, sort by size with `python3 tools/todo.py`: - **Haiku 5.5**: small functions, up to 400 bytes, many workers at once. - **Sonnet 5.5**: 401 to 1,000 bytes, and what Haiku gave up on. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 35b69e206..22252cbd3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ ## Matching a function -1. Set up the build once ([docs/BUILDING.md](docs/BUILDING.md)) and run `tools/build.py all`. +1. Set up the build once ([docs/build/building.md](docs/build/building.md)) and run `tools/build.py all`. 2. Pick a function from `config/boot/functions.tsv` or `config/levels/functions.tsv`. A level function's row lists all its places; one definition serves them all. 3. Find its `INCLUDE_ASM` line in `src/` (`grep -rn func_XXXXXXXX src`) and replace it with @@ -15,14 +15,43 @@ 5. `tools/build.py` the programs that have it (`boot`, `level 4`, or `all`). It places the function everywhere the catalogue says and prints `OK` per program, or why a place does not match. A function counts once its C links into a program that equals the retail one. -6. Leave `progress/report.json` and the README table alone: a maintainer regenerates them on +6. Leave `progress/report.json` alone: a maintainer regenerates it on `master` after merging, so pull requests do not conflict on them (AGENTS.md, "The progress report"). Close but not matching: put it in `nonmatching/` with a line in its README. +## Tracking + +- **Reservations.** Before you start, reserve the functions, files or levels you will work on + in [issue #152](https://github.com/OpenRAC/rac2-decomp/issues/152): one comment per + contributor, edited in place, at most 500 unfinished functions. Read the other comments + first and do not overlap them. Release what you finish or drop. [AGENTS.md](AGENTS.md) has + the exact rules. +- **What is left.** `python3 tools/todo.py` lists the functions still to match by size + (`--min`, `--max`, `--kind boot|shared|level`, `--limit`). Before you start one, look for it + in [`nonmatching/README.md`](nonmatching/README.md): an earlier near miss and its notes may + be there. +- **Near misses.** C that does not match goes into `nonmatching/` with a row in its README, + and the function keeps its `INCLUDE_ASM` marker in `src/`. +- **The progress report.** `progress/report.json` is generated. Only a + maintainer refreshes it, on `master`, after merging. Leave it out of your pull request; + if you ran `tools/progress.py`, undo it with + `git checkout origin/master -- progress/report.json`. + `python3 tools/progress.py --check` passes while the report is behind and prints how many + new functions wait for the refresh. +- **Before a pull request**, all must pass: + ```sh + python3 tools/build.py all --strict # and with --version pal, --version us2 + python3 -m unittest discover -s tests + python3 tests/check_repository.py + python3 tools/progress.py --check + ``` + A function that matches in v1.01 but not in another release stays assembly there: + `tools/carry.py --version pal --demote` ([`docs/build/versions.md`](docs/build/versions.md)). + ## Improving shared knowledge -Every contributor can edit any entry in [`docs/MATCHING-BLOCKERS.md`](docs/MATCHING-BLOCKERS.md) +Every contributor can edit any entry in [`docs/contributing/matching-blockers.md`](docs/contributing/matching-blockers.md) using a fork/topic branch and a normal PR, even when another person wrote the original note. Maintainers review the merge; they are not the sole authors. A coding AI edits this Markdown file in the same checkout, with no separate wiki clone or browser automation. @@ -38,7 +67,7 @@ issue #157 is retired and kept only as historical provenance. Reservations stay Branch from `master`, commit, push the branch and open a pull request to `master`. In the pull request, give the commands you ran and what they printed (`tools/build.py all --strict`, `tools/progress.py`, the tests). CI runs the tests and the repository check; a maintainer merges. -Never push to `master` and never force-push a shared branch. AGENTS.md has the full rules and docs/AGENT_WORKFLOW.md says which model takes which functions, for +Never push to `master` and never force-push a shared branch. AGENTS.md has the full rules and docs/agents/agent-workflow.md says which model takes which functions, for people as much as for agents. Use `--strict` for the final build before pushing: it fails if any C function falls back diff --git a/README.md b/README.md index c021e52e1..bb03319e0 100644 --- a/README.md +++ b/README.md @@ -1,337 +1,97 @@

- RAC2 — Going Commando + + + + OpenRAC + +

-

Ratchet & Clank: Going Commando

+ +

Ratchet & Clank: Going Commando decompilation project

- OpenRAC website - OpenRAC Discord - Progress report Code matched - Functions matched + Progress report + openrac.dev + Discord

-A work-in-progress **matching decompilation** of *Ratchet & Clank: Going Commando* (Insomniac -Games, 2003) for the PlayStation 2 (USA v1.01, `SCUS_972.68`), part of the -**[OpenRAC](https://openrac.dev)** initiative. - -The disc's boot executable and its 27 level programs are rebuilt from C and assembly until -every loaded byte equals retail: - -| Program | What it is | Code | -| --- | --- | ---: | -| `SCUS_972.68` | the boot executable: the engine core and the game | 1,046,952 bytes | -| `G/LEVELn.WAD` | the level programs, one per level, 27 of them | 4,482,204 bytes | - -The objective is to produce C source code that, when compiled with the original toolchain, -generates a byte-identical copy of the retail programs. Matched code is then refactored -toward readable, idiomatic C with accurate types and naming, using matching builds as -continuous regression tests. - -> [!NOTE] -> This repository contains **no game assets, retail executables, or disassembly**. To build, -> you must provide your own legally obtained copy of the game. Please review -> [`LEGAL.md`](LEGAL.md) before contributing. +

+ A matching decompilation of Ratchet & Clank: Going Commando (2003) for PlayStation 2.
+ The boot executable and 27 levels, rebuilt as C that matches retail byte for byte. +

---- +> [!WARNING] +> This repository has no game data, executables, disc images or proprietary toolchains. +> You need your own copy of the game. Read [LEGAL.md](LEGAL.md) before contributing. ## Progress -Decompilation progress is tracked live on **[openrac.dev](https://openrac.dev)** and -**[bytematch.dev/OpenRAC/rac2-decomp](https://bytematch.dev/OpenRAC/rac2-decomp)**. - -| Version | Region | Target ID | Code Matched | Functions Matched | -|---|---|---|---|---| -| v1.01 | USA | `SCUS_972.68` | [![](https://bytematch.dev/OpenRAC/rac2-decomp.svg?mode=shield&label=Code&measure=matched_code_percent)](https://bytematch.dev/OpenRAC/rac2-decomp) | [![](https://bytematch.dev/OpenRAC/rac2-decomp.svg?mode=shield&label=Functions&measure=matched_functions)](https://bytematch.dev/OpenRAC/rac2-decomp) | -| v1.00 | Europe/Australia | `SCES_516.07` | [![](https://bytematch.dev/OpenRAC/rac2-decomp.svg?mode=shield&label=Code&measure=matched_code_percent&version=SCES_516.07)](https://bytematch.dev/OpenRAC/rac2-decomp/SCES_516.07) | [![](https://bytematch.dev/OpenRAC/rac2-decomp.svg?mode=shield&label=Functions&measure=matched_functions&version=SCES_516.07)](https://bytematch.dev/OpenRAC/rac2-decomp/SCES_516.07) | -| v2.00 (Greatest Hits) | USA | `SCUS_972.68_v2` | [![](https://bytematch.dev/OpenRAC/rac2-decomp.svg?mode=shield&label=Code&measure=matched_code_percent&version=SCUS_972.68_v2)](https://bytematch.dev/OpenRAC/rac2-decomp/SCUS_972.68_v2) | [![](https://bytematch.dev/OpenRAC/rac2-decomp.svg?mode=shield&label=Functions&measure=matched_functions&version=SCUS_972.68_v2)](https://bytematch.dev/OpenRAC/rac2-decomp/SCUS_972.68_v2) | - - -| | Functions | Code | -| --- | ---: | ---: | -| Boot executable | 3,060 of 3,078 | 1,005,500 of 1,041,544 bytes | -| Levels | 5,431 of 5,449 | 4,453,760 of 4,482,516 bytes | -| **Total** | 8,491 of 8,527 | 5,459,260 of 5,524,060 bytes (98.83%) | - -The 27 levels hold 100,562 function placements; C covers 94,363 of them. -449 of the matched functions are the original's own assembly (`ASM_FUNC`), kept as assembly. - - -Every distinct function counts once: the boot executable's functions and the level functions -that the boot does not also have. A function many levels share is one function, however many -places it has. A function is C when its C links into a program that comes out equal to the -retail one ([`progress/report.json`](progress/report.json), `tools/progress.py`). The table is -v1.01's; the PAL and v2.00 releases are built and counted the same way from the same C -([docs/VERSIONS.md](docs/VERSIONS.md)). - -The report also gives the boot executable's data (383,391 bytes of initialised data, for the -Data bar on [ByteMatch](https://bytematch.dev)). None of it is matched yet: the build places C -functions' code and discards their objects' data, so all of it still comes from retail's dumps. -The levels' data is not measured. - -Every function links at its original retail address. Functions not yet decompiled are built -from disassembly, so a program links and matches retail byte for byte until a C function -differs; the build then stops at that byte and says which function it was. - -### What counts as a match - -Only `tools/build.py` saying `OK` counts: the C has to link into a program that comes out -equal to the retail one. A compiled object, a few matching instructions or a masked -comparison do not. A near miss belongs in [`nonmatching/`](nonmatching), never in `src/`. - ---- - -## Quick Start - -### What you need - -| What | Notes | -| --- | --- | -| Linux x86-64 with 32-bit support (`gcc -m32`) and Wine | or podman/docker, see [Container](#container) | -| Python 3.12 | `tools/build.py` does not run on 3.11 | -| Your own disc of the game, USA v1.01 | the main release; PAL and v2.00 are optional | -| SN ProDG's `ps2eeas.exe` and `ld.exe` | downloaded in step 3; proprietary, never commit them | - -### 1. Get the code and the Python tools - -```sh -git clone https://github.com/OpenRAC/rac2-decomp.git -cd rac2-decomp -python3 -m pip install -r requirements.txt # splat, spimdisasm, rabbitizer (pinned) -``` - -### 2. Add your discs - -```sh -cp /path/to/SCUS_972.68.iso baserom/SCUS_972.68.iso # USA v1.01, required -cp /path/to/SCES_516.07.iso baserom/SCES_516.07.iso # PAL, optional -cp /path/to/SCUS_972.68_v2.00.iso baserom/SCUS_972.68_v2.00.iso # USA v2.00, optional -sha256sum baserom/SCUS_972.68.iso -# 9db2e33e276133cc283647fa3279b37911955e123d6199d10065547eaa9b1ce5 -``` - -The hashes of the other two are in [`docs/VERSIONS.md`](docs/VERSIONS.md). A pull request -must build in all three releases, so add them if you plan to send one. - -### 3. Download the SN ProDG assembler and linker - -Both come from public mirrors of SN Systems' ProDG for PS2: the assembler from 2.0, the -linker from 3.01. `toolchain/` is in `.gitignore`. - -Make the folder: - -```sh -mkdir -p toolchain/sn-prodg-2.0/ee/bin -``` - -The assembler, `ps2eeas.exe` from ProDG 2.0: +Progress is tracked on [ByteMatch](https://bytematch.dev/OpenRAC/rac2-decomp), one badge per version below. Each +function counts once, however many of the 27 levels share it. -```sh -curl -L -o /tmp/sn20.tar.gz https://github.com/AngheloAlf/SN-Systems-ProDG_for_PS2_2.0/releases/download/1/eegcc_sn_v2.73a.tar.gz -``` -```sh -tar xzf /tmp/sn20.tar.gz -O ee/bin/ps2eeas.exe > toolchain/sn-prodg-2.0/ee/bin/Ps2EeAs.exe -``` - -The linker, `ld.exe` from ProDG 3.01: +## Supported versions -```sh -curl -L -o /tmp/sn301.tar.gz https://github.com/AngheloAlf/SN-Systems-ProDG_for_PS2_3.01/releases/download/1/eegcc_2.95.3_sn_v1.36.tar.gz -``` -```sh -tar xzf /tmp/sn301.tar.gz -O ee/bin/ld.exe > toolchain/sn-prodg-2.0/ee/bin/ld.exe -``` +| Release | Region | Boot executable | Code | Notes | +| --- | --- | --- | --- | --- | +| v1.01 | USA | `SCUS_972.68` | [![Code](https://bytematch.dev/OpenRAC/rac2-decomp.svg?mode=shield&label=Code&measure=matched_code_percent)](https://bytematch.dev/OpenRAC/rac2-decomp) | Primary version | +| v1.00 | Europe/Australia | `SCES_516.07` | [![Code](https://bytematch.dev/OpenRAC/rac2-decomp.svg?mode=shield&label=Code&measure=matched_code_percent&version=SCES_516.07)](https://bytematch.dev/OpenRAC/rac2-decomp/SCES_516.07) | Shares the USA C source | +| v2.00 (Greatest Hits) | USA | `SCUS_972.68` | [![Code](https://bytematch.dev/OpenRAC/rac2-decomp.svg?mode=shield&label=Code&measure=matched_code_percent&version=SCUS_972.68_v2)](https://bytematch.dev/OpenRAC/rac2-decomp/SCUS_972.68_v2) | Report id `SCUS_972.68_v2` | -Check both files: +All releases share one C source. Expected SHA-256 of the primary disc image: -```sh -sha256sum toolchain/sn-prodg-2.0/ee/bin/*.exe +```text +9db2e33e276133cc283647fa3279b37911955e123d6199d10065547eaa9b1ce5 ``` -| File | SHA-256 | -| --- | --- | -| `Ps2EeAs.exe` | `c839dd63facabe7b76573c114056be61eaa7b3aa2329dd546930ab8f98898c76` | -| `ld.exe` | `80f3724a63ce3c77c8b00d075f5c49448636518700ae251e5d6a6c809bbf5f48` | +The other hashes and how the releases fit together: [docs/build/versions.md](docs/build/versions.md). -### 4. Build the compilers and dump the discs +## Quick setup -```sh -tools/compiler/build.sh # the game's compiler, into build/compiler/tools/ -python3 tools/compiler/sdk/build.py # the SDK units' compiler, into build/compiler/sdk/ -python3 tools/levels.py dump # USA v1.01: the boot executable and 27 levels -python3 tools/levels.py dump --version pal # only with the PAL disc -python3 tools/levels.py dump --version us2 # only with the v2.00 disc -``` - -### 5. Build and compare +You need x86-64 Linux with 32-bit support (`gcc -m32`), Wine and Python 3.12, or the build container. +SN ProDG's assembler and linker go in `toolchain/` first: [docs/build/building.md](docs/build/building.md#get-the-sn-tools). ```sh -python3 tools/build.py all # one "OK" per program, or the first byte that differs -python3 tools/build.py all --strict --version pal -python3 tools/build.py all --strict --version us2 +git clone https://github.com/OpenRAC/rac2-decomp.git && cd rac2-decomp +python3 -m pip install -r requirements.txt +cp /path/to/your-disc.iso baserom/SCUS_972.68.iso +tools/compiler/build.sh +python3 tools/compiler/sdk/build.py +python3 tools/levels.py dump +python3 tools/build.py all # one "OK" per program ``` -All 28 programs print `OK` on a clean checkout. `tools/build.py boot`, `level 04` and `levels` -build a part of it. - -### Working on a function - -```sh -python3 tools/todo.py --max 400 # functions still to match, up to 400 bytes -python3 tools/check.py func_XXXXXXXX --diff # compile one function, show it beside retail -``` - -Reserve your functions in issue #152 first: see [Tracking](#tracking). `tools/progress.py` -writes `progress/report.json` and the table above; run it for your own counts, but do not -commit what it writes. - -### Container - -On macOS, Windows or a host without 32-bit support, run the same commands in the build image -(32-bit Debian with Wine and the build tools): - -```sh -bash tools/docker/run.sh tools/compiler/build.sh -bash tools/docker/run.sh python3 tools/levels.py dump -bash tools/docker/run.sh python3 tools/build.py all -``` - -- `run.sh` uses podman if installed, else docker. It uses a local `rac2-build:latest` image, - else pulls `ghcr.io/openrac/rac2-build`, else builds `tools/docker/Dockerfile` (about 15 - minutes). -- The image has Python 3.11, and `tools/build.py` needs 3.12. Until that is fixed, run Python - on the host and only the tools in the container (the "faster split" in - [`docs/BUILDING.md`](docs/BUILDING.md)), or add Python 3.12 to the image. -- With SELinux (Fedora, RHEL) the container cannot read the mounted repository. Add - `--security-opt label=disable` to the `run` line, or relabel the mount. - -For the details: - -- [`docs/BUILDING.md`](docs/BUILDING.md) – the build, the toolchain, the container, CI -- [`docs/LEVELS.md`](docs/LEVELS.md) – the 27 level programs and the function catalogue -- [`docs/COMPILER-NOTES.md`](docs/COMPILER-NOTES.md) – the compiler and what was measured of it -- [`docs/PERMUTER.md`](docs/PERMUTER.md) – decomp-permuter for near misses, through OpenRAC's shared tool -- [`docs/VERSIONS.md`](docs/VERSIONS.md) – the three releases (v1.01, PAL, v2.00): their dumps, catalogues, builds and reports - ---- - -## Tracking - -- **Reservations.** Before you start, reserve the functions, files or levels you will work on - in [issue #152](https://github.com/OpenRAC/rac2-decomp/issues/152): one comment per - contributor, edited in place, at most 500 unfinished functions. Read the other comments - first and do not overlap them. Release what you finish or drop. [`AGENTS.md`](AGENTS.md) has - the exact rules. -- **What is left.** `python3 tools/todo.py` lists the functions still to match by size - (`--min`, `--max`, `--kind boot|shared|level`, `--limit`). Before you start one, look for it - in [`nonmatching/README.md`](nonmatching/README.md): an earlier near miss and its notes may - be there. -- **Near misses.** C that does not match goes into `nonmatching/` with a row in its README, - and the function keeps its `INCLUDE_ASM` marker in `src/`. -- **The progress report.** `progress/report.json` and the table above are generated. Only a - maintainer refreshes them, on `master`, after merging. Leave both out of your pull request; - if you ran `tools/progress.py`, undo it with - `git checkout origin/master -- progress/report.json README.md`. - `python3 tools/progress.py --check` passes while the report is behind and prints how many - new functions wait for the refresh. -- **Before a pull request**, all must pass: - ```sh - python3 tools/build.py all --strict # and with --version pal, --version us2 - python3 -m unittest discover -s tests - python3 tests/check_repository.py - python3 tools/progress.py --check - ``` - A function that matches in v1.01 but not in another release stays assembly there: - `tools/carry.py --version pal --demote` ([`docs/VERSIONS.md`](docs/VERSIONS.md)). - -## How it works - -- `tools/levels.py dump` reads the boot executable and the 27 level programs from the disc. -- `config/boot/functions.tsv` and `config/levels/functions.tsv` list every function once, - with its places ([docs/LEVELS.md](docs/LEVELS.md)). -- `src/` lists every function once, in address order, as its C or as an `INCLUDE_ASM` marker, - as rac1-decomp does. -- `tools/build.py all` disassembles each program, links the C in place of its assembly, and - compares every loaded byte with the retail program ([docs/BUILDING.md](docs/BUILDING.md)). - -## Tools - -| Tool | Purpose | -|---|---| -| `tools/levels.py` | The disc's programs and the catalogue of their functions | -| `tools/build.py` | The build: one `OK` per program, or the first byte that differs | -| `tools/check.py` | Compiles one function with its file's flags and diffs it against retail | -| `tools/todo.py` | Sorts the functions still to match into size tiers | -| `tools/progress.py` | The counts, the README table and `progress/report.json` | -| `tools/expand_asm.py` | Adapts splat's assembly to SN ProDG's assembler | -| `tools/elf.py` | The ELF32 reading and writing the build needs | -| `tools/compiler/` | The game's compiler, rebuilt from public source, with its patches | -| `tools/docker/run.sh` | Runs a command in the `rac2-build` image (32-bit Linux, Wine, Python) | -| `tests/check_repository.py` | The pre-commit check: catalogues, sources, no game data (CI runs it) | - -## Layout - -| Path | What | -| --- | --- | -| `src/core/` | the boot's core code (SDK, libc), one file per original object | -| `src/game/` | the boot's own program | -| `src/levels/shared/`, `src/levels/NN_name/` | level functions in several levels, and in one | -| `nonmatching/` | near misses, never linked | -| `config/` | the function catalogues, per-file compiler flags, naming research | -| `tools/` | disc reader and catalogue, build, progress, the compiler recipes | -| `include/` | shared headers | -| `ports/pal-functional/` | functional C for the PAL release (not matching) | -| `docs/` | building, the level format, the compiler, game research | +macOS, Windows, the container and the other releases: [docs/build/building.md](docs/build/building.md). ## Contributing -Contributions are warmly welcome! Whether you are interested in decompiling functions, -researching engine quirks, or improving documentation: +Start with [CONTRIBUTING.md](CONTRIBUTING.md) and [AGENTS.md](AGENTS.md). Reserve your functions in +[issue #152](https://github.com/OpenRAC/rac2-decomp/issues/152) first. All docs are listed in [docs/README.md](docs/README.md): -- Read [`AGENTS.md`](AGENTS.md) and [`CONTRIBUTING.md`](CONTRIBUTING.md) first; they hold the - rules. -- [`docs/PORT_HANDOFF.md`](docs/PORT_HANDOFF.md) hands over the work where this repository meets the native port: functional C, the behaviour gate, the bugs player testing traced here, and what is open. -- [`docs/AGENT_WORKFLOW.md`](docs/AGENT_WORKFLOW.md) says which tier of model takes which - function, and how a wave is spread over the workers. -- `python3 tools/todo.py` sorts the functions still to match into buckets, and - `tools/check.py func_XXXXXXXX --diff` shows a draft beside retail in seconds. -- Everyone can improve the [shared matching blockers](docs/MATCHING-BLOCKERS.md) through a - pull request, with discussion in the relevant PR. Coordinate scopes in - [issue #152](https://github.com/OpenRAC/rac2-decomp/issues/152). -- Join our **[Discord Community](https://discord.gg/Sfd2B54PDG)** to discuss progress, ask - questions, and collaborate! +- [docs/build/](docs/build) - building, versions, levels and the compiler +- [docs/contributing/](docs/contributing) - matching blockers and the permuter +- [docs/game/](docs/game) - how the game works: symbols, moby dispatch, VU microprograms +- [docs/research/](docs/research) - prototype builds and what carries over from the first game +- [docs/port/](docs/port) - where this repository meets the native port +- [docs/agents/](docs/agents) - instructions for AI agents + +Keep the build matching retail. Never commit game images, extracted game data or proprietary tool binaries. ## Credits -- [`CREDITS.md`](CREDITS.md) is the record of every outside project and person this - repository uses, and of what was taken from each. -- **[rac1-decomp](https://github.com/OpenRAC/rac1-decomp)** – the first game's matching - decompilation: the layout of `src/`, the units and the categories of the report follow it. -- **[rac3-decomp](https://github.com/OpenRAC/rac3-decomp)** (PAL *Up Your Arsenal*), - **[rac3-uya-decomp](https://github.com/OpenRAC/rac3-uya-decomp)** (NTSC) and - **[rac5-sm-decomp](https://github.com/OpenRAC/rac5-sm-decomp)** (*Size Matters*) – the other - OpenRAC decompilations, with the same tools and the same rules. -- **[OpenRAC](https://github.com/OpenRAC/OpenRAC)** – the native PC port of the first game: - the reference for the engine this game shares, and for what this work is for. -- **[Lombyte](https://github.com/lombyte-project/Lombyte)** by mateuszklysz – the NTSC - decompilation of the first game: its compiler patch stack and the SDK compiler recipe come - from it. -- **[Promises/RC2-Going-Decompiled](https://github.com/Promises/RC2-Going-Decompiled)** – the - other *Going Commando* decompilation: symbol maps and names, as leads. -- **[splat](https://github.com/ethteck/splat)**, - **[spimdisasm](https://github.com/Decompollaborate/spimdisasm)** and - **[rabbitizer](https://github.com/Decompollaborate/rabbitizer)** – the disassembler and the - instruction decoder. -- **[objdiff](https://github.com/encounter/objdiff)** – the report format, and - **[ByteMatch](https://bytematch.dev)** – the progress dashboard. +Every outside project and person this repository uses is listed in [CREDITS.md](CREDITS.md). The main ones: + +- [rac1-decomp](https://github.com/OpenRAC/rac1-decomp) - layout of `src/`, units and report categories. +- [rac3-decomp](https://github.com/OpenRAC/rac3-decomp), [rac3-uya-decomp](https://github.com/OpenRAC/rac3-uya-decomp) + and [rac5-sm-decomp](https://github.com/OpenRAC/rac5-sm-decomp) - the other OpenRAC decompilations. +- [OpenRAC](https://github.com/OpenRAC/OpenRAC) - the native port, and the reference for the shared engine. +- [Lombyte](https://github.com/lombyte-project/Lombyte) - compiler patch stack and the SDK compiler recipe. +- [Promises/RC2-Going-Decompiled](https://github.com/Promises/RC2-Going-Decompiled) - symbol maps and names, as leads. +- [splat](https://github.com/ethteck/splat), [spimdisasm](https://github.com/Decompollaborate/spimdisasm), + [rabbitizer](https://github.com/Decompollaborate/rabbitizer), [objdiff](https://github.com/encounter/objdiff) + and [ByteMatch](https://bytematch.dev). -## Licence +## License -- Code written for this project is licensed under the - [GNU General Public License v3.0](LICENSE). -- Reconstructed libraries and third-party components retain their original licenses (see - [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md) and [`LEGAL.md`](LEGAL.md)). -- The decompiled game code in `src/` is a derivative of the original game (copyright Sony - Interactive Entertainment LLC): no copyright is claimed over it and it is not licensed. -- *Ratchet & Clank* is a registered trademark of Sony Interactive Entertainment. This project - is not affiliated with or endorsed by Sony or Insomniac Games. +Code written for this project is under the [GNU General Public License v3.0](LICENSE). Code reconstructed from +third-party binaries belongs to its copyright holders; see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). diff --git a/assets/rac2-logo.png b/assets/rac2-logo.png deleted file mode 100644 index e94ad1147..000000000 Binary files a/assets/rac2-logo.png and /dev/null differ diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..f23be9519 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,90 @@ +# Docs + +Every doc of this repository, by folder. New here? Read [building.md](build/building.md), then +[CONTRIBUTING.md](../CONTRIBUTING.md). + +## build/: build and check the match + +| Doc | What it covers | +|---|---| +| [building.md](build/building.md) | Setup, the SN tools, the build, the container, CI, the compilers | +| [versions.md](build/versions.md) | USA v1.01, PAL and USA v2.00: dumps, catalogues, builds and reports | +| [levels.md](build/levels.md) | The 27 level programs and the function catalogue | +| [compiler-notes.md](build/compiler-notes.md) | The compiler and what was measured of it | + +## contributing/: write and review code + +| Doc | What it covers | +|---|---| +| [matching-blockers.md](contributing/matching-blockers.md) | Shared matching blockers: what was tried and what is open | +| [permuter.md](contributing/permuter.md) | decomp-permuter for near misses | + +## game/: how the game works + +| Doc | What it covers | +|---|---| +| [engine-symbol-names.md](game/engine-symbol-names.md) | Engine symbol names from the August 8 prototype | +| [assert-message-names.md](game/assert-message-names.md) | Names from assert messages ([table](game/assert-message-names.tsv)) | +| [moby-dispatch-tables.md](game/moby-dispatch-tables.md) | Moby dispatch tables ([table](game/moby-dispatch.tsv)) | +| [vu-microprograms.md](game/vu-microprograms.md) | VU microprograms | +| [community-engine-reference.md](game/community-engine-reference.md) | Community research on the engine | + +## research/: other builds and games + +| Doc | What it covers | +|---|---| +| [prototype-builds.md](research/prototype-builds.md) | Public prototype builds | +| [aug6-retail-anchors.md](research/aug6-retail-anchors.md) | Code shared by the August preview and retail | +| [rac1-decomp-findings.md](research/rac1-decomp-findings.md) | Findings from rac1-decomp | +| [rac1-to-rac2.md](research/rac1-to-rac2.md) | What carries over from the first game | + +## port/: the native port + +| Doc | What it covers | +|---|---| +| [port-handoff.md](port/port-handoff.md) | Functional C, the behaviour gate, bugs traced here, what is open | + +## agents/: AI agents + +| Doc | What it covers | +|---|---| +| [agent-workflow.md](agents/agent-workflow.md) | Which model takes which function, and how a wave is spread | + +## How it works + +- `tools/levels.py dump` reads the boot executable and the 27 level programs from the disc. +- `config/boot/functions.tsv` and `config/levels/functions.tsv` list every function once, + with its places ([docs/build/levels.md](build/levels.md)). +- `src/` lists every function once, in address order, as its C or as an `INCLUDE_ASM` marker, + as rac1-decomp does. +- `tools/build.py all` disassembles each program, links the C in place of its assembly, and + compares every loaded byte with the retail program ([docs/build/building.md](build/building.md)). + +## Tools + +| Tool | Purpose | +|---|---| +| `tools/levels.py` | The disc's programs and the catalogue of their functions | +| `tools/build.py` | The build: one `OK` per program, or the first byte that differs | +| `tools/check.py` | Compiles one function with its file's flags and diffs it against retail | +| `tools/todo.py` | Sorts the functions still to match into size tiers | +| `tools/progress.py` | The counts and `progress/report.json` | +| `tools/expand_asm.py` | Adapts splat's assembly to SN ProDG's assembler | +| `tools/elf.py` | The ELF32 reading and writing the build needs | +| `tools/compiler/` | The game's compiler, rebuilt from public source, with its patches | +| `tools/docker/run.sh` | Runs a command in the `rac2-build` image (32-bit Linux, Wine, Python) | +| `tests/check_repository.py` | The pre-commit check: catalogues, sources, no game data (CI runs it) | + +## Layout + +| Path | What | +| --- | --- | +| `src/core/` | the boot's core code (SDK, libc), one file per original object | +| `src/game/` | the boot's own program | +| `src/levels/shared/`, `src/levels/NN_name/` | level functions in several levels, and in one | +| `nonmatching/` | near misses, never linked | +| `config/` | the function catalogues, per-file compiler flags, naming research | +| `tools/` | disc reader and catalogue, build, progress, the compiler recipes | +| `include/` | shared headers | +| `ports/pal-functional/` | functional C for the PAL release (not matching) | +| `docs/` | building, the level format, the compiler, game research | diff --git a/docs/AGENT_WORKFLOW.md b/docs/agents/agent-workflow.md similarity index 94% rename from docs/AGENT_WORKFLOW.md rename to docs/agents/agent-workflow.md index 4648c2704..343879f9b 100644 --- a/docs/AGENT_WORKFLOW.md +++ b/docs/agents/agent-workflow.md @@ -35,7 +35,7 @@ counts once in the progress, but one match removes it from 27 programs. ## Before any model: tools Much of the easy work needs no model at all. Before a wave, check whether a function is -byte-identical to one already matched in rac1-decomp (docs/RAC1-TO-RAC2.md) or to another +byte-identical to one already matched in rac1-decomp (docs/research/rac1-to-rac2.md) or to another function already matched here; those are carried over, not rewritten. ### Where the body may already exist @@ -119,7 +119,7 @@ Workers may divide work locally only within that contributor's active reserved s Each worker gets its own git worktree and branch (`git worktree add ../rac2-w3 -b match/w3 origin/master`) and functions nobody else in the same contributor's wave has. Hand out whole files where you can: two workers editing one file is the usual source of conflicts. No separate per-worker issue #152 comment, committed queue, claim file or reservation database is required; the lead's local list of which worker has which functions is enough. -Workers need the build environment (docs/BUILDING.md) but not a full build: `tools/check.py` compiles one file and compares one function in seconds. +Workers need the build environment (docs/build/building.md) but not a full build: `tools/check.py` compiles one file and compares one function in seconds. ## Landing @@ -133,13 +133,13 @@ The lead, for each finished worker: 3. runs `python3 tools/progress.py`, the tests, `tests/check_repository.py` and `tools/progress.py --check`; 4. commits coherent batches (prefer at least five related functions when practical; - see the [exceptions in AGENTS.md](../AGENTS.md#commits), and keep the report in its + see the [exceptions in AGENTS.md](../../AGENTS.md#commits), and keep the report in its own commit) and opens the pull request as AGENTS.md describes, with the build output in it. Shared blockers, measured attempts and reopening conditions go into -[MATCHING-BLOCKERS.md](MATCHING-BLOCKERS.md). Every contributor can improve an existing entry +[matching-blockers.md](../contributing/matching-blockers.md). Every contributor can improve an existing entry through a PR; there is no per-person ownership. Settled compiler mechanisms belong in -[COMPILER-NOTES.md](COMPILER-NOTES.md), linked from the relevant blocker rather than duplicated. +[compiler-notes.md](../build/compiler-notes.md), linked from the relevant blocker rather than duplicated. Discuss findings in the relevant PR; issue #157 is historical provenance only. No wave logs, lot documents or separate registers. diff --git a/docs/assets/openrac-logo-dark.svg b/docs/assets/openrac-logo-dark.svg new file mode 100644 index 000000000..5436b5974 --- /dev/null +++ b/docs/assets/openrac-logo-dark.svg @@ -0,0 +1,18 @@ + +OpenRAC + + + + + + + + + + + + + + + + diff --git a/docs/assets/openrac-logo-light.svg b/docs/assets/openrac-logo-light.svg new file mode 100644 index 000000000..a99544ef1 --- /dev/null +++ b/docs/assets/openrac-logo-light.svg @@ -0,0 +1,18 @@ + +OpenRAC + + + + + + + + + + + + + + + + diff --git a/docs/BUILDING.md b/docs/build/building.md similarity index 80% rename from docs/BUILDING.md rename to docs/build/building.md index 169f20fc7..8e3b42998 100644 --- a/docs/BUILDING.md +++ b/docs/build/building.md @@ -16,6 +16,46 @@ from the game comes from your own disc; nothing game-derived is committed. - An x86-64 Linux environment with 32-bit support (`gcc -m32`) for the compilers, and Wine for the SN tools. +## Get the SN tools + +Both come from public mirrors of SN Systems' ProDG for PS2: the assembler from 2.0, the +linker from 3.01. `toolchain/` is in `.gitignore`. + +Make the folder: + +```sh +mkdir -p toolchain/sn-prodg-2.0/ee/bin +``` + +The assembler, `ps2eeas.exe` from ProDG 2.0: + +```sh +curl -L -o /tmp/sn20.tar.gz https://github.com/AngheloAlf/SN-Systems-ProDG_for_PS2_2.0/releases/download/1/eegcc_sn_v2.73a.tar.gz +``` +```sh +tar xzf /tmp/sn20.tar.gz -O ee/bin/ps2eeas.exe > toolchain/sn-prodg-2.0/ee/bin/Ps2EeAs.exe +``` + +The linker, `ld.exe` from ProDG 3.01: + +```sh +curl -L -o /tmp/sn301.tar.gz https://github.com/AngheloAlf/SN-Systems-ProDG_for_PS2_3.01/releases/download/1/eegcc_2.95.3_sn_v1.36.tar.gz +``` +```sh +tar xzf /tmp/sn301.tar.gz -O ee/bin/ld.exe > toolchain/sn-prodg-2.0/ee/bin/ld.exe +``` + +Check both files: + +```sh +sha256sum toolchain/sn-prodg-2.0/ee/bin/*.exe +``` + +| File | SHA-256 | +| --- | --- | +| `Ps2EeAs.exe` | `c839dd63facabe7b76573c114056be61eaa7b3aa2329dd546930ab8f98898c76` | +| `ld.exe` | `80f3724a63ce3c77c8b00d075f5c49448636518700ae251e5d6a6c809bbf5f48` | + ## Steps ```sh @@ -107,6 +147,17 @@ RAC2_LINUX="docker exec rac2-gnu" RAC2_WINE="docker exec -w {cwd} rac2-wine wine `RAC2_LINUX` prefixes the compiler commands and `RAC2_WINE` the SN tools (`{cwd}` becomes the directory they run in). On x86-64 Linux with Wine and `gcc -m32`, neither is needed. +### Container notes + +- `run.sh` uses podman if installed, else docker. It uses a local `rac2-build:latest` image, + else pulls `ghcr.io/openrac/rac2-build`, else builds `tools/docker/Dockerfile` (about 15 + minutes). +- The image has Python 3.11, and `tools/build.py` needs 3.12. Until that is fixed, run Python + on the host and only the tools in the container (the faster split above), or add Python 3.12 + to the image. +- With SELinux (Fedora, RHEL) the container cannot read the mounted repository. Add + `--security-opt label=disable` to the `run` line, or relabel the mount. + ## CI `.github/workflows/checks.yml` runs the tool tests and the repository check; @@ -120,7 +171,7 @@ no SN tools, so the build is run locally and its result committed. - **The game's code**: GNU EE-GCC 2.9-ee-991111b rebuilt from its public source with Lombyte's patch stack and the transformers this project measured - ([tools/compiler/](../tools/compiler/README.md)). The build stops unless the pinned modified + ([tools/compiler/](../../tools/compiler/README.md)). The build stops unless the pinned modified sources hash as recorded; the binaries' own hashes depend on the host, so a compiler is checked by its output. - **The boot's SDK units** (the `src/core/` files `config/cflags.txt` marks `sdk`): Sony EE-GCC 2.9-ee-991111-01, unmodified, from diff --git a/docs/COMPILER-NOTES.md b/docs/build/compiler-notes.md similarity index 93% rename from docs/COMPILER-NOTES.md rename to docs/build/compiler-notes.md index e45d703c3..2a9e5c88b 100644 --- a/docs/COMPILER-NOTES.md +++ b/docs/build/compiler-notes.md @@ -130,7 +130,7 @@ units, then recheck their existing C and all carrying releases. Stop a tied approach instead of manufacturing references or trying equivalent permutations. This reference groups qualified compiler mechanisms and useful C source shapes by -topic. Open cases belong in [MATCHING-BLOCKERS.md](MATCHING-BLOCKERS.md). The source +topic. Open cases belong in [matching-blockers.md](../contributing/matching-blockers.md). The source recipe, per-file flags and complete linked build take precedence over historical observations; this document is not a progress report or a reconstruction of the unknown shipping toolchain. @@ -143,15 +143,15 @@ belong to that PR; they are not additional project-progress claims. | Input | Maintained implementation | Role | | --- | --- | --- | -| Game C | [tools/compiler/build.sh](../tools/compiler/build.sh) | Public GNU EE 2.9-ee-991111b cpp/cc1, pinned Lombyte sce-991111b patches and measured transformers | -| SDK C | [tools/compiler/sdk/build.py](../tools/compiler/sdk/build.py) | Public-source Sony EE-GCC 2.9-ee-991111-01, selected with the sdk prefix in config/cflags.txt | +| Game C | [tools/compiler/build.sh](../../tools/compiler/build.sh) | Public GNU EE 2.9-ee-991111b cpp/cc1, pinned Lombyte sce-991111b patches and measured transformers | +| SDK C | [tools/compiler/sdk/build.py](../../tools/compiler/sdk/build.py) | Public-source Sony EE-GCC 2.9-ee-991111-01, selected with the sdk prefix in config/cflags.txt | | Assembly from either C frontend | Source-built game GNU as | Qualified immediate, hazard and padding rules | | Remaining assembly reconstructed from the disc | Locally supplied SN ProDG 2.0 Ps2EeAs.exe | Separate from the GNU-assembled C-object path | | Final link | Locally supplied SN ld.exe | Places the objects before comparison against retail | The GNU archive is `gnu-ee-binutils-gcc-1.1.tar.gz`, SHA-256 `1f518043e252d6eda726386971d52eda26541ab936ea73a9783d73712b595f92`, target -`mips64r5900-sf-elf`. [CREDITS.md](../CREDITS.md) records the public sources. +`mips64r5900-sf-elf`. [CREDITS.md](../../CREDITS.md) records the public sources. Proprietary SDK source, samples and headers are not inputs. Matching bodies establishes compatibility, not Insomniac's exact original binary, @@ -171,8 +171,8 @@ python3 tools/check.py func_XXXXXXXX --diff python3 tools/build.py all --strict --jobs 8 ``` -See [BUILDING.md](BUILDING.md) for setup and [VERSIONS.md](VERSIONS.md) for releases. -Flags are per file in [config/cflags.txt](../config/cflags.txt). A `*-g8.c` sibling +See [building.md](building.md) for setup and [versions.md](versions.md) for releases. +Flags are per file in [config/cflags.txt](../../config/cflags.txt). A `*-g8.c` sibling gets `-G8` unless an earlier explicit rule matches. Qualify every C definition in a unit after changing its profile. @@ -307,13 +307,13 @@ before every FPR save. Emission order is not final scheduled order. Fixing a fra does not fix unrelated entry-block allocation or address formation. The implementations are -[neutralise_timode_anchor.py](../tools/compiler/transformers/neutralise_timode_anchor.py), -[ascending_save_order.py](../tools/compiler/transformers/ascending_save_order.py) and -[reorder_save_blocks.py](../tools/compiler/transformers/reorder_save_blocks.py). +[neutralise_timode_anchor.py](../../tools/compiler/transformers/neutralise_timode_anchor.py), +[ascending_save_order.py](../../tools/compiler/transformers/ascending_save_order.py) and +[reorder_save_blocks.py](../../tools/compiler/transformers/reorder_save_blocks.py). The reorder consumes the ascending-order transformer's output. Replacing `mips.c` with an older file also removes the zero-fold code called by `toplev.c`. -[disable_frame_order_default.py](../tools/compiler/transformers/disable_frame_order_default.py) +[disable_frame_order_default.py](../../tools/compiler/transformers/disable_frame_order_default.py) keeps generic frame scheduling by default; `-mastra-keep-frame-order` is an explicit experiment. `func_002B7170` needs the generic order plus measured source lifetimes and stores. No frame-order option fixes arbitrary entry-block allocation. @@ -322,14 +322,14 @@ and stores. No frame-order option fixes arbitrary entry-block allocation. Short-loop padding must run after delay-slot scheduling: this C pipeline uses GNU `as`, rather than the assembler assumed by patch 0054. -[enable_loop_padding.py](../tools/compiler/transformers/enable_loop_padding.py) +[enable_loop_padding.py](../../tools/compiler/transformers/enable_loop_padding.py) restores the hook. -[count_trap_length.py](../tools/compiler/transformers/count_trap_length.py) counts a +[count_trap_length.py](../../tools/compiler/transformers/count_trap_length.py) counts a `TRAP_IF` by its machine-description length: a division guard is branch plus break, not one word. Counting one overpads `func_0028B950`; `func_0034FB20` is the 168-byte polling-loop witness. -[count_block_move_length.py](../tools/compiler/transformers/count_block_move_length.py) +[count_block_move_length.py](../../tools/compiler/transformers/count_block_move_length.py) does the same for block moves. A structure copy of 0x40 bytes or more becomes a loop of one `movstrsi_internal2` insn (its loads) and one `movstrsi_internal3` insn (its stores); counted as one word each, every such loop looked five words long and got two @@ -339,10 +339,10 @@ length (20 and 1) keeps both above the seven-word threshold. Retail witnesses, a unpadded: `func_L08_003C6AB0` (both loops, three places), `func_002F7738` (seven loops), `func_L00_002FC980` (27 places), `func_L30_0040B9D8`, `func_002AF0A8`. No matched function changes: `build.py all --strict` is OK with and without the patch. -Control: [test_block_move_loop.py](../tests/test_block_move_loop.py), where a +Control: [test_block_move_loop.py](../../tests/test_block_move_loop.py), where a four-word store loop keeps its three NOPs. -[block_move_quad.py](../tools/compiler/transformers/block_move_quad.py) gives those loops +[block_move_quad.py](../../tools/compiler/transformers/block_move_quad.py) gives those loops retail's quadword form. `expand_block_move` capped the alignment at the 8-byte word, so a structure of 16-byte alignment copied by `ld`/`sd`: four scratch registers per 32-byte round and four more for the leftover. Retail has `lq ; lq ; sq ; sq ; addiu ; bne ; addiu` with @@ -352,11 +352,11 @@ and four more for the leftover. Retail has `lq ; lq ; sq ; sq ; addiu ; bne ; ad `move_by_pieces` at word alignment. Witnesses: `func_L12_00418418` (one place) and `func_L03_003E4148` (eight) become exact, `func_L12_0041BF88`'s six copies each lose their six extra words. All 5,849 compilable units give the same assembly with and without the patch, so -nothing already matched moves. Control: [test_block_move_quad.py](../tests/test_block_move_quad.py), +nothing already matched moves. Control: [test_block_move_quad.py](../../tests/test_block_move_quad.py), where an 8-byte-aligned page keeps `ld`/`sd` and a 0x40-byte copy keeps its pieces. The padding counts words, as the retail assembler does, for the two 16-byte copies too: -[count_asm_words.py](../tools/compiler/transformers/count_asm_words.py) counts an asm +[count_asm_words.py](../../tools/compiler/transformers/count_asm_words.py) counts an asm statement by its instruction count (`qcopy` is `lq` plus `sq`) and the grouped `rac2_qcopy` pattern by its length of 2. Counted as one, a copy loop got one NOP too many. Retail evidence: in levels 0, 4 and 11, 44 seven-word loops holding an `lq`/`sq` pair @@ -365,7 +365,7 @@ and six-word ones (`lq ; sq ; addiu ; addiu ; bgtz ; addiu`, `func_L00_003ABEC0` exactly one. Witness: the three-row matrix copy in `func_L00_0041CB80` (27 places). It runs after count_block_move_length.py and extends the same counter condition. No matched function changes: `build.py all --strict` is OK with both patches. -Test: [test_count_asm_words.py](../tests/test_count_asm_words.py). +Test: [test_count_asm_words.py](../../tests/test_count_asm_words.py). Inserted padding also advances the running word count. The pass places nops before the branch it is already visiting, so they will not be visited later. @@ -388,7 +388,7 @@ This applies only to simple SI register SETs on R5900/GAS: not delay-slot sequences, stores, FP/wide operations, PIC or buffered extern output. Explicit `SDATA` aliases are excluded because their `__gp` metadata is supplied by the existing assembly-input pass. Scheduler lengths and compiler-authored operations -are untouched. [test_symbolic_loop_words.py](../tests/test_symbolic_loop_words.py) +are untouched. [test_symbolic_loop_words.py](../../tests/test_symbolic_loop_words.py) checks unknown addresses/loads, early SDA, incomplete SDATA, both alias spellings and an external defined between functions. The previous compiler produces eight-word loops in the three positive cases; all seven controls pass with the @@ -412,7 +412,7 @@ catalogue prologue finds one other frame mixing `sq` and `sd` GPR saves, func_L0 (`sq $s2` in a 16-byte slot below 8-byte ones), which `-frac2-wide-saves-below` covers (below). The stock EE attribute `register_precision(ra, 64)` on the prototype narrows the `$ra` save to `sd`, but the save loop still gives `$ra` the top slot. -[narrow_saves_below.py](../tools/compiler/transformers/narrow_saves_below.py) adds +[narrow_saves_below.py](../../tools/compiler/transformers/narrow_saves_below.py) adds `-frac2-narrow-saves-below`: with `-mastra-quad-saves` the loop walks the registers saved at the slot's full width first, then the narrower ones, so the narrower save sits below. A frame whose saves all have one width keeps `$ra` on top, as func_00115C90 and func_00115FC0 do; @@ -429,7 +429,7 @@ It is the only one of 7,980 framed retail functions that holds TImode in a saved `-mastra-quad-saves` every GPR slot is 8 bytes, but save_restore_insns saves a register in the widest mode the function writes it in, so stock cc1 gives a 0x680 frame and `sq $s2,0x630` in an 8-byte slot. `sq` ignores the low four address bits, so that store overwrites `$s3`'s save. -[wide_saves_below.py](../tools/compiler/transformers/wide_saves_below.py) adds +[wide_saves_below.py](../../tools/compiler/transformers/wide_saves_below.py) adds `-frac2-wide-saves-below`. In a unit without `-mastra-quad-saves`, a saved GPR whose save is wider than the slot then takes a 16-byte slot at the bottom of the GPR save area. The other GPRs keep 8-byte slots above it in register order, and compute_frame_size adds 8 bytes for @@ -466,7 +466,7 @@ Cygnus-based tree fold runs the CYGNUS LOCAL reduce_expression_tree_depth on eve +0xC8..+0xF8. SN's "GNU C version 2.95.2 SN BUILD v2.73a" cc1 (FSF 2.95.2, which has no CYGNUS LOCAL code) builds the same giv (its loop dump matches) but emits retail's nested sum; Sony's 991111a/-01/b-r4 cc1 flatten like ours. Only that observable output was used. -[mult_add_no_flatten.py](../tools/compiler/transformers/mult_add_no_flatten.py) adds +[mult_add_no_flatten.py](../../tools/compiler/transformers/mult_add_no_flatten.py) adds `-frac2-mult-add-no-flatten`: the fold calls inside expand_mult_add run with flag_schedule_insns clear, so the flattening is skipped there and nowhere else. Without the switch all 8,733 game-compiler units of `src/` compile to the same assembly as before. Forced @@ -481,7 +481,7 @@ func_002F7320 (132 bytes, 28 places) starts `daddu $t1,$zero,$zero ... daddu $a3 for `int result = 0; int i = result;`. Stock cse folds that copy to `move $a3,$0`: CONST_INT costs 0 and a pseudo 1. With the copy alive, result=0 has a dependent, sched1 moves it ahead of the argument copies and allocation becomes retail's (a0 value, a3 i, t1 result). -[keep_block_zero_copy.py](../tools/compiler/transformers/keep_block_zero_copy.py) adds +[keep_block_zero_copy.py](../../tools/compiler/transformers/keep_block_zero_copy.py) adds `-frac2-keep-block-zero-copy`: cse keeps a copy from one pseudo to another whose value is known to be 0 when its source is set earlier in the same basic block; the REG_EQUAL note still gives loop.c the zero start. Without the same-block limit, forced on, it changes one matched body, @@ -495,7 +495,7 @@ forward search from the first call passes the second qcopy asm (volatile). The E resource.c predates GCC 2.95's 6 May 1999 change (Mark Mitchell: "Make volatil monotonically increasing"): mark_referenced_resources assigns `res->volatil = MEM_VOLATILE_P (x)`, so the next ordinary load clears the flag and the search goes on to the addiu. Upstream 2.95 and -2.95.3 use `|=` there. [sticky_volatile_refs.py](../tools/compiler/transformers/sticky_volatile_refs.py) +2.95.3 use `|=` there. [sticky_volatile_refs.py](../../tools/compiler/transformers/sticky_volatile_refs.py) adds `-frac2-sticky-volatile-refs`: the MEM and ASM_OPERANDS cases of mark_referenced_resources keep the flag set. Forced on in every game unit it changes none of 8,510 others. The mark_set_resources half of the same upstream change is the compiler's @@ -521,7 +521,7 @@ find_dead_or_set_registers' budget (one conditional branch) runs out before the path sets `$fp`, `$fp` stays live there and the slot must be annulled. GCC 2.95 has John Wehle's change of 2 June 1999 (gcc/ChangeLog: "Only give FRAME_POINTER_REGNUM and HARD_FRAME_POINTER_REGNUM special treatment if reload hasn't run or the frame pointer is -needed"). [frame_pointer_liveness.py](../tools/compiler/transformers/frame_pointer_liveness.py) +needed"). [frame_pointer_liveness.py](../../tools/compiler/transformers/frame_pointer_liveness.py) adds `-frac2-frame-pointer-liveness`, which puts that condition, `! reload_completed || frame_pointer_needed`, on the five flow.c tests and attach_deaths. Checked against gcc-core-2.95.tar.gz from ftp.gnu.org (flow.c 2276, 2965/2968, 3278/3281, 3740/3746, 3923/3926; @@ -554,7 +554,7 @@ the symbol, which cannot alias a stack slot. GCC removed both seeds: Richard Hen struct_value_incoming_rtx or static_chain_rtx as pointing to stack memory.") reverses an addition of October 1997 (ChangeLog.0) and is in gcc-2.95.3. A Pmode ADDRESS is the base the stack, frame and argument pointers get, so the seed declared the static chain, which points into another -function's frame, a stack reference. [no_static_chain_base.py](../tools/compiler/transformers/no_static_chain_base.py) +function's frame, a stack reference. [no_static_chain_base.py](../../tools/compiler/transformers/no_static_chain_base.py) adds `-frac2-no-static-chain-base`, which leaves both seeds out. Checked against gcc-core-2.95 and gcc-core-2.95.3 from ftp.gnu.org: their alias.c files differ only in the copyright line and these nine lines (2.95 lines 1458-1466); gcc-3.0, 3.4.0 and 4.0.0 have neither seed (3.4 moves the other @@ -753,10 +753,10 @@ observations, not qualified rules: ## Symbols, addresses and small data Use catalogue function identities, resident `D_XXXXXXXX` within the resident region -described in [AGENTS.md](../AGENTS.md#naming-in-c), and level `D_LNN_XXXXXXXX` +described in [AGENTS.md](../../AGENTS.md#naming-in-c), and level `D_LNN_XXXXXXXX` data names. When a callee/data pairing changes by program, use `ref_LNN_XXXXXXXX_kk`. Zero-padding a level address is not enough to -identify its canonical function. See [LEVELS.md](LEVELS.md). +identify its canonical function. See [levels.md](levels.md). GP and overlay base depend on the release. USA v1.01 uses GP `0x001AEFF0`; take other releases' constants from the selected layout, not a copied literal. @@ -872,7 +872,7 @@ with distinct incoming values can retain a register address. An artificial volat pointer adds a stack round trip and is not a production remedy. Never change the real offset or add fake barriers just to force that shape. -[qcopy_pattern.py](../tools/compiler/transformers/qcopy_pattern.py) implements the +[qcopy_pattern.py](../../tools/compiler/transformers/qcopy_pattern.py) implements the default-off, per-file `-frac2-qcopy`: aligned TImode memory-to-memory assignments, register addresses, grouped lq/sq and a declared `$v0` scratch. Memory flags/alias sets survive; it adds no memory barrier. The MD length is two instructions. Scalar @@ -886,7 +886,7 @@ The ordinary folded-copy witness `func_L00_00395240` stays on its default profil ### Scheduling around copies -[qcopy_barrier.py](../tools/compiler/transformers/qcopy_barrier.py) adds +[qcopy_barrier.py](../../tools/compiler/transformers/qcopy_barrier.py) adds `-frac2-qcopy-barrier`, effective only with qcopy. Its TImode memory-to-memory SET plus `(clobber (reg:TI 2))` gets both schedulers' volatile-copy dependences, cost 1 and unit classification. Operands, allocation inputs and memory flags stay unchanged; @@ -940,7 +940,7 @@ and `sp+0x10`; two separate locals reverse their placement. ### Default fold and live return values -[fold_zero_ti_store.py](../tools/compiler/transformers/fold_zero_ti_store.py) folds a +[fold_zero_ti_store.py](../../tools/compiler/transformers/fold_zero_ti_store.py) folds a materialized zero to `sq $zero,0(reg)` only for a bare register address and no other live reader. Its bounded search stops at calls/jumps/labels and looks back at most twelve instructions. Shared zeros feeding several stores keep their register. @@ -953,7 +953,7 @@ loop store order. Positive controls: `func_00282C88` (8 bytes), `func_002E5FE0` witnesses: `func_L20_0044AB68` (204), `func_L20_00443908` (264), `func_L00_003ACC88` (112 at 27 places). -[fold_zero_ti_return_value.py](../tools/compiler/transformers/fold_zero_ti_return_value.py) +[fold_zero_ti_return_value.py](../../tools/compiler/transformers/fold_zero_ti_return_value.py) admits exactly one reconstructible function-value USE of that same `REG_FUNCTION_VALUE_P` register, without an intervening call/jump. The store fold and conversion of USE back into an explicit zero return SET form one @@ -968,7 +968,7 @@ not harmless one-word differences. ### Reload registers for quadword values -[ti_one_gpr.py](../tools/compiler/transformers/ti_one_gpr.py) is unconditional. The +[ti_one_gpr.py](../../tools/compiler/transformers/ti_one_gpr.py) is unconditional. The sce-991111b stack makes `HARD_REGNO_NREGS` 1 for TImode in a general register, but `CLASS_MAX_NREGS` still answered 16/`UNITS_PER_WORD` = 2 for every non-FP class. Reload sizes its needs and its reload registers by the latter, so each TImode reload @@ -985,14 +985,14 @@ where retail has `$a0`. Ten more unmatched retail functions clear with holds a call's value and `$v1` a constant across the clear). Every matched function of the 28 programs compiles to the same bytes with and without it, so it changes only code that had no matching C yet. A non-quadword pseudo can move too, since regclass -and local-alloc read the same macro. [tests/test_ti_one_gpr.py](../tests/test_ti_one_gpr.py) +and local-alloc read the same macro. [tests/test_ti_one_gpr.py](../../tests/test_ti_one_gpr.py) checks authored controls: five reloaded zeros between calls rotate `$v1`, `$v0`, `$v1`, `$v0`, `$v1` (the pair model: `$a1`, `$v0`, ...), a shared stack zero beside a live call value takes `$v1` (pair model: `$a1`), and a lone zero keeps `$v0`. ### Inheritance into a register the insn's pseudos hold -[reload_reg_used_guard.py](../tools/compiler/transformers/reload_reg_used_guard.py) is +[reload_reg_used_guard.py](../../tools/compiler/transformers/reload_reg_used_guard.py) is unconditional. The archive's `reload_reg_free_for_value_p` returns 0 for a register in `reload_reg_used`, which `choose_reload_regs` fills with every register outside the insn's spill set (`IOR_COMPL_HARD_REG_SET (reload_reg_used, chain->used_spill_regs)`). Patch 0047, @@ -1019,7 +1019,7 @@ under it: the count reload after `n++` and the index reloads in `$a0` were their differences. Every matched function of the 28 programs compiles to the same bytes with and without it (`tools/build.py all --strict`, 28 of 28). Of 625 profiled near misses in `nonmatching/`, 57 compile differently; 9 of their functions come closer to retail, -3 move further and the rest are unchanged. [tests/test_reload_reg_used_guard.py](../tests/test_reload_reg_used_guard.py) +3 move further and the rest are unchanged. [tests/test_reload_reg_used_guard.py](../../tests/test_reload_reg_used_guard.py) checks the anchors and the two original object controls. A native harness additionally compiles the actual prepared GCC predicate and covers self-inheritance, non-spill exclusions, other reload lifetimes, equal read-only inputs, multi-register overlaps @@ -1034,13 +1034,13 @@ not substitutes for linked game builds. | -frac2-zero-ti-barrier | Selected isolated zero stores get a scheduling barrier | The pre-reload heuristic also selects some displaced clears; it is not a recovered macro detector | | -frac2-zero-ti-por | Suppress zero folding at bare 0($sp) | Other register addresses still fold; mixed forms at the same address cannot be distinguished per file | -[zero_ti_store_barrier.py](../tools/compiler/transformers/zero_ti_store_barrier.py) +[zero_ti_store_barrier.py](../../tools/compiler/transformers/zero_ti_store_barrier.py) selects before reload an isolated const-zero TImode store between calls/labels/jumps; shared zeros keep normal C lowering. After reload it selects an architectural-zero TImode store. Lowering ready cost alone did not reproduce the dependences. `func_L00_003BFF68` matches 12/12 with the switch and 0/12 without it. -[zero_ti_por.py](../tools/compiler/transformers/zero_ti_por.py) is a statement about +[zero_ti_por.py](../../tools/compiler/transformers/zero_ti_por.py) is a statement about a unit's stack clears, not a detector. The control-flow-aware retail census found 183 register-fed zero stores in 153 functions, all at `0($sp)`, but also 396 architectural-zero stores in 263 functions, including 82 at `0($sp)`. The address @@ -1150,7 +1150,7 @@ Four shapes from `func_L01_003B9798`, `func_L00_003E8A98` and the near misses ### gcse: every store kills every load -[gcse_store_kills_loads.py](../tools/compiler/transformers/gcse_store_kills_loads.py) is +[gcse_store_kills_loads.py](../../tools/compiler/transformers/gcse_store_kills_loads.py) is unconditional: it is the real compiler's memory model, not an option. This Cygnus tree's gcse keeps a list of each block's stores and asks alias analysis (`mems_conflict_for_gcse_p`, `true_dependence`) whether a store kills a load, so a load of a global survives stores to other @@ -1175,13 +1175,13 @@ analysis and matches as a plain `for (p = start; p < end; p += 256)` loop. The decider family (`func_L04_002EDBC8`, `func_L08_002D90C8`, `func_L09_002C9738`, `func_L19_002CAAB0`) had read case 0x32's 0x1B4 through a `char *hb` copy of the hero pointer to force retail's reload; under this model it reads the field directly and matches. -[tests/test_gcse_store_kills_loads.py](../tests/test_gcse_store_kills_loads.py) checks the +[tests/test_gcse_store_kills_loads.py](../../tests/test_gcse_store_kills_loads.py) checks the transformer and authored controls: a global is loaded again after stores to another record, reused when no store intervenes, and loaded again after a call. ### reorg: a division guard ends the delay-slot search -[trap_delay_barrier.py](../tools/compiler/transformers/trap_delay_barrier.py) is +[trap_delay_barrier.py](../../tools/compiler/transformers/trap_delay_barrier.py) is unconditional: it is the real compiler's delay-slot rule, not an option. `fill_simple_delay_slots` (reorg.c) fills a call's slot by scanning backward from the call for an independent insn, and stops at a label, a jump or an insn that already holds a slot (`stop_search_p`). On the R5900 an @@ -1204,13 +1204,13 @@ retail sets `li $a0,2`/`li $a0,3` in the preceding `beq` slot (fill_eager_delay_ leaves both `jal` slots empty, where this tree filled the `jal` slots first and the `beq` took an insn from its target (12 instructions). `func_L12_002CF7B0` and `func_L11_002DE9E0`, members of that family, match with the barrier; of every other matched function in the 28 programs none -compiled differently. [tests/test_trap_delay_barrier.py](../tests/test_trap_delay_barrier.py) +compiled differently. [tests/test_trap_delay_barrier.py](../../tests/test_trap_delay_barrier.py) checks the transformer and authored controls: an argument set above a guard stays there and the call keeps an empty slot, and without a division the call's slot is filled as before. ### reorg: a division guard ends the redundant-insn search -[trap_redundant_barrier.py](../tools/compiler/transformers/trap_redundant_barrier.py) is +[trap_redundant_barrier.py](../../tools/compiler/transformers/trap_redundant_barrier.py) is unconditional: like the barrier above, it is the real compiler's rule, not an option, and no file needs a switch for it. It is that barrier's companion for reorg's other backward search. When `fill_slots_from_thread` takes an insn from a branch's target for the slot, it first asks @@ -1233,7 +1233,7 @@ ends with a `% 6` test whose `beqz` goes to the epilogue's `ld $s0` in retail; t restore already in an earlier `bne` slot as redundant and branched one word further. M10's draft (26 October 2026) differed only there and matches 3/3 in us, pal and us2 with the barrier. Of every matched function in `tools/build.py all --strict`, none compiled differently. -[tests/test_trap_redundant_barrier.py](../tests/test_trap_redundant_barrier.py) +[tests/test_trap_redundant_barrier.py](../../tests/test_trap_redundant_barrier.py) checks the transformer and authored controls: across a guard the reload fills the slot, and without a division the redundant reload is still dropped. @@ -1249,7 +1249,7 @@ the dropped restore, which the test's `stored` control checks. ### reorg: a volatile store stays marked -[sticky_volatile_sets.py](../tools/compiler/transformers/sticky_volatile_sets.py) is the +[sticky_volatile_sets.py](../../tools/compiler/transformers/sticky_volatile_sets.py) is the default: it is the real compiler's rule, so no file needs a switch for it, and `-fno-rac2-sticky-volatile-sets` restores the EE tree's rule for a unit (none uses it). The lead maintainer chose this shape on 8 November 2026. It supersedes the note of 11876eec6 (7 November) @@ -1301,14 +1301,14 @@ The signature: retail's `jal` slot is a nop while the insns just before the call store and then a volatile store. A store retail lets into a slot past a volatile store is a store the rule cannot see as plain: func_00132498's argument, stored as the second element of an incomplete sda array (`D_001A74D0_1[1]`), is the length-2 `m` alternative and never a slot -candidate (below). [tests/test_sticky_volatile_sets.py](../tests/test_sticky_volatile_sets.py) +candidate (below). [tests/test_sticky_volatile_sets.py](../../tests/test_sticky_volatile_sets.py) checks the transformer's anchors and authored controls: the empty slot by default, the filled slot with the opt-out, that `-frac2-sticky-volatile-refs` alone does not empty it, and three controls that no rule changes. ### SDK objects: the assembler fills the slots the compiler leaves -[swap_branch_slots.py](../tools/compiler/transformers/swap_branch_slots.py) adds the opt-in +[swap_branch_slots.py](../../tools/compiler/transformers/swap_branch_slots.py) adds the opt-in assembler option `--r5900-swap-branch-slots`, passed per file as `-Wa,--r5900-swap-branch-slots` after `sdk` in `config/cflags.txt`. It is an assembler profile, not a compiler change: the SDK compiler's output is identical either way. @@ -1344,7 +1344,7 @@ With the option on every one of the 112 SDK-profile units, only `func_00133988` (989snd's snd_GetDopplerPitchMod, `mflo` before `jr $ra`) changes: retail keeps its `nop`. rac1-decomp shows that 989snd was assembled with SN's ps2eeas, not GNU as, so the option is a per-object profile, like rac1's `gas210` column. Game C and 989snd units leave it off. -[tests/test_swap_branch_slots.py](../tests/test_swap_branch_slots.py) checks the transformer +[tests/test_swap_branch_slots.py](../../tests/test_swap_branch_slots.py) checks the transformer and authored controls: without the option every slot gets a `nop`; with it a store, a load and a sign extension move into the slot, and a branch that reads the previous result or carries a label still gets a `nop`. @@ -1383,7 +1383,7 @@ retail keeps in a `bnez` slot is read into a local before the wave test. ### loop: no giv recombination -[no_recombine_givs.py](../tools/compiler/transformers/no_recombine_givs.py) is unconditional: +[no_recombine_givs.py](../../tools/compiler/transformers/no_recombine_givs.py) is unconditional: it is the real compiler's strength reduction, not an option. After `combine_givs` has chosen the reduced giv each address giv is expressed from, this tree's `strength_reduce` (loop.c) calls `recombine_givs`, which moves every combined giv onto the most recently set DEST_REG giv it can @@ -1404,7 +1404,7 @@ matched function in `tools/build.py all --strict`, none compiled differently; of compilable near-miss drafts in nonmatching/, 9 compile differently, none close to a match. The same release-branch change also turned off the biv-to-giv conversion of extra biv increments (`n_extra_increment`); that half is the opt-in `-frac2-no-extra-increment-givs` -(below), with func_00284268 as its witness. [tests/test_no_recombine_givs.py](../tests/test_no_recombine_givs.py) +(below), with func_00284268 as its witness. [tests/test_no_recombine_givs.py](../../tests/test_no_recombine_givs.py) checks the transformer and an authored loop: the digit read stays on the entry pointer (`lbu 36(entry)`, not `-1(name + 5)`), and a one-pointer loop is unchanged. @@ -1421,7 +1421,7 @@ from the last one (`sd 0($a)`, `sd 0($b)`, `sd -24($c)` ... and one add of 32 ea disabled the conversion for the gcc-2.95 release: Jeffrey A Law, 10 June 1999 (gcc-2.95 gcc/ChangeLog: "loop.c (strength_reduce): Disable biv->giv translations and giv recombination. For the release branch only."), the change that also put `recombine_givs` under `#if 0` -(no_recombine_givs.py, above). [no_extra_increment_givs.py](../tools/compiler/transformers/no_extra_increment_givs.py) +(no_recombine_givs.py, above). [no_extra_increment_givs.py](../../tools/compiler/transformers/no_extra_increment_givs.py) adds `-frac2-no-extra-increment-givs`, which adds `! flag_rac2_no_extra_increment_givs` to that guard, so every increment stays a biv increment: store, add, store, add. @@ -1485,8 +1485,8 @@ gives that shape only with the switch (two steps of 12, 8 and 4, two `+1`s); wit of 24, 16 and 8 and one `+2`. So retail's compiler kept every increment in this function too, as in func_00284268, and the switch is not a per-function one. The goto-loop record copy of `func_L00_00300618` (below) does not change: a goto loop has no loop notes. -[tests/test_no_extra_increment_givs.py](../tests/test_no_extra_increment_givs.py) and -[no_extra_increment_givs.c](../tests/fixtures/no_extra_increment_givs.c) cover the anchors (the +[tests/test_no_extra_increment_givs.py](../../tests/test_no_extra_increment_givs.py) and +[no_extra_increment_givs.c](../../tests/fixtures/no_extra_increment_givs.c) cover the anchors (the option table on its own), the default/disabled equality, a pointer cursor and an index cursor whose four increments the default splits and the switch keeps, the twice-incremented counter above, and controls that do not change: one increment, a volatile read, increments under a test @@ -1510,7 +1510,7 @@ conversion in the compiler; the complete loader still does not match (11,280 ver ### Compiler control-flow passes -[tidy_condjump_fallthru.py](../tools/compiler/transformers/tidy_condjump_fallthru.py) +[tidy_condjump_fallthru.py](../../tools/compiler/transformers/tidy_condjump_fallthru.py) lets flow remove a conditional jump to its immediate successor and its now-dead input load. The rule follows GCC fixes from April/August 1999; witnesses include `func_L02_003FD070`, `func_L02_003FC268`, `func_L02_003FC518`, `func_L02_003FD228`, @@ -1523,9 +1523,9 @@ input load. The rule follows GCC fixes from April/August 1999; witnesses include | Both | Compose the two independent changes | func_L02_003FCCA0 | | -frac2-no-null-pointer-checks | Skip the Cygnus delete_null_pointer_checks calls (after jump1 and after the jump pass behind cse); with keep-block-order no block count runs before cse | func_L20_002CD928 case 0x7C, 34,964 bytes/1 place | -Implementations: [gcse_unsplit_edges.py](../tools/compiler/transformers/gcse_unsplit_edges.py), -[keep_block_order.py](../tools/compiler/transformers/keep_block_order.py), -[no_null_pointer_checks.py](../tools/compiler/transformers/no_null_pointer_checks.py). These are +Implementations: [gcse_unsplit_edges.py](../../tools/compiler/transformers/gcse_unsplit_edges.py), +[keep_block_order.py](../../tools/compiler/transformers/keep_block_order.py), +[no_null_pointer_checks.py](../../tools/compiler/transformers/no_null_pointer_checks.py). These are inferences from retail and comparison with GCC 2.95, not the recovered original patches. A lone lui at a branch target is not a unique signature of edge splitting. @@ -1552,7 +1552,7 @@ passes leaves the other's count, so such a unit needs both options. ### Wide stores and annulled slots -[no_annulled_wide_stores.py](../tools/compiler/transformers/no_annulled_wide_stores.py) +[no_annulled_wide_stores.py](../../tools/compiler/transformers/no_annulled_wide_stores.py) adds `-frac2-no-annulled-wide-stores`. Thread search stops at a wide store that cannot occupy an ordinary slot; target-delay-list stealing refuses it for an annulled slot. Searching past the store selects a different instruction. @@ -1614,7 +1614,7 @@ read twice: output reload is lost: **`lui; mtc1` and no `ori`, wrong code from the default compiler** (see the known issue below). -With `-frac2-keep-float-pun-source` ([keep_float_pun_source.py](../tools/compiler/transformers/keep_float_pun_source.py)) +With `-frac2-keep-float-pun-source` ([keep_float_pun_source.py](../../tools/compiler/transformers/keep_float_pun_source.py)) cse_insn leaves that insn's source register alone, and combinable_i3pat refuses it as I3 unless f has a single use (find_single_use, which finds the use even where it returns 0). g crosses no call, so sched_analyze_1 keeps its set after the previous call; f, with one SUBREG store against @@ -1692,8 +1692,8 @@ The expanded retail survey found 915 immediate dependencies: 897 delayed, 18 not the original 598 sites (587 delayed) are a subset. Cleared assembler history can occur within a function and is not a function-boundary detector. -[u4_mtc1_helper.py](../tools/compiler/transformers/u4_mtc1_helper.py) and -[restrict_mtc1_exemption.py](../tools/compiler/transformers/restrict_mtc1_exemption.py) +[u4_mtc1_helper.py](../../tools/compiler/transformers/u4_mtc1_helper.py) and +[restrict_mtc1_exemption.py](../../tools/compiler/transformers/restrict_mtc1_exemption.py) retain two measured cleared-history exemptions: a0 to f0 before cvt.s.w f0,f0, and zero to f0 before c.lt.s f12,f0. Eight first-transfer witnesses split four requiring a nop and four not. A distance-two producer rule failed `func_002A7878`. @@ -1730,7 +1730,7 @@ The private 89-case fixture history does not qualify every transfer in the game. Mfc1 is interlocked on R5900: the measured boot text had 88 immediate readers in 47 functions; its only extra-nop example used handwritten VU code. -[r5900_mfc1_interlock.py](../tools/compiler/transformers/r5900_mfc1_interlock.py) +[r5900_mfc1_interlock.py](../../tools/compiler/transformers/r5900_mfc1_interlock.py) removes the MIPS-I coprocessor-load delay in append_insn for mfc1 only. Cfc2 and label padding are not waived. `func_002ED4B8` and `func_00290680` match at 28 places. @@ -1744,14 +1744,14 @@ host-dependent. An assembler built on 3 November 2026 emitted `lui; addu; lbu`; from the same sources on and after 8 November copied backwards and left the second word in the first slot (`lbu $2,0($2); addu; lbu`), so `func_00117278` (strtol, the -O0 unit that uses this form) and `func_00132498` stopped matching in `tools/build.py boot --strict` on those hosts. -[convert_frag_memmove.py](../tools/compiler/transformers/convert_frag_memmove.py) makes the -copy a `memmove`; [the test](../tests/test_convert_frag_memmove.py) assembles the two forms. +[convert_frag_memmove.py](../../tools/compiler/transformers/convert_frag_memmove.py) makes the +copy a `memmove`; [the test](../../tests/test_convert_frag_memmove.py) assembles the two forms. The 4/8-byte pairs overlap the same way; patch 0020 already rebuilds their first word, and that rebuild is kept unchanged. ### Labels on hazard nops -[hazard_labels.py](../tools/compiler/transformers/hazard_labels.py) adds +[hazard_labels.py](../../tools/compiler/transformers/hazard_labels.py) adds `-frac2-hazard-labels`: cc1 emits `.set rac2_hazard_labels`, and gas retains labels on the needed nop rather than moving them behind it, including the noreorder flush. This connects a per-file option to the assembler without patching output. @@ -1770,16 +1770,16 @@ not the shipping assembler's identity. Witnesses: `func_L18_004308F0` (1,384/1), Compose these three mechanisms; none replaces the others: -1. [neutralise_div_dslot.py](../tools/compiler/transformers/neutralise_div_dslot.py) +1. [neutralise_div_dslot.py](../../tools/compiler/transformers/neutralise_div_dslot.py) keeps single-precision division/root operations out of compiler-selected delay slots. Retail had 0 of 19,806 div.s in slots, but 61 integer HI/LO divisions. Sqrt.s/rsqrt.s are extrapolated: neither occurred in that retail census and GNU as does not assemble rsqrt.s. -2. [pad_div_erratum_nops.py](../tools/compiler/transformers/pad_div_erratum_nops.py) +2. [pad_div_erratum_nops.py](../../tools/compiler/transformers/pad_div_erratum_nops.py) models a measured SN 3.01 two-slot incoming floor after code labels for div.s, sqrt.s and rsqrt.s. Needed hazard nops count toward it; labels stay on padding. Sync.p imposes one nop, unlike sync/sync.l. -3. [pad_div_erratum_branch.py](../tools/compiler/transformers/pad_div_erratum_branch.py) +3. [pad_div_erratum_branch.py](../../tools/compiler/transformers/pad_div_erratum_branch.py) extends the floor to branch groups: bc1t/ld/div.s gains two nops; one intervening instruction gains one, two gain none. Conditional/likely branches and b trigger it; j/jal/jr/jalr do not. Integer division is unaffected. It consumes the label @@ -1803,7 +1803,7 @@ build changed one matched function, func_002C3280, whose C had the second call a the division in separate statements; retail has no padding there, so the division now sits in the call's statement (no line label between), which matches with and without the change. A division emitted inside a noreorder region still gets no new -floor. [test_div_label_after_call.py](../tests/test_div_label_after_call.py) covers +floor. [test_div_label_after_call.py](../../tests/test_div_label_after_call.py) covers the label, no-label and noreorder cases. Across a non-PIC GP relaxation fragment, the label-distance calculation follows @@ -1813,7 +1813,7 @@ instructions. Thus a labelled GP load followed by `div.s` receives one nop, just like an explicit GP operand; an absolute two-word expansion receives none. Unknown forward symbols, alignment and unsupported fragment kinds retain the previous fallback. This is a bounded distance correction, not general relaxation -or SDK equivalence. [test_div_gp_fragments.py](../tests/test_div_gp_fragments.py) +or SDK equivalence. [test_div_gp_fragments.py](../../tests/test_div_gp_fragments.py) checks authored GP, absolute, two-load, `.word` and unknown-symbol controls. The previous assembler fails only the one-load GP macro control. The corresponding C witnesses are `func_L05_00419CC8` and `func_L00_00424F98`, at four and 27 @@ -1903,7 +1903,7 @@ form, and no compiled game code in the boot or any level loads a `.lit8` value ( `lui $at`/`ld` pair reads the scratchpad in hand-written blocks). The SDK's objects pool: `func_0011C090` (libc's exponent formatter, SDK profile) loads 0.1 and 10.0 from `.lit8` and matches only that way. So the game code's form is the assembler's default: -[inline_li_d.py](../tools/compiler/transformers/inline_li_d.py) takes the whole value for every +[inline_li_d.py](../../tools/compiler/transformers/inline_li_d.py) takes the whole value for every `li.d` into a GPR on a 64-bit ISA (the `F` operand of M_LI_D) and builds it with load_register; FPR forms and 32-bit ISAs are unchanged. `--r5900-pool-li-d` restores the archive's pooling, and `tools/build.py` (`split_flags`) passes it to every unit of the SDK profile (`sdk` flags in @@ -1911,7 +1911,7 @@ FPR forms and 32-bit ISAs are unchanged. `--r5900-pool-li-d` restores the archiv unit needs a switch. `--r5900-inline-li-d` states the default explicitly. An earlier default-on trial without the SDK opt-out broke `func_0011C090`. With the default, no matched function in any release compiled differently (`build.py all --strict`: us 28/28; pal and us2 only their -known failures). [tests/test_inline_li_d.py](../tests/test_inline_li_d.py) checks the +known failures). [tests/test_inline_li_d.py](../../tests/test_inline_li_d.py) checks the transformer, both profiles' words, and that only SDK units get the opt-out. Two sign-bit loads need a zero-extending seed. Patch 0059 exposes per-object @@ -1984,7 +1984,7 @@ table+16. That address is past the 4-float table, so it is a separate retail object; the unit now names it `D_L00_001B0A80` (owner ruling 8 November 2026) and the forced-on sweep changes nothing. With every access through the SDATA alias, func_00294F10's coefficient loops and func_L01_003801B8's column -pointers take retail's form. [tests/test_la_addend.py](../tests/test_la_addend.py) +pointers take retail's form. [tests/test_la_addend.py](../../tests/test_la_addend.py) covers both profiles, unchanged loads/stores and invalid options. ## Reading output and identifying limits @@ -2017,7 +2017,7 @@ addresses within an object, cross-object borrowing, overlapping claims, partial spans, and relocated/mixed pools are rejected. No generated section is rewritten; data credit is not inferred from placing it. Unsupported `.sdata` remains unplaceable. See -[switch-table qualification and limits](MATCHING-BLOCKERS.md#blocker-06). +[switch-table qualification and limits](../contributing/matching-blockers.md#blocker-06). A legacy table already in retail can use its measured symbol and computed goto; a static label-address array keeps legitimate arms in discarded data. That array @@ -2039,33 +2039,33 @@ oracle, and a same-size draft screen is not a match or current progress. | Mechanism | Maintained controls | | --- | --- | -| Zero folding/clear scheduling | [test_zero_ti_store.py](../tests/test_zero_ti_store.py) | -| CSE edges/block order | [test_gcse_unsplit_edges.py](../tests/test_gcse_unsplit_edges.py), [test_keep_block_order.py](../tests/test_keep_block_order.py) | -| gcse load kills | [test_gcse_store_kills_loads.py](../tests/test_gcse_store_kills_loads.py) | -| Division-guard slot barrier | [test_trap_delay_barrier.py](../tests/test_trap_delay_barrier.py), [test_trap_redundant_barrier.py](../tests/test_trap_redundant_barrier.py) | -| SDK assembler slot swaps | [test_swap_branch_slots.py](../tests/test_swap_branch_slots.py) | -| Division label floor after a call | [test_div_label_after_call.py](../tests/test_div_label_after_call.py) | -| No giv recombination | [test_no_recombine_givs.py](../tests/test_no_recombine_givs.py) | -| Copies and RTL contract | [test_qcopy.py](../tests/test_qcopy.py), [test_qcopy_rtl.py](../tests/test_qcopy_rtl.py) | -| Copy scheduling | [test_qcopy_barrier.py](../tests/test_qcopy_barrier.py) | -| Annulled wide stores | [test_no_annulled_wide_stores.py](../tests/test_no_annulled_wide_stores.py) | -| Stack por form | [test_zero_ti_por.py](../tests/test_zero_ti_por.py) | -| TImode reload count | [test_ti_one_gpr.py](../tests/test_ti_one_gpr.py) | -| Inheritance into non-spill registers | [test_reload_reg_used_guard.py](../tests/test_reload_reg_used_guard.py) | -| Null-pointer-check pass | [test_no_null_pointer_checks.py](../tests/test_no_null_pointer_checks.py) | -| Width-ordered quad saves | [test_narrow_saves_below.py](../tests/test_narrow_saves_below.py) | -| 16-byte slots for TImode GPR saves | [test_wide_saves_below.py](../tests/test_wide_saves_below.py), [wide_saves_below.c](../tests/fixtures/wide_saves_below.c) | -| Unflattened giv start | [test_mult_add_no_flatten.py](../tests/test_mult_add_no_flatten.py), [mult_add_no_flatten.c](../tests/fixtures/mult_add_no_flatten.c) | -| Static-chain alias seed | [test_no_static_chain_base.py](../tests/test_no_static_chain_base.py), [no_static_chain_base.c](../tests/fixtures/no_static_chain_base.c) | -| Integer words stored into floats | [test_keep_float_pun_source.py](../tests/test_keep_float_pun_source.py), [keep_float_pun_source.c](../tests/fixtures/keep_float_pun_source.c) | -| Extra biv increments | [test_no_extra_increment_givs.py](../tests/test_no_extra_increment_givs.py), [no_extra_increment_givs.c](../tests/fixtures/no_extra_increment_givs.c) | -| Sticky volatile sets (default) | [test_sticky_volatile_sets.py](../tests/test_sticky_volatile_sets.py), [sticky_volatile_sets.c](../tests/fixtures/sticky_volatile_sets.c) | -| Masks/sign seed | [test_dli_masks.py](../tests/test_dli_masks.py) | -| Inline li.d into GPRs | [test_inline_li_d.py](../tests/test_inline_li_d.py) | -| Hazard labels | [test_hazard_labels.py](../tests/test_hazard_labels.py), [hazard_labels.s](../tests/fixtures/hazard_labels.s) | -| Block-move loop padding | [test_block_move_loop.py](../tests/test_block_move_loop.py) | -| Quadword block moves | [test_block_move_quad.py](../tests/test_block_move_quad.py) | -| Asm-word loop padding | [test_count_asm_words.py](../tests/test_count_asm_words.py) | +| Zero folding/clear scheduling | [test_zero_ti_store.py](../../tests/test_zero_ti_store.py) | +| CSE edges/block order | [test_gcse_unsplit_edges.py](../../tests/test_gcse_unsplit_edges.py), [test_keep_block_order.py](../../tests/test_keep_block_order.py) | +| gcse load kills | [test_gcse_store_kills_loads.py](../../tests/test_gcse_store_kills_loads.py) | +| Division-guard slot barrier | [test_trap_delay_barrier.py](../../tests/test_trap_delay_barrier.py), [test_trap_redundant_barrier.py](../../tests/test_trap_redundant_barrier.py) | +| SDK assembler slot swaps | [test_swap_branch_slots.py](../../tests/test_swap_branch_slots.py) | +| Division label floor after a call | [test_div_label_after_call.py](../../tests/test_div_label_after_call.py) | +| No giv recombination | [test_no_recombine_givs.py](../../tests/test_no_recombine_givs.py) | +| Copies and RTL contract | [test_qcopy.py](../../tests/test_qcopy.py), [test_qcopy_rtl.py](../../tests/test_qcopy_rtl.py) | +| Copy scheduling | [test_qcopy_barrier.py](../../tests/test_qcopy_barrier.py) | +| Annulled wide stores | [test_no_annulled_wide_stores.py](../../tests/test_no_annulled_wide_stores.py) | +| Stack por form | [test_zero_ti_por.py](../../tests/test_zero_ti_por.py) | +| TImode reload count | [test_ti_one_gpr.py](../../tests/test_ti_one_gpr.py) | +| Inheritance into non-spill registers | [test_reload_reg_used_guard.py](../../tests/test_reload_reg_used_guard.py) | +| Null-pointer-check pass | [test_no_null_pointer_checks.py](../../tests/test_no_null_pointer_checks.py) | +| Width-ordered quad saves | [test_narrow_saves_below.py](../../tests/test_narrow_saves_below.py) | +| 16-byte slots for TImode GPR saves | [test_wide_saves_below.py](../../tests/test_wide_saves_below.py), [wide_saves_below.c](../../tests/fixtures/wide_saves_below.c) | +| Unflattened giv start | [test_mult_add_no_flatten.py](../../tests/test_mult_add_no_flatten.py), [mult_add_no_flatten.c](../../tests/fixtures/mult_add_no_flatten.c) | +| Static-chain alias seed | [test_no_static_chain_base.py](../../tests/test_no_static_chain_base.py), [no_static_chain_base.c](../../tests/fixtures/no_static_chain_base.c) | +| Integer words stored into floats | [test_keep_float_pun_source.py](../../tests/test_keep_float_pun_source.py), [keep_float_pun_source.c](../../tests/fixtures/keep_float_pun_source.c) | +| Extra biv increments | [test_no_extra_increment_givs.py](../../tests/test_no_extra_increment_givs.py), [no_extra_increment_givs.c](../../tests/fixtures/no_extra_increment_givs.c) | +| Sticky volatile sets (default) | [test_sticky_volatile_sets.py](../../tests/test_sticky_volatile_sets.py), [sticky_volatile_sets.c](../../tests/fixtures/sticky_volatile_sets.c) | +| Masks/sign seed | [test_dli_masks.py](../../tests/test_dli_masks.py) | +| Inline li.d into GPRs | [test_inline_li_d.py](../../tests/test_inline_li_d.py) | +| Hazard labels | [test_hazard_labels.py](../../tests/test_hazard_labels.py), [hazard_labels.s](../../tests/fixtures/hazard_labels.s) | +| Block-move loop padding | [test_block_move_loop.py](../../tests/test_block_move_loop.py) | +| Quadword block moves | [test_block_move_quad.py](../../tests/test_block_move_quad.py) | +| Asm-word loop padding | [test_count_asm_words.py](../../tests/test_count_asm_words.py) | Copy controls cover volatile/unaligned/scalar/quadword memory, calls, integer/FP branches, loop placement and complete pairs outside slots. Clear controls cover @@ -2094,6 +2094,6 @@ neutrality does not prove that enabling an option in another unit is safe. Final acceptance is tools/build.py with --strict for every affected program: all C placements accepted and every loaded byte, segment property and entry point equal to the matching private reference. Check every applicable release under -VERSIONS.md. Progress consistency uses --check; regeneration is separate. +versions.md. Progress consistency uses --check; regeneration is separate. Keep unresolved attempts and reopening evidence in nonmatching/ and the shared blocker document, rather than appending session diaries here. diff --git a/docs/LEVELS.md b/docs/build/levels.md similarity index 100% rename from docs/LEVELS.md rename to docs/build/levels.md diff --git a/docs/VERSIONS.md b/docs/build/versions.md similarity index 99% rename from docs/VERSIONS.md rename to docs/build/versions.md index 33495c213..f143b989a 100644 --- a/docs/VERSIONS.md +++ b/docs/build/versions.md @@ -30,7 +30,7 @@ Every tool takes `--version us|pal|us2` (or `$RAC2_VERSION`): python3 tools/levels.py dump # and --version pal, --version us2 Each release has the same layout: the boot executable in the ISO's root and 27 level programs -in `G/LEVELn.WAD` (docs/LEVELS.md). `dump` writes them, checks that every level's dump +in `G/LEVELn.WAD` (docs/build/levels.md). `dump` writes them, checks that every level's dump converts back to the bytes the disc stores (its section records and sections, a `.bss` as zeros), and records every program's SHA-1, size and entry point in `config/programs.tsv`; `tools/levels.py check` compares the dumps with it, without the disc. diff --git a/docs/MATCHING-BLOCKERS.md b/docs/contributing/matching-blockers.md similarity index 99% rename from docs/MATCHING-BLOCKERS.md rename to docs/contributing/matching-blockers.md index 9ee2e0447..a211aeeaa 100644 --- a/docs/MATCHING-BLOCKERS.md +++ b/docs/contributing/matching-blockers.md @@ -45,7 +45,7 @@ This removes the partial color/flag spills in `func_002E7338`, `func_00337210`, `func_00316E08`, `func_L00_00418230` and `func_L03_002ECF08`; ordinary mode-store ordering and independent switch-case placement supply the remaining source differences. No register or opcode is -patched. See [the mechanism and controls](COMPILER-NOTES.md#inheritance-into-a-register-the-insns-pseudos-hold). +patched. See [the mechanism and controls](../build/compiler-notes.md#inheritance-into-a-register-the-insns-pseudos-hold). Two existing counters had separate accounting errors. The post-delay-slot loop pass must include nops inserted before an already-visited branch: otherwise @@ -87,7 +87,7 @@ placement and the complete 84-program strict build. Only ordinary unannounced external SI loads/addresses count as two words; prior metadata, SDATA aliases, PIC, buffering and delay-slot sequences remain excluded. Seven authored controls cover the boundary; the old compiler fails the three positive cases. -See [the counter model](COMPILER-NOTES.md#short-loop-padding). +See [the counter model](../build/compiler-notes.md#short-loop-padding). The retained counter-display draft `func_0034DB98` is no longer registered as functional native C. Its wide font read and sign-preserving style inputs are @@ -161,7 +161,7 @@ coordinate from the later owner pointer; its canonical identity is absent in the other releases. The two setup hooks accept the owner explicitly supplied by retail. Each hook passes 28 placements in every release, and their complete compiled object is unchanged. All 84 loaded images and the 46 new C placements -are verified, with PAL rebuilt after the latest carry update. See [source interfaces](COMPILER-NOTES.md#source-methods-shared-with-rac1). +are verified, with PAL rebuilt after the latest carry update. See [source interfaces](../build/compiler-notes.md#source-methods-shared-with-rac1). One-pass groups are not a general remedy. In `func_L01_0031B300`, grouping the actual height read restores the register map but adds an alignment nop at @@ -203,7 +203,7 @@ pointer left in `$v0` at return. A scan of all 505 retail calls in the 26 USA pr that carry it finds no path that reads `$v0` before overwriting it. Every `void *` caller declaration in `src/` (35 files, all releases) now says `void`; each file's compiled object is unchanged. As with `func_002AE730` -([prototypes](COMPILER-NOTES.md#prototypes-lifetimes-and-varargs)), definition and +([prototypes](../build/compiler-notes.md#prototypes-lifetimes-and-varargs)), definition and callers were corrected together. With the true contract `func_L11_00420C88` matches (1/1, 15,832 bytes) in @@ -521,8 +521,8 @@ older claims that the following mechanisms are universally impossible are supers | `_fpadd_parts` and `dpmul` | [#265](https://github.com/OpenRAC/rac2-gc-decomp/pull/265) and [#267](https://github.com/OpenRAC/rac2-gc-decomp/pull/267) are merged SDK-profile matches. | | `game/00308600` follow-up | [#287](https://github.com/OpenRAC/rac2-gc-decomp/pull/287) adds `func_0030E700`, 424 bytes at 28/28 places in each USA release, with 28 strict programs per release and 139 tests passing. `func_00308600` remains retained at 356/360 bytes, 0/28 USA v1.01 places. | | Existing compiler profiles, argument ABIs and shared-data identities | Qualification on 23 October 2026 adds 39 primary shared functions (9,208 distinct bytes): ten in [#300](https://github.com/OpenRAC/rac2-decomp/pull/300), then 29 and a separate 148-byte PAL timer in [#301](https://github.com/OpenRAC/rac2-decomp/pull/301). The combined source tree passes 28 strict programs in each release (84 total), 143 tests and all 2,781 new-function placements. Three existing ABI controls add 171 placement checks. No report was regenerated. | -| Truthful external sizes and shared update helpers | [Source qualification](https://github.com/OpenRAC/rac2-decomp/commit/609753de4e58218805f4ff0983f36bdb15648d9e) adds 18 primary C functions (9,236 distinct bytes): 168 USA v1.01, 140 USA v2.00 and 77 PAL placements. All 84 strict programs, 385 placement checks and 143 tests pass. Three dedicated units use measured 16-byte vectors or a 15-byte label with standard compiler/assembler `-G16 -Wa,-G16`, preserving every instruction and relocation. See [the small-data notes](COMPILER-NOTES.md#symbols-addresses-and-small-data). Four obsolete no-C screens are retired; the remaining reconstructions stay unlinked. No report was regenerated. | -| GP addresses of objects whose full extent is unknown | The existing `SDATA(symbol)` macro now has the explicit alias-metadata support used by current RAC1. Four callers (`func_L08_003CFE68`, `func_L12_00421110`, `func_L12_00429868`, `func_L09_003B6550`) retain their local matches with incomplete arrays, replacing false 4-/8-byte declarations. The alias names the same canonical storage; its assembler metadata does not define a C object size. Independent compiled/link controls cover full 14-/28-byte reads, ordinary-code identity, signed GP limits and same-prefix decoys. The separate `func_L18_003FB620` repair uses its measured 24-byte settings type with `-G32 -Wa,-G32`. These are source-contract repairs with no additional matching credit; see [the address-view method](COMPILER-NOTES.md#symbols-addresses-and-small-data). | +| Truthful external sizes and shared update helpers | [Source qualification](https://github.com/OpenRAC/rac2-decomp/commit/609753de4e58218805f4ff0983f36bdb15648d9e) adds 18 primary C functions (9,236 distinct bytes): 168 USA v1.01, 140 USA v2.00 and 77 PAL placements. All 84 strict programs, 385 placement checks and 143 tests pass. Three dedicated units use measured 16-byte vectors or a 15-byte label with standard compiler/assembler `-G16 -Wa,-G16`, preserving every instruction and relocation. See [the small-data notes](../build/compiler-notes.md#symbols-addresses-and-small-data). Four obsolete no-C screens are retired; the remaining reconstructions stay unlinked. No report was regenerated. | +| GP addresses of objects whose full extent is unknown | The existing `SDATA(symbol)` macro now has the explicit alias-metadata support used by current RAC1. Four callers (`func_L08_003CFE68`, `func_L12_00421110`, `func_L12_00429868`, `func_L09_003B6550`) retain their local matches with incomplete arrays, replacing false 4-/8-byte declarations. The alias names the same canonical storage; its assembler metadata does not define a C object size. Independent compiled/link controls cover full 14-/28-byte reads, ordinary-code identity, signed GP limits and same-prefix decoys. The separate `func_L18_003FB620` repair uses its measured 24-byte settings type with `-G32 -Wa,-G32`. These are source-contract repairs with no additional matching credit; see [the address-view method](../build/compiler-notes.md#symbols-addresses-and-small-data). | These summaries do not award additional matching credit or replace the build. Check current source before relying on any older count, profile or pending-PR label in the imported notes. @@ -922,7 +922,7 @@ The actual lower/upper state stores and a typed `func_002A8D70` argument interleaving close the remaining differences. It preserves object, bone, mode, first, second and third in A0-A3/T0-T1 and x/y/z in F12-F14, with no stack arguments. This follows the current RAC1 call-shape practice in -COMPILER-NOTES.md; it does not fabricate a prototype or reorder a bank. +compiler-notes.md; it does not fabricate a prototype or reorder a bank. Six targeted trials required no compiler change. Both USA placements pass, PAL has no corresponding catalogue identity, and all 84 strict programs pass with the integrated source set. Different source declarations still @@ -1009,7 +1009,7 @@ The wave 8 allocator investigation remains useful: global.c prioritizes approxim **28 October 2026 requalification:** the retained 364-byte `-O2 -G8 -ffunction-sections -frac2-qcopy` candidate still matches 0/28 USA v1.01 placements; the first difference is at +0x20 in head register allocation. Its later copy and table-byte observations remain as recorded. **Large state dispatchers (23 October 2026).** The retained -[`func_L22_002BF1B0`](../nonmatching/levels/func_L22_002BF1B0.c) now contains the +[`func_L22_002BF1B0`](../../nonmatching/levels/func_L22_002BF1B0.c) now contains the complete Dobbo Orbit state body: 147 selector values, 63 retail destinations and all 135 distinct direct callees. Its 37,268-byte extent has one normal epilogue and no external non-call branches; its size does not justify splitting the @@ -1174,7 +1174,7 @@ aligned differences, 564/708 bytes in aligned agreement. `__attribute__((sda))` in a `-G0` file on an **absolute** symbol stops the hoist: `lui %hi ; lw %lo` stay inside the loop as retail has them (side effect: the prologue's index/base register pair swaps). And there is a sub-case of its own: an **absolute global addressed in a loop that contains calls** - cc1 hoists the address into a callee-saved register (`lui $s1,%hi`, frame 0x20, two extra saves) where retail re-materialises `lui $v1,%hi ; lw $v1,%lo($v1)` at every iteration with one register (`func_L11_00440868`, one instruction off; `func_L11_004411C8`, 8 bytes off - bodies otherwise identical instruction for instruction). In those tested units the attribute produced an unwanted `$gp` form; qualify the actual unit instead of generalizing the declaration rule. -**28 October 2026, `func_L11_004411C8` closed (DrLegitamate, USA v1.01, 1/1 place, `tools/build.py level 11 --strict` OK):** the loop-with-calls sub-case above yields to the incomplete-array `sda` alias from COMPILER-NOTES.md. Declaring the class table base as `extern u8 *D_L11_001B359C_base[] __asm__("D_L11_001B359C") __attribute__((sda));` and reading `D_L11_001B359C_base[0]` inside the loop gives retail's per-iteration `lui $v1,%hi ; lw $v1,%lo($v1)` with no hoist and no extra saves in the `-G0` unit; the head then needs the list in its own variable (`list = table[index]; if (list == 0) return; p = list;`). The same declaration is the next experiment for `func_L11_00440868`. +**28 October 2026, `func_L11_004411C8` closed (DrLegitamate, USA v1.01, 1/1 place, `tools/build.py level 11 --strict` OK):** the loop-with-calls sub-case above yields to the incomplete-array `sda` alias from compiler-notes.md. Declaring the class table base as `extern u8 *D_L11_001B359C_base[] __asm__("D_L11_001B359C") __attribute__((sda));` and reading `D_L11_001B359C_base[0]` inside the loop gives retail's per-iteration `lui $v1,%hi ; lw $v1,%lo($v1)` with no hoist and no extra saves in the `-G0` unit; the head then needs the list in its own variable (`list = table[index]; if (list == 0) return; p = list;`). The same declaration is the next experiment for `func_L11_00440868`. **Update, 3 November 2026 (pending review):** now matched. `func_L11_00440868` is C in `src/levels/11_joba/L11_0043E1A0.c` (`-O2 -G0 -ffunction-sections`), placed at 1 US, 1 US2 places (PAL catalogue has no function of this name). The measurements above are kept as history. @@ -1387,7 +1387,7 @@ pools, `.sdata` and unsupported assembly data directives remain rejected. **7 November 2026, local match (branch `fp-liveness-1107`, not merged):** the cause was the compiler, not the source. The EE tree's flow.c keeps `$fp` live after reload even without a frame pointer, so reorg annuls a branch slot that sets it. `-frac2-frame-pointer-liveness` (GCC 2.95's -2 June 1999 flow.c/haifa-sched.c change, docs/COMPILER-NOTES.md) gives `bne`: +2 June 1999 flow.c/haifa-sched.c change, docs/build/compiler-notes.md) gives `bne`: `func_L07_0040EC08` (1 place in each USA release) and `func_L04_00414698` (2 places in each) match from their retained drafts unchanged, each a unit of its own with the option. Neither is in the PAL catalogue. A `bnel` whose slot sets `$fp` from the target, in a function without a frame @@ -1453,7 +1453,7 @@ using `$a1`, then stores `$v1` instead of `$a1`. A dedicated `-G8` replay is for these attempts; the C remains in `nonmatching/game/func_002CC528.c`. **3 November 2026, resolved:** the catalogue row included an 8-byte filler prefix (`addiu $sp,$sp,0x60; nop`); with the entry corrected to `0x002CC530` (32 bytes, see -docs/LEVELS.md) the body is C in `src/game/002CC528-g8.c` under `-G8 -mno-split-addresses`, +docs/build/levels.md) the body is C in `src/game/002CC528-g8.c` under `-G8 -mno-split-addresses`, 28 of 28 places. Two documented names may express different addressing forms of one real global. They must derive the same true identity; malformed names, invented addresses or aliases hiding inconsistent bindings are not remedies. PR #244's review corrected such naming defects. Use ref_LNN_XXXXXXXX_kk where a target's identity genuinely varies by placement, not as a substitute for a known correct data name. @@ -1688,7 +1688,7 @@ but remained 1060/1024 bytes, 0/2 placements in each release. This supersedes th historical MMI-only screen without claiming a whole-body match. **Update, 28 October 2026 (pending review):** `func_L11_0046BD18` is C in -[L11_0046BD18-g8.c](../src/levels/shared/L11_0046BD18-g8.c) +[L11_0046BD18-g8.c](../../src/levels/shared/L11_0046BD18-g8.c) (`-O2 -G8 -ffunction-sections -g`), at 1024 bytes and placed 2/2 in each of US, PAL and US2 ([PR #348](https://github.com/OpenRAC/rac2-decomp/pull/348)); the near-miss file is removed. `tools/build.py all --strict` passes in all three @@ -1876,7 +1876,7 @@ from bits and read twice. Retail sets the word in `$v0` after the previous call, into a saved FPR and `mov.s`es it per read; the same kind of word read once is `li.s`. None of the forms tried in stock cc1 (listed in nonmatching/game/func_0034B2C8.c's header) gives that: cse2 or combine loses the integer register. The spelling is the write-side pun, -`f32 f; *(s32 *)&f = K;`, with the opt-in `-frac2-keep-float-pun-source` (docs/COMPILER-NOTES.md, +`f32 f; *(s32 *)&f = K;`, with the opt-in `-frac2-keep-float-pun-source` (docs/build/compiler-notes.md, "Integer words stored into floats"; no GCC release has this rule, and the switch was designed from retail's form, so these matches do not show its origin). Results: func_00349D58 28 of 28 places in us, pal and us2 in `src/game/00349D58.c`; func_0034B2C8 stays a near miss, 6,048 of 6,048 bytes, 12 @@ -2050,7 +2050,7 @@ the change `-frac2-sticky-volatile-refs` takes the other half of) is now the com the EE hands it its address and invalidates the block's cache lines before each poll read (src/core/00131DE8.c names the functions). The one matched function it changed, `func_00132498`, had stored its callback argument through a volatile no second context writes; as an sda array element -(`D_001A74D0_1[1]`) it matches with either rule (docs/COMPILER-NOTES.md, "reorg: a volatile store +(`D_001A74D0_1[1]`) it matches with either rule (docs/build/compiler-notes.md, "reorg: a volatile store stays marked"). `func_L00_002A3B88` also needed the true `void` type of `func_002C9C20`, which has no return statement and whose result no caller reads (C or assembly, in any release); the `int` return's unused `$v0` swapped `$v0`/`$v1` at 0x002A3D7C. Signature: retail's slot is a nop right @@ -2185,7 +2185,7 @@ if-chain is 44/16. No match is established. the `if` so the scheduler can fill the slot itself. Otherwise retain the exact near miss/profile and a bounded stopping reason; a few unchanged forms do not prove that no meaningful source/profile lever exists. A remaining setup-order control is -[func_L00_003A8238](../nonmatching/levels/func_L00_003A8238.c): 720/720 bytes, +[func_L00_003A8238](../../nonmatching/levels/func_L00_003A8238.c): 720/720 bytes, 0/27 placements in each USA release, with no carried PAL name. Only the two non-relocation instructions materializing `-1` and `7` exchange positions. Typed effect records and the measured floating/integer call interface resolve @@ -2503,7 +2503,7 @@ right before the slot-24 table store. Cause: alias.c seeds `$v0`, the static-cha a base, loses it at `$v0 = lo_sum ($a1, D_001F2340)`, and sched2 then orders every store through `$v0` after the saves. No C form changes this. The opt-in `-frac2-no-static-chain-base` (GCC 2.95.3's alias seeds; in nested functions it also gives the chain no base, see -docs/COMPILER-NOTES.md) together with `-frac2-frame-pointer-liveness` matches the ascending-slot +docs/build/compiler-notes.md) together with `-frac2-frame-pointer-liveness` matches the ascending-slot draft in `src/game/002BA0E0.c`: strict linked builds of us, pal and us2 place it at 28 of 28 places each; with either switch alone it stays 0 of 28. The signature is saves at the top ahead of stores through a pointer that `$v0` holds after reload. The argument registers keep their @@ -2913,7 +2913,7 @@ particular label spelling does not unblock the loop layout. canonical identity for this body. Both intended C placements also pass the [strict linked qualification](https://github.com/OpenRAC/rac2-decomp/commit/96a2e817a258351edbd25db3583ab416e8fdcbcf). See the source-method explanation in - [COMPILER-NOTES.md](COMPILER-NOTES.md#source-methods-shared-with-rac1). + [compiler-notes.md](../build/compiler-notes.md#source-methods-shared-with-rac1). - **The dialect is C89:** `long` and `long long` are 64-bit, `int` and pointers are 32-bit, and there is no declaration after a statement. - **The patched gas expands `move $rd,$zero` into `daddu $rd,$zero,$zero` and `li $v0,1` into @@ -3105,8 +3105,8 @@ Historical examples, to requalify in the final unit: - A rule table loop with an unreduced `mult` at an aligned label is a loop that loop.c skipped (func_L05_00444840): `for` with a `break` gets the same tail, but jump1 copies the test and the found arm ahead of the loop, which gives "multiple entry points". **6 November 2026, USA v1.01, check.py only:** grouping the real class read inside an infinite indexed scan stops that peeling (retained 732/736 bytes, 0/4 places, `-O2 -G8 -ffunction-sections -fno-rerun-loop-opt`). The first scan still hoists its table base/base+4 and the second becomes a pointer walk; allocation also differs. A macro-address table gives 740/712-byte alternatives with worse loop shapes, and a volatile indexed read gives 836. The next experiment needs both indexed scans and base rematerialization while preserving outer-loop invariant motion; the retained C is in `nonmatching/levels/func_L05_00444840.c`. **6 November 2026, round 11:** the current retained `-O2 -G8 -ffunction-sections -freduce-all-givs` source is 736/736 bytes, USA v1.01 0/4 places, with only the second projection call's a1/a2 address-load order differing (+0x124/+0x128). Inline coordinate-delta helpers, genuine pointer-role reuse and signed/unsigned coordinate storage do not improve it; storing coordinate differences adds stores. Aggregate projection APIs also fail to resolve the order. The matched callee requires x then y; swapping its physical pointer arguments is invalid. Seventy source variants checked; retained C unchanged, no linked build run. - **UI lifetime constraints, 6 November 2026 (USA v1.01, check.py only): matched.** `func_L08_003DB518` matched 2/2 (src/levels/shared/L08_003DB518-g8.c). The 0x80 frame came from the dead-test helper (as func_L11_004456A0) on the height rounding, which makes `one2` global (s5) and needs `half2` for f20. Reusing `fade` for the 1.0 constant, and `t` for the product and a scale, makes them global: no local tie, so the temporaries take f1 like retail. A one-pass group ends the rounding block. `-malign-loops=2` stops the pad of the label after it. - **Museum vehicle initialization, 6 November 2026 (USA v1.01, check.py only):** `func_L30_0040B9D8` now compiles to 2200/2200 bytes, 1/1 placements, in its own `-O2 -G8 -ffunction-sections` unit. The owner explicitly allowed the dead-test helper under the accepted `label_y` precedent. Extracting the retained diagnostic test into `initial_health` preserves the block through reload, retaining case 0's second `addiu v1,zero,1`; flow2 removes the dead test. This supersedes the retained note's 2196-byte result and its former admissibility restriction for this owner-approved case. No rounding conversion is present at this site: the helper returns the initial health unchanged. PAL and US2 report this identity in neither catalogue. Linked build and placement verification remain for the lead; no linked match is claimed. -- **Menu label rounding, 6 November 2026 (USA v1.01, retained near miss):** `func_L02_003C43F0` improves from seven to three real instruction differences, 752/752 bytes and 0/8 placements, using the exact accepted `label_y` with the text handle for the final 373.0f text and retaining the earlier owner-approved helpers. Own `-O2 -G8 -fno-expensive-optimizations -ffunction-sections -fno-builtin -fno-gcse` unit. The fourth sprite draw still schedules `f15 = f14` before the zero argument, leaving the color `ori` in the delay slot. All-label_y gives seven words (first two conversion results move from v1 to a1); zero-offset screen_coord, real statement groups, result lifetimes and supported scheduling controls do not close it. Next useful input is a real lifetime that avoids REG_DEAD on that f15 argument while preserving its f14 copy. See the [retained source](../nonmatching/levels/func_L02_003C43F0.c); no linked build or alternate-release match is claimed. -- **Timer rounding lifetime, 6 November 2026 (USA v1.01, retained near miss):** `func_L04_00417028` improves from 732/736 bytes to 736/736 with seven instruction words off in both Barlow and Joba. The shared HUD rounding helper works when its active local starts after formatting and each second-box result is immediately converted to float; a plain final cast in a nonempty group and reuse of the scale local for 118.0f preserve the remaining FP order. Own `-O2 -G0 -ffunction-sections -fno-gcse` unit, check.py only; alternate-release identities are absent. Remaining: second-box x uses v1 rather than v0 (two words), and the final width numerator ties to s0 rather than v1 (five words). A float-returning helper fixes the first register but moves its conversion ahead of y's multiply (nine words). Reusing necessary integer roles and grouping/reordering the actual divide did not improve seven; see the [retained source](../nonmatching/levels/func_L04_00417028.c). No linked match or build is claimed. +- **Menu label rounding, 6 November 2026 (USA v1.01, retained near miss):** `func_L02_003C43F0` improves from seven to three real instruction differences, 752/752 bytes and 0/8 placements, using the exact accepted `label_y` with the text handle for the final 373.0f text and retaining the earlier owner-approved helpers. Own `-O2 -G8 -fno-expensive-optimizations -ffunction-sections -fno-builtin -fno-gcse` unit. The fourth sprite draw still schedules `f15 = f14` before the zero argument, leaving the color `ori` in the delay slot. All-label_y gives seven words (first two conversion results move from v1 to a1); zero-offset screen_coord, real statement groups, result lifetimes and supported scheduling controls do not close it. Next useful input is a real lifetime that avoids REG_DEAD on that f15 argument while preserving its f14 copy. See the [retained source](../../nonmatching/levels/func_L02_003C43F0.c); no linked build or alternate-release match is claimed. +- **Timer rounding lifetime, 6 November 2026 (USA v1.01, retained near miss):** `func_L04_00417028` improves from 732/736 bytes to 736/736 with seven instruction words off in both Barlow and Joba. The shared HUD rounding helper works when its active local starts after formatting and each second-box result is immediately converted to float; a plain final cast in a nonempty group and reuse of the scale local for 118.0f preserve the remaining FP order. Own `-O2 -G0 -ffunction-sections -fno-gcse` unit, check.py only; alternate-release identities are absent. Remaining: second-box x uses v1 rather than v0 (two words), and the final width numerator ties to s0 rather than v1 (five words). A float-returning helper fixes the first register but moves its conversion ahead of y's multiply (nine words). Reusing necessary integer roles and grouping/reordering the actual divide did not improve seven; see the [retained source](../../nonmatching/levels/func_L04_00417028.c). No linked match or build is claimed. - A float kept off a free register: a pseudo set by local-alloc in a hard register gives its operands a preference that keeps other pseudos off that register (global.c regs_someone_prefers). In func_L12_0041BF88 a scratch float shared with a later loop made the pseudo global and freed $f1. - **A variable reused across blocks is a global pseudo (1 November 2026).** Local-alloc only takes pseudos of one block. A function-scope `f`, `n`, `id` or `history` reused for an unrelated later job goes to global-alloc after the block-local temporaries, which swaps whole register groups around it. A fresh block-local closed func_L22_003FC870 (f0/f1 group), func_L18_00428AE8 (a0/a2), and the history pointers of func_L08_003D4190. Check this before reading allocator dumps. @@ -3217,7 +3217,7 @@ selecting SN's measured seed for `0x00000000ffffffff`. This permits the complete native `func_00117CA0` (`log`, 1,556 bytes) and `func_001182B8` (`log10`, 424 bytes) with their actual 96/32-byte scalar pools. The initially matching `log10` text with a wrong overlapping pool was rejected. See -[the mechanism and controls](COMPILER-NOTES.md#positive-32-bit-masks-in-64-bit-registers). +[the mechanism and controls](../build/compiler-notes.md#positive-32-bit-masks-in-64-bit-registers). The same existing profile closes the caption pair `func_L00_002E3340` (476 bytes) and `func_L00_002E3520` (468 bytes). Their shared unit keeps the actual @@ -3937,7 +3937,7 @@ For #267, a same-source comparison of 781 configured units changed code only in including when paired with a second tint-update group (at least 75). Two-component UV aggregate assignments grow to 884-932 bytes. The retained C is unchanged; reopen on this store lifetime/dependency. -- [`func_L03_003C5668`](../nonmatching/levels/func_L03_003C5668.c): +- [`func_L03_003C5668`](../../nonmatching/levels/func_L03_003C5668.c): 644/644 bytes, 0/2 places with default game flags. Current reproduction corrects the older one-displaced-instruction claim: 12 prologue positions and 10 loop spill offsets differ. Tail alias, concrete output view and height local tie; @@ -4122,16 +4122,16 @@ attributed for provenance, not exclusive ownership. | **Vector copy as a scheduling barrier:** `func_002B5E48`, `func_002EE988`, `func_L23_00418650`, `func_L23_003C01F8`, `func_L23_00418DC0`, `func_L23_004256F0`, `func_L02_003DB158`, `func_L02_003D47B0`, `func_L26_003D4930`, `func_L12_00413CE0` (close: `func_002B50E0`, `func_L23_0041D0A0`); from today's first attempts at level 2, also `func_L02_00401BF8`, `func_L02_003F1A88`, `func_L02_003EE938`, `func_L02_003E8CE8` | Merged, #275 | Each is retail's size with `-frac2-qcopy` in a unit of its own. Each is exact when the copy is the volatile asm `lq $2,0(%1) ; sq $2,0(%0)`, in scratch copies only. The profile's grouped pair lets independent instructions cross it. About 50 single flags and many source orders, including `-frac2-zero-ti-barrier`, do not. #275 (merged) adds the opt-in `-frac2-qcopy-barrier`, the clear's block in `sched_analyze_insn` applied to the qcopy insn. Twelve witnesses landed with it. Twenty-three more near misses are exact with it as their records stand, in #283; two more follow. `func_002B50E0` is still 3 words off, a sched1 tie on argument moves. A 29 October aligned 48-byte character-buffer probe under `-O2 -G0 -ffunction-sections -frac2-qcopy -frac2-qcopy-barrier` preserves the same `+0x124` three-move difference, so byte-buffer storage does not change the soft-frame-pointer tie. | | **The barrier copy's link costs:** `func_L02_003F1A88` | Qualified with reviewed helper | The TI/barrier profile was five words off because its producer-link latency differed from the real macro dependency graph. The approved `qcopy` helper closes the 228-byte function under default G0, 1/1 in each release. No compiler link-cost patch is needed. Preserve the original distinction: modeling only part of an inline helper's optimizer effects need not reproduce the actual helper. | | **A TImode zero reloaded as a register pair:** `func_L02_004388A0` | Open, measured | 5 words off with the barrier: its 16-byte zero clear is reloaded as a group of two general registers, though the R5900 holds it in one. That makes `$v1` a spill register, so a hoisted sentinel -1 left unassigned gets `$v1` where retail has `$v0`. Without the clear (diagnostic only), every -1 is in `$v0`. | -| **A register saved and never used:** `func_L02_003FF6E8` | Open, measured | Retail saves `$s2` and never uses it; 21 retail game functions do so, and none of them is C. A plain C spelling reproduces the frame and register roles but keeps the value in `$s2`, 700 bytes against 696. Also `func_00326A10` (28 October 2026): retail saves `$s0` unused beside `$s1` = 0x8710; the C reaches 444/444 bytes with frame 0x90 and the constant in `$s0` ([record](../nonmatching/game/func_00326A10.c)). | +| **A register saved and never used:** `func_L02_003FF6E8` | Open, measured | Retail saves `$s2` and never uses it; 21 retail game functions do so, and none of them is C. A plain C spelling reproduces the frame and register roles but keeps the value in `$s2`, 700 bytes against 696. Also `func_00326A10` (28 October 2026): retail saves `$s0` unused beside `$s1` = 0x8710; the C reaches 444/444 bytes with frame 0x90 and the constant in `$s0` ([record](../../nonmatching/game/func_00326A10.c)). | | **Clear through a register address at a nonzero offset:** `func_002B5678` | Open | Retail has `addiu $v0,$s1,0x60 ; sq $zero,0($v0)`, the clear macro's register operand. After #258, C gives `por` + `sq $v0,0x60($s1)`. **Next:** a profile that materializes the address of a displaced const-0 TImode store. | | **A `por` clear at a zero displacement:** `func_L02_003DB200`, `func_L02_00423520`, `func_L23_00422798`, and with the copy barrier `func_L02_003DBA70`, `func_L23_00429DC8` | Proposed: PR #281 | These functions clear a stack vector with `por $v0,$zero,$zero` + `sq $v0,0($sp)`. Since #258, the fold still makes that `sq $zero,0($sp)`, because the address is register-only.

A finer retail count (one place per catalogue row, boot game code from 0x26EA00, a store counted when every path into it starts from the `por`) finds 183 `por`-fed stores at a zero displacement, in 153 functions. Every one of them is to `0($sp)`; none goes through another register. Against that, 396 `sq $zero,0(reg)` stores in 263 functions, of which 82 (58 functions) are also to `0($sp)`.

PR #281 adds the opt-in `-frac2-zero-ti-por`: with it, the fold leaves a store to `0($sp)` alone, and stores through any other register still fold. It is neutral by default (806 of 806 units). Forced on everywhere, it changes exactly the 4 units whose matched functions clear `0($sp)` with `sq $zero`. The five functions above are exact with it. `func_L23_003C16D8` stays out: three of its clears go through a computed stack address. | | **A cse block boundary that leaves no instruction:** `func_L02_003D26B0`; the five 632-byte twins `func_L11_00436D08`, `func_L11_00449C70`, `func_L11_0044C850`, `func_L11_004530E0`, `func_L11_004566C0`; `func_L02_003F1BA8`, `func_L02_0040BBD8`; `func_L07_00378898` | Open | Retail keeps a gcse (PRE) copy, for example `daddu $s5,$s4,$zero` of `e + 0x10`, that this cc1 makes only when cse1 and cse2 both see a label at that spot. Adding `\|\| x == 7 \|\| y == 9` to the test reproduces it, with extra code. An empty `do { } while (0)` reproduces it exactly but is rejected (#257, fc4a6c6). For `func_L07_00378898`, `-fno-cse-follow-jumps -fno-cse-skip-blocks` reproduces the block, as a probe only. **Next:** the original's construct at that spot, or a measured cse rule. | -| **History-copy address association:** `func_L20_0039F968` | Matched (7 November 2026) | **USA v1.01, 1/1 (us2 1/1 by check.py):** the 10 rows at +0xC8..+0xF8 were the CYGNUS LOCAL tree flattening (fold-const.c reduce_expression_tree_depth) inside expand_mult_add, which regrouped the source cursor start as `(last*16 + i*400) + (D-16)`; retail and FSF 2.95.2 (SN v2.73a) keep `last*16 + (i*400 + (D-16))`. The retained body matches with `-frac2-mult-add-no-flatten` in its own unit `src/levels/20_yeedil/L20_0039F968-g8.c`; see docs/COMPILER-NOTES.md. | -| **Meaningful nonempty one-pass groups:** `func_L02_003FB000`, `func_002CDA20`, `func_002CF2B8`, `func_002D53F0` | Strict linked matches; pending PR review | **4 November 2026:** current active RAC1 game C supplies the missing source-method precedent; see [source methods](COMPILER-NOTES.md#source-methods-shared-with-rac1). The actual last store gives Maktar 288 bytes, 1/1 places in each USA release (PAL identity absent). Actual formatter-call groups give the three text draws 140/136/140 bytes and 28/28 USA v1.01 places each; their canonical identities are absent in the other releases. The formatter style remains 64 bits; its return and the signed32 address getter follow their real definitions. Old narrower-style trials remain invalid. All 84 strict programs pass with zero C fallbacks, and all 92 placements of the five added functions are present in their carrying programs. This supersedes the earlier blanket hold on nonempty groups, not the ban on empty loops or fake dependencies. `func_0033A520` is still unresolved: its historical exact probe also assumes an unevidenced const global and fails both alternate releases.

**5 November 2026:** `func_L02_003FE768` (shared boss bar) matches the same way, with each real conversion of its rounded edge in a group (`t = v * scale + 0.5f; do { top = t; } while (0);`): 2 of 2 USA places, 0 of 2 without the groups. A scratch cc1 that made every float-to-int conversion a sched2 barrier broke 95 of 228 matched functions, so the barrier belongs to these sources. In `func_L11_00444400` the same groups give retail's boundary before each box call (4692/4684 bytes); its register allocation is still open. | +| **History-copy address association:** `func_L20_0039F968` | Matched (7 November 2026) | **USA v1.01, 1/1 (us2 1/1 by check.py):** the 10 rows at +0xC8..+0xF8 were the CYGNUS LOCAL tree flattening (fold-const.c reduce_expression_tree_depth) inside expand_mult_add, which regrouped the source cursor start as `(last*16 + i*400) + (D-16)`; retail and FSF 2.95.2 (SN v2.73a) keep `last*16 + (i*400 + (D-16))`. The retained body matches with `-frac2-mult-add-no-flatten` in its own unit `src/levels/20_yeedil/L20_0039F968-g8.c`; see docs/build/compiler-notes.md. | +| **Meaningful nonempty one-pass groups:** `func_L02_003FB000`, `func_002CDA20`, `func_002CF2B8`, `func_002D53F0` | Strict linked matches; pending PR review | **4 November 2026:** current active RAC1 game C supplies the missing source-method precedent; see [source methods](../build/compiler-notes.md#source-methods-shared-with-rac1). The actual last store gives Maktar 288 bytes, 1/1 places in each USA release (PAL identity absent). Actual formatter-call groups give the three text draws 140/136/140 bytes and 28/28 USA v1.01 places each; their canonical identities are absent in the other releases. The formatter style remains 64 bits; its return and the signed32 address getter follow their real definitions. Old narrower-style trials remain invalid. All 84 strict programs pass with zero C fallbacks, and all 92 placements of the five added functions are present in their carrying programs. This supersedes the earlier blanket hold on nonempty groups, not the ban on empty loops or fake dependencies. `func_0033A520` is still unresolved: its historical exact probe also assumes an unevidenced const global and fails both alternate releases.

**5 November 2026:** `func_L02_003FE768` (shared boss bar) matches the same way, with each real conversion of its rounded edge in a group (`t = v * scale + 0.5f; do { top = t; } while (0);`): 2 of 2 USA places, 0 of 2 without the groups. A scratch cc1 that made every float-to-int conversion a sched2 barrier broke 95 of 228 matched functions, so the barrier belongs to these sources. In `func_L11_00444400` the same groups give retail's boundary before each box call (4692/4684 bytes); its register allocation is still open. | | **Jump tables the build cannot place:** `func_L06_003FDA08`, `func_L13_00422CB8`, `func_L03_003C4578`, `func_L02_003F9AF0`, `func_L02_003F9B70`, `func_L02_003FE1B0` | Solved, PR pending | A computed goto through retail's own table, in the form of `func_00310A40`, matches all six. The three level 2 records' in-order switches were wrong: retail's tables map the values to the bodies in another order. Decode the table before trusting a record's `switch`. | | **Labels behind hazard nops:** `func_L18_004308F0`, `func_L02_00411530` | Merged, #264 | Opt-in `-frac2-hazard-labels`. 198 retail branch targets after a compare or `mtc1` sit on the nop, and SN's `Ps2EeAs` agrees. Forced on everywhere it is neutral (778 of 779, one `.mdebug`). | | **gcse critical-edge split, Cygnus block merge:** `func_002CD720`, `func_L02_003E79F8`, `func_L02_003FCCA0`, `func_L18_004283F0` | Merged, #261 | Opt-in `-frac2-gcse-unsplit-edges` and `-frac2-keep-block-order`. `func_L23_0041B9F0` reaches retail's size with the gcse switch and still stops at the copy order. | -| **PRE keeps a load across a store to another offset:** `func_L02_003EEC58` (and `func_L23_0041C0E8`) | Open | Retail reloads the index after a store to a disjoint offset of the same base. A historical `volatile` probe reproduces it. Current RAC1 also uses volatile fields; this is not a blanket-forbidden source method (see COMPILER-NOTES.md). **Next:** establish whether a faithful field qualifier describes the target's actual accesses before considering an opt-in PRE rule. The old probe alone does not qualify either solution. | +| **PRE keeps a load across a store to another offset:** `func_L02_003EEC58` (and `func_L23_0041C0E8`) | Open | Retail reloads the index after a store to a disjoint offset of the same base. A historical `volatile` probe reproduces it. Current RAC1 also uses volatile fields; this is not a blanket-forbidden source method (see compiler-notes.md). **Next:** establish whether a faithful field qualifier describes the target's actual accesses before considering an opt-in PRE rule. The old probe alone does not qualify either solution. | | **Wide store in an annulled slot:** `func_L02_0042EDD0`, `func_L23_00422668`, `func_L02_003E7370` | Merged, #279 | Retail's game code has no `sd` or `sq` among the 2,895 stores in annulled slots. Ordinary slots hold 547 `sq` and 497 `sd`, and annulled slots do take wide loads.

PR #279 adds the opt-in `-frac2-no-annulled-wide-stores` (reorg's thread and steal paths; `optimize_skip` is kept). It is neutral by default (796 of 796 units); forced on in every game-compiler unit, it changes only the two witnesses. `func_L02_0042EDD0` (2 places, with the clear barrier) and `func_L23_00422668` (with `-frac2-qcopy`) are exact with it. `func_L02_003E7370` also needs the clear barrier in a call slot, and the barrier then also catches a plain C clear. | | **Suspected catalogue remnants:** `func_0011E920` (24 bytes), `func_0012FB10` (52 bytes), `func_00131CB0` (8 bytes), `func_00288D40` (20 bytes), `func_00290718` (4), `func_00298D10` (20), `func_0029CEC8` (12), `func_00130AAC` (4), `func_002B0A08` (12), `func_002B4068` (36), `func_002B49A0` (12), `func_002B8530` (20), `func_0026FEF0` (20), `func_00272540` (12), `func_002725C8` (20), `func_002DF630` (12), `func_002ED570` (4), `func_002ED848` (36), `func_002ED880` (28), `func_002ED8F0` (84), `func_002ED9A8` (20), `func_002EEBB0` (20), `func_002FC460`, `func_002FC510`, `func_002FC618`, `func_002FC868` (4 each), `func_0033B538` (4), `func_003380A8` (4) | Evidence, left as markers | Each row starts after a completed return or tail jump and its delay slot, possibly with alignment padding, and ends at the next function. No branch or jump in any catalogued boot function reaches it (scan of every boot row). Their words, like `sw $v0,-0x3D14($gp)` or `addiu $sp,$sp,0x10 ; nop`, read as unreachable tails. **28 October 2026 follow-up:** `func_0029CEC8` is 12 bytes in the boot and 23 level placements. Each USA v1.01 copy is `sw $a0,-0x4000($gp); nop; addiu $sp,$sp,0x10`, immediately after the preceding completed return and its stack-adjustment delay slot; there are no direct branch/jump targets or aligned non-text pointers into the extent in any of the 24 programs. A temporary `return 0` probe emits 8 bytes and matches 0/24. PAL and USA v2.00 retail dumps are unavailable here, so retain the catalogue row and marker pending release-qualified review. **28 October 2026 follow-up:** `func_002ED9A8` is 20 bytes in the boot and 23 level placements. Every USA v1.01 copy begins immediately after the preceding completed return and its stack-adjustment delay slot; its body is `lw $v0,0xC($v1); nop; addiu $sp,$sp,0x20; nop; addiu $sp,$sp,0x20`. No direct branch/jump targets or aligned non-text pointers enter the extent in any of the 24 programs. A temporary empty C probe emits 8 bytes and matches 0/24. PAL and USA v2.00 retail dumps are unavailable, so leave the marker and catalogue row pending release-qualified maintainer review. **28 October 2026 follow-up:** `func_002ED848` is 36 bytes in the boot and 23 level placements. Its USA v1.01 body is an orphan GP store, register no-ops, zero assignments, and two stores through the zero register; all 24 copies follow a completed return, its delay slot and padding, with no direct branch/jump target or aligned non-text pointer into the extent. The empty C probe emits 8 bytes, 0/24. PAL and USA v2.00 dumps are unavailable; keep the marker and row pending maintainer review. **28 October 2026 follow-up:** `func_002EEBB0` is 20 bytes in the boot and 23 level placements. Each USA v1.01 copy begins immediately after the preceding completed return and its stack-adjustment delay slot; the body is `daddu $v0,$zero,$zero; nop; addiu $sp,$sp,0x140; nop; addiu $sp,$sp,0xC0`. No direct branch/jump target or aligned non-text pointer enters the extent in any of the 24 programs. An empty C probe emits 8 bytes and matches 0/24. PAL and USA v2.00 retail dumps are unavailable, so keep the marker and catalogue row pending maintainer review. **28 October 2026 follow-up:** `func_002ED8F0` is 84 bytes in the boot and 23 level placements. All USA v1.01 copies follow a completed return and its value-move delay slot, then one `nop`; the 21-word extent contains only stack adjustments with nops, one float load, one word load, and `daddu $v0,$a2,$zero`. No direct branch/jump target or aligned non-text pointer enters the extent in any of the 24 programs. An empty C probe emits 8 bytes and matches 0/24. Other-release dumps are absent; leave the marker/catalogue row pending maintainer review. **28 October 2026 follow-up:** `func_002ED880` is 28 bytes in the boot and 23 level placements. Each USA v1.01 copy follows a completed return and its load delay slot, then one `nop`; the body is `sw $a0,0x48($v0); lw $v0,0x48($v1); nop; nop; nop; addiu $sp,$sp,0xB0`. No direct branch/jump target or aligned non-text pointer enters the extent in any of the 24 programs. An empty C probe emits 8 bytes and matches 0/24. PAL and USA v2.00 retail dumps are unavailable, so the marker/catalogue row remain pending maintainer review. **28 October 2026 follow-up:** `func_002B0A08` is 12 bytes in the boot and three level placements. Its four USA v1.01 copies begin with `sh $zero,0x1C($a0); nop; addiu $sp,$sp,0x30`, immediately after the preceding completed return and its stack-adjustment delay slot; no direct branch/jump target or aligned non-text pointer enters the extent. An empty C probe emits 8 bytes and matches 0/4. PAL and USA v2.00 dumps are unavailable; leave the marker and row pending maintainer review.

Three level rows fit the same pattern (DrLegitamate, 28 October 2026, USA v1.01): `func_L09_003FB780` (4 bytes; levels 09, 22, 23: `daddu $v0,$zero,$zero`), `func_L25_00373ED0` (4: `addiu $v0,$v0,0`) and `func_L25_00373F08` (4: `sw $a0,0x48($v0)`). Each of the five placements follows a `jr $ra`, its delay slot and padding, and is followed by a `nop` and the next function. No branch or jump label in its program's disassembly reaches it, and no aligned word in the level's `text.bin`, `data.bin`, `lit.bin` or vtables equals its address. Left as markers. **28 October 2026 follow-up:** `func_0011B320` is a single `daddu $v0,$zero,$zero` word at the boot address. It follows `func_0011B270`’s completed `jr $ra`, its `addiu $sp,$sp,0x40` delay slot and one padding `nop`; no direct branch/jump target in any catalogued boot function or aligned word reference in the loaded boot image points into it. A temporary empty C probe is 8 bytes, 0/1. Keep its marker/catalogue row pending maintainer review; do not add it to the residue mechanism without release-qualified review. **28 October 2026 follow-up:** `func_0011B450` is a single `and $a0,$a0,$v0` word. It follows `func_0011B3D0`’s completed `jr $ra`, stack-adjustment delay slot and one padding `nop`; a scan of catalogued boot functions found no direct incoming target, and no aligned loaded-word pointer equals its address. The empty C probe emits 8 bytes, 0/1. Leave the marker pending the maintainer’s catalogue-rule decision. **28 October 2026 follow-up:** `func_00130AAC` is the single word `pref 0x0D,-0x3233($t6)` between `func_00130AA0`’s tail jump and the `func_00130AB0` prologue. The preceding jump is `j func_130C68` with its `addiu $a0,$a0,0x4C` delay slot. No direct boot branch/jump target or aligned loaded-word pointer enters the one-word row. The empty C probe is 8 bytes and matches 0/1. Keep its marker/catalogue row pending maintainer review. **28 October 2026 follow-up:** `func_003380A8` is the word `sw $zero,0($v0)`, after the completed return and padding of `func_00338098` and before `func_003380B0`’s prologue. No direct branch/jump target in a catalogued boot function or aligned loaded-word pointer enters the row. The empty C probe is 8 bytes, 0/1. Leave the marker/catalogue row pending maintainer review. **28 October 2026 follow-up:** `func_00131CB0` is 8 bytes: `addiu $v1,$zero,0x23; syscall 0`. It follows `func_00131CA8`’s completed tail jump to `func_11FA20` and its `daddu $a0,$zero,$zero` delay slot; the next row begins with a new prologue. No direct boot branch/jump target or aligned loaded-word pointer enters this row. An empty C probe is also 8 bytes but emits `jr $ra; nop`, matching 0/1. Keep its marker/catalogue row pending maintainer review. **28 October 2026 follow-up:** boot-only `func_00272540` is 12 bytes: `daddu $v0,$a1,$zero; nop; addiu $sp,$sp,0x10`, immediately after the preceding function’s return/delay sequence and before the next stack-frame prologue. No direct target in a catalogued boot function or aligned loaded-word pointer enters the row. A temporary empty C probe emits 8 bytes and matches 0/1. Leave its marker and row pending maintainer review. **28 October 2026 follow-up:** boot-only `func_002DF630` is `daddu $v0,$zero,$zero; nop; addiu $sp,$sp,0x30`, after a completed return, its stack-adjustment delay slot and one `nop`, and before the next prologue. No direct target in a catalogued boot function or aligned loaded-word pointer enters it. The empty C probe is 8 bytes, 0/1. Keep the marker and catalogue row pending maintainer review. **28 October 2026 follow-up:** boot-only `func_0026FEF0` is 20 bytes: `sw $v0,0x38($a0); nop; addiu $sp,$sp,0x10; nop; addiu $sp,$sp,0x20`. It follows a completed return and its `+0x80` stack-adjustment delay slot, immediately before a new frame prologue. No direct target in catalogued boot code or aligned loaded-word pointer enters the row. An empty C probe is 8 bytes, 0/1. Leave its marker and row pending maintainer review. **28 October 2026 follow-up:** boot-only `func_002725C8` is 20 bytes: `addiu $sp,$sp,0x80; nop; nop; nop; sw $zero,0x4($v0); nop`. It follows the preceding function’s completed return and stack-adjustment delay slot, then three `nop`s; the next row starts a new frame. No direct boot target or aligned loaded-word pointer enters the extent. An empty C probe is 8 bytes, 0/1. Keep the marker and row pending maintainer review. **28 October 2026 follow-up:** boot-only `func_002B8530` is 20 bytes: `lwc1 $f0,0x2D0($a2); nop; lwc1 $f0,0x258($a2); nop; addiu $sp,$sp,0x30`. It follows a completed `jr $ra`, its `sh $zero,0x18E($v0)` delay slot and one padding `nop`; a new stack prologue follows the row. No direct target in catalogued boot code or aligned loaded-word pointer enters it. An empty C probe is 8 bytes, 0/1. Keep its marker/row pending maintainer review. **28 October 2026 follow-up:** boot-only `func_0011E920` is 24 bytes: `addiu $sp,$sp,0x10; nop; addiu $sp,$sp,0x30; nop; addiu $sp,$sp,0x30; pref 0x0D,-0x3233($t6)`. It follows a completed return and its stack-adjustment delay slot; a fresh `-0x50` frame follows the row. No direct target in catalogued boot code or aligned loaded-word pointer enters it. An empty C probe is 8 bytes, 0/1. Keep its marker and row pending maintainer review. **28 October 2026 follow-up:** boot-only `func_0012FB10` is 52 bytes and follows a completed return, its stack-adjustment delay slot and one `nop`. Its store/load/offset-add sequence has no direct branch/jump target in catalogued USA v1.01 boot functions and no aligned loaded-word pointer into the range. The row exists in PAL and USA v2.00 catalogues, but those dumps are absent; preserve its marker and row pending release-qualified maintainer review. **Next:** a `tools/levels.py` rule for unreachable inter-function words, if maintainers want one. | | **Rows that begin inside the previous epilogue:** 113 unmatched level-catalogue rows, 8 of them reserved by me (`func_L02_003EA180`, `func_L02_004004D0`, `func_L02_00439CD8`, `func_L03_00421398`, `func_L19_003DCCC8`, `func_L19_003DD9D8`, `func_L23_00415988`, `func_L23_004277B0`) | Evidence for §47's entry corrections | Each row starts with 8 to 104 bytes of epilogue words (a positive `addiu $sp,$sp,N`, `nop`), then directly a prologue `addiu $sp,$sp,-N`. None of the 137 rows that begin with such words is C. In all 1,065 placements, no branch or jump reaches the leading words.

106 rows have a reference to the prologue (a code address pair, a data pointer or a call) and none into the leading words. Two also have a data word equal to a leading address. Five have no reference found.

`func_L02_004004D0` is one of them: `func_L02_003FAF28` passes `D_L02_004004D8` as a callback, and its C equals retail from +8 except for the copy barrier. **Next:** entry corrections such as `LEVEL_ENTRY_OFFSETS`, keyed so that boot and level identity holds. I can prepare them for my eight if wanted. The list is below the table. | @@ -4746,7 +4746,7 @@ code in either release. section-8 `do` loop becomes extra address induction variables. An explicit `goto` loop preserves all four cursor advances and the full 24-word record-copy sequence, byte-identically at all 27 USA placements. Other loops still receive strength - reduction; see [the strength-reduction notes](COMPILER-NOTES.md#loop-no-giv-recombination). + reduction; see [the strength-reduction notes](../build/compiler-notes.md#loop-no-giv-recombination). This is not a full loader match. - **Use the newly supported structure copies.** In `003AE900`, two ordinary assignments of the real aligned 0x50-byte template replace the manual pointer @@ -4796,7 +4796,7 @@ Its normal aligned 0x50-byte template assignment uses the current compiler's quadword block moves. Correct helper return declarations then resolve the one remaining store/call scheduling difference. The four carrying USA v1.01/v2.00 programs pass strict linked builds; PAL has no row with this canonical identity. -See [prototypes and lifetimes](COMPILER-NOTES.md#prototypes-lifetimes-and-varargs). +See [prototypes and lifetimes](../build/compiler-notes.md#prototypes-lifetimes-and-varargs). The old nonmatching copy is retired; no ad-hoc assembly or compiler change is needed. The related retained investigations establish these constraints: @@ -4839,7 +4839,7 @@ The related retained investigations establish these constraints: `-frac2-no-extra-increment-givs` (and `-fno-rerun-loop-opt`), RAC1's plain `*p++` wording matches all seven loops, with the index form `store(&q[n], X); n++` in the two D_001A6860 loops: 2,988 bytes, 28 of 28 places in USA v1.01, PAL and v2.00, - in `src/game/00284268.c` (docs/COMPILER-NOTES.md, "loop: extra biv increments stay + in `src/game/00284268.c` (docs/build/compiler-notes.md, "loop: extra biv increments stay bivs"). The goto controls above and the earlier `put(&p)` inline only imitated whole increments; a volatile view of the state pointer reproduced them only through `loop_has_volatile` and is not allowed. @@ -4856,7 +4856,7 @@ buffer repairs remain distinct from a complete matching or functional build. ### Retained controls and newly identified entry witnesses The 2 November 2026 findings below distinguish source problems from boundary -problems. The fourteen named entry repairs described in [LEVELS.md](LEVELS.md) +problems. The fourteen named entry repairs described in [levels.md](../build/levels.md) were subsequently verified in every carrying release and added to the existing generator mechanism. The other split/residue proposals remain open. @@ -4924,7 +4924,7 @@ open, so the row stays. Direct or nearby address-pair absence alone is not proof an independently evidenced initialization/lifetime change is still needed. - `func_00131D98`'s former secondary-release boundary blocker is resolved by the guarded main/sound split in `tools/carry.py --entries` (see - [LEVELS.md](LEVELS.md)). Both complete bodies, their named callees and their + [levels.md](../build/levels.md)). Both complete bodies, their named callees and their EE section bounds are checked before writing either table. Its faithful 80-byte loop uses the actual indirect entry returned by ParseBin, cache synchronization modes 0/2, a release-scoped initial entry and independently diff --git a/docs/PERMUTER.md b/docs/contributing/permuter.md similarity index 100% rename from docs/PERMUTER.md rename to docs/contributing/permuter.md diff --git a/docs/ASSERT-MESSAGE-NAMES.md b/docs/game/assert-message-names.md similarity index 96% rename from docs/ASSERT-MESSAGE-NAMES.md rename to docs/game/assert-message-names.md index afb745a22..6ea7a3226 100644 --- a/docs/ASSERT-MESSAGE-NAMES.md +++ b/docs/game/assert-message-names.md @@ -17,7 +17,7 @@ messages, extracted instructions or assets. ## First pass: the remaining 59 messages **Historical investigation, 2–3 October 2026:** after the dispatch-table pass -described in [`MOBY-DISPATCH-TABLES.md`](MOBY-DISPATCH-TABLES.md), a selected set +described in [`moby-dispatch-tables.md`](moby-dispatch-tables.md), a selected set of **59 diagnostic messages** remained without a carrying function in the analyzed retail boot. The comparison used the retail target and the **9 September 2003 PAL review disc**, whose boot identifier is `SCES_516.07`. These 59 cases were resolved @@ -161,7 +161,7 @@ name fidelity; it does not repair missing references or certify every semantic d The previous pass contributed 102 annotations across 25 overlays. Its independent class-identifier cross-check remains documented in -[`MOBY-DISPATCH-TABLES.md`](MOBY-DISPATCH-TABLES.md). Functions with conflicting +[`moby-dispatch-tables.md`](moby-dispatch-tables.md). Functions with conflicting diagnostic labels and names already assigned by another method were preserved. No game text is necessary to reproduce the application step. The privately owned @@ -169,7 +169,7 @@ retail image and analysis database remain the source for semantic review. ## Applying or verifying the table -[`ApplyAssertMessageNames.java`](../tools/ghidra/ApplyAssertMessageNames.java) +[`ApplyAssertMessageNames.java`](../../tools/ghidra/ApplyAssertMessageNames.java) accepts a program identifier and the TSV path. It previews by default; the explicit third argument `--apply` enables renaming. It checks the complete level `.text` SHA-256 against its recorded retail fingerprint before processing names, resolves a @@ -180,7 +180,7 @@ non-generic name. Conflicting rows fail before any renames. Example arguments for the level 0 overlay: ```text -0_aranos_tutorial docs/assert-message-names.tsv +0_aranos_tutorial docs/game/assert-message-names.tsv ``` With `--apply`, review the summary and save the program explicitly after inspecting @@ -190,6 +190,6 @@ transfer prototype addresses, or apply the cross-build anchor survey. ## Separate cross-build evidence The Aug6/retail survey is qualified separately in -[`AUG6-RETAIL-ANCHORS.md`](AUG6-RETAIL-ANCHORS.md). Matching code windows, diagnostic +[`aug6-retail-anchors.md`](../research/aug6-retail-anchors.md). Matching code windows, diagnostic annotations and class-table identifiers are different forms of evidence. None of them alone establishes a byte-exact C reconstruction or a playable runtime. diff --git a/docs/assert-message-names.tsv b/docs/game/assert-message-names.tsv similarity index 100% rename from docs/assert-message-names.tsv rename to docs/game/assert-message-names.tsv diff --git a/docs/COMMUNITY-ENGINE-REFERENCE.md b/docs/game/community-engine-reference.md similarity index 100% rename from docs/COMMUNITY-ENGINE-REFERENCE.md rename to docs/game/community-engine-reference.md diff --git a/docs/ENGINE-SYMBOL-NAMES.md b/docs/game/engine-symbol-names.md similarity index 99% rename from docs/ENGINE-SYMBOL-NAMES.md rename to docs/game/engine-symbol-names.md index 2d0922225..4ffb2cee7 100644 --- a/docs/ENGINE-SYMBOL-NAMES.md +++ b/docs/game/engine-symbol-names.md @@ -93,7 +93,7 @@ a seven-case menu-action switch — sits at `0x0026ede8`, inside retail FRONTEND - **Do not apply these addresses, or any name derived from them, to the retail Ghidra project.** Every one indexes the prototype's image. -- The **6 356 level names** in `docs/MOBY-DISPATCH-TABLES.md` are unaffected: they come +- The **6 356 level names** in `docs/game/moby-dispatch-tables.md` are unaffected: they come from the *overlay* dispatch tables and were verified against the retail overlays, not from this linker script. - The prototype's section table is itself a usable result: it documents the engine's diff --git a/docs/MOBY-DISPATCH-TABLES.md b/docs/game/moby-dispatch-tables.md similarity index 98% rename from docs/MOBY-DISPATCH-TABLES.md rename to docs/game/moby-dispatch-tables.md index 675407179..de4b3a520 100644 --- a/docs/MOBY-DISPATCH-TABLES.md +++ b/docs/game/moby-dispatch-tables.md @@ -80,7 +80,7 @@ The retail camera identifier set (`0,3,4,5,6,7,17,19,20,23,24,25,26,27`) contains the prototype's set exactly, plus `20`; camera transfer rests on that set equality and on the record layout, not on the pair control above, which is moby-specific. -The data is in **`docs/moby-dispatch.tsv`** — +The data is in **`docs/game/moby-dispatch.tsv`** — `table · level · id · role · address · aux · proposed_name · source · class_label · class_label_source · class_label_basis` — with virtual addresses as they appear in the overlay ELF. A tool that loads an overlay at file offset rather than at its virtual address must translate first. @@ -229,7 +229,7 @@ For example, run this Python code from the repository root to generate the looku import csv from pathlib import Path -with Path("docs/moby-dispatch.tsv").open(encoding="utf-8", newline="") as stream: +with Path("docs/game/moby-dispatch.tsv").open(encoding="utf-8", newline="") as stream: labels = {int(r["id"]): r["class_label"] for r in csv.DictReader(stream, delimiter="\t") if r["table"] == "moby" and r["class_label_basis"] == "ps2-source"} Path("build").mkdir(exist_ok=True) @@ -246,7 +246,7 @@ does not verify it. Keep the complete set in the one TSV, with its basis intact. - **Not designer names.** `UpdateMoby_3032` states a moby class, not a creature. The sourced community labels above describe classes or equipment models, not the full behaviour of their handlers. Assert-message evidence is a separate source of semantic information - (see [`ASSERT-MESSAGE-NAMES.md`](ASSERT-MESSAGE-NAMES.md)). + (see [`assert-message-names.md`](assert-message-names.md)). - **Not boot code.** Crossing all 5,788 handlers with the boot bodies this project has mapped into the levels (82 per level in `config/level-catalog.json`, 288 in the wider prefix survey) gives **zero overlap**. The dispatch tables point at level-local code — @@ -256,7 +256,7 @@ does not verify it. Keep the complete set in the one TSV, with its basis intact. ## Applying the names -`tools/ghidra/ApplyMobyNames.java` is a Ghidra script: it reads `docs/moby-dispatch.tsv` +`tools/ghidra/ApplyMobyNames.java` is a Ghidra script: it reads `docs/game/moby-dispatch.tsv` and names the functions of the overlay currently open, taking the level name as its first argument. It builds both address candidates from the ELF section table on disk (virtual address and file offset, because an overlay may have been imported either way), leaves an diff --git a/docs/moby-dispatch.tsv b/docs/game/moby-dispatch.tsv similarity index 100% rename from docs/moby-dispatch.tsv rename to docs/game/moby-dispatch.tsv diff --git a/docs/VU-MICROPROGRAMS.md b/docs/game/vu-microprograms.md similarity index 99% rename from docs/VU-MICROPROGRAMS.md rename to docs/game/vu-microprograms.md index 648fa4007..7f7c223e3 100644 --- a/docs/VU-MICROPROGRAMS.md +++ b/docs/game/vu-microprograms.md @@ -8,7 +8,7 @@ is given. The overlay convention itself is documented by the community project **re-rac/rerac** (`docs/formats/vu_microprograms.md`, ISC) for RAC1. Their renderer attributions are a **lead to verify**, never evidence — the same rule as -COMMUNITY-ENGINE-REFERENCE.md. Section 4 separates what is measured here from what is +community-engine-reference.md. Section 4 separates what is measured here from what is not. The `.DVP.ovly*` tables are already pinned in `config/boot-sections.json`; this page diff --git a/docs/PORT_HANDOFF.md b/docs/port/port-handoff.md similarity index 99% rename from docs/PORT_HANDOFF.md rename to docs/port/port-handoff.md index 382cea022..5c84042ae 100644 --- a/docs/PORT_HANDOFF.md +++ b/docs/port/port-handoff.md @@ -8,8 +8,8 @@ native port of Going Commando (NTSC-U, SCUS_972.68). It covers four things: - the bugs found in two days of player testing, and which of them traced back to this repository; - the methods and lessons that saved time. -Matching itself is covered by [AGENT_WORKFLOW.md](AGENT_WORKFLOW.md) and -[COMPILER-NOTES.md](COMPILER-NOTES.md). This document is about **behaviour**: C that has to do +Matching itself is covered by [agent-workflow.md](../agents/agent-workflow.md) and +[compiler-notes.md](../build/compiler-notes.md). This document is about **behaviour**: C that has to do what the retail code does when the port runs it. ## How this repository feeds the port diff --git a/docs/AUG6-RETAIL-ANCHORS.md b/docs/research/aug6-retail-anchors.md similarity index 98% rename from docs/AUG6-RETAIL-ANCHORS.md rename to docs/research/aug6-retail-anchors.md index dd145f8f0..3644881ed 100644 --- a/docs/AUG6-RETAIL-ANCHORS.md +++ b/docs/research/aug6-retail-anchors.md @@ -63,7 +63,7 @@ already unique under the mask. those 41 selected correspondences. Prototype CSV exports examined as data corroborate all 41 addresses; no third-party script was executed. The independent evidence is the class/role identifier in the two privately owned dispatch tables, using the -record layouts documented in [`MOBY-DISPATCH-TABLES.md`](MOBY-DISPATCH-TABLES.md). +record layouts documented in [`moby-dispatch-tables.md`](../game/moby-dispatch-tables.md). This recovers known structural relationships, including `UpdateMoby_775`, `UpdateMoby_2384`, `UpdateMoby_3077`, `UpdateCamera_6` and `InitCamera_24`. diff --git a/docs/aug6-retail-anchors.tsv b/docs/research/aug6-retail-anchors.tsv similarity index 100% rename from docs/aug6-retail-anchors.tsv rename to docs/research/aug6-retail-anchors.tsv diff --git a/docs/aug6-retail-dispatch-qualified.tsv b/docs/research/aug6-retail-dispatch-qualified.tsv similarity index 100% rename from docs/aug6-retail-dispatch-qualified.tsv rename to docs/research/aug6-retail-dispatch-qualified.tsv diff --git a/docs/PROTOTYPE-BUILDS.md b/docs/research/prototype-builds.md similarity index 100% rename from docs/PROTOTYPE-BUILDS.md rename to docs/research/prototype-builds.md diff --git a/docs/RAC1-DECOMP-FINDINGS.md b/docs/research/rac1-decomp-findings.md similarity index 98% rename from docs/RAC1-DECOMP-FINDINGS.md rename to docs/research/rac1-decomp-findings.md index a7af64fe6..5b3be4940 100644 --- a/docs/RAC1-DECOMP-FINDINGS.md +++ b/docs/research/rac1-decomp-findings.md @@ -81,7 +81,7 @@ original date and toolchain scope. `sce_ps2_sdk_24` kit) lays out callee-saved slots like RAC1 retail; v1.36 (`ee-gcc2953` in ProDG 3.01) uses a mirrored layout. No command-line flag changes either. This matches the `sd`/`sq` difference in - [COMPILER-NOTES.md](COMPILER-NOTES.md), which compared the ProDG 3.01 kit. + [compiler-notes.md](../build/compiler-notes.md), which compared the ProDG 3.01 kit. - **GCC 2.95.3 has no sibling-call optimisation**: `-foptimize-sibling-calls` is rejected and `-O3` does not help. RAC1's bare `j target` tail calls came from post-processing. Sony's 2.9-ee performs tail calls natively, also in eight @@ -92,14 +92,14 @@ original date and toolchain scope. - **Short-loop erratum.** SN's `ps2eeas` pads every backward branch whose loop is shorter than six instructions with nops. The 2.9-ee driver's own `as.exe` pads call-free short loops; the standalone GNU `ee-as` of the same version - pads none. RAC2's [RAC1-TO-RAC2.md](RAC1-TO-RAC2.md) records the same + pads none. RAC2's [rac1-to-rac2.md](rac1-to-rac2.md) records the same hazard-nop behaviour for the ProDG 3.01 assembler. ## 4. Candidate-generation aids rac1-decomp uses these to propose candidates. These observations do not permit source permutations on parked RAC2 targets: the concrete reopening -evidence required by `AGENTS.md` and `docs/MATCHING-BLOCKERS.md` still applies. +evidence required by `AGENTS.md` and `docs/contributing/matching-blockers.md` still applies. - **m2c with generated context** for a first C sketch. Its output is never matching as emitted and mis-decodes branch-likely conditions. diff --git a/docs/RAC1-TO-RAC2.md b/docs/research/rac1-to-rac2.md similarity index 100% rename from docs/RAC1-TO-RAC2.md rename to docs/research/rac1-to-rac2.md diff --git a/include/engine.h b/include/engine.h index ea4fb217d..e5a041bc6 100644 --- a/include/engine.h +++ b/include/engine.h @@ -35,7 +35,7 @@ s32 Camera_CollPrimTest(void *prim, void *grid); /* Measured USA v1.01 memory views, not native host-pointer structures. * Addresses are 32-bit PS2 values; decode them explicitly in a PC port/debugger. * Field purposes not established by callers and the consumer retain offset names. - * See docs/COMMUNITY-ENGINE-REFERENCE.md, "Measured retail impact queue". */ + * See docs/game/community-engine-reference.md, "Measured retail impact queue". */ typedef struct Rac2Impact32 { /* 0x00 */ f32 vector[4]; /* 0x10 */ u32 sourceAddress; diff --git a/nonmatching/README.md b/nonmatching/README.md index 3bca7d034..017243a99 100644 --- a/nonmatching/README.md +++ b/nonmatching/README.md @@ -156,7 +156,7 @@ the placement rules are in `tools/fcheck.py`'s docstring; its tests are | [`func_L00_0036D29C`](levels/func_L00_0036D29C.c) | 40 | near miss: 9 of 10 instructions (ours 36 bytes, retail 40). Same wait-loop family as func_L00_0036BE28: retail loads through $at, hand-placed nops, jr ra then lui ra, 0x7000. | | [`func_L01_00304270`](levels/func_L01_00304270.c) | 52 | Does not match: the attempt compiles to 36 bytes, retail is 52 (13 instructions). Retail loads its pointer from $v1, a register that is not an argument, and tests with a2; the self-jump (j func_304270) on a 0x100 test suggests a caller-set … | | [`func_L00_002E2048`](levels/func_L00_002E2048.c) | 44 | Wave 5 w11: 0 of 27 places match; 8 of 11 instructions; retail keeps the block base in $at (lui+addiu, +0x1C as the load's literal), the mask 0x60 in $v1 via ADDI and a nop in the first jr delay slot, where this cc1 loads through the destination register, folds the mask into ANDI and fills the delay slot with the shift. | -| [`func_L00_002EDCF0`](levels/func_L00_002EDCF0.c) | 16 | Wave 5 w04: G8 draft; retail `lwc1 $f0,-0x76DC($gp); nop; jr $ra; mul.s $f0,$f0,$f12`. With `extern float D_001A7914 __attribute__((sda));` in the -g8 sibling the load and the `mul.s` operand order match, but the pinned cc1 emits the compact three-instruction form (12 bytes): retail's `nop` before `jr $ra` is the wall recorded in docs/COMPILER-NOTES.md. | +| [`func_L00_002EDCF0`](levels/func_L00_002EDCF0.c) | 16 | Wave 5 w04: G8 draft; retail `lwc1 $f0,-0x76DC($gp); nop; jr $ra; mul.s $f0,$f0,$f12`. With `extern float D_001A7914 __attribute__((sda));` in the -g8 sibling the load and the `mul.s` operand order match, but the pinned cc1 emits the compact three-instruction form (12 bytes): retail's `nop` before `jr $ra` is the wall recorded in docs/build/compiler-notes.md. | | [`func_L00_002EDD28`](levels/func_L00_002EDD28.c) | 16 | Wave 5 w04: G8 draft, twin of func_L00_002EDCF0 at gp-0x76D8 (0x001A7918); same `nop`-before-`jr` wall. | | [`func_L00_002EDD38`](levels/func_L00_002EDD38.c) | 16 | Wave 5 w04: G8 draft, twin of func_L00_002EDCF0 at gp-0x76D4 (0x001A791C); same `nop`-before-`jr` wall. | | [`func_0011ED28`](core/func_0011ED28.c) | 56 | Not a function: thirteen stack-pointer adjustments and linker fill, with no `jr $ra` or reference from the boot. A catalogue fragment, not C. | @@ -189,7 +189,7 @@ the placement rules are in `tools/fcheck.py`'s docstring; its tests are | [`func_L25_00373EE0`](levels/func_L25_00373EE0.c) | 20 | Not code: no `jr $ra`, both stores go through `$v0 = 0`, words interleaved with `nop`s, and it clobbers the previous entry delay-slot load. Catalogue entry, not C | | [`func_L20_0039BEC8`](levels/func_L20_0039BEC8.c) | 1252 | Retained near miss, 1260/1252 B; us: 0/1 placements; us2: 0/1 placements; pal: 0/1 placements. Profile `-O2 -G32 -Wa,-G32 -ffunction-sections -frac2-qcopy -frac2-qcopy-barrier`. 1260 bytes versus1252; correct1D0 frame, interleaved-table base choices and allocation differ. Reopen: Recover the actual interleaved UV table declarations and independently qualified GP/absolute addressing mechanism. Do not shrink32-byte tables to fitG8. Historical checker results; linked-image equality and semantics unverified. | | [`func_L23_0035CE90`](levels/func_L23_0035CE90.c) | 288 | Catalogue defect: the row starts 16 bytes early, in the padding after the previous function's `jr $ra` at 0x35CE84 (`sw $t3,0x40($v1); nop; addiu $sp,$sp,0x20; nop`); the body is 0x35CEA0-0x35CFAF, 272 bytes, and this C, for the `-g8` sibling, gives 264 (two `nop`s and some order) | -| [`func_002A677C`](game/func_002A677C.c) | 296 | USA v1.01 retail behavior traced; no function candidate/check. Its pextlb/pextlh, pcpyh, pmulth, paddub, psrlh, ppach and ppacb operations are outside this compiler's measured C-emitted MMI set (`por`, `mult1`, `madd`); see the compiler survey in `docs/MATCHING-BLOCKERS.md`. This is evidence for handwritten assembly, with `ASM_FUNC` pending maintainer agreement. Marker remains. | +| [`func_002A677C`](game/func_002A677C.c) | 296 | USA v1.01 retail behavior traced; no function candidate/check. Its pextlb/pextlh, pcpyh, pmulth, paddub, psrlh, ppach and ppacb operations are outside this compiler's measured C-emitted MMI set (`por`, `mult1`, `madd`); see the compiler survey in `docs/contributing/matching-blockers.md`. This is evidence for handwritten assembly, with `ASM_FUNC` pending maintainer agreement. Marker remains. | | [`func_0032FDC0`](game/func_0032FDC0.c) | 12 | Not a C row: catalogue defect. The 12-byte extent is daddu $v0, $a1, $a2 ; nop ; daddu $v0, $zero, $zero ; nop - two 64-bit moves and no `jr $ra` - with no `jr $ra`, between the previous function's `jr $ra` and the next function's prologue; the same shape as func_L00_00341798. No C can be placed | | [`func_003356E8`](game/func_003356E8.c) | 172 | USA v1.01 boot extent 0x003356E8-0x00335794: begins with `sb $a0,0x88($v1)`, then stack adjustments, delay-slot nops and stores, without a function prologue, control-flow body or return. It lies between func_003355A8 (ends 0x003356E4) and func_00335798; scans found no direct branch/jump target or aligned literal pointer into the extent. Catalogue-residue hypothesis pending maintainer review and release audit; no C attempt or generator rule | | [`func_00335270`](game/func_00335270.c) | 252 | Not a C row as catalogued: the extent is 8 bytes too long at the front, on the previous function's epilogue area (addiu $sp, $sp, 0x40 ; nop); the body starts at +0x8 with `addiu $sp, $sp, 0x30`. A fix in tools/levels.py would make it matchable | @@ -211,10 +211,10 @@ the placement rules are in `tools/fcheck.py`'s docstring; its tests are | [`func_L00_002EDC90`](levels/func_L00_002EDC90.c) | 40 | Wave 8 w06: retail `lui $at,0x3E80 ; lwc1 $f2,-0x76E4($gp) ; mtc1 $a0,$f1 ; mtc1 $at,$f3 ; cvt.s.w $f1,$f1 ; 0x46031818 ; 0x4602085C ; cvt.w.s $f1,$f1 ; jr $ra ; mfc1 $v0,$f1`; the two unknown words are the R5900 ADDA.S (funct 0x18) and MADD.S (funct 0x1C), so the body is (int)(value * D_001A790C + 0.0625f) through the FP accumulator. The G8 candidate with the ordinary mul.s/add.s is 48 bytes, 0/27 places. Not reachable in plain C here: the compiler's `madd.s` pattern needs -mips4 and prints the four-operand form the game's assembler refuses, and no `adda.s` exists in either. | | [`func_L00_002EDCC8`](levels/func_L00_002EDCC8.c) | 40 | Wave 8 w06: retail `lui $at,0x3E80 ; lwc1 $f2,-0x76E0($gp) ; mtc1 $a0,$f1 ; mtc1 $at,$f3 ; cvt.s.w $f1,$f1 ; 0x46031818 ; 0x4602085C ; cvt.w.s $f1,$f1 ; jr $ra ; mfc1 $v0,$f1`; the two unknown words are the R5900 ADDA.S (funct 0x18) and MADD.S (funct 0x1C), so the body is (int)(value * D_001A7910 + 0.0625f) through the FP accumulator. The G8 candidate with the ordinary mul.s/add.s is 48 bytes, 0/27 places. Not reachable in plain C here: the compiler's `madd.s` pattern needs -mips4 and prints the four-operand form the game's assembler refuses, and no `adda.s` exists in either. | | [`func_L00_002EDD00`](levels/func_L00_002EDD00.c) | 40 | Wave 8 w06: retail `lui $at,0x3E80 ; lwc1 $f2,-0x76DC($gp) ; mtc1 $a0,$f1 ; mtc1 $at,$f3 ; cvt.s.w $f1,$f1 ; 0x46031818 ; 0x4602085C ; cvt.w.s $f1,$f1 ; jr $ra ; mfc1 $v0,$f1`; the two unknown words are the R5900 ADDA.S (funct 0x18) and MADD.S (funct 0x1C), so the body is (int)(value * D_001A7914 + 0.0625f) through the FP accumulator. The G8 candidate with the ordinary mul.s/add.s is 48 bytes, 0/27 places. Not reachable in plain C here: the compiler's `madd.s` pattern needs -mips4 and prints the four-operand form the game's assembler refuses, and no `adda.s` exists in either. | -| [`func_L00_002EDCF0`](levels/func_L00_002EDCF0.c) | 16 | Wave 8 w06: G8 draft; retail `lwc1 $f0,-0x76DC($gp); nop; jr $ra; mul.s $f0,$f0,$f12`. With `extern float D_001A7914 __attribute__((sda));` the load and the `mul.s` operand order match, but the candidate is 12 bytes: the `nop` between the load and the jump is missing (the wall recorded in docs/COMPILER-NOTES.md). Measured this wave: -O0/-O1/-O2/-O3, -G0/-G8, -fno-schedule-insns, -fno-delayed-branch, -ffast-math, a volatile global and a double read all give the compact three-instruction form, and assembling the retail's four instructions with the game's `as` gives 12 bytes too, so the `nop` is not the assembler's. | +| [`func_L00_002EDCF0`](levels/func_L00_002EDCF0.c) | 16 | Wave 8 w06: G8 draft; retail `lwc1 $f0,-0x76DC($gp); nop; jr $ra; mul.s $f0,$f0,$f12`. With `extern float D_001A7914 __attribute__((sda));` the load and the `mul.s` operand order match, but the candidate is 12 bytes: the `nop` between the load and the jump is missing (the wall recorded in docs/build/compiler-notes.md). Measured this wave: -O0/-O1/-O2/-O3, -G0/-G8, -fno-schedule-insns, -fno-delayed-branch, -ffast-math, a volatile global and a double read all give the compact three-instruction form, and assembling the retail's four instructions with the game's `as` gives 12 bytes too, so the `nop` is not the assembler's. | | [`func_L00_002EDD28`](levels/func_L00_002EDD28.c) | 16 | Wave 8 w06: G8 draft, twin of func_L00_002EDCF0 at gp-0x76D8 (0x001A7918); same `nop`-before-`jr` wall (12 bytes against the retail's 16, 0/27 places). | | [`func_L00_002EDD38`](levels/func_L00_002EDD38.c) | 16 | Wave 8 w06: G8 draft, twin of func_L00_002EDCF0 at gp-0x76D4 (0x001A791C); same `nop`-before-`jr` wall (12 bytes against the retail's 16, 0/27 places). | -| [`func_L00_002EDCB8`](levels/func_L00_002EDCB8.c) | 16 | Wave 8 w06: G8 draft; retail `lwc1 $f0,-0x76E0($gp); nop; jr $ra; mul.s $f0,$f0,$f12`. With `extern float D_001A7910 __attribute__((sda));` the load and the `mul.s` operand order match, but the candidate is 12 bytes: the `nop` between the load and the jump is missing (the wall recorded in docs/COMPILER-NOTES.md). Measured this wave: -O0/-O1/-O2/-O3, -G0/-G8, -fno-schedule-insns, -fno-delayed-branch, -ffast-math, a volatile global and a double read all give the compact three-instruction form, and assembling the retail's four instructions with the game's `as` gives 12 bytes too, so the `nop` is not the assembler's. | +| [`func_L00_002EDCB8`](levels/func_L00_002EDCB8.c) | 16 | Wave 8 w06: G8 draft; retail `lwc1 $f0,-0x76E0($gp); nop; jr $ra; mul.s $f0,$f0,$f12`. With `extern float D_001A7910 __attribute__((sda));` the load and the `mul.s` operand order match, but the candidate is 12 bytes: the `nop` between the load and the jump is missing (the wall recorded in docs/build/compiler-notes.md). Measured this wave: -O0/-O1/-O2/-O3, -G0/-G8, -fno-schedule-insns, -fno-delayed-branch, -ffast-math, a volatile global and a double read all give the compact three-instruction form, and assembling the retail's four instructions with the game's `as` gives 12 bytes too, so the `nop` is not the assembler's. | | [`func_L00_002EE0C0`](levels/func_L00_002EE0C0.c) | 16 | Wave 8 w06: retail `sd $zero,0($a0); nop; jr $ra; sw $zero,8($a0)`, a twelve-byte clear. The struct candidate gives the two stores in the retail's order (the 8-byte store first) but is 12 bytes: the `nop` between the first store and the jump is missing, the same wall as func_L00_002EDCB8 and its three twins. Measured this wave: -O0/-O1/-O2/-O3, -fno-schedule-insns and -fno-delayed-branch (which puts the two stores first and the `nop` in the return slot) never give the retail's 16 bytes; 0/27 places. | | [`func_L09_00352150`](levels/func_L09_00352150.c) | 288 | Not a C row: the extent starts with `sw $t3, 0x40($v1)`, and neither register is set in the 288 bytes that follow (the body sets $v1 itself, eight instructions later). Nothing in level 09 calls or points at 0x00352150, and the boot's copy of the same 288 bytes (func_002DB988) starts the same way and is referenced by nothing either. A catalogue fragment - the fix belongs in `tools/levels.py` | diff --git a/nonmatching/core/func_001157AC.c b/nonmatching/core/func_001157AC.c index 421147e01..d6835e770 100644 --- a/nonmatching/core/func_001157AC.c +++ b/nonmatching/core/func_001157AC.c @@ -1,4 +1,4 @@ /* from src/core/00115200.c: strlen. symbol_addrs/eu: "SDK strlen(s) hand-written r5900 strlen.S - scans for the zero byte 16 then 8 then 1 byte at a time"; the file it sits in - is one of the two 16-byte-slot units of docs/COMPILER-NOTES.md. Handwritten assembly: + is one of the two 16-byte-slot units of docs/build/compiler-notes.md. Handwritten assembly: no C is written. */ diff --git a/nonmatching/core/func_0011F864.c b/nonmatching/core/func_0011F864.c index 0edebfec9..f5a2f5876 100644 --- a/nonmatching/core/func_0011F864.c +++ b/nonmatching/core/func_0011F864.c @@ -4,7 +4,7 @@ * `j func_11F058` and its `addiu $sp,$sp,0x10` delay slot end at 0x0011F860) * and func_0011F868 at 0x0011F868 - the two are in different source objects * (src/core/0011F828.c and src/core/0011F868.c), and the fill is what the - * catalogue's object boundary is made of (docs/LEVELS.md). Nothing in the boot + * catalogue's object boundary is made of (docs/build/levels.md). Nothing in the boot * jumps to 0x0011F864. * * tools/check.py func_0011F864: the smallest C function is `jr $ra; nop`, so the diff --git a/nonmatching/core/func_001290BC.c b/nonmatching/core/func_001290BC.c index b70b170cd..ba9394259 100644 --- a/nonmatching/core/func_001290BC.c +++ b/nonmatching/core/func_001290BC.c @@ -1,6 +1,6 @@ -/* Not C: VU0 macro code, and the extent starts one word late. - Bytes 0x1290BC..0x1290BF are 0xCDCDCDCD, the linker's fill between objects - (docs/LEVELS.md); the 92 bytes that follow are sqc2/lqc2 plus a run of COP2 - macro-mode words, and end in `jr $ra; sqc2 $vf6,0($a0)`. Nothing here can - come out of a C compiler, so the catalogue extent is either one word too - early or the function is ASM_FUNC. */ +/* Not C: VU0 macro code, and the extent starts one word late. + Bytes 0x1290BC..0x1290BF are 0xCDCDCDCD, the linker's fill between objects + (docs/build/levels.md); the 92 bytes that follow are sqc2/lqc2 plus a run of COP2 + macro-mode words, and end in `jr $ra; sqc2 $vf6,0($a0)`. Nothing here can + come out of a C compiler, so the catalogue extent is either one word too + early or the function is ASM_FUNC. */ diff --git a/nonmatching/game/func_002702B8.c b/nonmatching/game/func_002702B8.c index 1a9c7b74f..a7906fa01 100644 --- a/nonmatching/game/func_002702B8.c +++ b/nonmatching/game/func_002702B8.c @@ -7,4 +7,4 @@ extent, and the aligned literal-pointer scan found no reference to any placement. A plain C zeroing store compiles to sw / jr $ra / daddu in the same 12 bytes, so it cannot reproduce this fallthrough. Retained only as catalogue-defect evidence; - no C is deposited. See docs/LEVELS.md. */ + no C is deposited. See docs/build/levels.md. */ diff --git a/nonmatching/game/func_0029CF48.c b/nonmatching/game/func_0029CF48.c index 38da7852c..9990c8223 100644 --- a/nonmatching/game/func_0029CF48.c +++ b/nonmatching/game/func_0029CF48.c @@ -2,5 +2,5 @@ * Not a callable function. The 12-byte extent follows a completed return and * alignment nop, contains a GP-relative store and an unmatched 0x50-byte stack * restore, and has no direct or measured data reference in any placement. See - * docs/LEVELS.md for the retained catalogue-boundary evidence. + * docs/build/levels.md for the retained catalogue-boundary evidence. */ diff --git a/nonmatching/game/func_002A0488.c b/nonmatching/game/func_002A0488.c index 78aa6ffb7..112f1b21e 100644 --- a/nonmatching/game/func_002A0488.c +++ b/nonmatching/game/func_002A0488.c @@ -15,7 +15,7 @@ the return slot. */ /* 3 November 2026: a non-volatile form now reproduces every retail word except the `nop` at +0x24 (blocker 4, "`nop` before `jr $ra`", in - docs/MATCHING-BLOCKERS.md). The body below, with u64 arguments and the + docs/contributing/matching-blockers.md). The body below, with u64 arguments and the duplicate-arm `if` already used by the matched neighbour func_002A0468 (Lombyte's PackRenderCommandFields), keeps the whole-word `ld` because the load and the `(x << 32) >> 32` sit in different blocks when combine runs, diff --git a/nonmatching/game/func_002A677C.c b/nonmatching/game/func_002A677C.c index 98a4754a1..43b2bb34e 100644 --- a/nonmatching/game/func_002A677C.c +++ b/nonmatching/game/func_002A677C.c @@ -17,7 +17,7 @@ * A separate compiler probe using standard 16-byte C vector types and lane * addition, multiplication and shift lowered to scalar GPR operations; it * emitted none of the packed instructions listed above. The compiler survey - * in docs/MATCHING-BLOCKERS.md establishes that game C emits only por and the + * in docs/contributing/matching-blockers.md establishes that game C emits only por and the * SPECIAL2 mult1/madd forms in the MMI space; the packed operations here fall * outside that set. This is evidence for handwritten assembly under * AGENTS.md, but ASM_FUNC still needs maintainer agreement. Marker remains; diff --git a/nonmatching/game/func_002DC3C8.c b/nonmatching/game/func_002DC3C8.c index ac0f01069..9a4102d8a 100644 --- a/nonmatching/game/func_002DC3C8.c +++ b/nonmatching/game/func_002DC3C8.c @@ -1,6 +1,6 @@ /* 9 November 2026 (o16): measured with the catalogue row shifted +8 (to 0x002DC3D0, 848 bytes), * the C below equals retail in all 214 instructions (opcodes and registers; relocations not - * compared). The +8 shift itself stays unproved under docs/MATCHING-BLOCKERS.md "Entry still + * compared). The +8 shift itself stays unproved under docs/contributing/matching-blockers.md "Entry still * unproved" (no traced entry; a store at the start is not an entry witness); the owner chose to * keep the 856-byte row. Reopen only on a traced entry or object-origin evidence. */ /* 8 November 2026: no RAC1/RAC4 twin. ~/rac1-decomp/src and ~/rac4-decomp have no timed text-page state machine with these text ids, timers (180/240/300) or the 0x900 pad test; the open question stays the catalogue entry (+0 vs +8). */ diff --git a/nonmatching/game/func_002DE7F0.c b/nonmatching/game/func_002DE7F0.c index 847dd27c3..14a1e8ac2 100644 --- a/nonmatching/game/func_002DE7F0.c +++ b/nonmatching/game/func_002DE7F0.c @@ -2,5 +2,5 @@ * Not an independently callable row. The extent follows a completed return, * has no return of its own, and runs into the next initializer. The static scan * found no direct branch/call, aligned pointer or straight-line address - * construction to any placement. See docs/LEVELS.md for the retained evidence. + * construction to any placement. See docs/build/levels.md for the retained evidence. */ diff --git a/nonmatching/game/func_003411B0.c b/nonmatching/game/func_003411B0.c index 336d2dc33..4933c8b40 100644 --- a/nonmatching/game/func_003411B0.c +++ b/nonmatching/game/func_003411B0.c @@ -2,6 +2,6 @@ * Not an independently callable function. The two stores follow a completed * return and alignment nop, then fall through into the next function's prologue. * No direct code, aligned data-pointer or straight-line address-construction - * reference reaches this extent in any placement. See docs/LEVELS.md for the + * reference reaches this extent in any placement. See docs/build/levels.md for the * retained catalogue-boundary evidence. */ diff --git a/nonmatching/game/func_00342BE8.c b/nonmatching/game/func_00342BE8.c index 7ff52e723..b24840ed7 100644 --- a/nonmatching/game/func_00342BE8.c +++ b/nonmatching/game/func_00342BE8.c @@ -13,7 +13,7 @@ /* 9 November 2026 (o74, -dS -dR -fsched-verbose-6, retained C re-measured 900/900, 2 words +0x230/+0x234): sched2 block 6 t=4 ready 271 249 256 266: y1 store 266 and x1 store 256 tie on priority 4/4, class 3/3 (last insn 269 li 1), dependents 1/1 (277); LUID from sched1 gives 266 first. Sched1: 256 is ready only at t=4 (width load 252 latency 2, then 254 at t=3); at t=3 the memory slot takes 266 (prio 4, weight -1 by REG_DEAD r179) over 249 (prio 4, weight 0); 261 (y0) already went at t=2 beside 264 (li 340, prio 5). Retail's order needs the t=3 memory slot filled by 261 (so 261 not issued at t=2) or 266 made ready after t=3 (264 issued after t=2); every honest store order ties as before. No C candidate found that does this; nothing new compiled. */ /* 9 November 2026, EE call-contract audit: the retained alpha declaration is * a bank-preserving argument interleaving permitted by AGENTS.md and - * docs/COMPILER-NOTES.md, not an omitted/reordered physical argument. Both + * docs/build/compiler-notes.md, not an omitted/reordered physical argument. Both * forms pass object/limit in a0/a1 and low/current/high/scale in f12..f15, * with no stack arguments and the same int return. This supersedes the * historical "false prototype" classification below; it does not establish diff --git a/nonmatching/game/func_0034B2C8.c b/nonmatching/game/func_0034B2C8.c index e8c7e796e..9a3fb1feb 100644 --- a/nonmatching/game/func_0034B2C8.c +++ b/nonmatching/game/func_0034B2C8.c @@ -99,7 +99,7 @@ /* 7 November 2026: compile as its own unit (src/game/0034B2C8.c) with -O2 -G0 -ffunction-sections * -frac2-keep-float-pun-source. 6048 of 6048 bytes, 1508 of 1512 instructions aligned, 12 words differ * in place, 0 of 28 places. All 16 colours are right with the one helper hud_bits_34B2C8, the double-use - * colours and 0x1EFF included (docs/COMPILER-NOTES.md), and the spill slots and FPR numbers follow. The two + * colours and 0x1EFF included (docs/build/compiler-notes.md), and the spill slots and FPR numbers follow. The two * items left are not colours. In both, sched2 puts a set of a saved register into a jal slot where retail * has the store that reads the old value there, and the set after the jal: * 1. before func_00335F20: ours `sw $s5,0x48($sp); sw $v1,0x30($sp); jal; move $s5,$0`, diff --git a/nonmatching/game/func_0034CEA0.c b/nonmatching/game/func_0034CEA0.c index d3d98867d..6446cd6c0 100644 --- a/nonmatching/game/func_0034CEA0.c +++ b/nonmatching/game/func_0034CEA0.c @@ -5,7 +5,7 @@ /* 8 November 2026 (Sonnet round, o13): retail is li a1,7; movz s0,a1,v0, so `previous += 8` (addiu v1,s1,7) is not retail's spelling, though it is equivalent C. Plain `previous = i - 1; if (previous < 0) previous = 7;` and 12 other forms (ternaries, ==-1, i<1, (i+7)%8, &7) let pass-1 loop hoist the 7 (life 3) so pass 2 counts 58 and hoists 0x1C (frame 0x110). The honest two-step `previous = i; previous--; if (previous < 0) previous = 7;` (or -= 1, or --previous) gives retail's exact colour-loop bytes and frame 0x100, but 3328 bytes (extra self+K spill reloads near +0x2A0). low movn: retail's jump.c cmove used the x=x form (reg_set_last failed); 7 threshold/test spellings and char/short/long low types keep movz. Lesson-48 move sweep (560 moves on the two-step C) and a 25-min permuter (best honest = seed) found nothing better; best stays the C below. */ /* 8 November 2026 (Sonnet round): best C below is now 3316/3316 bytes, frame 0x100, 0/28 (first difference +0x38). Two fixes: (1) the colour loop constant 0x1C was hoisted because loop pass 2 counted 58 real insns against a threshold of 61 (-dL: 2164 moved at 61, 816 at 58, 842 refused at 55), so i*0x1C became a giv and took a callee-saved register; `if (previous < 0) previous += 8` raises the count to 59 and restores retail's in-loop mult (it costs addiu v1,s1,7 for li 7; p = i; if (--p < 0) p = 7 also gives 59 but reshuffles elsewhere, 3328). (2) a named local bolt = &self->boltWidgets[0] used for func_00349B20, the state test and func_00349AE0 raises its refs from 3 to 4 (tools/regalloc.py priority 4/91, was 1/60, against 2/59 for the self+0x148 copy), so it takes $fp and the self+0x11F4 spill and 0x110 frame go. Left: spill-slot order (391 gcse buckets vs 415; the forbidden 84-dummy/48-asm diagnostic on this C puts 0x20-0x34 and 0x4C-0x70 in retail order), prologue schedule, hero bne delay slot (retail fills from the fallthrough; sda, section(.sdata), nosda and struct declarations of D_001A7A00 do not change it; switch arms do not either), low (low = 1 at the top of the selected block gives retail's movn but +12 bytes). Inline SetAlpha helpers and full ITEM() rereads leave the bucket count at 391. One permuter run (stopped after 12 min, best 4130 against 4815) only found fake hoisted temporaries. */ /* 8 November 2026 (round 12): re-measured on master's compiler (sticky-volatile default, mtc1 nop fix): still 3328/3316, 0/28, frame 0x110. No RAC1/RAC4 twin (0x70202080, 0x60442D00, 0.11666667 absent). Flags: -g, -Wa,--r5900-la-addend=sn, -fno-sched-interblock, -frac2-keep-block-zero-copy, -frac2-hazard-labels change nothing; -fno-strength-reduce 3332, -fmove-all-movables 3356, -fno-rerun-loop-opt 3328 (worse), -fno-expensive-optimizations 3304, -fno-schedule-insns 3312, -fno-gcse 3108. A static inline SetAlpha(w, a) helper for the four single-use alpha merges gives 3324 but keeps the 0x110 frame, the self+0x11F4 spill and the slot order. */ -/* 7 November 2026 (round 9): -fcall-used-hi1/lo1/accum1 do not change the gcse PRE order here; no constant pseudo-number or cuid offset shared by the hash-order near misses fixes it (docs/COMPILER-NOTES.md, patch 0055). */ +/* 7 November 2026 (round 9): -fcall-used-hi1/lo1/accum1 do not change the gcse PRE order here; no constant pseudo-number or cuid offset shared by the hash-order near misses fixes it (docs/build/compiler-notes.md, patch 0055). */ /* 2026-11-03 w02 faithful-contract review, US object checks only. * Profile: -O2 -G8 -ffunction-sections -ffast-math. * Current check.py: 3328/3316 bytes,0/28 places. diff --git a/nonmatching/levels/func_L00_002EC230.c b/nonmatching/levels/func_L00_002EC230.c index 3d02b5dc5..ffc1597c6 100644 --- a/nonmatching/levels/func_L00_002EC230.c +++ b/nonmatching/levels/func_L00_002EC230.c @@ -3,6 +3,6 @@ * $sp,$sp,N; nop` epilogue fragments (N = 0x30, 0x80, 0xC0), no prologue and no * `jr $ra`. The body before it ends at 0x002EC22C (`jr $ra; addiu $sp,$sp,0x20`) * and the next function starts at 0x002EC248. Same defect class as the ten rows - * found in 0032EB28.c (docs/COMPILER-NOTES.md, "Catalogue rows that are not + * found in 0032EB28.c (docs/build/compiler-notes.md, "Catalogue rows that are not * functions"): a tools/levels.py extent fix, no C can produce these bytes. * Data for the maintainers, nothing to attempt here. */ diff --git a/nonmatching/levels/func_L00_002ED7F8.c b/nonmatching/levels/func_L00_002ED7F8.c index 9a4bb88d9..284518381 100644 --- a/nonmatching/levels/func_L00_002ED7F8.c +++ b/nonmatching/levels/func_L00_002ED7F8.c @@ -3,5 +3,5 @@ * padding after the body that ends at 0x002ED7F8 (`jr $ra` at 0x002ED7F0 with * `addiu $sp,$sp,0x150` in its slot), cut mid-pair; no prologue and no `jr $ra` in * the row. The - * next function starts at 0x002ED808. Defect class of docs/COMPILER-NOTES.md, + * next function starts at 0x002ED808. Defect class of docs/build/compiler-notes.md, * "Catalogue rows that are not functions": a tools/levels.py fix, no C reaches it. */ diff --git a/nonmatching/levels/func_L00_002EDCB8.c b/nonmatching/levels/func_L00_002EDCB8.c index 24c9d2c0f..c132be0a6 100644 --- a/nonmatching/levels/func_L00_002EDCB8.c +++ b/nonmatching/levels/func_L00_002EDCB8.c @@ -2,7 +2,7 @@ lwc1 $f0,-0x76E0($gp) ; nop ; jr $ra ; mul.s $f0,$f0,$f12. The candidate is 12 bytes with the same load and the same `mul.s` operand order: lwc1 ; jr $ra ; mul.s. The one missing word is the `nop` between the - load and the jump (the wall recorded in docs/COMPILER-NOTES.md). + load and the jump (the wall recorded in docs/build/compiler-notes.md). Measured this wave (wave 8 w06): -O0, -O1, -O2, -O3, -G0, -G8, -fno-schedule-insns, -fno-delayed-branch, -ffast-math, a volatile global and a double read of the global all give the compact three-instruction form diff --git a/nonmatching/levels/func_L00_002EF688.c b/nonmatching/levels/func_L00_002EF688.c index 16d549164..1642584af 100644 --- a/nonmatching/levels/func_L00_002EF688.c +++ b/nonmatching/levels/func_L00_002EF688.c @@ -3,5 +3,5 @@ * after the function whose epilogue ends at 0x002EF684 (`jr $ra` at 0x002EF67C, * `addiu $sp,$sp,0x10` in the slot); the row itself has no prologue and no * `jr $ra`. The next function starts at 0x002EF6A0. Defect class of - * docs/COMPILER-NOTES.md, "Catalogue rows that are not functions": a + * docs/build/compiler-notes.md, "Catalogue rows that are not functions": a * tools/levels.py fix, no C reaches it. */ diff --git a/nonmatching/levels/func_L00_003B7908.c b/nonmatching/levels/func_L00_003B7908.c index c33bf3a04..e03eb0361 100644 --- a/nonmatching/levels/func_L00_003B7908.c +++ b/nonmatching/levels/func_L00_003B7908.c @@ -1,4 +1,4 @@ -/* 156 bytes vs retail 148 (39 instructions vs 37): the loop, the calls and the frame match retail instruction for instruction; the difference is that retail takes the addresses of two $gp-resident globals with one addiu each (addiu s1,$gp,0xBA8 and addiu s3,$gp,0xBB8), while cc1 emits lui+addiu for each. Same ordinary-flow $gp boundary as func_L00_003B8138 (docs/COMPILER-NOTES.md, -G8 .extern route). */ +/* 156 bytes vs retail 148 (39 instructions vs 37): the loop, the calls and the frame match retail instruction for instruction; the difference is that retail takes the addresses of two $gp-resident globals with one addiu each (addiu s1,$gp,0xBA8 and addiu s3,$gp,0xBB8), while cc1 emits lui+addiu for each. Same ordinary-flow $gp boundary as func_L00_003B8138 (docs/build/compiler-notes.md, -G8 .extern route). */ typedef struct OBJ6 { char pad0[0x68]; char *f68; diff --git a/nonmatching/levels/func_L00_003B9290.c b/nonmatching/levels/func_L00_003B9290.c index 5b39bd023..97920c599 100644 --- a/nonmatching/levels/func_L00_003B9290.c +++ b/nonmatching/levels/func_L00_003B9290.c @@ -1,4 +1,4 @@ -/* 72 bytes vs retail 64 (18 instructions vs 16): the loop body matches retail instruction for instruction; the difference is the first load. Retail reads the count straight off $gp (lw a0,0xD8C($gp)); cc1 emits lui+lw of the symbol address. Same ordinary-flow $gp boundary as func_L00_003B8138 (docs/COMPILER-NOTES.md, -G8 .extern route). */ +/* 72 bytes vs retail 64 (18 instructions vs 16): the loop body matches retail instruction for instruction; the difference is the first load. Retail reads the count straight off $gp (lw a0,0xD8C($gp)); cc1 emits lui+lw of the symbol address. Same ordinary-flow $gp boundary as func_L00_003B8138 (docs/build/compiler-notes.md, -G8 .extern route). */ typedef struct OBJ3 { char pad0[0x98]; int f98; diff --git a/nonmatching/levels/func_L02_003C43F0.c b/nonmatching/levels/func_L02_003C43F0.c index ca598ed3e..9103e64c2 100644 --- a/nonmatching/levels/func_L02_003C43F0.c +++ b/nonmatching/levels/func_L02_003C43F0.c @@ -207,7 +207,7 @@ void func_L02_003C43F0(char *obj) { * only because they are locals, as in the siblings' `zero`. * What still differs: * - retail saves $s1 and never uses it (obj is in $s2); here obj takes $s1 and the - * function is two words short (docs/MATCHING-BLOCKERS.md, "A register saved and never + * function is two words short (docs/contributing/matching-blockers.md, "A register saved and never * used"; func_00326A10 is the same family). * - retail sets every call's constant arguments after the rounding chain * (lwc1/mul/add/cvt/mfc1 v1/mtc1/cvt), as if a block boundary followed each diff --git a/nonmatching/levels/func_L03_003564C0.c b/nonmatching/levels/func_L03_003564C0.c index 7752ce6f2..0409a9906 100644 --- a/nonmatching/levels/func_L03_003564C0.c +++ b/nonmatching/levels/func_L03_003564C0.c @@ -15,7 +15,7 @@ The body proper is the 272 bytes from 03:003564D0 and 19:00352E28. The same body is the level 00 row func_L00_00354DF8 (272 bytes at 00:00354DF8), whose extent the catalogue already starts after the same 16-byte prefix - - docs/LEVELS.md names that correction ("00354DE8 to 00354DF8") among the + docs/build/levels.md names that correction ("00354DE8 to 00354DF8") among the three measured Aranos entries. The two rows here carry the prefix, so they are 16 bytes too long and start 16 bytes too early. diff --git a/nonmatching/levels/func_L05_00416E08.c b/nonmatching/levels/func_L05_00416E08.c index 01a30e44a..d045d2e71 100644 --- a/nonmatching/levels/func_L05_00416E08.c +++ b/nonmatching/levels/func_L05_00416E08.c @@ -1,6 +1,6 @@ /* Matched byte for byte, but not landable: this body needs `qcopy`/`qzero` from include/common.h, which are inline assembly, and AGENTS.md's "Plain C" rule (no inline - assembly in a function) forbids that as a matching route - docs/COMPILER-NOTES.md says it + assembly in a function) forbids that as a matching route - docs/build/compiler-notes.md says it in as many words. Kept here as a starting point: the shape is right, and only the 128-bit copies would need a plain-C spelling that neither `mode(TI)` nor an aggregate gives yet. Removed from src/ on 2026-10-13. */ diff --git a/nonmatching/levels/func_L11_002FE4C0.c b/nonmatching/levels/func_L11_002FE4C0.c index 4ddf57e6d..c29850f53 100644 --- a/nonmatching/levels/func_L11_002FE4C0.c +++ b/nonmatching/levels/func_L11_002FE4C0.c @@ -4,6 +4,6 @@ no `jr $ra` at all. The rows either side fit exactly around it: func_L02_002EF968 ends at 0x002FE4BC, func_L00_002EC248 begins at 0x002FE4D0, leaving the 4-byte align gaps at 0x002FE4BC and 0x002FE4CC. Same class as func_L00_002ED7F8 (`addiu $sp,$sp,0x150; nop; - addiu $sp,$sp,0x30`, documented in docs/COMPILER-NOTES.md) and as func_L05_0037B6C0 in this + addiu $sp,$sp,0x30`, documented in docs/build/compiler-notes.md) and as func_L05_0037B6C0 in this lot. Maintainer data: no C spelling; absorb into the neighbour or drop the row. */ diff --git a/nonmatching/levels/func_L20_0039BEC8.c b/nonmatching/levels/func_L20_0039BEC8.c index c1e2d7a59..4cc051945 100644 --- a/nonmatching/levels/func_L20_0039BEC8.c +++ b/nonmatching/levels/func_L20_0039BEC8.c @@ -21,7 +21,7 @@ /* 8 November 2026 (Sonnet round, lesson 48 sweep): every single statement move of 1-6 places inside a run of simple statements, every top-level simple statement moved up to 6 top-level items, and pairs of the best singles: no variant beats the base (27 words); moves either tie or get worse. */ /* 8 November 2026 (named-local slot order, the func_00281A08 technique): slots 0x148..0x168 hold, in retail, &normal, &packet.color, rotated, &uv.x, &uv.y, rotated+12, (obj+0x10 at 0x160), &direction, &tangent; ours put &tangent first, the rest in the same order. Named locals take slots before every PRE register (arg 0x140, vars 0x144 already do), so one named pointer can only move &tangent earlier, not last; moving it last would need eight named stack pointers, which get hard registers or fold back (as in func_L00_0038E880 and func_L13_0041AD00). Not pursued further. */ /* 8 November 2026 (round 12): size is already 1252/1252; only the PRE spill-slot order and the loop-3 neg.s register differ. gcse hash of (plus (reg R) c) is (14029 + R + c) mod N (frame reg 1); over N 11..999 only N=129 (max_cuid 256..259, ours 268..271) gives retail's order, with the obj+0x10 base pseudo in 113..127. A late `char *object = arg;` after loop 1 (top uses via arg) gives base 149, plus per-iteration pos/color pointers 133, code unchanged but the insn count is not. Pointer-cursor loops reach base 113/115 and 261..265 insns but give 1212/1228 bytes (retail reloads sp+0x40/0x50/0x54 from PRE slots before loop 2, so its loops are indexed). Single-table `D_L20_001AEC80_gp[i].y` / `D_L20_001AECB8_gp[i].y` with -Wa,--r5900-la-addend=sn gives the same output as the D_..84/BC NOT_SDA views. No -f flag tried (cse-follow-jumps, expensive-optimizations, force-addr, thread-jumps, ...) gives 129 buckets; the color ternary on loads or an if/else around the loop-3 call changes size. No RAC1 twin found. */ -/* 7 November 2026 (round 9): -fcall-used-hi1/lo1/accum1 do not change the gcse PRE order here; no constant pseudo-number or cuid offset shared by the hash-order near misses fixes it (docs/COMPILER-NOTES.md, patch 0055). */ +/* 7 November 2026 (round 9): -fcall-used-hi1/lo1/accum1 do not change the gcse PRE order here; no constant pseudo-number or cuid offset shared by the hash-order near misses fixes it (docs/build/compiler-notes.md, patch 0055). */ /* * func_L20_0039BEC8 -- non-matching draft * Tier: Claude Opus 5.5, xhigh effort, task v2204 (opus-1). diff --git a/nonmatching/levels/shared/func_L00_0031CDD0.c b/nonmatching/levels/shared/func_L00_0031CDD0.c index 36a8e755e..dddab13ef 100644 --- a/nonmatching/levels/shared/func_L00_0031CDD0.c +++ b/nonmatching/levels/shared/func_L00_0031CDD0.c @@ -5,7 +5,7 @@ retail jal func_00282E20 ; daddu $a0,$sp,$zero ; nop ; nop ; div.s $f4,$f0,$f20 ours jal func_00282E20 ; daddu $a0,$sp,$zero ; div.s $f4,$f0,$f20 cc1's .s emits no marker at all there (`div.s` directly after the delay slot), so this - is the division-padding hole described in docs/COMPILER-NOTES.md item (4): the retail + is the division-padding hole described in docs/build/compiler-notes.md item (4): the retail two-`nop` runs at a division site that the SDK 3.01 assembler does not produce from that sequence, i.e. compiler-emitted and not recovered by this lot. Meaning of the body (for a future pass): buf = a - b (0x00282CF0), t = |buf| diff --git a/nonmatching/levels/shared/func_L00_00329FF0.c b/nonmatching/levels/shared/func_L00_00329FF0.c index 18034cc62..e75ee5ccf 100644 --- a/nonmatching/levels/shared/func_L00_00329FF0.c +++ b/nonmatching/levels/shared/func_L00_00329FF0.c @@ -1,7 +1,7 @@ /* 0 of 27 places match - last line: 00_aranos_tutorial: 100 bytes, expected 108 The body is recovered and every instruction is retail's; the eight missing bytes are the two NOPs the patched assembler puts in front of a `div.s` - (see docs/COMPILER-NOTES.md, the division-erratum section): + (see docs/build/compiler-notes.md, the division-erratum section): retail jal func_00283CE0 ; nop ; mtc1 $s0,$f12 ; nop ; cvt.s.w $f12,$f12 ; nop ; nop ; div.s $f12,$f0,$f12 diff --git a/src/core/0011BEE0-kputchar.c b/src/core/0011BEE0-kputchar.c index 2dfa3a9e1..8eb23dcbd 100644 --- a/src/core/0011BEE0-kputchar.c +++ b/src/core/0011BEE0-kputchar.c @@ -4,7 +4,7 @@ and return it. Both accesses are real hardware registers. The store reaches the return's delay slot through the SDK assembler's slot swap, not through reorg, so this unit needs -Wa,--r5900-swap-branch-slots like the rest of kprintf.o (src/core/0011BF18.c); see - docs/COMPILER-NOTES.md, "SDK objects". */ + docs/build/compiler-notes.md, "SDK objects". */ int func_0011BEE0(int c) { while (SIO_ISR & 0x8000) ; diff --git a/src/core/00131780-bank-g8.c b/src/core/00131780-bank-g8.c index 984b21e60..edb830565 100644 --- a/src/core/00131780-bank-g8.c +++ b/src/core/00131780-bank-g8.c @@ -10,7 +10,7 @@ extern unsigned int D_001A7180 MACRO_ADDR; record (00132028-g8.c): a word callback, 4 bytes of padding and a doubleword argument, as the return callback and its argument at 0x001A74E0/E8 do. This doubleword view of the record stores the argument as its second element, an sda - symbol+offset access, which docs/COMPILER-NOTES.md ("reorg: which global loads may + symbol+offset access, which docs/build/compiler-notes.md ("reorg: which global loads may fill a slot") shows never fills a delay slot: the store stays where retail has it. */ extern long D_001A74D0_1[] __attribute__((sda)); extern int D_001A7140 MACRO_ADDR; diff --git a/src/core/00131DE8.c b/src/core/00131DE8.c index 32a066fa6..0ba02531a 100644 --- a/src/core/00131DE8.c +++ b/src/core/00131DE8.c @@ -2,7 +2,7 @@ * the init request. A game-compiler unit of its own (00131780.c is an SDK unit). The last stores * before the request's jal are the plain payload stores and then the volatile D_001A7110 store; * the compiler's default set-side volatile rule (GCC 2.95's) leaves that slot empty, as retail - * does, where the EE tree's rule fills it with `move $a2,$sp` (docs/COMPILER-NOTES.md). */ + * does, where the EE tree's rule fills it with `move $a2,$sp` (docs/build/compiler-notes.md). */ #include "common.h" /* Adapted from RAC1's GPL-3.0 989snd.c StartSoundSystem reconstruction, diff --git a/src/game/00284268.c b/src/game/00284268.c index 582fcc96d..389aa40ca 100644 --- a/src/game/00284268.c +++ b/src/game/00284268.c @@ -3,7 +3,7 @@ * own for -fno-rerun-loop-opt and -frac2-no-extra-increment-givs (config/cflags.txt): every loop * advances its cursor several times per iteration and retail keeps each increment, as GCC 2.95's * strength reduction does, where this tree's default turns all but the last into givs - * (docs/COMPILER-NOTES.md). The two D_001A6860 loops index q[n] with n++ after each store, so + * (docs/build/compiler-notes.md). The two D_001A6860 loops index q[n] with n++ after each store, so * the cursor is a reduced giv of n whose init comes last in the pre-header; the other five use * *p++. */ typedef unsigned int u128_FAB40 __attribute__((mode(TI))); diff --git a/src/game/00298F00.c b/src/game/00298F00.c index 8b5777852..93404a009 100644 --- a/src/game/00298F00.c +++ b/src/game/00298F00.c @@ -6,7 +6,7 @@ * What it takes: * - The directory loop reads the slot digit as entry->name[4] from the entry pointer * (lbu 0x24(entry)); that needs a compiler without giv recombination, as retail's - * (tools/compiler/transformers/no_recombine_givs.py, docs/COMPILER-NOTES.md). + * (tools/compiler/transformers/no_recombine_givs.py, docs/build/compiler-notes.md). * - Error chains: sched1 puts the last of a run of independent zero stores first, so the * source order is state, cur, busy (state 3's chain is err, state, cur, busy), which makes * the state-25 reset block the cross-jump target. diff --git a/src/game/002A7750.c b/src/game/002A7750.c index b4e0bd504..556411f5c 100644 --- a/src/game/002A7750.c +++ b/src/game/002A7750.c @@ -9,7 +9,7 @@ typedef float f32; /* The campaign: call-bearing bodies reproduced by the reconstructed toolchain (2.9-ee + refined gas) -- sd saves in 8-byte slots, no sibcall, the s32 return prototype determines v0 versus v1, and || chains - with a shared exit. Measurements and evidence: docs/COMPILER-NOTES.md. */ + with a shared exit. Measurements and evidence: docs/build/compiler-notes.md. */ /* func_002A77E0 : random draw bounded by the argument */ extern s32 func_001163B0(void); diff --git a/src/game/002BA0E0.c b/src/game/002BA0E0.c index 1743d58a0..2461809f1 100644 --- a/src/game/002BA0E0.c +++ b/src/game/002BA0E0.c @@ -2,7 +2,7 @@ * of its own for -frac2-frame-pointer-liveness and -frac2-no-static-chain-base (config/cflags.txt): * the first deletes a dead `lui $fp`; without the second the table base in $v0 has no alias base * after reload, so every store depends on the prologue's saves and sched2 issues them at the top, - * where retail has them among the stores (docs/COMPILER-NOTES.md). */ + * where retail has them among the stores (docs/build/compiler-notes.md). */ extern void func_002B9288(); extern void func_002B9380(); extern void func_002BA6A0(); diff --git a/src/game/002BEBC0-hazard-labels.c b/src/game/002BEBC0-hazard-labels.c index 120f777e5..06cbba67b 100644 --- a/src/game/002BEBC0-hazard-labels.c +++ b/src/game/002BEBC0-hazard-labels.c @@ -3,7 +3,7 @@ * while at full size or growing, 0.15 while shrinking) and eases towards the camera. * * Retail keeps the branch label of the limit selection on its mtc1 hazard nop, so the - * unit is built with -frac2-hazard-labels (docs/COMPILER-NOTES.md, "Labels on hazard + * unit is built with -frac2-hazard-labels (docs/build/compiler-notes.md, "Labels on hazard * nops"). The two limit cases are separate statements: cross-jumping merges their * common compare, which keeps the two constant loads in their own arms. */ #include "common.h" diff --git a/src/game/002BF6F0.c b/src/game/002BF6F0.c index 02362af9a..36b611235 100644 --- a/src/game/002BF6F0.c +++ b/src/game/002BF6F0.c @@ -1151,7 +1151,7 @@ void func_002C3280(u8 *self) n = *(int *)(self + 4) & 0xFFFFFF; a = func_00283CE0_4(*(short *)(self + 10) - 1); /* One statement: retail has no line label between the second call and the - division, so no division padding (docs/COMPILER-NOTES.md). */ + division, so no division padding (docs/build/compiler-notes.md). */ *(int *)(self + 4) = func_00283D38_3(n, *(int *)(self + 4), a / func_00283CE0_4(*(short *)(self + 10))); } diff --git a/src/game/002CC528-g8.c b/src/game/002CC528-g8.c index 712c68097..3be790a8a 100644 --- a/src/game/002CC528-g8.c +++ b/src/game/002CC528-g8.c @@ -2,7 +2,7 @@ * otherwise &D_001AB658, and returns 0. Retail reaches it through data pointers. * * The catalogue entry is 0x002CC530 (32 bytes): the 8 bytes at 0x002CC528 are filler - * left after the previous body (docs/LEVELS.md). Retail addresses the two bytes via $gp + * left after the previous body (docs/build/levels.md). Retail addresses the two bytes via $gp * and the flag as an absolute `lui`/`lw` into its own destination register, which is * -G8 with sda/nosda declarations plus -mno-split-addresses (the assembler expands * `lw $3,D_001A7318` using $3 as the address temporary). Under that profile the diff --git a/src/game/002F5220-7320.c b/src/game/002F5220-7320.c index 3f0e98b61..16fecf1aa 100644 --- a/src/game/002F5220-7320.c +++ b/src/game/002F5220-7320.c @@ -1,4 +1,4 @@ -/* func_002F7320: needs -frac2-keep-block-zero-copy (cse keeps `i = result`); see docs/COMPILER-NOTES.md. */ +/* func_002F7320: needs -frac2-keep-block-zero-copy (cse keeps `i = result`); see docs/build/compiler-notes.md. */ #include "common.h" typedef struct { diff --git a/src/game/00308600-spring.c b/src/game/00308600-spring.c index edd00a9bb..ee7f5f50f 100644 --- a/src/game/00308600-spring.c +++ b/src/game/00308600-spring.c @@ -1,4 +1,4 @@ -/* Rests on the redundant-insn rule of docs/COMPILER-NOTES.md (volatile asm): the last jump of the +/* Rests on the redundant-insn rule of docs/build/compiler-notes.md (volatile asm): the last jump of the mode == 0 arm lands on the restore of $s0, across a qcopy. */ #include "common.h" diff --git a/src/game/0030A348-tether-g8.c b/src/game/0030A348-tether-g8.c index 0d097d28c..7a6deef76 100644 --- a/src/game/0030A348-tether-g8.c +++ b/src/game/0030A348-tether-g8.c @@ -341,7 +341,7 @@ void func_0030A348(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_00229170[0][0], anchor); diff --git a/src/game/00349D58.c b/src/game/00349D58.c index a9c45ae99..d8ae149c3 100644 --- a/src/game/00349D58.c +++ b/src/game/00349D58.c @@ -1,7 +1,7 @@ /* func_00349D58: initialises the HUD widget's text, sprites and fade keys. A unit of its own for * -frac2-keep-float-pun-source (config/cflags.txt): a colour read twice keeps its word in $v0, set * after the previous call, with one mtc1 into a saved FPR; a colour read once folds to li.s - * (docs/COMPILER-NOTES.md). WriteBits is the one spelling for every colour. Same-file C calls and + * (docs/build/compiler-notes.md). WriteBits is the one spelling for every colour. Same-file C calls and * prototypes differ from 00345358.c, so it cannot live there. */ static __inline__ float WriteBits(unsigned int bits) { diff --git a/src/levels/00_aranos_tutorial/L00_00393148-tether-g8.c b/src/levels/00_aranos_tutorial/L00_00393148-tether-g8.c index 7565bf6b4..37e95e9de 100644 --- a/src/levels/00_aranos_tutorial/L00_00393148-tether-g8.c +++ b/src/levels/00_aranos_tutorial/L00_00393148-tether-g8.c @@ -341,7 +341,7 @@ void func_L00_00393148(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L00_00239950[0][0], anchor); diff --git a/src/levels/01_oozla/L01_002BEB90-nsa-g8.c b/src/levels/01_oozla/L01_002BEB90-nsa-g8.c index f06db4761..086cd0955 100644 --- a/src/levels/01_oozla/L01_002BEB90-nsa-g8.c +++ b/src/levels/01_oozla/L01_002BEB90-nsa-g8.c @@ -8,7 +8,7 @@ extern int D_L01_001AEC38 __attribute__((sda)); /* The level's mode word: one read, which reorg copies into a branch delay slot ($gp form there) - and the fall-through keeps as the absolute macro (docs/COMPILER-NOTES.md, MACRO_ADDR). */ + and the fall-through keeps as the absolute macro (docs/build/compiler-notes.md, MACRO_ADDR). */ extern int D_L01_001B2088 MACRO_ADDR; /* The one-shot flag byte, absolute: retail leaves the preceding bgtz slot empty, so cc1 must not diff --git a/src/levels/01_oozla/L01_002C37C8.c b/src/levels/01_oozla/L01_002C37C8.c index e10d31f64..2197c5bc6 100644 --- a/src/levels/01_oozla/L01_002C37C8.c +++ b/src/levels/01_oozla/L01_002C37C8.c @@ -12,7 +12,7 @@ The unit's flags (config/cflags.txt): -G16 -Wa,-G16 for the 16-byte sda records, -frac2-zero-ti-por, -frac2-qcopy -frac2-qcopy-barrier for the 128-bit copies, -frac2-hazard-labels for the branches onto compare hazard nops. - See docs/COMPILER-NOTES.md for each option. */ + See docs/build/compiler-notes.md for each option. */ #include "common.h" typedef int TI128 __attribute__((mode(TI))); diff --git a/src/levels/01_oozla/L01_00388FD0-tether-g8.c b/src/levels/01_oozla/L01_00388FD0-tether-g8.c index a62dff87f..33ef2bd28 100644 --- a/src/levels/01_oozla/L01_00388FD0-tether-g8.c +++ b/src/levels/01_oozla/L01_00388FD0-tether-g8.c @@ -341,7 +341,7 @@ void func_L01_00388FD0(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L01_0023A050[0][0], anchor); diff --git a/src/levels/01_oozla/L01_003BAFB8-g8.c b/src/levels/01_oozla/L01_003BAFB8-g8.c index 0bd03e94e..5b2b183f6 100644 --- a/src/levels/01_oozla/L01_003BAFB8-g8.c +++ b/src/levels/01_oozla/L01_003BAFB8-g8.c @@ -5,7 +5,7 @@ * strip, a far quad, and two scrolling layers of grid cells around the camera. As in RAC1, the * scroll table is declared in .sdata, so its address is one `la` macro instruction rather than a * split %hi/%lo pair; that makes the loop optimizer hoist both per-layer scroll pointers. This - * unit needs -g for the measured division padding (docs/COMPILER-NOTES.md). + * unit needs -g for the measured division padding (docs/build/compiler-notes.md). */ typedef unsigned int Quadword __attribute__((mode(TI))); diff --git a/src/levels/01_oozla/L01_003C0408-g8.c b/src/levels/01_oozla/L01_003C0408-g8.c index da05dbf34..7a95610f2 100644 --- a/src/levels/01_oozla/L01_003C0408-g8.c +++ b/src/levels/01_oozla/L01_003C0408-g8.c @@ -4,7 +4,7 @@ it over the height store, the height temporary takes $f0 as in retail, and the -1000 fallback's store (through $f1) is not cross-jumped with it. -frac2-hazard-labels keeps the forward branch into the division on its hazard nop - (docs/COMPILER-NOTES.md, Labels on hazard nops). */ + (docs/build/compiler-notes.md, Labels on hazard nops). */ #include "common.h" extern float func_00282E80(void *, void *); @@ -26,7 +26,7 @@ typedef struct { } L01ArenaRow; /* The table pointer, read with retail's absolute macro load at each use - (docs/COMPILER-NOTES.md, incomplete sda array read as element 0). */ + (docs/build/compiler-notes.md, incomplete sda array read as element 0). */ extern L01ArenaRow *D_L01_001B264C[] __attribute__((sda)); extern float D_L01_001AFED0 __attribute__((sda)); diff --git a/src/levels/01_oozla/L01_003C09C0-g8.c b/src/levels/01_oozla/L01_003C09C0-g8.c index 8e3e3ac64..9d25e600f 100644 --- a/src/levels/01_oozla/L01_003C09C0-g8.c +++ b/src/levels/01_oozla/L01_003C09C0-g8.c @@ -6,7 +6,7 @@ load behind them). Tables are indexed as (index << n) + (s32)base, the operand order retail adds in. Two addressing forms of the 0x1B255C and 0x1B264C pointers: the _sda names are the $gp form. -frac2-hazard-labels gives the forward branch onto the 17.0f compare its hazard nop - target (docs/COMPILER-NOTES.md, Labels on hazard nops). */ + target (docs/build/compiler-notes.md, Labels on hazard nops). */ #include "common.h" typedef union { u8 b[0x800]; s8 c[0x800]; s16 h[0x400]; u16 uh[0x400]; s32 i[0x200]; u32 u[0x200]; f32 f[0x200]; void *p[0x200]; } UView; extern int func_002761F8(void *,void *,int,void *,void *); diff --git a/src/levels/02_maktar_nebula/L02_0039D708-tether-g8.c b/src/levels/02_maktar_nebula/L02_0039D708-tether-g8.c index 4b0973a28..cb562ac07 100644 --- a/src/levels/02_maktar_nebula/L02_0039D708-tether-g8.c +++ b/src/levels/02_maktar_nebula/L02_0039D708-tether-g8.c @@ -341,7 +341,7 @@ void func_L02_0039D708(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L02_0023B090[0][0], anchor); diff --git a/src/levels/02_maktar_nebula/L02_003CB0F0.c b/src/levels/02_maktar_nebula/L02_003CB0F0.c index 39344fb13..46d3469e3 100644 --- a/src/levels/02_maktar_nebula/L02_003CB0F0.c +++ b/src/levels/02_maktar_nebula/L02_003CB0F0.c @@ -1,6 +1,6 @@ /* func_L02_003CB0F0 (5,824 bytes; twin func_L15_003DBB08). The last spot, in case 11, was * D_L02_001B30C8's absolute macro read (lui $v1; lw $v1 in one register, before the sra of - * self): the sda incomplete-array declaration at -G0 gives it (docs/COMPILER-NOTES.md), and + * self): the sda incomplete-array declaration at -G0 gives it (docs/build/compiler-notes.md), and * retail compares the table value first. * The shape (big13): case bodies of the second switch in jump-table address order; * every state change written out per arm (retail cross-jumps the func_002A7750 tails); hero diff --git a/src/levels/02_maktar_nebula/L02_003EAE10-g8.c b/src/levels/02_maktar_nebula/L02_003EAE10-g8.c index 8cfe86daa..d824610ef 100644 --- a/src/levels/02_maktar_nebula/L02_003EAE10-g8.c +++ b/src/levels/02_maktar_nebula/L02_003EAE10-g8.c @@ -1,7 +1,7 @@ #include "include_asm.h" /* func_L02_003EAE10 is a unit of its own: it reads $gp data (hence -G8) and needs the opt-in - clear profile (-frac2-zero-ti-barrier, docs/COMPILER-NOTES.md): with it, the load of the + clear profile (-frac2-zero-ti-barrier, docs/build/compiler-notes.md): with it, the load of the state's +0x3C float follows the 16-byte clear at once, as retail has it. */ typedef unsigned char u8; diff --git a/src/levels/02_maktar_nebula/L02_00411D18.c b/src/levels/02_maktar_nebula/L02_00411D18.c index 73960c396..1f0eae82b 100644 --- a/src/levels/02_maktar_nebula/L02_00411D18.c +++ b/src/levels/02_maktar_nebula/L02_00411D18.c @@ -1,6 +1,6 @@ /* func_L02_00411D18 is a unit of its own: $s2 holds a TImode value, and retail saves it with `sq` in a 16-byte slot below the 8-byte GPR saves (-frac2-wide-saves-below, - docs/COMPILER-NOTES.md). The option keys on that mode, and the widened save mode + docs/build/compiler-notes.md). The option keys on that mode, and the widened save mode carries over to later functions of a unit, so nothing else shares this file. */ #include "common.h" diff --git a/src/levels/03_endako/L03_002BF8F0.c b/src/levels/03_endako/L03_002BF8F0.c index f0372f922..7f509ddea 100644 --- a/src/levels/03_endako/L03_002BF8F0.c +++ b/src/levels/03_endako/L03_002BF8F0.c @@ -6,7 +6,7 @@ states. Ported from that match by renaming the per-level symbols and pruning. The unit's flags (config/cflags.txt) are the ones L20_002CD928 needs, minus the two for its - state 0x7C (see its header and docs/COMPILER-NOTES.md): -g, -ffast-math, -frac2-zero-ti-por, + state 0x7C (see its header and docs/build/compiler-notes.md): -g, -ffast-math, -frac2-zero-ti-por, -frac2-hazard-labels. Dropping any of them breaks the match. */ #include "common.h" diff --git a/src/levels/03_endako/L03_00399230-tether-g8.c b/src/levels/03_endako/L03_00399230-tether-g8.c index ef1c241e9..d37911fe8 100644 --- a/src/levels/03_endako/L03_00399230-tether-g8.c +++ b/src/levels/03_endako/L03_00399230-tether-g8.c @@ -341,7 +341,7 @@ void func_L03_00399230(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L03_0023C6D0[0][0], anchor); diff --git a/src/levels/04_barlow/L04_002E04A8.c b/src/levels/04_barlow/L04_002E04A8.c index d95d1d7a1..41b18a535 100644 --- a/src/levels/04_barlow/L04_002E04A8.c +++ b/src/levels/04_barlow/L04_002E04A8.c @@ -14,7 +14,7 @@ -frac2-no-null-pointer-checks (the quadword clears' zero in $a0 is the compiler's one-GPR TImode reload); -frac2-hazard-labels branch targets on the mtc1 hazard nops. - See docs/COMPILER-NOTES.md for each option. + See docs/build/compiler-notes.md for each option. Stack: retail keeps the 0x20-byte vector at 0x20/0x40/0x30 and the matrix at 0x80 in case 0x2B, so that case uses v20, v40, v30 and m80 (v70 only reserves the 0x70 slot). */ diff --git a/src/levels/04_barlow/L04_002EDBC8.c b/src/levels/04_barlow/L04_002EDBC8.c index 0303eafb0..2c4b4eca7 100644 --- a/src/levels/04_barlow/L04_002EDBC8.c +++ b/src/levels/04_barlow/L04_002EDBC8.c @@ -13,7 +13,7 @@ The unit's flags (config/cflags.txt): -G16 -Wa,-G16 for the 16-byte sda records, -frac2-zero-ti-por, -frac2-qcopy -frac2-qcopy-barrier for the 128-bit copies, -frac2-hazard-labels for the branches onto compare hazard nops. - See docs/COMPILER-NOTES.md for each option. */ + See docs/build/compiler-notes.md for each option. */ #include "common.h" typedef int TI128 __attribute__((mode(TI))); diff --git a/src/levels/04_barlow/L04_003B8B60-tether-g8.c b/src/levels/04_barlow/L04_003B8B60-tether-g8.c index 9e96224bb..d6b5ae5fe 100644 --- a/src/levels/04_barlow/L04_003B8B60-tether-g8.c +++ b/src/levels/04_barlow/L04_003B8B60-tether-g8.c @@ -341,7 +341,7 @@ void func_L04_003B8B60(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L04_0023A210[0][0], anchor); diff --git a/src/levels/05_feltzin_system/L05_003A96D8-tether-g8.c b/src/levels/05_feltzin_system/L05_003A96D8-tether-g8.c index 32b8fd67b..6eac1196a 100644 --- a/src/levels/05_feltzin_system/L05_003A96D8-tether-g8.c +++ b/src/levels/05_feltzin_system/L05_003A96D8-tether-g8.c @@ -341,7 +341,7 @@ void func_L05_003A96D8(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L05_0023B4D0[0][0], anchor); diff --git a/src/levels/05_feltzin_system/L05_0041EF00-g8.c b/src/levels/05_feltzin_system/L05_0041EF00-g8.c index cf6c0e55a..caa4c409d 100644 --- a/src/levels/05_feltzin_system/L05_0041EF00-g8.c +++ b/src/levels/05_feltzin_system/L05_0041EF00-g8.c @@ -7,7 +7,7 @@ The row y values go through the HUD label helpers (label_y, label_y3, MenuSpriteY); their tests have no effect on the result. Each do-while(0) group holds real statements (allowed, - docs/COMPILER-NOTES.md); it ends the scheduling block after the reward row's y. */ + docs/build/compiler-notes.md); it ends the scheduling block after the reward row's y. */ extern f32 func_00283CE0(s32); extern s32 func_00283CF0(f32); diff --git a/src/levels/06_notak/L06_003D64C0-tether-g8.c b/src/levels/06_notak/L06_003D64C0-tether-g8.c index 19f881491..4623e4c9b 100644 --- a/src/levels/06_notak/L06_003D64C0-tether-g8.c +++ b/src/levels/06_notak/L06_003D64C0-tether-g8.c @@ -341,7 +341,7 @@ void func_L06_003D64C0(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L06_0023A3D0[0][0], anchor); diff --git a/src/levels/07_siberius/L07_002C3CC0.c b/src/levels/07_siberius/L07_002C3CC0.c index 8e0e40c38..888a8fa55 100644 --- a/src/levels/07_siberius/L07_002C3CC0.c +++ b/src/levels/07_siberius/L07_002C3CC0.c @@ -12,7 +12,7 @@ The unit's flags (config/cflags.txt): -G16 -Wa,-G16 for the 16-byte sda records, -frac2-zero-ti-por, -frac2-qcopy -frac2-qcopy-barrier for the 128-bit copies, -frac2-hazard-labels for the branches onto compare hazard nops. - See docs/COMPILER-NOTES.md for each option. */ + See docs/build/compiler-notes.md for each option. */ #include "common.h" typedef int TI128 __attribute__((mode(TI))); diff --git a/src/levels/07_siberius/L07_00389EB8-tether-g8.c b/src/levels/07_siberius/L07_00389EB8-tether-g8.c index e6386c53f..e49501236 100644 --- a/src/levels/07_siberius/L07_00389EB8-tether-g8.c +++ b/src/levels/07_siberius/L07_00389EB8-tether-g8.c @@ -341,7 +341,7 @@ void func_L07_00389EB8(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L07_00239D90[0][0], anchor); diff --git a/src/levels/07_siberius/L07_0040EC08-g8.c b/src/levels/07_siberius/L07_0040EC08-g8.c index 8145295a4..f8ad3fb22 100644 --- a/src/levels/07_siberius/L07_0040EC08-g8.c +++ b/src/levels/07_siberius/L07_0040EC08-g8.c @@ -1,7 +1,7 @@ /* func_L07_0040EC08: the five path riders' update (Siberius). A unit of its own for -G8 and * -frac2-frame-pointer-liveness (config/cflags.txt): without the option flow keeps $fp, which * holds the rider index here, live after the loop, and reorg annuls the slot of the first - * player-mode test (bnel where retail has bne, docs/COMPILER-NOTES.md). */ + * player-mode test (bnel where retail has bne, docs/build/compiler-notes.md). */ #include "common.h" extern f32 func_002AA2E8(void *,f32,f32); extern s32 func_002E5CF8(s32,s32,void *); diff --git a/src/levels/08_tabora/L08_002BE8E8.c b/src/levels/08_tabora/L08_002BE8E8.c index 1153427a3..370279e8b 100644 --- a/src/levels/08_tabora/L08_002BE8E8.c +++ b/src/levels/08_tabora/L08_002BE8E8.c @@ -2,7 +2,7 @@ * src/levels/18_damosel/L18_002C2D48-reset.c is the level 18 copy). As there, the two arms on * the busy field at +0x340 are one in retail: jump2 cross-jumps them, the branch goes and its * load stays as the dead lw, which also ends the extended block, so later blocks rebuild the - * hero address (docs/MATCHING-BLOCKERS.md, "jump2 cross-jumps two arms that end the same"). + * hero address (docs/contributing/matching-blockers.md, "jump2 cross-jumps two arms that end the same"). * The rebase of `h` before the 0x1494 store is retail's address rebuild there. */ #include "common.h" diff --git a/src/levels/08_tabora/L08_002CB6E8.c b/src/levels/08_tabora/L08_002CB6E8.c index 1f494e69d..b02dfdb3e 100644 --- a/src/levels/08_tabora/L08_002CB6E8.c +++ b/src/levels/08_tabora/L08_002CB6E8.c @@ -14,7 +14,7 @@ -frac2-no-null-pointer-checks (the quadword clears' zero in $a0 is the compiler's one-GPR TImode reload); -frac2-hazard-labels branch targets on the mtc1 hazard nops. - See docs/COMPILER-NOTES.md for each option. + See docs/build/compiler-notes.md for each option. Stack: retail keeps the 0x20-byte vector at 0x20/0x40/0x30 and the matrix at 0x80 in case 0x2B, so that case uses v20, v40, v30 and m80 (v70 only reserves the 0x70 slot). */ diff --git a/src/levels/08_tabora/L08_002D90C8-fn.c b/src/levels/08_tabora/L08_002D90C8-fn.c index 4ea3266bc..945030e0d 100644 --- a/src/levels/08_tabora/L08_002D90C8-fn.c +++ b/src/levels/08_tabora/L08_002D90C8-fn.c @@ -12,7 +12,7 @@ The unit's flags (config/cflags.txt): -G16 -Wa,-G16 for the 16-byte sda records, -frac2-zero-ti-por, -frac2-qcopy -frac2-qcopy-barrier for the 128-bit copies, -frac2-hazard-labels for the branches onto compare hazard nops. - See docs/COMPILER-NOTES.md for each option. */ + See docs/build/compiler-notes.md for each option. */ #include "common.h" typedef int TI128 __attribute__((mode(TI))); diff --git a/src/levels/09_dobbo/L09_002BC490.c b/src/levels/09_dobbo/L09_002BC490.c index 95135c13d..b1ec6ab6e 100644 --- a/src/levels/09_dobbo/L09_002BC490.c +++ b/src/levels/09_dobbo/L09_002BC490.c @@ -21,7 +21,7 @@ -frac2-no-null-pointer-checks (the quadword clears' zero in $a0 is the compiler's one-GPR TImode reload); -frac2-hazard-labels branch targets on the mtc1 hazard nops. - See docs/COMPILER-NOTES.md for each option. */ + See docs/build/compiler-notes.md for each option. */ #include "common.h" typedef int TI128 __attribute__((mode(TI))); diff --git a/src/levels/09_dobbo/L09_002C9738.c b/src/levels/09_dobbo/L09_002C9738.c index 44cd6020c..4d250ccf2 100644 --- a/src/levels/09_dobbo/L09_002C9738.c +++ b/src/levels/09_dobbo/L09_002C9738.c @@ -11,7 +11,7 @@ The unit's flags (config/cflags.txt): -G16 -Wa,-G16 for the 16-byte sda records, -frac2-zero-ti-por, -frac2-qcopy -frac2-qcopy-barrier for the 128-bit copies, -frac2-hazard-labels for the branches onto compare hazard nops. - See docs/COMPILER-NOTES.md for each option. */ + See docs/build/compiler-notes.md for each option. */ #include "common.h" typedef int TI128 __attribute__((mode(TI))); diff --git a/src/levels/09_dobbo/L09_002F8BA0-mover.c b/src/levels/09_dobbo/L09_002F8BA0-mover.c index 488171ca9..551286551 100644 --- a/src/levels/09_dobbo/L09_002F8BA0-mover.c +++ b/src/levels/09_dobbo/L09_002F8BA0-mover.c @@ -4,7 +4,7 @@ D_001A79F0 and the record bases are not the ones the rest of that file uses. Retail reads each of them with the absolute `lui`/`lw` pair in ordinary flow and with one $gp-relative `lw` where reorg puts the load in a branch delay slot; a single MACRO_ADDR declaration gives exactly that - mix (docs/COMPILER-NOTES.md, small data). */ + mix (docs/build/compiler-notes.md, small data). */ /* The trailing flags have no measured fixed extent. */ typedef struct { diff --git a/src/levels/09_dobbo/L09_003910F8-tether-g8.c b/src/levels/09_dobbo/L09_003910F8-tether-g8.c index ed502e6e8..9d5ce4574 100644 --- a/src/levels/09_dobbo/L09_003910F8-tether-g8.c +++ b/src/levels/09_dobbo/L09_003910F8-tether-g8.c @@ -341,7 +341,7 @@ void func_L09_003910F8(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L09_0023B190[0][0], anchor); diff --git a/src/levels/10_hrugis_cloud/L10_003170D0-beam.c b/src/levels/10_hrugis_cloud/L10_003170D0-beam.c index f346f88f7..ea6aa145d 100644 --- a/src/levels/10_hrugis_cloud/L10_003170D0-beam.c +++ b/src/levels/10_hrugis_cloud/L10_003170D0-beam.c @@ -27,7 +27,7 @@ typedef struct { } Moby; /* The moby table. Read as [0] of an incomplete sda array, for the absolute lui/lw - load (docs/COMPILER-NOTES.md). */ + load (docs/build/compiler-notes.md). */ extern Moby *D_L10_001B251C[] __attribute__((sda)); extern u8 D_L10_001B9680[]; diff --git a/src/levels/10_hrugis_cloud/L10_003ABDF8-tether-g8.c b/src/levels/10_hrugis_cloud/L10_003ABDF8-tether-g8.c index d8456d301..b6a8bec09 100644 --- a/src/levels/10_hrugis_cloud/L10_003ABDF8-tether-g8.c +++ b/src/levels/10_hrugis_cloud/L10_003ABDF8-tether-g8.c @@ -341,7 +341,7 @@ void func_L10_003ABDF8(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L10_0023B690[0][0], anchor); diff --git a/src/levels/10_hrugis_cloud/L10_004227A0-g8.c b/src/levels/10_hrugis_cloud/L10_004227A0-g8.c index 07894fa7e..bd18c8fc6 100644 --- a/src/levels/10_hrugis_cloud/L10_004227A0-g8.c +++ b/src/levels/10_hrugis_cloud/L10_004227A0-g8.c @@ -7,7 +7,7 @@ The row y values go through the HUD label helpers (label_y, label_y3, MenuSpriteY); their tests have no effect on the result. Each do-while(0) group holds real statements (allowed, - docs/COMPILER-NOTES.md); it ends the scheduling block after the reward row's y. */ + docs/build/compiler-notes.md); it ends the scheduling block after the reward row's y. */ extern f32 func_00283CE0(s32); extern s32 func_00283CF0(f32); diff --git a/src/levels/11_joba/L11_002D22C8.c b/src/levels/11_joba/L11_002D22C8.c index 6e5095c5c..f9559aa9b 100644 --- a/src/levels/11_joba/L11_002D22C8.c +++ b/src/levels/11_joba/L11_002D22C8.c @@ -3,7 +3,7 @@ one return. Same routine as the Yeedil, Grelbin and Dobbo handlers with a different set of states; state 0x71 comes from the Todano/Damosel copy. - The unit takes the Yeedil unit's flags (config/cflags.txt); see docs/COMPILER-NOTES.md for + The unit takes the Yeedil unit's flags (config/cflags.txt); see docs/build/compiler-notes.md for each option. */ #include "common.h" diff --git a/src/levels/11_joba/L11_003A89E8-tether-g8.c b/src/levels/11_joba/L11_003A89E8-tether-g8.c index daadce17a..d4b399c2a 100644 --- a/src/levels/11_joba/L11_003A89E8-tether-g8.c +++ b/src/levels/11_joba/L11_003A89E8-tether-g8.c @@ -341,7 +341,7 @@ void func_L11_003A89E8(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L11_0023B450[0][0], anchor); diff --git a/src/levels/11_joba/L11_0043E1A0.c b/src/levels/11_joba/L11_0043E1A0.c index aa5900024..31079005c 100644 --- a/src/levels/11_joba/L11_0043E1A0.c +++ b/src/levels/11_joba/L11_0043E1A0.c @@ -306,7 +306,7 @@ extern void func_L11_003F3098(u8 *); /* The class table base read through an incomplete `sda` array: cc1 emits the one-instruction load, which the assembler expands absolute (`lui $r` / `lw $r,%lo($r)`) and which is not - hoisted out of the loop, as retail has it (docs/COMPILER-NOTES.md). */ + hoisted out of the loop, as retail has it (docs/build/compiler-notes.md). */ extern u8 *D_L11_001B359C_base[] __asm__("D_L11_001B359C") __attribute__((sda)); void func_L11_004411C8(u8 *obj) diff --git a/src/levels/12_todano/L12_00398E50-tether-g8.c b/src/levels/12_todano/L12_00398E50-tether-g8.c index f7cf8bd4b..45ee233e2 100644 --- a/src/levels/12_todano/L12_00398E50-tether-g8.c +++ b/src/levels/12_todano/L12_00398E50-tether-g8.c @@ -341,7 +341,7 @@ void func_L12_00398E50(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L12_0023A990[0][0], anchor); diff --git a/src/levels/13_boldan/L13_0039CFF8-tether-g8.c b/src/levels/13_boldan/L13_0039CFF8-tether-g8.c index 74483b7a3..7594c55eb 100644 --- a/src/levels/13_boldan/L13_0039CFF8-tether-g8.c +++ b/src/levels/13_boldan/L13_0039CFF8-tether-g8.c @@ -341,7 +341,7 @@ void func_L13_0039CFF8(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L13_0023A450[0][0], anchor); diff --git a/src/levels/14_aranos_prison/L14_0039F820-tether-g8.c b/src/levels/14_aranos_prison/L14_0039F820-tether-g8.c index 286d0a6d6..858f8b22e 100644 --- a/src/levels/14_aranos_prison/L14_0039F820-tether-g8.c +++ b/src/levels/14_aranos_prison/L14_0039F820-tether-g8.c @@ -341,7 +341,7 @@ void func_L14_0039F820(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L14_0023C190[0][0], anchor); diff --git a/src/levels/14_aranos_prison/L14_003E4EA0-g8.c b/src/levels/14_aranos_prison/L14_003E4EA0-g8.c index 346412c3d..e32eb240b 100644 --- a/src/levels/14_aranos_prison/L14_003E4EA0-g8.c +++ b/src/levels/14_aranos_prison/L14_003E4EA0-g8.c @@ -1,7 +1,7 @@ /* func_L14_003E4EA0 is a unit of its own: it reads four float constants through $gp (hence -g8), and it is built with -g, whose line label in front of the `mul.s` before the second division makes the assembler pad that `div.s` with retail's `nop` (the division-floor class - of docs/COMPILER-NOTES.md, as in func_L23_004160B0). func_00282C48 is `fabs`. The state + of docs/build/compiler-notes.md, as in func_L23_004160B0). func_00282C48 is `fabs`. The state block is read as a halfword key table at +0x00, with the current position at +0x14 and the current key index at +0x16: the span runs from the key to the previous one (index != 0), or to one of three words (index == 0). Computing both results before the two stores gives diff --git a/src/levels/15_gorn/L15_003AF878-tether-g8.c b/src/levels/15_gorn/L15_003AF878-tether-g8.c index 90e3aa295..2e519024f 100644 --- a/src/levels/15_gorn/L15_003AF878-tether-g8.c +++ b/src/levels/15_gorn/L15_003AF878-tether-g8.c @@ -341,7 +341,7 @@ void func_L15_003AF878(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L15_0023B7D0[0][0], anchor); diff --git a/src/levels/15_gorn/L15_003DBB08.c b/src/levels/15_gorn/L15_003DBB08.c index 3b0d28dc7..60504b51a 100644 --- a/src/levels/15_gorn/L15_003DBB08.c +++ b/src/levels/15_gorn/L15_003DBB08.c @@ -1,6 +1,6 @@ /* func_L15_003DBB08 (5,824 bytes), ported from its twin func_L02_003CB0F0. The last spot, in case 11, was * D_L15_001B2148's absolute macro read (lui $v1; lw $v1 in one register, before the sra of - * self): the sda incomplete-array declaration at -G0 gives it (docs/COMPILER-NOTES.md), and + * self): the sda incomplete-array declaration at -G0 gives it (docs/build/compiler-notes.md), and * retail compares the table value first. * The shape (big13): case bodies of the second switch in jump-table address order; * every state change written out per arm (retail cross-jumps the func_002A7750 tails); hero diff --git a/src/levels/15_gorn/L15_0042EFE0-g8.c b/src/levels/15_gorn/L15_0042EFE0-g8.c index 10b63502d..127b09fae 100644 --- a/src/levels/15_gorn/L15_0042EFE0-g8.c +++ b/src/levels/15_gorn/L15_0042EFE0-g8.c @@ -7,7 +7,7 @@ The row y values go through the HUD label helpers (label_y, label_y3, MenuSpriteY); their tests have no effect on the result. Each do-while(0) group holds real statements (allowed, - docs/COMPILER-NOTES.md); it ends the scheduling block after the reward row's y. */ + docs/build/compiler-notes.md); it ends the scheduling block after the reward row's y. */ extern f32 func_00283CE0(s32); extern s32 func_00283CF0(f32); diff --git a/src/levels/16_snivelak/L16_003F5708-g8.c b/src/levels/16_snivelak/L16_003F5708-g8.c index 8cc30ac7a..0366498e3 100644 --- a/src/levels/16_snivelak/L16_003F5708-g8.c +++ b/src/levels/16_snivelak/L16_003F5708-g8.c @@ -8,7 +8,7 @@ -membedded-data makes cc1 count a small-data reference as two words (no delay slot, no displacement fold), which gives both, unchanged elsewhere: retail puts no $gp access of this function into a slot either. The `sda` incomplete arrays are the bare absolute reads - (COMPILER-NOTES.md); the sized `sda` declarations are the $gp ones. */ + (compiler-notes.md); the sized `sda` declarations are the $gp ones. */ typedef int Quadword __attribute__((mode(TI))); diff --git a/src/levels/17_smolg/L17_003966D8-tether-g8.c b/src/levels/17_smolg/L17_003966D8-tether-g8.c index 99d630c4c..5e1b10afa 100644 --- a/src/levels/17_smolg/L17_003966D8-tether-g8.c +++ b/src/levels/17_smolg/L17_003966D8-tether-g8.c @@ -341,7 +341,7 @@ void func_L17_003966D8(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L17_0023A750[0][0], anchor); diff --git a/src/levels/18_damosel/L18_003B12C0-tether-g8.c b/src/levels/18_damosel/L18_003B12C0-tether-g8.c index 0ba229496..c74b0fcf8 100644 --- a/src/levels/18_damosel/L18_003B12C0-tether-g8.c +++ b/src/levels/18_damosel/L18_003B12C0-tether-g8.c @@ -341,7 +341,7 @@ void func_L18_003B12C0(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L18_0023A350[0][0], anchor); diff --git a/src/levels/18_damosel/L18_004308F0.c b/src/levels/18_damosel/L18_004308F0.c index 055e77c78..e496cf1bb 100644 --- a/src/levels/18_damosel/L18_004308F0.c +++ b/src/levels/18_damosel/L18_004308F0.c @@ -4,7 +4,7 @@ the compare its other path already made, so a forward branch reaches the join label between that c.le.s and its bc1fl. Retail keeps the label on the hazard nop the assembler inserts there; GNU as without the switch moves it behind the nop, and the bc1f at +0xE4 - lands one word late (docs/COMPILER-NOTES.md, labels in front of the hazard nop). */ + lands one word late (docs/build/compiler-notes.md, labels in front of the hazard nop). */ typedef unsigned char u8; typedef unsigned short u16; diff --git a/src/levels/19_grelbin/L19_002BCA50.c b/src/levels/19_grelbin/L19_002BCA50.c index 5b4d66881..5556706e3 100644 --- a/src/levels/19_grelbin/L19_002BCA50.c +++ b/src/levels/19_grelbin/L19_002BCA50.c @@ -20,7 +20,7 @@ -frac2-no-null-pointer-checks (the quadword clears' zero in $a0 is the compiler's one-GPR TImode reload); -frac2-hazard-labels branch targets on the mtc1 hazard nops. - See docs/COMPILER-NOTES.md for each option. */ + See docs/build/compiler-notes.md for each option. */ #include "common.h" typedef int TI128 __attribute__((mode(TI))); diff --git a/src/levels/19_grelbin/L19_002CAAB0-fn.c b/src/levels/19_grelbin/L19_002CAAB0-fn.c index 156c3f762..13339bd6d 100644 --- a/src/levels/19_grelbin/L19_002CAAB0-fn.c +++ b/src/levels/19_grelbin/L19_002CAAB0-fn.c @@ -7,7 +7,7 @@ What the last differences needed (they apply to the family): - case 0x32: retail loads 0x1B4 again for the 1/30 test after the 0xC20 store, which is - gcse letting every store kill every load (docs/COMPILER-NOTES.md), not anything in the C; + gcse letting every store kill every load (docs/build/compiler-notes.md), not anything in the C; - case 0x1: one local per call result (anim0 / anim), giving retail's v0 -> a0 copy order; - case 0x7B: `flag = 0; if (348 != 0) flag = 1;` with literal constants, so jump.c keeps (eq ? 0 : 1) and reload ties the 1 (retail's daddu a0,a1 + movz a0,$zero); @@ -19,7 +19,7 @@ The unit's flags (config/cflags.txt): -G16 -Wa,-G16 for the 16-byte sda records, -frac2-zero-ti-por, -frac2-qcopy -frac2-qcopy-barrier for the 128-bit copies, -frac2-hazard-labels for the branches onto compare hazard nops (0x002CBEE8, 0x002CEA94). - See docs/COMPILER-NOTES.md for each option. */ + See docs/build/compiler-notes.md for each option. */ #include "common.h" typedef int TI128 __attribute__((mode(TI))); diff --git a/src/levels/19_grelbin/L19_00390F78-tether-g8.c b/src/levels/19_grelbin/L19_00390F78-tether-g8.c index 101e37121..8547c885c 100644 --- a/src/levels/19_grelbin/L19_00390F78-tether-g8.c +++ b/src/levels/19_grelbin/L19_00390F78-tether-g8.c @@ -341,7 +341,7 @@ void func_L19_00390F78(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L19_0023A5D0[0][0], anchor); diff --git a/src/levels/20_yeedil/L20_002CD928.c b/src/levels/20_yeedil/L20_002CD928.c index 33fd2ce25..e50343fd6 100644 --- a/src/levels/20_yeedil/L20_002CD928.c +++ b/src/levels/20_yeedil/L20_002CD928.c @@ -11,7 +11,7 @@ -frac2-no-null-pointer-checks block keeps no stray no-op after its last call; (the quadword clears' zero in $a0 (cases 0x0, 0x32) is the compiler's one-GPR TImode reload); -frac2-hazard-labels branch targets on the mtc1 hazard nops (cases 0x32, 0x13). - See docs/COMPILER-NOTES.md for each option. */ + See docs/build/compiler-notes.md for each option. */ #include "common.h" typedef int TI128 __attribute__((mode(TI))); diff --git a/src/levels/20_yeedil/L20_0039F968-g8.c b/src/levels/20_yeedil/L20_0039F968-g8.c index 03996086b..b5dfffe8c 100644 --- a/src/levels/20_yeedil/L20_0039F968-g8.c +++ b/src/levels/20_yeedil/L20_0039F968-g8.c @@ -1,5 +1,5 @@ /* func_L20_0039F968 is a unit of its own: the history-copy loop needs -frac2-mult-add-no-flatten - (docs/COMPILER-NOTES.md) so its source cursor keeps retail's i*400 + (D - 16) grouping. */ + (docs/build/compiler-notes.md) so its source cursor keeps retail's i*400 + (D - 16) grouping. */ #include "common.h" typedef int Quadword __attribute__((mode(TI))); typedef union { Quadword q; float f[4]; } Vector; diff --git a/src/levels/20_yeedil/L20_003B0A88-tether-g8.c b/src/levels/20_yeedil/L20_003B0A88-tether-g8.c index 5272e9e32..131b985ef 100644 --- a/src/levels/20_yeedil/L20_003B0A88-tether-g8.c +++ b/src/levels/20_yeedil/L20_003B0A88-tether-g8.c @@ -341,7 +341,7 @@ void func_L20_003B0A88(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L20_0023CAB0[0][0], anchor); diff --git a/src/levels/20_yeedil/L20_003E6728.c b/src/levels/20_yeedil/L20_003E6728.c index 9698ca582..1e0d95e2f 100644 --- a/src/levels/20_yeedil/L20_003E6728.c +++ b/src/levels/20_yeedil/L20_003E6728.c @@ -1,4 +1,4 @@ -/* func_L20_003E6728: Yeedil's own copy of func_L00_003C5310 (same C); needs -frac2-sticky-volatile-refs, see docs/COMPILER-NOTES.md. */ +/* func_L20_003E6728: Yeedil's own copy of func_L00_003C5310 (same C); needs -frac2-sticky-volatile-refs, see docs/build/compiler-notes.md. */ #include "common.h" /* Own unit: -fmove-all-movables lets loop.c hoist the second strip loop's diff --git a/src/levels/22_dobbo_orbit/L22_002BF1B0.c b/src/levels/22_dobbo_orbit/L22_002BF1B0.c index 93a11f7f1..f7c71977a 100644 --- a/src/levels/22_dobbo_orbit/L22_002BF1B0.c +++ b/src/levels/22_dobbo_orbit/L22_002BF1B0.c @@ -11,7 +11,7 @@ -frac2-no-null-pointer-checks block keeps no stray no-op after its last call; (the quadword clears' zeros in $a0/$v0 are the compiler's one-GPR TImode reload); -frac2-hazard-labels the branch target on the mtc1 hazard nop. - See docs/COMPILER-NOTES.md for each option. + See docs/build/compiler-notes.md for each option. The common 0xF0 tail is GCC's cross-jumping of the inline func_L00_002D34C0 calls, not a source label. Call argument orders follow retail's register setup (same ABI). diff --git a/src/levels/22_dobbo_orbit/L22_003955D8-tether-g8.c b/src/levels/22_dobbo_orbit/L22_003955D8-tether-g8.c index edb7bfb13..34329203d 100644 --- a/src/levels/22_dobbo_orbit/L22_003955D8-tether-g8.c +++ b/src/levels/22_dobbo_orbit/L22_003955D8-tether-g8.c @@ -341,7 +341,7 @@ void func_L22_003955D8(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L22_0023A810[0][0], anchor); diff --git a/src/levels/23_damosel_orbit/L23_002C38B0.c b/src/levels/23_damosel_orbit/L23_002C38B0.c index 7daebf1cc..ee9c7511f 100644 --- a/src/levels/23_damosel_orbit/L23_002C38B0.c +++ b/src/levels/23_damosel_orbit/L23_002C38B0.c @@ -11,7 +11,7 @@ -frac2-no-null-pointer-checks block keeps no stray no-op after its last call; (the quadword clears' zero in $a0/$v0/$a0/$v0 is the compiler's one-GPR TImode reload); -frac2-hazard-labels the branch target on the mtc1 hazard nop at 0x002C46FC. - See docs/COMPILER-NOTES.md for each option. + See docs/build/compiler-notes.md for each option. Function-scope shared_rate and shared_limit: one variable each serves two cases, as retail's register choices show. shared_rate is case 0x00's frame count (state 3) and 0x57's diff --git a/src/levels/23_damosel_orbit/L23_00399C10-tether-g8.c b/src/levels/23_damosel_orbit/L23_00399C10-tether-g8.c index a798cbf32..294c89c64 100644 --- a/src/levels/23_damosel_orbit/L23_00399C10-tether-g8.c +++ b/src/levels/23_damosel_orbit/L23_00399C10-tether-g8.c @@ -341,7 +341,7 @@ void func_L23_00399C10(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L23_0023B1D0[0][0], anchor); diff --git a/src/levels/23_damosel_orbit/L23_0041A3B8-g8.c b/src/levels/23_damosel_orbit/L23_0041A3B8-g8.c index ec5df76b8..57a61d641 100644 --- a/src/levels/23_damosel_orbit/L23_0041A3B8-g8.c +++ b/src/levels/23_damosel_orbit/L23_0041A3B8-g8.c @@ -4,7 +4,7 @@ play sound 0x1A (with the explosion) or 0x21. Returns 1 when the object was handled. The kind 0xF arm groups its effect in a nonempty one-pass statement, as current RAC1 game - C does (docs/COMPILER-NOTES.md, source methods shared with RAC1): its loop notes keep the + C does (docs/build/compiler-notes.md, source methods shared with RAC1): its loop notes keep the `la` of D_L23_001B3850 ahead of the two frame-address arguments. func_L00_0035F5A0 returns int, as its matched definition does. -mno-split-addresses keeps retail's adjacent lui/addiu pairs; D_L23_001B3850 is MACRO_ADDR so each use rematerializes its address. */ diff --git a/src/levels/23_damosel_orbit/L23_00422668.c b/src/levels/23_damosel_orbit/L23_00422668.c index 37fe353ec..5dc5aab28 100644 --- a/src/levels/23_damosel_orbit/L23_00422668.c +++ b/src/levels/23_damosel_orbit/L23_00422668.c @@ -2,7 +2,7 @@ /* func_L23_00422668 is a unit of its own: it needs the opt-in quadword-copy profile (-frac2-qcopy) for the grouped `lq`/`sq` of its else branch, and - -frac2-no-annulled-wide-stores (docs/COMPILER-NOTES.md): reorg's search of that branch's + -frac2-no-annulled-wide-stores (docs/build/compiler-notes.md): reorg's search of that branch's target then stops at the 16-byte copy, so the `beqz` keeps an empty slot and `$a0 = $sp` stays on each path, as retail has it, where it would otherwise take the `$a0` move from behind the copy and fill the return test's `beql` slot from the epilogue. */ diff --git a/src/levels/24_ship_shack/L24_00389210-tether-g8.c b/src/levels/24_ship_shack/L24_00389210-tether-g8.c index 746700d38..c77134e19 100644 --- a/src/levels/24_ship_shack/L24_00389210-tether-g8.c +++ b/src/levels/24_ship_shack/L24_00389210-tether-g8.c @@ -341,7 +341,7 @@ void func_L24_00389210(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L24_00239250[0][0], anchor); diff --git a/src/levels/25_wupash_nebula/L25_003A2378-tether-g8.c b/src/levels/25_wupash_nebula/L25_003A2378-tether-g8.c index f8110823c..a92d5ad4e 100644 --- a/src/levels/25_wupash_nebula/L25_003A2378-tether-g8.c +++ b/src/levels/25_wupash_nebula/L25_003A2378-tether-g8.c @@ -341,7 +341,7 @@ void func_L25_003A2378(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L25_0023B010[0][0], anchor); diff --git a/src/levels/26_jamming_array/L26_00391520-tether-g8.c b/src/levels/26_jamming_array/L26_00391520-tether-g8.c index 7612a8418..77626ae3e 100644 --- a/src/levels/26_jamming_array/L26_00391520-tether-g8.c +++ b/src/levels/26_jamming_array/L26_00391520-tether-g8.c @@ -341,7 +341,7 @@ void func_L26_00391520(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L26_00239710[0][0], anchor); diff --git a/src/levels/30_insomniac_museum/L30_003941D8-tether-g8.c b/src/levels/30_insomniac_museum/L30_003941D8-tether-g8.c index 99f6a1fa9..3faa5e689 100644 --- a/src/levels/30_insomniac_museum/L30_003941D8-tether-g8.c +++ b/src/levels/30_insomniac_museum/L30_003941D8-tether-g8.c @@ -341,7 +341,7 @@ void func_L30_003941D8(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L30_0023FC90[0][0], anchor); diff --git a/src/levels/shared/L00_002A3B80.c b/src/levels/shared/L00_002A3B80.c index d3854c3f9..392998354 100644 --- a/src/levels/shared/L00_002A3B80.c +++ b/src/levels/shared/L00_002A3B80.c @@ -153,7 +153,7 @@ extern int D_001A79F0; extern void func_L00_002D24B8(void); extern int func_L00_002C42E0(int, int); extern void func_L00_003B09F8(void); -/* Incomplete sda array: retail clears this word as `lui $at; sw` (COMPILER-NOTES.md). */ +/* Incomplete sda array: retail clears this word as `lui $at; sw` (compiler-notes.md). */ extern int D_001A73A0[] __attribute__((sda)); void func_L00_002A4D18(void) diff --git a/src/levels/shared/L00_002A3B88-g8.c b/src/levels/shared/L00_002A3B88-g8.c index 98d067599..6ffed57b8 100644 --- a/src/levels/shared/L00_002A3B88-g8.c +++ b/src/levels/shared/L00_002A3B88-g8.c @@ -2,7 +2,7 @@ * until loading ends. A -G8 unit of its own. In the loop header the plain `sd` of D_001A7200 and * then the volatile T1_COUNT (0x10000800) clear come just before the first jal; the compiler's * default set-side volatile rule (GCC 2.95's) leaves that slot empty, as retail does, where the - * EE tree's rule fills it with the clear of the frame accumulator (docs/COMPILER-NOTES.md). + * EE tree's rule fills it with the clear of the frame accumulator (docs/build/compiler-notes.md). * T1_COUNT (include/common.h) is the EE timer 1 count register, 0x10000800. */ #include "common.h" diff --git a/src/levels/shared/L00_002A9460.c b/src/levels/shared/L00_002A9460.c index 03398d86c..7b404a617 100644 --- a/src/levels/shared/L00_002A9460.c +++ b/src/levels/shared/L00_002A9460.c @@ -1,4 +1,4 @@ -/* func_L00_002A9460: needs -frac2-sticky-volatile-refs (reorg stops at the second qcopy asm); see docs/COMPILER-NOTES.md. */ +/* func_L00_002A9460: needs -frac2-sticky-volatile-refs (reorg stops at the second qcopy asm); see docs/build/compiler-notes.md. */ #include "common.h" /* Its first two callees differ by level (ref_ names); func_L00_002ADF80 is C in diff --git a/src/levels/shared/L00_002C2DE8-g8.c b/src/levels/shared/L00_002C2DE8-g8.c index e294a1034..78bf3f3fc 100644 --- a/src/levels/shared/L00_002C2DE8-g8.c +++ b/src/levels/shared/L00_002C2DE8-g8.c @@ -1,6 +1,6 @@ /* func_L00_002C2DE8 (708 bytes, 27 places), in its own -G8 unit: the frame counter * D_L00_001B19C8 is one .sdata word, read as a lui/lw macro in ordinary flow and through $gp - * in a delay slot (the reload after the % 3 guard, docs/COMPILER-NOTES.md, "a division guard + * in a delay slot (the reload after the % 3 guard, docs/build/compiler-notes.md, "a division guard * ends the redundant-insn search"). */ #include "common.h" typedef struct { diff --git a/src/levels/shared/L00_002FC7A0-00300618-g8.c b/src/levels/shared/L00_002FC7A0-00300618-g8.c index 9ce9428c5..80e8e71d0 100644 --- a/src/levels/shared/L00_002FC7A0-00300618-g8.c +++ b/src/levels/shared/L00_002FC7A0-00300618-g8.c @@ -5,7 +5,7 @@ * this follows: the function-scope scratch int `tmp`, sequential record reads through * char pointers, and header words read as ints (RAC1 reads them as struct members). * The section-8 records are copied by an explicit goto loop: a do loop has its four - * cursor advances turned into address givs by this cc1 (docs/COMPILER-NOTES.md). + * cursor advances turned into address givs by this cc1 (docs/build/compiler-notes.md). */ #include "common.h" diff --git a/src/levels/shared/L00_0031BDC0.c b/src/levels/shared/L00_0031BDC0.c index 9b6262bfe..cbae079e2 100644 --- a/src/levels/shared/L00_0031BDC0.c +++ b/src/levels/shared/L00_0031BDC0.c @@ -2,7 +2,7 @@ /* func_L00_0031BDC0 is a unit of its own: it needs -ffast-math, the flag that lets the fold turn the clamp into the retail's `max.s` and `min.s` (the float MIN/MAX class of - docs/COMPILER-NOTES.md). The flag changes every other body of a shared unit, so the + docs/build/compiler-notes.md). The flag changes every other body of a shared unit, so the function lives here alone. */ typedef float f32; diff --git a/src/levels/shared/L00_00329FF0-g.c b/src/levels/shared/L00_00329FF0-g.c index 2fd60d74b..078e35523 100644 --- a/src/levels/shared/L00_00329FF0-g.c +++ b/src/levels/shared/L00_00329FF0-g.c @@ -2,7 +2,7 @@ /* func_L00_00329FF0 is a unit of its own: it is built with -g, whose line label in front of the `div.s` makes the assembler pad that division with retail's two nops (the - division-floor class of docs/COMPILER-NOTES.md). The division has to be a statement of + division-floor class of docs/build/compiler-notes.md). The division has to be a statement of its own for the label to land on it, and the integer divisor has to be converted on a line of its own too, so that the conversion lands between the call and the division. */ diff --git a/src/levels/shared/L00_0037E898-g8.c b/src/levels/shared/L00_0037E898-g8.c index 8cbb4310c..2bb96adea 100644 --- a/src/levels/shared/L00_0037E898-g8.c +++ b/src/levels/shared/L00_0037E898-g8.c @@ -79,7 +79,7 @@ extern char D_L00_001AEED0[]; /* A six-word block of x/y pairs and a three-word block of colours. Retail reads * element 0 of each through $gp and the other elements as absolute macro loads at * an offset from the block, which keeps them out of the call delay slots - * (docs/COMPILER-NOTES.md, "reorg: which global loads may fill a slot"). */ + * (docs/build/compiler-notes.md, "reorg: which global loads may fill a slot"). */ extern int D_L00_001AEE98[] MACRO_ADDR; extern int D_L00_001AEE98_gp SDATA(D_L00_001AEE98); diff --git a/src/levels/shared/L00_003C5310.c b/src/levels/shared/L00_003C5310.c index ca755cf90..8baa1bf6b 100644 --- a/src/levels/shared/L00_003C5310.c +++ b/src/levels/shared/L00_003C5310.c @@ -1,4 +1,4 @@ -/* func_L00_003C5310: needs -frac2-sticky-volatile-refs (keeps the f20=f21 move after the final copy); see docs/COMPILER-NOTES.md. */ +/* func_L00_003C5310: needs -frac2-sticky-volatile-refs (keeps the f20=f21 move after the final copy); see docs/build/compiler-notes.md. */ #include "common.h" /* Own unit: -fmove-all-movables lets loop.c hoist the second strip loop's diff --git a/src/levels/shared/L00_003EE100-g8.c b/src/levels/shared/L00_003EE100-g8.c index 985100282..4cc207e6f 100644 --- a/src/levels/shared/L00_003EE100-g8.c +++ b/src/levels/shared/L00_003EE100-g8.c @@ -3,7 +3,7 @@ /* A body whose retail bytes address its global through $gp in ordinary flow: the assembler only selects that form when the back end emits the .extern size directive, which the - pinned profile does at -G8 (see docs/COMPILER-NOTES.md). */ + pinned profile does at -G8 (see docs/build/compiler-notes.md). */ extern unsigned char *func_0029EFD0(int size); extern unsigned char *D_L00_001B0A20 __attribute__((sda)); diff --git a/src/levels/shared/L00_003F4C68-g8-dbg.c b/src/levels/shared/L00_003F4C68-g8-dbg.c index 46b4696eb..1b2b494e6 100644 --- a/src/levels/shared/L00_003F4C68-g8-dbg.c +++ b/src/levels/shared/L00_003F4C68-g8-dbg.c @@ -61,7 +61,7 @@ char *func_L00_003F4C68(char *position,char *velocity,int id,int mode) frames=0.016666668f * (D_L00_00262570 + D_0013957F[0])->speed; state->frames=func_00283CF0(128.0f / frames); /* Call and division in one statement: retail has no line label between them, - so no division padding (docs/COMPILER-NOTES.md). */ + so no division padding (docs/build/compiler-notes.md). */ state->rate=1.0f/func_00283CE0(state->frames); state->id=id; state->target=func_L00_0032A088(0xc04,id); diff --git a/src/levels/shared/L00_003FFA70-reaction-g8.c b/src/levels/shared/L00_003FFA70-reaction-g8.c index 1d89b74a8..e1514f526 100644 --- a/src/levels/shared/L00_003FFA70-reaction-g8.c +++ b/src/levels/shared/L00_003FFA70-reaction-g8.c @@ -1,7 +1,7 @@ /* func_L00_00408E58 has a unit of its own for -g: the entry pointer and the * division are two statements, so the second one's line label sits before the * lwc1 and the assembler pads one nop in front of div.s (the division floor, - * docs/COMPILER-NOTES.md). + * docs/build/compiler-notes.md). * * done starts at 0 and takes the timer's result after the loop. Its value is * then equal to i's at the loop guard and it outlives i, so cse keeps it as the diff --git a/src/levels/shared/L00_0041A490-dbg.c b/src/levels/shared/L00_0041A490-dbg.c index a571c9a4f..fd9e3107b 100644 --- a/src/levels/shared/L00_0041A490-dbg.c +++ b/src/levels/shared/L00_0041A490-dbg.c @@ -3,7 +3,7 @@ /* func_L00_0041A490 is a unit of its own: it needs the debug profile (-g, config/cflags.txt). The line labels cc1 emits with -g ($LM) are code labels, and a code label right in front of a `div.s` is what the assembler's division floor needs to add retail's two nops before the - division (docs/COMPILER-NOTES.md, the division-erratum section). The float division is + division (docs/build/compiler-notes.md, the division-erratum section). The float division is therefore a statement of its own, so the label lands on the `div.s`. */ typedef int s32; diff --git a/src/levels/shared/L02_002EFB60-g8.c b/src/levels/shared/L02_002EFB60-g8.c index f0a6489fc..d1d3b3aa4 100644 --- a/src/levels/shared/L02_002EFB60-g8.c +++ b/src/levels/shared/L02_002EFB60-g8.c @@ -1,4 +1,4 @@ -/* func_L02_002EFB60: needs -frac2-sticky-volatile-refs (reorg stops at the copy's volatile asm); see docs/COMPILER-NOTES.md. */ +/* func_L02_002EFB60: needs -frac2-sticky-volatile-refs (reorg stops at the copy's volatile asm); see docs/build/compiler-notes.md. */ #include "common.h" typedef int TI128 __attribute__((mode(TI))); typedef union { TI128 q; float f[4]; } Vec4; diff --git a/src/levels/shared/L02_0032E508.c b/src/levels/shared/L02_0032E508.c index f48e91df7..3054af3a3 100644 --- a/src/levels/shared/L02_0032E508.c +++ b/src/levels/shared/L02_0032E508.c @@ -25,7 +25,7 @@ typedef struct L02RegionLink { polygons (up to 32 layers, CAPACITY nodes), then the links between the found nodes, then each node's centre and radius. Returns the node count. -g, as retail's two-nop floor before div.s shows. The first slot search is a label - loop inside a one-pass group (docs/COMPILER-NOTES.md:54): retail keeps its constant and + loop inside a one-pass group (docs/build/compiler-notes.md:54): retail keeps its constant and address unhoisted and its loop-start alignment nop, which a for loop does not give. The neighbour reset indexes the node array as flat bytes, which gives retail's address grouping. */ diff --git a/src/levels/shared/L02_003FE768-g8.c b/src/levels/shared/L02_003FE768-g8.c index ff7277d88..bc1d80286 100644 --- a/src/levels/shared/L02_003FE768-g8.c +++ b/src/levels/shared/L02_003FE768-g8.c @@ -1,6 +1,6 @@ /* func_L02_003FE768: the boss health bar (shared by Maktar and Joba). * Own -G8 unit. Each screen edge is rounded into t and converted in a one-pass group - * (docs/COMPILER-NOTES.md, source methods shared with RAC1): the group's loop notes keep + * (docs/build/compiler-notes.md, source methods shared with RAC1): the group's loop notes keep * the D_001A7B90 mode load after the conversions, as retail schedules it. Without the * groups the load is hoisted across them (0 of 2 places). */ diff --git a/src/levels/shared/L02_00411530.c b/src/levels/shared/L02_00411530.c index 11b1ae8b4..79c762d0e 100644 --- a/src/levels/shared/L02_00411530.c +++ b/src/levels/shared/L02_00411530.c @@ -4,7 +4,7 @@ join label forward, between the reload of 1.0f (lui $at ; mtc1 $at,$f0) and the sub.s that reads it. Retail keeps the label on the hazard nop the assembler inserts there; GNU as without the switch moves it behind the nop, and the bc1tl lands one word late - (docs/COMPILER-NOTES.md, labels in front of the hazard nop). */ + (docs/build/compiler-notes.md, labels in front of the hazard nop). */ typedef unsigned char u8; diff --git a/src/levels/shared/L02_0042EDD0-g8.c b/src/levels/shared/L02_0042EDD0-g8.c index a0f8a0017..8e8b42584 100644 --- a/src/levels/shared/L02_0042EDD0-g8.c +++ b/src/levels/shared/L02_0042EDD0-g8.c @@ -1,7 +1,7 @@ #include "include_asm.h" /* func_L02_0042EDD0 is a unit of its own: it reads D_L02_001B2434 through $gp (hence -G8) and - needs two opt-in profiles (docs/COMPILER-NOTES.md). -frac2-no-annulled-wide-stores keeps + needs two opt-in profiles (docs/build/compiler-notes.md). -frac2-no-annulled-wide-stores keeps reorg from copying the 16-byte clear at the top of the second loop into the annulled slot of a `bnel` that closes the loop: retail keeps the clear under the loop label and ends with a plain `bnez` whose slot is the fall-through's `lwc1`. -frac2-zero-ti-barrier schedules diff --git a/src/levels/shared/L03_0043AD30-g8.c b/src/levels/shared/L03_0043AD30-g8.c index cdb9afbf0..75e9a9219 100644 --- a/src/levels/shared/L03_0043AD30-g8.c +++ b/src/levels/shared/L03_0043AD30-g8.c @@ -1,7 +1,7 @@ /* func_L03_0043AD30 (512 bytes, 12 places), in its own -G8 -g unit: D_L03_001B21B8 is small * data, D_00138284 is read by lui/lwc1 (an incomplete array), and each angle's dot product is * its own statement. Under -g the second one leaves a line label right after the call's delay - * slot, and the division there gets retail's two-nop floor (docs/COMPILER-NOTES.md, division + * slot, and the division there gets retail's two-nop floor (docs/build/compiler-notes.md, division * label floor after a call). The size window is written as retail's negated tests. */ #include "include_asm.h" diff --git a/src/levels/shared/L04_003C8380-dome.c b/src/levels/shared/L04_003C8380-dome.c index 4b6067087..59dc55362 100644 --- a/src/levels/shared/L04_003C8380-dome.c +++ b/src/levels/shared/L04_003C8380-dome.c @@ -2,7 +2,7 @@ /* func_L04_003FBEF8 has a unit of its own because it needs -g: retail pads its first division (lo's div.s) against the statement's line label, see the div.s floor in - docs/COMPILER-NOTES.md. */ + docs/build/compiler-notes.md. */ typedef int QuadWord __attribute__((mode(TI))); diff --git a/src/levels/shared/L04_003C8380-stream.c b/src/levels/shared/L04_003C8380-stream.c index 654eea81a..1a327707b 100644 --- a/src/levels/shared/L04_003C8380-stream.c +++ b/src/levels/shared/L04_003C8380-stream.c @@ -1,7 +1,7 @@ #include "common.h" /* Built with -g: retail has the two-nop floor that the line label before the division - gives (docs/COMPILER-NOTES.md, "Division/root scheduling and padding"). */ + gives (docs/build/compiler-notes.md, "Division/root scheduling and padding"). */ typedef int Quad __attribute__((mode(TI))); diff --git a/src/levels/shared/L04_00402D90-hud-g8.c b/src/levels/shared/L04_00402D90-hud-g8.c index a7ba5700c..b9704a462 100644 --- a/src/levels/shared/L04_00402D90-hud-g8.c +++ b/src/levels/shared/L04_00402D90-hud-g8.c @@ -27,7 +27,7 @@ extern int func_00283D38(int,int,float); extern float func_00283198(float),func_00283180(float); extern void func_0027AB40(void); extern int func_00336D50(char *,int); -/* Typed interleaving (docs/COMPILER-NOTES.md, "Source methods shared with RAC1"): the same two +/* Typed interleaving (docs/build/compiler-notes.md, "Source methods shared with RAC1"): the same two integer and five float arguments, in the same order within each register bank. */ extern void func_00300A00(int,float,int,float,float,float,float); extern void func_002FC560(int,unsigned long long); diff --git a/src/levels/shared/L04_00414698-g8.c b/src/levels/shared/L04_00414698-g8.c index 3e7899db8..f895bfd54 100644 --- a/src/levels/shared/L04_00414698-g8.c +++ b/src/levels/shared/L04_00414698-g8.c @@ -2,7 +2,7 @@ * -G8, -g, -frac2-hazard-labels and -frac2-frame-pointer-liveness (config/cflags.txt). The hazard * option puts the 00414AD8 bc1f target on the nop at 00414BF8, as retail. Without the liveness * option flow keeps $fp live after reload and reorg annuls the slot of the branch at 004147A8 - * (bnel where retail has bne, same slot `addiu $fp,$s7,0x10`; docs/COMPILER-NOTES.md). + * (bnel where retail has bne, same slot `addiu $fp,$s7,0x10`; docs/build/compiler-notes.md). * D_L04_001B08F0 and D_001A7CC0 are sda ($gp); D_00189FC0 and D_00138320 are NOT_SDA (absolute); * D_L04_001B24DC is plain (unsplit -G8 macro load); D_L04_001A8F20 is MACRO_ADDR (unsplit la). * Source notes: diff --git a/src/levels/shared/L08_003A35E0-tether-g8.c b/src/levels/shared/L08_003A35E0-tether-g8.c index ab4b3fa80..edddec7fe 100644 --- a/src/levels/shared/L08_003A35E0-tether-g8.c +++ b/src/levels/shared/L08_003A35E0-tether-g8.c @@ -341,7 +341,7 @@ void func_L08_003A35E0(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L08_0023A690[0][0], anchor); diff --git a/src/levels/shared/L08_003D3928.c b/src/levels/shared/L08_003D3928.c index 7e5499418..9d44e9e27 100644 --- a/src/levels/shared/L08_003D3928.c +++ b/src/levels/shared/L08_003D3928.c @@ -114,7 +114,7 @@ typedef struct { extern L08Moby *D_L08_001B29DC MACRO_ADDR; /* Words retail never moves into a delay slot are fields read at an offset from - an earlier symbol; bare names are loaded by $gp in a slot (docs/COMPILER-NOTES.md). */ + an earlier symbol; bare names are loaded by $gp in a slot (docs/build/compiler-notes.md). */ extern u8 D_001A7A00[] __attribute__((sda)); extern u8 D_L08_001A7C80[] __attribute__((sda)); extern int D_L08_001B2724[] __attribute__((sda)); diff --git a/src/levels/shared/L12_00424078-g8.c b/src/levels/shared/L12_00424078-g8.c index 9b26c1d5c..590f68d2d 100644 --- a/src/levels/shared/L12_00424078-g8.c +++ b/src/levels/shared/L12_00424078-g8.c @@ -1,7 +1,7 @@ #include "common.h" /* func_L12_00424078 is a unit of its own: -g (config/cflags.txt) gives the line label that - puts retail's two-nop floor in front of each div.s (docs/COMPILER-NOTES.md, "Division/root + puts retail's two-nop floor in front of each div.s (docs/build/compiler-notes.md, "Division/root scheduling and padding"), and -G8 reads the frame counter through $gp. */ extern u8 *D_L12_001B2E9C[3] __attribute__((sda)); /* entity base, read bare (lui/lw) */ diff --git a/src/levels/shared/L13_0040A3B0.c b/src/levels/shared/L13_0040A3B0.c index 61a8b12c4..eb595eaf6 100644 --- a/src/levels/shared/L13_0040A3B0.c +++ b/src/levels/shared/L13_0040A3B0.c @@ -1,7 +1,7 @@ #include "include_asm.h" /* func_L13_0040A3B0 is a unit of its own: it needs the opt-in clear profile - (-frac2-zero-ti-barrier, docs/COMPILER-NOTES.md), as its level-0 twin func_L00_003BFF68 does. */ + (-frac2-zero-ti-barrier, docs/build/compiler-notes.md), as its level-0 twin func_L00_003BFF68 does. */ typedef int Rac2TI128 __attribute__((mode(TI))); diff --git a/src/levels/shared/L18_002C4000-hero.c b/src/levels/shared/L18_002C4000-hero.c index 8255e3552..0a5af9d5c 100644 --- a/src/levels/shared/L18_002C4000-hero.c +++ b/src/levels/shared/L18_002C4000-hero.c @@ -1,7 +1,7 @@ /* func_L18_002C4000 (496 bytes, 3 places: levels 18, 19 and 20), in its own unit for the hero * union view: the hero's fixed-address fields are read through one based register. The last * beqz (the % 6 test) reaches the epilogue's ld $s0 through the division-guard barrier on - * reorg's redundant-insn search (docs/COMPILER-NOTES.md, "a division guard ends the + * reorg's redundant-insn search (docs/build/compiler-notes.md, "a division guard ends the * redundant-insn search"). */ #include "common.h" typedef unsigned int TI128 __attribute__((mode(TI))); diff --git a/src/pal/game/003096D0-tether-g8.c b/src/pal/game/003096D0-tether-g8.c index 24182557b..8697378fb 100644 --- a/src/pal/game/003096D0-tether-g8.c +++ b/src/pal/game/003096D0-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_0030B6C0(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_00229170[0][0], anchor); diff --git a/src/pal/game/00339BF8.c b/src/pal/game/00339BF8.c index cc83c2602..6e807cc8b 100644 --- a/src/pal/game/00339BF8.c +++ b/src/pal/game/00339BF8.c @@ -1,5 +1,5 @@ /* func_PAL_00339BF8: needs -frac2-keep-block-zero-copy (cse keeps `i = length`); see - docs/COMPILER-NOTES.md. */ + docs/build/compiler-notes.md. */ extern void *func_00115484(void *, int, unsigned int); extern int func_0027F178(unsigned char *text, int length); /* text width */ diff --git a/src/pal/game/0033A9B0.c b/src/pal/game/0033A9B0.c index df188dd2a..f3052226f 100644 --- a/src/pal/game/0033A9B0.c +++ b/src/pal/game/0033A9B0.c @@ -7,7 +7,7 @@ Bordered text panel: background box, six border pieces, then the text lines. The border offsets are per-program records (ref_ names); 0x1D0 is a float field. Retail reaches the offset table's x column through $gp and its y column as an absolute address, - so the table has two names, as in other mixed GP/absolute accesses (docs/COMPILER-NOTES.md). */ + so the table has two names, as in other mixed GP/absolute accesses (docs/build/compiler-notes.md). */ typedef struct { float x, y; } BorderOffset_00338AD8; typedef struct { int x, y; } TextOffset_00338AD8; extern BorderOffset_00338AD8 ref_00338AD8_00; diff --git a/src/pal/levels/00_aranos_tutorial/L00_00396350-tether-g8.c b/src/pal/levels/00_aranos_tutorial/L00_00396350-tether-g8.c index 771ea0486..77589fdf9 100644 --- a/src/pal/levels/00_aranos_tutorial/L00_00396350-tether-g8.c +++ b/src/pal/levels/00_aranos_tutorial/L00_00396350-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L00_00396350(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L00_00239950[0][0], anchor); diff --git a/src/pal/levels/01_oozla/L01_003864B0-tether-g8.c b/src/pal/levels/01_oozla/L01_003864B0-tether-g8.c index 6ca4b6235..a54470d7c 100644 --- a/src/pal/levels/01_oozla/L01_003864B0-tether-g8.c +++ b/src/pal/levels/01_oozla/L01_003864B0-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L01_0038BFD8(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L01_0023A050[0][0], anchor); diff --git a/src/pal/levels/01_oozla/L01_003C4878-sw-g8.c b/src/pal/levels/01_oozla/L01_003C4878-sw-g8.c index 7be0c378e..7bd8f0b7c 100644 --- a/src/pal/levels/01_oozla/L01_003C4878-sw-g8.c +++ b/src/pal/levels/01_oozla/L01_003C4878-sw-g8.c @@ -9,7 +9,7 @@ load behind them). Tables are indexed as (index << n) + (s32)base, the operand order retail adds in. Two addressing forms of the 0x1B255C and 0x1B264C pointers: the _sda names are the $gp form. -frac2-hazard-labels gives the forward branch onto the 17.0f compare its hazard nop - target (docs/COMPILER-NOTES.md, Labels on hazard nops). */ + target (docs/build/compiler-notes.md, Labels on hazard nops). */ #include "common.h" typedef union { u8 b[0x800]; s8 c[0x800]; s16 h[0x400]; u16 uh[0x400]; s32 i[0x200]; u32 u[0x200]; f32 f[0x200]; void *p[0x200]; } UView; extern int func_002761F8(void *,void *,int,void *,void *); diff --git a/src/pal/levels/02_maktar_nebula/L02_0039AED0-tether-g8.c b/src/pal/levels/02_maktar_nebula/L02_0039AED0-tether-g8.c index 43ac9f77c..0a4f31ba1 100644 --- a/src/pal/levels/02_maktar_nebula/L02_0039AED0-tether-g8.c +++ b/src/pal/levels/02_maktar_nebula/L02_0039AED0-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L02_003A09F8(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L02_0023B090[0][0], anchor); diff --git a/src/pal/levels/03_endako/L03_00396A58-tether-g8.c b/src/pal/levels/03_endako/L03_00396A58-tether-g8.c index a79c6fdca..86ae7ca0b 100644 --- a/src/pal/levels/03_endako/L03_00396A58-tether-g8.c +++ b/src/pal/levels/03_endako/L03_00396A58-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L03_0039C580(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L03_0023C6D0[0][0], anchor); diff --git a/src/pal/levels/04_barlow/L04_003B6250-tether-g8.c b/src/pal/levels/04_barlow/L04_003B6250-tether-g8.c index a639b6dc0..e6407be0d 100644 --- a/src/pal/levels/04_barlow/L04_003B6250-tether-g8.c +++ b/src/pal/levels/04_barlow/L04_003B6250-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L04_003BBD78(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L04_0023A210[0][0], anchor); diff --git a/src/pal/levels/05_feltzin_system/L05_003A6EA8-tether-g8.c b/src/pal/levels/05_feltzin_system/L05_003A6EA8-tether-g8.c index b8540e6ae..e4168df96 100644 --- a/src/pal/levels/05_feltzin_system/L05_003A6EA8-tether-g8.c +++ b/src/pal/levels/05_feltzin_system/L05_003A6EA8-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L05_003AC9D0(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L05_0023B4D0[0][0], anchor); diff --git a/src/pal/levels/07_siberius/L07_003873C8-tether-g8.c b/src/pal/levels/07_siberius/L07_003873C8-tether-g8.c index 92e427136..2d76e8670 100644 --- a/src/pal/levels/07_siberius/L07_003873C8-tether-g8.c +++ b/src/pal/levels/07_siberius/L07_003873C8-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L07_0038CEF0(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L07_00239D90[0][0], anchor); diff --git a/src/pal/levels/07_siberius/L07_00413B40-cc-g8.c b/src/pal/levels/07_siberius/L07_00413B40-cc-g8.c index 6011734f2..0a70379db 100644 --- a/src/pal/levels/07_siberius/L07_00413B40-cc-g8.c +++ b/src/pal/levels/07_siberius/L07_00413B40-cc-g8.c @@ -2,7 +2,7 @@ /* func_PAL_L07_00413B40: the five path riders' update (Siberius). A unit of its own for -G8 and * -frac2-frame-pointer-liveness (config/cflags.txt): without the option flow keeps $fp, which * holds the rider index here, live after the loop, and reorg annuls the slot of the first - * player-mode test (bnel where retail has bne, docs/COMPILER-NOTES.md). */ + * player-mode test (bnel where retail has bne, docs/build/compiler-notes.md). */ #include "common.h" extern f32 func_002AA2E8(void *,f32,f32); extern s32 func_002E5CF8(s32,s32,void *); diff --git a/src/pal/levels/08_tabora/L08_003A0D28-tether-g8.c b/src/pal/levels/08_tabora/L08_003A0D28-tether-g8.c index 9ceeffc88..60b0d18e1 100644 --- a/src/pal/levels/08_tabora/L08_003A0D28-tether-g8.c +++ b/src/pal/levels/08_tabora/L08_003A0D28-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L08_003A6850(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L08_0023A690[0][0], anchor); diff --git a/src/pal/levels/09_dobbo/L09_0038E768-tether-g8.c b/src/pal/levels/09_dobbo/L09_0038E768-tether-g8.c index 883aa8182..bf660bdec 100644 --- a/src/pal/levels/09_dobbo/L09_0038E768-tether-g8.c +++ b/src/pal/levels/09_dobbo/L09_0038E768-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L09_00394290(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L09_0023B190[0][0], anchor); diff --git a/src/pal/levels/10_hrugis_cloud/L10_003A95E8-tether-g8.c b/src/pal/levels/10_hrugis_cloud/L10_003A95E8-tether-g8.c index 44e5d95e9..3f5005c32 100644 --- a/src/pal/levels/10_hrugis_cloud/L10_003A95E8-tether-g8.c +++ b/src/pal/levels/10_hrugis_cloud/L10_003A95E8-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L10_003AF110(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L10_0023B690[0][0], anchor); diff --git a/src/pal/levels/11_joba/L11_0039B240-tether-g8.c b/src/pal/levels/11_joba/L11_0039B240-tether-g8.c index e4304f6a5..db778342c 100644 --- a/src/pal/levels/11_joba/L11_0039B240-tether-g8.c +++ b/src/pal/levels/11_joba/L11_0039B240-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L11_003ABC10(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L11_0023B450[0][0], anchor); diff --git a/src/pal/levels/11_joba/L11_00449550-cc.c b/src/pal/levels/11_joba/L11_00449550-cc.c index 223f3919e..3dcbc6135 100644 --- a/src/pal/levels/11_joba/L11_00449550-cc.c +++ b/src/pal/levels/11_joba/L11_00449550-cc.c @@ -46,7 +46,7 @@ extern u8 *D_L11_001B359C; /* The class table base read through an incomplete `sda` array: cc1 emits the one-instruction load, which the assembler expands absolute (`lui $r` / `lw $r,%lo($r)`) and which is not - hoisted out of the loop, as retail has it (docs/COMPILER-NOTES.md). */ + hoisted out of the loop, as retail has it (docs/build/compiler-notes.md). */ /* func_L11_00441350 lives below, after the id-list macros it uses. */ /* arms the one-shot flag block when it is idle and the state agrees */ diff --git a/src/pal/levels/12_todano/L12_0038C940-tether-g8.c b/src/pal/levels/12_todano/L12_0038C940-tether-g8.c index 893d6b67c..51657f901 100644 --- a/src/pal/levels/12_todano/L12_0038C940-tether-g8.c +++ b/src/pal/levels/12_todano/L12_0038C940-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L12_0039BF28(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L12_0023A990[0][0], anchor); diff --git a/src/pal/levels/12_todano/L12_004294CC-g8.c b/src/pal/levels/12_todano/L12_004294CC-g8.c index 6912407f1..ee47576c1 100644 --- a/src/pal/levels/12_todano/L12_004294CC-g8.c +++ b/src/pal/levels/12_todano/L12_004294CC-g8.c @@ -6,7 +6,7 @@ loop's preserved subexpressions, so both loops call an inline helper that holds the colour. A unit of its own: -g (config/cflags.txt) gives the line label that - puts retail's two-nop floor in front of each div.s (docs/COMPILER-NOTES.md, "Division/root + puts retail's two-nop floor in front of each div.s (docs/build/compiler-notes.md, "Division/root scheduling and padding"), and -G8 is the template's flag. */ extern u8 *D_L12_001B2E9C[3] __attribute__((sda)); /* entity base, read bare (lui/lw) */ diff --git a/src/pal/levels/14_aranos_prison/L14_0039CF70-tether-g8.c b/src/pal/levels/14_aranos_prison/L14_0039CF70-tether-g8.c index a06274154..f48638eb0 100644 --- a/src/pal/levels/14_aranos_prison/L14_0039CF70-tether-g8.c +++ b/src/pal/levels/14_aranos_prison/L14_0039CF70-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L14_003A2A98(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L14_0023C190[0][0], anchor); diff --git a/src/pal/levels/14_aranos_prison/L14_003E92D8-g8.c b/src/pal/levels/14_aranos_prison/L14_003E92D8-g8.c index e7b729ca3..f5218f776 100644 --- a/src/pal/levels/14_aranos_prison/L14_003E92D8-g8.c +++ b/src/pal/levels/14_aranos_prison/L14_003E92D8-g8.c @@ -2,7 +2,7 @@ span are key positions scaled to 50 Hz and the current position stays unscaled. A unit of its own: it reads four float constants through $gp (hence -g8), and it is built with -g, whose line label in front of the `mul.s` before the second division makes the assembler pad that `div.s` - with retail's `nop` (the division-floor class of docs/COMPILER-NOTES.md, as in + with retail's `nop` (the division-floor class of docs/build/compiler-notes.md, as in func_L23_004160B0). func_00282C48 is `fabs`. The state block is read as a halfword key table at +0x00, with the current position at +0x14 and the current key index at +0x16: the span runs from the key to the previous one (index != 0), or to one of three words (index == 0). `raw` diff --git a/src/pal/levels/15_gorn/L15_003B2BE8-tether-g8.c b/src/pal/levels/15_gorn/L15_003B2BE8-tether-g8.c index 14ea6a724..357a877e6 100644 --- a/src/pal/levels/15_gorn/L15_003B2BE8-tether-g8.c +++ b/src/pal/levels/15_gorn/L15_003B2BE8-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L15_003B2BE8(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L15_0023B7D0[0][0], anchor); diff --git a/src/pal/levels/16_snivelak/L16_0034F000-tether-g8.c b/src/pal/levels/16_snivelak/L16_0034F000-tether-g8.c index c9e93a09d..be0c018e1 100644 --- a/src/pal/levels/16_snivelak/L16_0034F000-tether-g8.c +++ b/src/pal/levels/16_snivelak/L16_0034F000-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L16_00390B70(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L08_0023A690[0][0], anchor); diff --git a/src/pal/levels/16_snivelak/L16_003F9F10-ed-g8.c b/src/pal/levels/16_snivelak/L16_003F9F10-ed-g8.c index b95f4165a..adb309e40 100644 --- a/src/pal/levels/16_snivelak/L16_003F9F10-ed-g8.c +++ b/src/pal/levels/16_snivelak/L16_003F9F10-ed-g8.c @@ -11,7 +11,7 @@ -membedded-data makes cc1 count a small-data reference as two words (no delay slot, no displacement fold), which gives both, unchanged elsewhere: retail puts no $gp access of this function into a slot either. The `sda` incomplete arrays are the bare absolute reads - (COMPILER-NOTES.md); the sized `sda` declarations are the $gp ones. */ + (compiler-notes.md); the sized `sda` declarations are the $gp ones. */ typedef int Quadword __attribute__((mode(TI))); diff --git a/src/pal/levels/17_smolg/L17_00388C38-tether-g8.c b/src/pal/levels/17_smolg/L17_00388C38-tether-g8.c index 5b1958bb6..44c15e500 100644 --- a/src/pal/levels/17_smolg/L17_00388C38-tether-g8.c +++ b/src/pal/levels/17_smolg/L17_00388C38-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L17_00399930(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L17_0023A750[0][0], anchor); diff --git a/src/pal/levels/18_damosel/L18_003A39A8-tether-g8.c b/src/pal/levels/18_damosel/L18_003A39A8-tether-g8.c index 04952f70a..c0e657937 100644 --- a/src/pal/levels/18_damosel/L18_003A39A8-tether-g8.c +++ b/src/pal/levels/18_damosel/L18_003A39A8-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L18_003B4448(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L18_0023A350[0][0], anchor); diff --git a/src/pal/levels/19_grelbin/L19_0038E3B0-tether-g8.c b/src/pal/levels/19_grelbin/L19_0038E3B0-tether-g8.c index efa6d6a3c..54eac50f3 100644 --- a/src/pal/levels/19_grelbin/L19_0038E3B0-tether-g8.c +++ b/src/pal/levels/19_grelbin/L19_0038E3B0-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L19_00393ED8(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L19_0023A5D0[0][0], anchor); diff --git a/src/pal/levels/20_yeedil/L20_003A28D8-cc-g8.c b/src/pal/levels/20_yeedil/L20_003A28D8-cc-g8.c index 3537d9516..be8e0108f 100644 --- a/src/pal/levels/20_yeedil/L20_003A28D8-cc-g8.c +++ b/src/pal/levels/20_yeedil/L20_003A28D8-cc-g8.c @@ -1,6 +1,6 @@ /* func_PAL_L20_003A28D8, the PAL copy of func_L20_0039F968: the same C. */ /* func_PAL_L20_003A28D8 is a unit of its own: the history-copy loop needs -frac2-mult-add-no-flatten - (docs/COMPILER-NOTES.md) so its source cursor keeps retail's i*400 + (D - 16) grouping. */ + (docs/build/compiler-notes.md) so its source cursor keeps retail's i*400 + (D - 16) grouping. */ #include "common.h" typedef int Quadword __attribute__((mode(TI))); typedef union { Quadword q; float f[4]; } Vector; diff --git a/src/pal/levels/20_yeedil/L20_003A28D8-tether-g8.c b/src/pal/levels/20_yeedil/L20_003A28D8-tether-g8.c index e2bc8f84d..177e5b922 100644 --- a/src/pal/levels/20_yeedil/L20_003A28D8-tether-g8.c +++ b/src/pal/levels/20_yeedil/L20_003A28D8-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L20_003B3DD0(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L20_0023CAB0[0][0], anchor); diff --git a/src/pal/levels/22_dobbo_orbit/L22_00388CB8-tether-g8.c b/src/pal/levels/22_dobbo_orbit/L22_00388CB8-tether-g8.c index 52dc0d836..35786b7d9 100644 --- a/src/pal/levels/22_dobbo_orbit/L22_00388CB8-tether-g8.c +++ b/src/pal/levels/22_dobbo_orbit/L22_00388CB8-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L22_00398758(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L22_0023A810[0][0], anchor); diff --git a/src/pal/levels/23_damosel_orbit/L23_00397268-tether-g8.c b/src/pal/levels/23_damosel_orbit/L23_00397268-tether-g8.c index 996f4dde8..967ccb638 100644 --- a/src/pal/levels/23_damosel_orbit/L23_00397268-tether-g8.c +++ b/src/pal/levels/23_damosel_orbit/L23_00397268-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L23_0039CD90(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L23_0023B1D0[0][0], anchor); diff --git a/src/pal/levels/23_damosel_orbit/L23_0041F458-cc-g8.c b/src/pal/levels/23_damosel_orbit/L23_0041F458-cc-g8.c index 5b5415c20..d48311098 100644 --- a/src/pal/levels/23_damosel_orbit/L23_0041F458-cc-g8.c +++ b/src/pal/levels/23_damosel_orbit/L23_0041F458-cc-g8.c @@ -5,7 +5,7 @@ play sound 0x1A (with the explosion) or 0x21. Returns 1 when the object was handled. The kind 0xF arm groups its effect in a nonempty one-pass statement, as current RAC1 game - C does (docs/COMPILER-NOTES.md, source methods shared with RAC1): its loop notes keep the + C does (docs/build/compiler-notes.md, source methods shared with RAC1): its loop notes keep the `la` of D_L23_001B3850 ahead of the two frame-address arguments. func_L00_0035F5A0 returns int, as its matched definition does. -mno-split-addresses keeps retail's adjacent lui/addiu pairs; D_L23_001B3850 is MACRO_ADDR so each use rematerializes its address. */ diff --git a/src/pal/levels/24_ship_shack/L24_0037C748-tether-g8.c b/src/pal/levels/24_ship_shack/L24_0037C748-tether-g8.c index 42399405d..892e2b3f0 100644 --- a/src/pal/levels/24_ship_shack/L24_0037C748-tether-g8.c +++ b/src/pal/levels/24_ship_shack/L24_0037C748-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L24_0038C318(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L24_00239250[0][0], anchor); diff --git a/src/pal/levels/25_wupash_nebula/L25_0039FA98-tether-g8.c b/src/pal/levels/25_wupash_nebula/L25_0039FA98-tether-g8.c index a0122fdae..8db70a318 100644 --- a/src/pal/levels/25_wupash_nebula/L25_0039FA98-tether-g8.c +++ b/src/pal/levels/25_wupash_nebula/L25_0039FA98-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L25_003A55C0(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L25_0023B010[0][0], anchor); diff --git a/src/pal/levels/26_jamming_array/L26_0038ECA0-tether-g8.c b/src/pal/levels/26_jamming_array/L26_0038ECA0-tether-g8.c index ddcbbf186..48eb82b49 100644 --- a/src/pal/levels/26_jamming_array/L26_0038ECA0-tether-g8.c +++ b/src/pal/levels/26_jamming_array/L26_0038ECA0-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L26_003947C8(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L26_00239710[0][0], anchor); diff --git a/src/pal/levels/30_insomniac_museum/L30_003890DC-tether-g8.c b/src/pal/levels/30_insomniac_museum/L30_003890DC-tether-g8.c index 2617e52b5..8f374e411 100644 --- a/src/pal/levels/30_insomniac_museum/L30_003890DC-tether-g8.c +++ b/src/pal/levels/30_insomniac_museum/L30_003890DC-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L30_00397300(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L30_0023FC90[0][0], anchor); diff --git a/src/pal/levels/30_insomniac_museum/L30_004180E8-g8.c b/src/pal/levels/30_insomniac_museum/L30_004180E8-g8.c index 3d1a05df2..79bb8adef 100644 --- a/src/pal/levels/30_insomniac_museum/L30_004180E8-g8.c +++ b/src/pal/levels/30_insomniac_museum/L30_004180E8-g8.c @@ -6,7 +6,7 @@ loop's preserved subexpressions, so both loops call an inline helper that holds the colour. A unit of its own: -g (config/cflags.txt) gives the line label that - puts retail's two-nop floor in front of each div.s (docs/COMPILER-NOTES.md, "Division/root + puts retail's two-nop floor in front of each div.s (docs/build/compiler-notes.md, "Division/root scheduling and padding"), and -G8 is the template's flag. */ extern u8 *D_L12_001B2E9C[3] __attribute__((sda)); /* entity base, read bare (lui/lw) */ diff --git a/src/pal/levels/shared/L00_003F5068-cc-g8.c b/src/pal/levels/shared/L00_003F5068-cc-g8.c index 072b2b8a3..524c5eb2a 100644 --- a/src/pal/levels/shared/L00_003F5068-cc-g8.c +++ b/src/pal/levels/shared/L00_003F5068-cc-g8.c @@ -3,7 +3,7 @@ /* A body whose retail bytes address its global through $gp in ordinary flow: the assembler only selects that form when the back end emits the .extern size directive, which the - pinned profile does at -G8 (see docs/COMPILER-NOTES.md). */ + pinned profile does at -G8 (see docs/build/compiler-notes.md). */ /* Per-frame update of the player's gadget table: it walks the records of one table entry, scales each record's pair of floats and asks the slot manager to refresh them, then folds the two level globals into the table's offset. */ diff --git a/src/pal/levels/shared/L00_003F9060-g8-dbg.c b/src/pal/levels/shared/L00_003F9060-g8-dbg.c index 9a6465eed..b610c7bcc 100644 --- a/src/pal/levels/shared/L00_003F9060-g8-dbg.c +++ b/src/pal/levels/shared/L00_003F9060-g8-dbg.c @@ -63,7 +63,7 @@ char *func_PAL_L00_003F9060(char *position,char *velocity,int id,int mode) frames=0.020000001f * (D_L00_00262570 + D_0013957F[0])->speed; state->frames=(func_00283CF0(128.0f / frames) * 5 + 2) / 6; /* Call and division in one statement: retail has no line label between them, - so no division padding (docs/COMPILER-NOTES.md). */ + so no division padding (docs/build/compiler-notes.md). */ state->rate=1.0f/func_00283CE0(state->frames); state->id=id; state->target=func_L00_0032A088(0xc04,id); diff --git a/src/pal/levels/shared/L00_0040D990-g8.c b/src/pal/levels/shared/L00_0040D990-g8.c index 31f6a7471..cf974e479 100644 --- a/src/pal/levels/shared/L00_0040D990-g8.c +++ b/src/pal/levels/shared/L00_0040D990-g8.c @@ -4,7 +4,7 @@ * It has a unit of its own for -g: the entry pointer and the * division are two statements, so the second one's line label sits before the * lwc1 and the assembler pads one nop in front of div.s (the division floor, - * docs/COMPILER-NOTES.md). + * docs/build/compiler-notes.md). * * done starts at 0 and takes the timer's result after the loop. Its value is * then equal to i's at the loop guard and it outlives i, so cse keeps it as the diff --git a/src/pal/levels/shared/L02_002F1100-g8.c b/src/pal/levels/shared/L02_002F1100-g8.c index 34bf60a03..58dab9855 100644 --- a/src/pal/levels/shared/L02_002F1100-g8.c +++ b/src/pal/levels/shared/L02_002F1100-g8.c @@ -1,6 +1,6 @@ /* func_PAL_L02_002F1100, the PAL copy of func_L02_002EFB60: the same C, where the gravity factor 0.00027777778f becomes 0.00040000002f and the three colour arguments are scaled for 50 Hz. Needs -frac2-sticky-volatile-refs - (reorg stops at the copy's volatile asm); see docs/COMPILER-NOTES.md. */ + (reorg stops at the copy's volatile asm); see docs/build/compiler-notes.md. */ #include "common.h" typedef int TI128 __attribute__((mode(TI))); typedef union { TI128 q; float f[4]; } Vec4; diff --git a/src/pal/levels/shared/L04_00400108-cc.c b/src/pal/levels/shared/L04_00400108-cc.c index 5350ee733..5124907e3 100644 --- a/src/pal/levels/shared/L04_00400108-cc.c +++ b/src/pal/levels/shared/L04_00400108-cc.c @@ -3,7 +3,7 @@ /* func_PAL_L04_00400108 has a unit of its own because it needs -g: retail pads its first division (lo's div.s) against the statement's line label, see the div.s floor in - docs/COMPILER-NOTES.md. */ + docs/build/compiler-notes.md. */ typedef int QuadWord __attribute__((mode(TI))); diff --git a/src/pal/levels/shared/L06_003D3C28-tether-g8.c b/src/pal/levels/shared/L06_003D3C28-tether-g8.c index 3656350af..36d17654f 100644 --- a/src/pal/levels/shared/L06_003D3C28-tether-g8.c +++ b/src/pal/levels/shared/L06_003D3C28-tether-g8.c @@ -341,7 +341,7 @@ void func_PAL_L06_003D9750(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L06_0023A3D0[0][0], anchor); diff --git a/src/pal/levels/shared/L08_0043B690.c b/src/pal/levels/shared/L08_0043B690.c index 76a3c9241..5cb003b87 100644 --- a/src/pal/levels/shared/L08_0043B690.c +++ b/src/pal/levels/shared/L08_0043B690.c @@ -57,7 +57,7 @@ typedef struct { extern L08Moby *D_L08_001B29DC MACRO_ADDR; /* Words retail never moves into a delay slot are fields read at an offset from - an earlier symbol; bare names are loaded by $gp in a slot (docs/COMPILER-NOTES.md). */ + an earlier symbol; bare names are loaded by $gp in a slot (docs/build/compiler-notes.md). */ extern u8 D_001A7A00[] __attribute__((sda)); extern u8 D_L08_001A7C80[] __attribute__((sda)); extern int D_L08_001B2724[] __attribute__((sda)); diff --git a/src/us2/game/00338D48.c b/src/us2/game/00338D48.c index d8589bfc3..bba783a8c 100644 --- a/src/us2/game/00338D48.c +++ b/src/us2/game/00338D48.c @@ -1,5 +1,5 @@ /* func_US2_00338D48: needs -frac2-keep-block-zero-copy (cse keeps `i = length`); see - docs/COMPILER-NOTES.md. */ + docs/build/compiler-notes.md. */ extern void *func_00115484(void *, int, unsigned int); extern int func_0027F178(unsigned char *text, int length); /* text width */ diff --git a/src/us2/levels/01_oozla/L01_003C1DB8-g8.c b/src/us2/levels/01_oozla/L01_003C1DB8-g8.c index c88f9aa02..31ec30693 100644 --- a/src/us2/levels/01_oozla/L01_003C1DB8-g8.c +++ b/src/us2/levels/01_oozla/L01_003C1DB8-g8.c @@ -8,7 +8,7 @@ load behind them). Tables are indexed as (index << n) + (s32)base, the operand order retail adds in. Two addressing forms of the 0x1B255C and 0x1B264C pointers: the _sda names are the $gp form. -frac2-hazard-labels gives the forward branch onto the 17.0f compare its hazard nop - target (docs/COMPILER-NOTES.md, Labels on hazard nops). */ + target (docs/build/compiler-notes.md, Labels on hazard nops). */ #include "common.h" typedef union { u8 b[0x800]; s8 c[0x800]; s16 h[0x400]; u16 uh[0x400]; s32 i[0x200]; u32 u[0x200]; f32 f[0x200]; void *p[0x200]; } UView; extern int func_002761F8(void *,void *,int,void *,void *); diff --git a/src/us2/levels/11_joba/L11_00445548-cc.c b/src/us2/levels/11_joba/L11_00445548-cc.c index 29fb488cb..2dbb2c98b 100644 --- a/src/us2/levels/11_joba/L11_00445548-cc.c +++ b/src/us2/levels/11_joba/L11_00445548-cc.c @@ -46,7 +46,7 @@ extern u8 *D_L11_001B359C; /* The class table base read through an incomplete `sda` array: cc1 emits the one-instruction load, which the assembler expands absolute (`lui $r` / `lw $r,%lo($r)`) and which is not - hoisted out of the loop, as retail has it (docs/COMPILER-NOTES.md). */ + hoisted out of the loop, as retail has it (docs/build/compiler-notes.md). */ /* func_L11_00441350 lives below, after the id-list macros it uses. */ /* arms the one-shot flag block when it is idle and the state agrees */ diff --git a/src/us2/levels/16_snivelak/L16_0034DE08-tether-g8.c b/src/us2/levels/16_snivelak/L16_0034DE08-tether-g8.c index 2780c2168..5c7993dbe 100644 --- a/src/us2/levels/16_snivelak/L16_0034DE08-tether-g8.c +++ b/src/us2/levels/16_snivelak/L16_0034DE08-tether-g8.c @@ -328,7 +328,7 @@ void func_US2_L16_0038EE98(Moby *self) /* These named pointers, and the short locals below, are live but also steer: each adds insns that exist at gcse and die later (asm operands keep the copy; the sign extension dies at combine). gcse's hash needs 677 buckets, max_cuid >= 1352, for retail's spill - slots. See F4 in docs/MATCHING-BLOCKERS.md. */ + slots. See F4 in docs/contributing/matching-blockers.md. */ { float *anchor = st->a; qcopy(&D_L08_0023A690[0][0], anchor); diff --git a/src/us2/levels/shared/L00_002A3B88-cc-g8.c b/src/us2/levels/shared/L00_002A3B88-cc-g8.c index bdd791332..b9021036a 100644 --- a/src/us2/levels/shared/L00_002A3B88-cc-g8.c +++ b/src/us2/levels/shared/L00_002A3B88-cc-g8.c @@ -3,7 +3,7 @@ * until loading ends. A -G8 unit of its own. In the loop header the plain `sd` of D_001A7200 and * then the volatile T1_COUNT (0x10000800) clear come just before the first jal; the compiler's * default set-side volatile rule (GCC 2.95's) leaves that slot empty, as retail does, where the - * EE tree's rule fills it with the clear of the frame accumulator (docs/COMPILER-NOTES.md). + * EE tree's rule fills it with the clear of the frame accumulator (docs/build/compiler-notes.md). * T1_COUNT (include/common.h) is the EE timer 1 count register, 0x10000800. */ #include "common.h" diff --git a/src/us2/levels/shared/L00_002A4928-cc-g8.c b/src/us2/levels/shared/L00_002A4928-cc-g8.c index 36869a993..428cefe13 100644 --- a/src/us2/levels/shared/L00_002A4928-cc-g8.c +++ b/src/us2/levels/shared/L00_002A4928-cc-g8.c @@ -140,7 +140,7 @@ void func_US2_L00_002A4928(void) } extern char D_00189E20[]; -/* Incomplete sda array: retail clears this word as `lui $at; sw` (COMPILER-NOTES.md). */ +/* Incomplete sda array: retail clears this word as `lui $at; sw` (compiler-notes.md). */ /* walks one loaded entry of an owner's list and hands it to the vector routine when the tag bits are set, else to the count-driven copy, then polls and returns the caller's handle */ diff --git a/tests/check_repository.py b/tests/check_repository.py index b15bfc3c0..51cabfa08 100644 --- a/tests/check_repository.py +++ b/tests/check_repository.py @@ -5,7 +5,7 @@ 1. config/levels/functions.tsv and config/boot/functions.tsv parse: right column count, unique names, kinds in {boot, shared, level}, places NN:XXXXXXXX of a known level; so do - the other releases' (config/pal/, config/us2/: docs/VERSIONS.md). + the other releases' (config/pal/, config/us2/: docs/build/versions.md). 2. Every function of the two catalogues appears in src/**/*.c exactly once, as its C or as an INCLUDE_ASM marker (rac1-decomp's layout), and nothing else does. A release's own functions (func_PAL_..., func_US2_...) are in its src// and only there; every other function @@ -197,7 +197,7 @@ def check_hygiene(errors: list) -> None: reports = {v["report"] for v in levels.VERSIONS.values()} if f.startswith("progress/") and f not in reports: errors.append(f"{f}: progress/ holds only the releases' report.json, written by tools/progress.py") - if path.is_file() and path.stat().st_size > MAX_BYTES and f != "assets/rac2-logo.png": + if path.is_file() and path.stat().st_size > MAX_BYTES: errors.append(f"{f}: {path.stat().st_size} bytes; generated data stays out of git") if f.startswith("src/") and f.endswith((".c", ".h")): text = path.read_text(errors="replace") diff --git a/tests/test_keep_float_pun_source.py b/tests/test_keep_float_pun_source.py index c7e3fc485..91cfd9757 100644 --- a/tests/test_keep_float_pun_source.py +++ b/tests/test_keep_float_pun_source.py @@ -184,7 +184,7 @@ def test_option_does_not_reuse_an_older_register(self): def test_default_loses_the_low_half(self): # The wrong code this tree gives by default: sched1 splits the store, the reads win # FP_REGS, and reload drops the ior's output reload. It must stay stock here - # (docs/COMPILER-NOTES.md, known issue). + # (docs/build/compiler-notes.md, known issue). for name in LOW_HALF_LOST: with self.subTest(name): lines = self.lines("default", name) diff --git a/tests/test_tools.py b/tests/test_tools.py index 3b9eb4496..c52508169 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -1376,7 +1376,7 @@ def test_schema_and_sourced_moby_labels(self): import csv import re - with (ROOT / "docs/moby-dispatch.tsv").open(encoding="utf-8", newline="") as stream: + with (ROOT / "docs/game/moby-dispatch.tsv").open(encoding="utf-8", newline="") as stream: reader = csv.DictReader(stream, delimiter="\t") self.assertEqual(reader.fieldnames, [ "table", "level", "id", "role", "address", "aux", "proposed_name", "source", diff --git a/toolchain/README.md b/toolchain/README.md index 900973f63..d5efc4fd8 100644 --- a/toolchain/README.md +++ b/toolchain/README.md @@ -6,4 +6,4 @@ Local tools that cannot be distributed. Never committed (see `.gitignore`). - `sn-prodg-2.0/ee/bin/ld.exe`: the SN ProDG 3.01 linker (SHA-256 `80f3724a…`). The compilers are not here: `tools/compiler/` rebuilds them from public source into -`build/compiler/`. See [docs/BUILDING.md](../docs/BUILDING.md). +`build/compiler/`. See [docs/build/building.md](../docs/build/building.md). diff --git a/tools/build.py b/tools/build.py index 992d85bd2..bd4b22823 100644 --- a/tools/build.py +++ b/tools/build.py @@ -8,7 +8,7 @@ python3 tools/build.py all # the boot and all 27 python3 tools/build.py all --jobs 12 # that many programs at once (default: half the cores, at most 8) python3 tools/build.py all --strict # release check: also reject any C falling back to assembly - python3 tools/build.py all --version pal # another release (pal, us2): build//, docs/VERSIONS.md + python3 tools/build.py all --version pal # another release (pal, us2): build//, docs/build/versions.md Each program is disassembled with splat into build//asm/, converted for SN ProDG 2.0's assembler (tools/expand_asm.py) and assembled with it; the C in src/ is @@ -18,7 +18,7 @@ that differs stops the build. Inputs: baserom/ (tools/levels.py dump), toolchain/sn-prodg-2.0/ee/bin/{Ps2EeAs,ld}.exe -and build/compiler/tools/{cpp,cc1,as} (docs/BUILDING.md). The Windows tools run through +and build/compiler/tools/{cpp,cc1,as} (docs/build/building.md). The Windows tools run through $RAC2_WINE (default `wine`; `{cwd}` is replaced by the directory they run in), the 32-bit Linux compiler through $RAC2_LINUX (default: directly). @@ -257,7 +257,7 @@ def split_flags(flags: str) -> tuple[list[str], list[str]]: Both frontends stop at assembly, so standard -Wa, options are forwarded here. SDK units (flags starting with "sdk") also get --r5900-pool-li-d: the SDK's objects pool li.d constants in .lit8, while the game code builds them inline, the assembler's default - (docs/COMPILER-NOTES.md, "Double constants in general registers").""" + (docs/build/compiler-notes.md, "Double constants in general registers").""" compiler_args, assembler_args = [], [] sdk = flags.startswith("sdk") if sdk: diff --git a/tools/carry.py b/tools/carry.py index aa9df816e..c75f49302 100644 --- a/tools/carry.py +++ b/tools/carry.py @@ -8,7 +8,7 @@ python3 tools/carry.py --version pal --demote # after tools/build.py all --version pal: what the build disproved Needs the dumps of both releases (tools/levels.py dump, and with --version V). Writes the two -catalogues of release V in the formats of USA v1.01's (docs/VERSIONS.md), and +catalogues of release V in the formats of USA v1.01's (docs/build/versions.md), and build//carry.json: which v1.01 function each of V's corresponds to and how it was found. Every program of V is compared with the same program of v1.01, one code section at a time @@ -418,7 +418,7 @@ def call_edges(boot_rows: list, rows: list) -> list[tuple[str, str]]: def write_catalogues(boot_rows: list, rows: list) -> None: """config//boot/functions.tsv and config//levels/functions.tsv, in the - formats docs/VERSIONS.md gives them.""" + formats docs/build/versions.md gives them.""" levels.BOOT_FUNCTIONS.parent.mkdir(parents=True, exist_ok=True) levels.BOOT_FUNCTIONS.write_text("# name\taddress\tsize -- tools/carry.py\n" + "".join( f"{name}\t{address:08X}\t{size}\n" for name, address, size in boot_rows), newline="\n") @@ -942,7 +942,7 @@ def main() -> None: write_catalogues([(n, a, s) for n, a, s, _, _ in sorted(boot_rows, key=lambda r: r[1])], out) - # What each v1.01 function became, for docs/VERSIONS.md and tools/progress.py's notes. + # What each v1.01 function became, for docs/build/versions.md and tools/progress.py's notes. report = {"boot": {}, "levels": {}} for name, a, n, how, was in boot_rows: report["boot"][name] = {"address": a, "size": n, "how": how, "was": was} diff --git a/tools/compiler/README.md b/tools/compiler/README.md index a5c24f0e0..fec19da1f 100644 --- a/tools/compiler/README.md +++ b/tools/compiler/README.md @@ -20,14 +20,14 @@ The retail code was built with GNU EE-GCC 2.9-ee-991111b. `build.sh` rebuilds it which interlocks it. `tidy_condjump_fallthru.py` lets flow delete a conditional jump to the next block, as GCC 2.95 does. `zero_ti_store_barrier.py` adds the opt-in `-frac2-zero-ti-barrier` model of the inferred quadword-clear macro - (docs/COMPILER-NOTES.md); default compilation retains ordinary scheduling. + (docs/build/compiler-notes.md); default compilation retains ordinary scheduling. `ti_one_gpr.py` makes reload count a TImode value as one general register on the R5900, as `HARD_REGNO_NREGS` already does, so quadword reloads (a `por` zero for `sq`) take - single spill registers as retail's do; it is unconditional (docs/COMPILER-NOTES.md). + single spill registers as retail's do; it is unconditional (docs/build/compiler-notes.md). `reload_reg_used_guard.py` puts back the archive's test that keeps reload from inheriting a value into a register a pseudo of the insn holds, which patch 0047 drops; it is unconditional. It snapshots the non-spill exclusions before reloads add their own marks, - so self-inheritance reaches the existing lifetime checks (docs/COMPILER-NOTES.md). + so self-inheritance reaches the existing lifetime checks (docs/build/compiler-notes.md). `count_asm_words.py` counts copy words and inserted loop nops, including nops before an already-visited branch. It also counts an unannounced external SI load/address as its two emitted words, preserving the scheduler's lengths and the explicit GP/PIC/buffering @@ -41,16 +41,16 @@ The retail code was built with GNU EE-GCC 2.9-ee-991111b. `build.sh` rebuilds it `swap_branch_slots.py` adds the opt-in assembler option `--r5900-swap-branch-slots`: gas fills a reorder-mode delay slot from the previous instruction, as the GNU as that built the SDK libraries does (this tree's swap guard reads `&&` for `&` and never swaps). SDK units - pass it as `-Wa,--r5900-swap-branch-slots` (docs/COMPILER-NOTES.md). + pass it as `-Wa,--r5900-swap-branch-slots` (docs/build/compiler-notes.md). `inline_li_d.py` makes gas build every `li.d` into a 64-bit general register inline (load_register's dli form) instead of pooling the values that lack a zero 16-bit chunk in each half in `.lit8`, as the game's own code does; this is the default. The SDK objects pool, so `tools/build.py` passes the opt-out `--r5900-pool-li-d` to every `sdk`-profile unit - (docs/COMPILER-NOTES.md). + (docs/build/compiler-notes.md). `convert_frag_memmove.py` makes md_convert_frag slide the chosen macro variant with memmove: the absolute form of a base-register load (12 bytes) overlaps the `$gp` form it replaces (8 bytes), and a host memcpy that copies backwards lost its `lui` - (func_00117278; docs/COMPILER-NOTES.md, "Overlapping variants in md_convert_frag"). + (func_00117278; docs/build/compiler-notes.md, "Overlapping variants in md_convert_frag"). `no_recombine_givs.py` drops strength_reduce's call to `recombine_givs` (loop.c), as the gcc-2.95 release branch did, so address givs stay on the giv combine_givs chose, as in retail; it is unconditional too. `no_extra_increment_givs.py` adds the opt-in @@ -123,7 +123,7 @@ macro. Instructions, operands, register-allocation inputs and memory flags are u and `-fno-rac2-qcopy-barrier` disables it again. Without the switch no object of `src/` changes; forced on in the eight `-frac2-qcopy` units, it changes none of their objects. Twelve functions (59 places) are the measured witnesses, each a unit of its own with both -switches (docs/COMPILER-NOTES.md). It models only the schedulers' view of such an asm: +switches (docs/build/compiler-notes.md). It models only the schedulers' view of such an asm: jump2, for one, still deletes a redundant register move across the copy, which a volatile asm keeps. `tests/test_qcopy_barrier.py` covers the selector, the default/disabled equality, and authored copies among neighbours, calls, branches and volatile accesses. @@ -139,7 +139,7 @@ so the address alone does not decide it and a unit says that its stack clears ar `-fno-rac2-zero-ti-por` disables it again. Without the switch no object of `src/` changes; forced on everywhere, it changes only the four units whose functions retail clears `0($sp)` with `sq $zero`. Five functions are the measured witnesses, each a unit of its own -(docs/COMPILER-NOTES.md). `tests/test_zero_ti_por.py` covers the transformer's anchors, the +(docs/build/compiler-notes.md). `tests/test_zero_ti_por.py` covers the transformer's anchors, the default/disabled equality, and authored zero stores to the stack, through pointers, to a global and in loops, a function with both kinds, nonzero displacements, shared and live zeros and the function-value fold. @@ -150,7 +150,7 @@ gas keeps a code label in front of the coprocessor hazard `nop` it inserts after (an FPU compare before its `bc1f`/`bc1t`, an `mtc1` before a reader of the register), where GNU `as` moves the label behind the nop. Retail has the label on the nop at all 198 such branch targets, and SN ProDG 2.0's `Ps2EeAs` keeps it there for forward, backward, `j` and -`.word` references alike (docs/COMPILER-NOTES.md). Without the switch neither program's +`.word` references alike (docs/build/compiler-notes.md). Without the switch neither program's output changes. `src/levels/18_damosel/L18_004308F0.c` and `src/levels/shared/L02_00411530.c` are the measured witnesses; forcing the switch on every unit changes only the `.mdebug` of one `-g` unit. `tests/test_hazard_labels.py` covers the fixed cases and the controls. @@ -161,7 +161,7 @@ branch's target (`fill_slots_from_thread`, `steal_delay_list_from_target` in `re wider than 32 bits (`sd`, `sq`) no longer goes into the annulled slot of a branch-likely, and the search of that thread stops at it. `optimize_skip`, which annuls the one instruction a branch skips, is unchanged. In retail's compiled code the annulled slots hold 2,895 stores and none is -wider than 32 bits, while the other slots hold 547 `sq` and 497 `sd` (docs/COMPILER-NOTES.md). +wider than 32 bits, while the other slots hold 547 `sq` and 497 `sd` (docs/build/compiler-notes.md). Without the switch the output is unchanged, and forcing it on every other game-compiler unit of `src/` changes no object. `src/levels/23_damosel_orbit/L23_00422668.c` (with `-frac2-qcopy`) and `src/levels/shared/L02_0042EDD0-g8.c` (with `-frac2-zero-ti-barrier`) @@ -324,7 +324,7 @@ witness has a file of its own. With `-mastra-quad-saves` the option does nothing default, enabled per unit in `config/cflags.txt`, and `-fno-rac2-wide-saves-below` disables it; `tests/test_wide_saves_below.py` covers the anchors, the default/disabled equality, one and two wide saves, stack arguments, a unit-wide carried mode, the quad-save profiles and a control -(docs/COMPILER-NOTES.md). +(docs/build/compiler-notes.md). `mult_add_no_flatten.py` adds `-frac2-mult-add-no-flatten`: expand_mult_add (expmed.c), which builds a reduced giv's start for loop.c, folds its `b*m + a` tree with flag_schedule_insns @@ -333,7 +333,7 @@ there. func_L20_0039F968's source cursor then starts as retail's `x*16 + (i*400 not `(x*16 + i*400) + (D-16)`; FSF 2.95.2 (SN v2.73a) gives the same shape. Forced on in every game unit, nothing else changes. It is off by default, enabled per unit in `config/cflags.txt`, and `-fno-rac2-mult-add-no-flatten` disables it; `tests/test_mult_add_no_flatten.py` covers the -anchors, the default/disabled equality, the kept grouping and a control (docs/COMPILER-NOTES.md). +anchors, the default/disabled equality, the kept grouping and a control (docs/build/compiler-notes.md). `keep_float_pun_source.py` adds `-frac2-keep-float-pun-source`. `f32 f; *(s32 *)&f = K;` reaches cse2 as a set of an integer pseudo g and `(set (subreg:SI (reg:SF f) 0) (reg:SI g))`. With the @@ -345,7 +345,7 @@ twice lose g: `li.s` hoisted to the block start, or sched1 splits the SUBREG sto saved GPR with an `mtc1` per read, or, if the reads win FP_REGS, reload drops the `ori` and the default compiler emits wrong code (a known issue of the EE 2.9 base, also in the SDK compiler). The rule is inferred from retail: no GCC release has it -(docs/COMPILER-NOTES.md). It is off by default, enabled per unit in `config/cflags.txt`, and +(docs/build/compiler-notes.md). It is off by default, enabled per unit in `config/cflags.txt`, and `-fno-rac2-keep-float-pun-source` disables it; `tests/test_keep_float_pun_source.py` covers the anchors, the default/disabled equality, the set after the previous call, the one move, the older-register case, the default's lost `ori` (three reads, reads in a loop, reads across a diff --git a/tools/compiler/build.sh b/tools/compiler/build.sh index c6f2c5f7e..0a63759c8 100755 --- a/tools/compiler/build.sh +++ b/tools/compiler/build.sh @@ -4,7 +4,7 @@ # tools/compiler/build.sh [OUT] # default build/compiler/tools # # Run it on 32-bit-capable x86-64 Linux (gcc -m32), e.g. in the rac2-linux container -# (docs/BUILDING.md). It downloads the two inputs to build/downloads/ and checks them, +# (docs/build/building.md). It downloads the two inputs to build/downloads/ and checks them, # applies the patches in tools/compiler/patches/ (Lombyte's sce-991111b stack, MIT, with its # retired 0034 and its earlier 0020), the 0057/0058 compiler profiles, the 0059/0061/0062 assembler options and the # transformers in tools/compiler/transformers/, diff --git a/tools/compiler/transformers/convert_frag_memmove.py b/tools/compiler/transformers/convert_frag_memmove.py index e7fb59560..fdf565ec1 100644 --- a/tools/compiler/transformers/convert_frag_memmove.py +++ b/tools/compiler/transformers/convert_frag_memmove.py @@ -10,7 +10,7 @@ an assembler built on 3 November 2026 gave the `lui`, the ones built from 8 November did not, and func_00117278 (strtol, an -O0 unit, the only C that uses the base-register form) stopped matching. memmove gives the 1999 hosts' forward copy on every host -(docs/COMPILER-NOTES.md, "Overlapping variants in md_convert_frag").""" +(docs/build/compiler-notes.md, "Overlapping variants in md_convert_frag").""" import sys from pathlib import Path diff --git a/tools/compiler/transformers/frame_pointer_liveness.py b/tools/compiler/transformers/frame_pointer_liveness.py index 246be7cc1..8c725e718 100644 --- a/tools/compiler/transformers/frame_pointer_liveness.py +++ b/tools/compiler/transformers/frame_pointer_liveness.py @@ -25,7 +25,7 @@ The option is off by default, enabled per unit in config/cflags.txt, and -fno-rac2-frame-pointer-liveness disables it again. tests/test_frame_pointer_liveness.py covers the anchors, the default/disabled equality, the un-annulled slot and controls. -See docs/COMPILER-NOTES.md. +See docs/build/compiler-notes.md. Usage: frame_pointer_liveness.py (the one holding toplev.c) """ diff --git a/tools/compiler/transformers/gcse_store_kills_loads.py b/tools/compiler/transformers/gcse_store_kills_loads.py index a6d9e4eba..96f526607 100644 --- a/tools/compiler/transformers/gcse_store_kills_loads.py +++ b/tools/compiler/transformers/gcse_store_kills_loads.py @@ -42,7 +42,7 @@ store-motion walk all call it), so it now reports a conflict for every memory destination, which is what mem_set_in_block amounts to. Calls already kill every load. tests/test_gcse_store_kills_loads.py covers the transformer and the compiler output; see -docs/COMPILER-NOTES.md. +docs/build/compiler-notes.md. Usage: gcse_store_kills_loads.py """ diff --git a/tools/compiler/transformers/inline_li_d.py b/tools/compiler/transformers/inline_li_d.py index 564db8e1f..7bb4075ce 100644 --- a/tools/compiler/transformers/inline_li_d.py +++ b/tools/compiler/transformers/inline_li_d.py @@ -25,7 +25,7 @@ so the SDK's objects assemble as before. `--r5900-inline-li-d` states the default explicitly. It is the li.d counterpart of patch 0027's inline `li.s`. tests/test_inline_li_d.py checks the transformer and the assembler output; see -docs/COMPILER-NOTES.md. +docs/build/compiler-notes.md. Usage: inline_li_d.py (after swap_branch_slots.py) """ diff --git a/tools/compiler/transformers/keep_block_zero_copy.py b/tools/compiler/transformers/keep_block_zero_copy.py index 25b8bea67..ec196f8d0 100644 --- a/tools/compiler/transformers/keep_block_zero_copy.py +++ b/tools/compiler/transformers/keep_block_zero_copy.py @@ -19,7 +19,7 @@ The option is off by default, enabled per unit in config/cflags.txt, and -fno-rac2-keep-block-zero-copy disables it again. tests/test_keep_block_zero_copy.py covers the anchors, the default/disabled equality, the kept copy and a cross-block control. -See docs/COMPILER-NOTES.md. +See docs/build/compiler-notes.md. Usage: keep_block_zero_copy.py (the one holding toplev.c) """ diff --git a/tools/compiler/transformers/keep_float_pun_source.py b/tools/compiler/transformers/keep_float_pun_source.py index 381bb4126..31cb6c164 100644 --- a/tools/compiler/transformers/keep_float_pun_source.py +++ b/tools/compiler/transformers/keep_float_pun_source.py @@ -26,7 +26,7 @@ SUBREG simplification and expand_field_assignment are the same in GCC 2.95, 2.95.1, 2.95.2, 2.95.3 and this tree, and no ChangeLog entry concerns it (nor, for the lost output reload, up to 3.4.6). The rule is inferred from retail alone and was designed to give retail's form; where -that form comes from is unverified (docs/COMPILER-NOTES.md). It also prevents the wrong code above. It keys on the insn's shape, never on a function, is off by default, +that form comes from is unverified (docs/build/compiler-notes.md). It also prevents the wrong code above. It keys on the insn's shape, never on a function, is off by default, is enabled per unit in config/cflags.txt, and -fno-rac2-keep-float-pun-source disables it again. Usage: keep_float_pun_source.py (the one holding toplev.c) diff --git a/tools/compiler/transformers/mult_add_no_flatten.py b/tools/compiler/transformers/mult_add_no_flatten.py index 89974f516..c576801bc 100644 --- a/tools/compiler/transformers/mult_add_no_flatten.py +++ b/tools/compiler/transformers/mult_add_no_flatten.py @@ -15,7 +15,7 @@ The option is off by default, enabled per unit in config/cflags.txt, and -fno-rac2-mult-add-no-flatten disables it again. tests/test_mult_add_no_flatten.py covers -the anchors, the default/disabled equality and the kept grouping. See docs/COMPILER-NOTES.md. +the anchors, the default/disabled equality and the kept grouping. See docs/build/compiler-notes.md. Usage: mult_add_no_flatten.py (the one holding toplev.c) """ diff --git a/tools/compiler/transformers/narrow_saves_below.py b/tools/compiler/transformers/narrow_saves_below.py index 1c5bd3b53..b13bfb346 100644 --- a/tools/compiler/transformers/narrow_saves_below.py +++ b/tools/compiler/transformers/narrow_saves_below.py @@ -19,7 +19,7 @@ The option is off by default, enabled per unit in config/cflags.txt (with -mastra-quad-saves), and -fno-rac2-narrow-saves-below disables it again. tests/test_narrow_saves_below.py covers the anchors, the default/disabled equality, an authored mixed-width frame and a control. -See docs/COMPILER-NOTES.md. +See docs/build/compiler-notes.md. Usage: narrow_saves_below.py (the one holding toplev.c) """ diff --git a/tools/compiler/transformers/no_extra_increment_givs.py b/tools/compiler/transformers/no_extra_increment_givs.py index 31b9f9e0e..37ab38bfb 100755 --- a/tools/compiler/transformers/no_extra_increment_givs.py +++ b/tools/compiler/transformers/no_extra_increment_givs.py @@ -32,7 +32,7 @@ only on the option. It is off by default, enabled per unit in config/cflags.txt, and -fno-rac2-no-extra-increment-givs disables it again. tests/test_no_extra_increment_givs.py covers the anchors, the default/disabled equality, a loop whose code changes only with the -option and controls. See docs/COMPILER-NOTES.md. +option and controls. See docs/build/compiler-notes.md. Usage: no_extra_increment_givs.py (the one holding toplev.c) """ diff --git a/tools/compiler/transformers/no_null_pointer_checks.py b/tools/compiler/transformers/no_null_pointer_checks.py index 25e900212..f846fcc20 100644 --- a/tools/compiler/transformers/no_null_pointer_checks.py +++ b/tools/compiler/transformers/no_null_pointer_checks.py @@ -24,7 +24,7 @@ -frac2-keep-block-order, whose pass also counts blocks before cse), and -fno-rac2-no-null-pointer-checks disables it again. Without it the source is unchanged in effect. tests/test_no_null_pointer_checks.py covers the anchors, the default/disabled -equality and an authored check the pass deletes. See docs/COMPILER-NOTES.md. +equality and an authored check the pass deletes. See docs/build/compiler-notes.md. Usage: no_null_pointer_checks.py (the one holding toplev.c) """ diff --git a/tools/compiler/transformers/no_recombine_givs.py b/tools/compiler/transformers/no_recombine_givs.py index bd3f288ed..2fa1114bb 100644 --- a/tools/compiler/transformers/no_recombine_givs.py +++ b/tools/compiler/transformers/no_recombine_givs.py @@ -21,7 +21,7 @@ The call to recombine_givs in strength_reduce is removed (the function itself stays, unused, as in the release branch). tests/test_no_recombine_givs.py covers the transformer and the -compiler output; see docs/COMPILER-NOTES.md. +compiler output; see docs/build/compiler-notes.md. Usage: no_recombine_givs.py """ diff --git a/tools/compiler/transformers/no_static_chain_base.py b/tools/compiler/transformers/no_static_chain_base.py index 6e8026d53..4d4abd4f3 100644 --- a/tools/compiler/transformers/no_static_chain_base.py +++ b/tools/compiler/transformers/no_static_chain_base.py @@ -40,7 +40,7 @@ base; it keys on no function, only on the option. It is off by default, enabled per unit in config/cflags.txt, and -fno-rac2-no-static-chain-base disables it again. tests/test_no_static_chain_base.py covers the anchors, the default/disabled equality, the late -saves, both nested-function store orders and controls. See docs/COMPILER-NOTES.md. +saves, both nested-function store orders and controls. See docs/build/compiler-notes.md. Usage: no_static_chain_base.py (the one holding toplev.c) """ diff --git a/tools/compiler/transformers/pad_div_erratum_nops.py b/tools/compiler/transformers/pad_div_erratum_nops.py index 653d28c1a..bd5d9273d 100644 --- a/tools/compiler/transformers/pad_div_erratum_nops.py +++ b/tools/compiler/transformers/pad_div_erratum_nops.py @@ -54,7 +54,7 @@ cases too, so that behavior remains unsupported. A division scheduled in a delay slot still loses the padding. Cross-fragment alignment/relaxation and speculative nop removal are not generally reproduced. This is a bounded improvement, not -whole-assembler equivalence. See docs/COMPILER-NOTES.md. +whole-assembler equivalence. See docs/build/compiler-notes.md. Usage: pad_div_erratum_nops.py """ @@ -183,7 +183,7 @@ reset: a label that follows a compiler .set noreorder group (a call and its delay slot) still gets the floor, as retail shows (jal, delay, nop, nop, div.s after a -g line label in func_L00_0040A4A8 and 23 other - level-00 sites). See docs/COMPILER-NOTES.md. + level-00 sites). See docs/build/compiler-notes.md. */ if (rac2_div_label_defined && ! mips_opts.mips16 diff --git a/tools/compiler/transformers/qcopy_barrier.py b/tools/compiler/transformers/qcopy_barrier.py index 9ae439a7d..b50bb5b60 100644 --- a/tools/compiler/transformers/qcopy_barrier.py +++ b/tools/compiler/transformers/qcopy_barrier.py @@ -24,7 +24,7 @@ macro, and models only the schedulers' view of a volatile asm (jump2, for one, still deletes a redundant move across the copy). tests/test_qcopy_barrier.py covers the selector, the default/disabled equality and authored copies around calls, branches, -volatile accesses and neighbouring instructions. See docs/COMPILER-NOTES.md. +volatile accesses and neighbouring instructions. See docs/build/compiler-notes.md. Usage: qcopy_barrier.py """ diff --git a/tools/compiler/transformers/r5900_mfc1_interlock.py b/tools/compiler/transformers/r5900_mfc1_interlock.py index 0a80913a0..3ebd9a956 100644 --- a/tools/compiler/transformers/r5900_mfc1_interlock.py +++ b/tools/compiler/transformers/r5900_mfc1_interlock.py @@ -6,7 +6,7 @@ before such a reader is in a hand-written VU0 routine (func_00283248). This assembler gave every mfc1 the load delay of the MIPS I coprocessor move, so no function with that pair could match from C. Only mfc1 is exempted: cfc2, the other move measured, has the nop in retail -(docs/COMPILER-NOTES.md, the mfc1 interlock).""" +(docs/build/compiler-notes.md, the mfc1 interlock).""" import sys from pathlib import Path diff --git a/tools/compiler/transformers/sticky_volatile_refs.py b/tools/compiler/transformers/sticky_volatile_refs.py index e3f033d67..03847c1cb 100644 --- a/tools/compiler/transformers/sticky_volatile_refs.py +++ b/tools/compiler/transformers/sticky_volatile_refs.py @@ -17,7 +17,7 @@ The option is off by default, enabled per unit in config/cflags.txt, and -fno-rac2-sticky-volatile-refs disables it again. tests/test_sticky_volatile_refs.py covers the anchors, the default/disabled equality, the moved delay slot and a control without an asm. -See docs/COMPILER-NOTES.md. +See docs/build/compiler-notes.md. Usage: sticky_volatile_refs.py (the one holding toplev.c) """ diff --git a/tools/compiler/transformers/sticky_volatile_sets.py b/tools/compiler/transformers/sticky_volatile_sets.py index 1f68c4150..e75ce12ce 100644 --- a/tools/compiler/transformers/sticky_volatile_sets.py +++ b/tools/compiler/transformers/sticky_volatile_sets.py @@ -24,7 +24,7 @@ default because retail's compiler shows it wherever it can be seen: with it, no matched function of src/ changes. -fno-rac2-sticky-volatile-sets restores the EE tree's assignment for a unit that needs it (none does). tests/test_sticky_volatile_sets.py covers the anchors, -the default, the explicit opt-out and controls. See docs/COMPILER-NOTES.md. +the default, the explicit opt-out and controls. See docs/build/compiler-notes.md. Usage: sticky_volatile_sets.py (the one holding toplev.c) """ diff --git a/tools/compiler/transformers/swap_branch_slots.py b/tools/compiler/transformers/swap_branch_slots.py index a776f77cc..cc3d5cc54 100644 --- a/tools/compiler/transformers/swap_branch_slots.py +++ b/tools/compiler/transformers/swap_branch_slots.py @@ -24,7 +24,7 @@ With the option the guard tests `prev_pinfo & INSN_SYNC`, as GNU 2.10-ee does; every other swap rule is unchanged. Without it the assembler is unchanged. Pass it per file with `-Wa,--r5900-swap-branch-slots` in config/cflags.txt. tests/test_swap_branch_slots.py checks -the transformer and the assembler output; see docs/COMPILER-NOTES.md. +the transformer and the assembler output; see docs/build/compiler-notes.md. Usage: swap_branch_slots.py """ diff --git a/tools/compiler/transformers/ti_one_gpr.py b/tools/compiler/transformers/ti_one_gpr.py index bd03e8d08..3033fb99e 100644 --- a/tools/compiler/transformers/ti_one_gpr.py +++ b/tools/compiler/transformers/ti_one_gpr.py @@ -15,7 +15,7 @@ a0, v1, a0 without it, and ten more unmatched functions clear with `por $a0,$0,$0` (func_002ABBB8, func_L00_0038ADD0, ...). Every matched function of all 28 programs compiles to the same bytes with and without it. tests/test_ti_one_gpr.py covers the anchor and the -register choice; see docs/COMPILER-NOTES.md. +register choice; see docs/build/compiler-notes.md. Usage: ti_one_gpr.py """ diff --git a/tools/compiler/transformers/trap_delay_barrier.py b/tools/compiler/transformers/trap_delay_barrier.py index 04cdd8279..24a7c4b17 100644 --- a/tools/compiler/transformers/trap_delay_barrier.py +++ b/tools/compiler/transformers/trap_delay_barrier.py @@ -29,7 +29,7 @@ The backward scan of fill_simple_delay_slots now stops at a TRAP_IF insn, as it stops at a jump. tests/test_trap_delay_barrier.py covers the transformer and the compiler output; see -docs/COMPILER-NOTES.md. +docs/build/compiler-notes.md. Usage: trap_delay_barrier.py """ diff --git a/tools/compiler/transformers/trap_redundant_barrier.py b/tools/compiler/transformers/trap_redundant_barrier.py index 01f8cf3f1..ee02fced9 100644 --- a/tools/compiler/transformers/trap_redundant_barrier.py +++ b/tools/compiler/transformers/trap_redundant_barrier.py @@ -37,7 +37,7 @@ The first backward scan of redundant_insn now ends, finding nothing, at a TRAP_IF insn or a volatile asm. tests/test_trap_redundant_barrier.py covers the transformer and the compiler output; see -docs/COMPILER-NOTES.md. +docs/build/compiler-notes.md. Usage: trap_redundant_barrier.py """ diff --git a/tools/compiler/transformers/u3_frame_order_on.py b/tools/compiler/transformers/u3_frame_order_on.py index 2c09e1178..b54039b45 100644 --- a/tools/compiler/transformers/u3_frame_order_on.py +++ b/tools/compiler/transformers/u3_frame_order_on.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """The earlier step that turned the frame-order option on by default, which -disable_frame_order_default.py later turns off again (docs/COMPILER-NOTES.md).""" +disable_frame_order_default.py later turns off again (docs/build/compiler-notes.md).""" import sys from pathlib import Path p = Path(sys.argv[1]) diff --git a/tools/compiler/transformers/u4_mtc1_helper.py b/tools/compiler/transformers/u4_mtc1_helper.py index 21646a9e2..7dc8c12ed 100644 --- a/tools/compiler/transformers/u4_mtc1_helper.py +++ b/tools/compiler/transformers/u4_mtc1_helper.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """The earlier transfer-hazard helper rac2_mtc1_nop_ok() in gas/config/tc-mips.c, which -restrict_mtc1_exemption.py then narrows (docs/COMPILER-NOTES.md, the tc-mips.c rows).""" +restrict_mtc1_exemption.py then narrows (docs/build/compiler-notes.md, the tc-mips.c rows).""" import sys from pathlib import Path tc_mips = Path(sys.argv[1]) diff --git a/tools/compiler/transformers/wide_saves_below.py b/tools/compiler/transformers/wide_saves_below.py index a88984632..d04b964ce 100644 --- a/tools/compiler/transformers/wide_saves_below.py +++ b/tools/compiler/transformers/wide_saves_below.py @@ -27,7 +27,7 @@ The option is off by default, enabled per unit in config/cflags.txt, has no effect with -mastra-quad-saves (every slot is already 16 bytes), and -fno-rac2-wide-saves-below disables it again. tests/test_wide_saves_below.py covers the anchors, the default/disabled equality, wide -frames with and without stack arguments, and controls. See docs/COMPILER-NOTES.md. +frames with and without stack arguments, and controls. See docs/build/compiler-notes.md. Usage: wide_saves_below.py (the one holding toplev.c) """ diff --git a/tools/compiler/transformers/zero_ti_por.py b/tools/compiler/transformers/zero_ti_por.py index 808755854..d0a23916e 100644 --- a/tools/compiler/transformers/zero_ti_por.py +++ b/tools/compiler/transformers/zero_ti_por.py @@ -25,7 +25,7 @@ 0($sp) with `sq $zero`. Five functions are the measured witnesses, each a unit of its own. tests/test_zero_ti_por.py covers the anchors, the default/disabled equality and authored zero stores through $sp, a pointer, a global and in a loop, at zero and nonzero displacements, a -mixed function, shared and live zeros and the function-value case. See docs/COMPILER-NOTES.md. +mixed function, shared and live zeros and the function-value case. See docs/build/compiler-notes.md. Usage: zero_ti_por.py (the one holding toplev.c) """ diff --git a/tools/compiler/transformers/zero_ti_store_barrier.py b/tools/compiler/transformers/zero_ti_store_barrier.py index 08e4d78a2..73b87bc85 100644 --- a/tools/compiler/transformers/zero_ti_store_barrier.py +++ b/tools/compiler/transformers/zero_ti_store_barrier.py @@ -26,7 +26,7 @@ The option does not change the separate zero-folding pass. Default output remains unchanged; the opt-in witness func_L00_003BFF68 matches at all 12 placements. Compiler regression tests cover the selector, call/branch boundaries, shared clears, displacement controls and disabling -the option again. See docs/COMPILER-NOTES.md for the evidence and limits. +the option again. See docs/build/compiler-notes.md for the evidence and limits. Usage: zero_ti_store_barrier.py """ diff --git a/tools/fcheck.py b/tools/fcheck.py index 18ff0fba1..3554e5ba2 100644 --- a/tools/fcheck.py +++ b/tools/fcheck.py @@ -100,7 +100,7 @@ Needs: OpenRAC's interpreter built (cmake -S tools/interpreter -B build/interpreter in OpenRAC; $OPENRAC_ROOT, default ~/Projects/RAC/OpenRAC; level00 needs its --stick), the -compiler (docs/BUILDING.md; $RAC2_LINUX runs it, e.g. "docker exec rac2-gate"), +compiler (docs/build/building.md; $RAC2_LINUX runs it, e.g. "docker exec rac2-gate"), build/boot/placed.json from `tools/build.py boot` (and build/00_aranos_tutorial/placed.json from `tools/build.py level 00` for level 00), and the disc image. Test-only: nothing here is linked or shipped. """ diff --git a/tools/ghidra/ApplyMobyNames.java b/tools/ghidra/ApplyMobyNames.java index 1c75625ad..48010896b 100644 --- a/tools/ghidra/ApplyMobyNames.java +++ b/tools/ghidra/ApplyMobyNames.java @@ -1,4 +1,4 @@ -// Apply the dispatch-table names of docs/moby-dispatch.tsv to the level overlay that is +// Apply the dispatch-table names of docs/game/moby-dispatch.tsv to the level overlay that is // currently open in Ghidra. // // Usage (Ghidra script, not a build script): @@ -44,7 +44,7 @@ public void run() throws Exception { return; } String level = argv[0]; - String tsv = argv.length > 1 ? argv[1] : "docs/moby-dispatch.tsv"; + String tsv = argv.length > 1 ? argv[1] : "docs/game/moby-dispatch.tsv"; String pins = argv.length > 2 ? argv[2] : "config/overlays.json"; String elfPath = currentProgram.getExecutablePath(); diff --git a/tools/levels.py b/tools/levels.py index 800cfe76f..2a6a0fa63 100644 --- a/tools/levels.py +++ b/tools/levels.py @@ -15,7 +15,7 @@ opens with twelve (offset, size) slots, and slot 0 is the program, a list of sections, each a 16-byte record (destination, size, type, entry point) followed by its bytes (a NOBITS section's too). Every section of one program repeats the entry point; the list ends -where it changes (docs/LEVELS.md). +where it changes (docs/build/levels.md). `catalogue` deduplicates the functions of the 27 programs against each other and against the boot executable, comparing instructions with only their address @@ -38,7 +38,7 @@ ROOT = Path(__file__).resolve().parent.parent -# The three releases (docs/VERSIONS.md). "us" (USA v1.01) is the primary target: its paths are +# The three releases (docs/build/versions.md). "us" (USA v1.01) is the primary target: its paths are # the repository's original ones. The others keep their dumps, tables, build and report under # a directory of their own, and their own C under src//. VERSIONS = { @@ -1309,7 +1309,7 @@ def is_epilogue_residue(body: bytes, previous: bytes) -> bool: # Each follows a completed return across alignment padding, with no direct # branch/call or aligned pointer into the extent in any of its programs. # Unlike is_padding, these include stores/float arithmetic; do not generalise -# their removal to arbitrary code without a return. See docs/LEVELS.md. +# their removal to arbitrary code without a return. See docs/build/levels.md. BOOT_RESIDUES = { 0x00289018: 4, # an isolated store using an uninitialised $v0 0x002A77C0: 28, # orphan float operations using live-in $f0/$f1 @@ -1368,7 +1368,7 @@ def level_text(directory: Path) -> tuple[int, bytes]: } -# Audited fingerprint collisions (docs/MATCHING-BLOCKERS.md section 34): the +# Audited fingerprint collisions (docs/contributing/matching-blockers.md section 34): the # fingerprint masks these field offsets with the address halves, so two bodies # share one key. The words after the leading SKIP bytes (the release-specific # address pair) are the same in every release and tell the bodies apart. Retired diff --git a/tools/progress.py b/tools/progress.py index 4eaae2ce1..a6cf284fa 100644 --- a/tools/progress.py +++ b/tools/progress.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 """ How much of the game's code is C: progress/report.json, in objdiff's report format (the one -ByteMatch reads, as rac1-decomp's), and the table in README.md. +ByteMatch reads, as rac1-decomp's). The README shows ByteMatch's badges, so it never changes. - python3 tools/progress.py # write both, after tools/build.py all (maintainers, on master) + python3 tools/progress.py # write it, after tools/build.py all (maintainers, on master) python3 tools/progress.py --check # the report counts nothing that is not C; it may be behind python3 tools/progress.py --check --strict # and it is not behind either python3 tools/progress.py --version pal # another release's (pal, us2): progress//report.json @@ -411,8 +411,8 @@ def release_check(version: str, report_path: Path) -> None: def release_table(version: str, report: dict) -> None: - """The release's line in docs/VERSIONS.md's table of totals.""" - doc = ROOT / "docs/VERSIONS.md" + """The release's line in docs/build/versions.md's table of totals.""" + doc = ROOT / "docs/build/versions.md" if not doc.is_file(): return m = report["measures"] @@ -455,7 +455,7 @@ def sync_check() -> None: problems.append(f"{a} and {b} disagree on {len(one)} function(s) both have, e.g. {', '.join(one[:6])}") if problems: sys.exit("the releases' reports are out of step (rebuild every release and regenerate " - "every report: docs/VERSIONS.md):\n " + "\n ".join(problems)) + "every report: docs/build/versions.md):\n " + "\n ".join(problems)) print(f"the reports of {', '.join(status)} agree on every function they share") @@ -555,22 +555,6 @@ def main() -> None: REPORT.write_text(json.dumps(report, indent=1) + "\n") release_table("us", report) - # README table - placements = sum(len(json.loads((ROOT / "build" / p / "placed.json").read_text())["functions"]) - for p in programs[1:]) - row = lambda label, m: (f"| {label} | {m['matched_functions']:,} of {m['total_functions']:,} | " - f"{int(m['matched_code']):,} of {int(m['total_code']):,} bytes |") - table = "\n".join(["| | Functions | Code |", "| --- | ---: | ---: |", - row("Boot executable", cats[0]["measures"]), row("Levels", cats[1]["measures"]), - row("**Total**", total)[:-2] + f" ({total['matched_code_percent']:.2f}%) |", "", - f"The 27 levels hold {sum(len(r[4]) for r in levels.read_catalogue()):,} function " - f"placements; C covers {placements:,} of them.", - f"{len(by_hand):,} of the matched functions are the original's own assembly " - f"(`ASM_FUNC`), kept as assembly."]) - readme = ROOT / "README.md" - text = readme.read_text() - a, b = text.index("") + len(""), text.index("") - readme.write_text(text[:a] + "\n" + table + "\n" + text[b:]) print(f"{total['matched_functions']} of {total['total_functions']} functions, {total['matched_code']} of " f"{total['total_code']} bytes ({total['matched_code_percent']:.2f}%)") diff --git a/tools/todo.py b/tools/todo.py index 1a1ced6f7..fb0b71ef3 100644 --- a/tools/todo.py +++ b/tools/todo.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -The functions still to match, to pick work by size (docs/AGENT_WORKFLOW.md). +The functions still to match, to pick work by size (docs/agents/agent-workflow.md). python3 tools/todo.py # a summary by size tier python3 tools/todo.py --max 400 # functions of at most 400 bytes