Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/COMPILER-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,24 @@ source method or adding a compiler mechanism. At revision
This does not justify permuting arguments inside a bank or omitting an argument.

RAC2 already uses this call-shape method in `func_0030E700` and Barlow helpers.
Meaningful one-pass statement groups are also present in active RAC1 game C at
[`1d8228cc`](https://github.com/OpenRAC/rac1-decomp/tree/1d8228cc0c81da7bc2bdc3e77479ee050e0ef434):
[`taxi_and_mine_layer.c`](https://github.com/OpenRAC/rac1-decomp/blob/1d8228cc0c81da7bc2bdc3e77479ee050e0ef434/src/overlays/l03_kerwan/taxi_and_mine_layer.c#L1391)
groups an actual mission-field read, and
[`map_menu_filters.c`](https://github.com/OpenRAC/rac1-decomp/blob/1d8228cc0c81da7bc2bdc3e77479ee050e0ef434/src/overlays/shared/map_menu_filters.c#L212)
groups an actual inner loop. `tools/overlay_asm.py` discovers these overlay
sources; the public release maps include matched entries from them. Following
the current RAC1 source-practice rule, a nonempty `do { real_operation; } while (0)`
can group necessary operations here too. It may preserve compiler loop notes,
scheduling or label alignment without adding runtime instructions. Empty loops,
dummy values, false tests, extra accesses and changed call contracts remain
excluded; a group must preserve the original control flow and side effects.
The three centered-text draws (`func_002CDA20`, `func_002CF2B8`, `func_002D53F0`)
group their real formatter call, preserving its 64-bit style argument and the
text getter's signed 32-bit EE address result. `func_L02_003FB000` groups its
final 16-bit report-state store. All 84 strict release programs pass, with the
functions present in each carrying program. These are source forms, not compiler patches.

Different C declarations across units need a consistent interface or adapter for
a native ABI; an EE match alone does not establish a working native port.
Qualify the actual RAC2 source/profile and every carrying release, rather than
Expand Down
18 changes: 17 additions & 1 deletion docs/MATCHING-BLOCKERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ agreed result here. A persistent personal issue comment is no longer required fo

## Recent verified updates

### Player initialization and retained allocation constraints

**4 November 2026, strict linked match; pending PR review.**
`func_L22_002A8B98` is 172 bytes and matches both placements in each release with
ordinary assignments and the real signed32 return of `func_L22_002C9898`.
Four parameter/selector grouping trials did not match; no group or additional
flag is needed. The borrowed player prefix describes the measured field offsets
without creating an owning allocation.

The same investigation did not resolve `func_002B6658`, `func_L22_003D0840` or
`func_L13_0041AB30`. True callback/SetState contracts, consumed one-pass groups
and measured grid views remain short or allocate differently; their markers
and near misses are retained. The second vector grid ends at the counter at
+0x620 (three rows of four cells), so an owning four-row view is not justified.


### `func_002B60B0` returns nothing

**4 November 2026, local match (pending PR).** `func_002B60B0` (the motion-parameter
Expand Down Expand Up @@ -3410,7 +3426,7 @@ attributed for provenance, not exclusive ownership.
| **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.<br><br>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)`.<br><br>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. |
| **A non-empty `do { ... } while (0)` as a block barrier:** `func_L02_003FB000` (288 bytes), `func_0033A520` (360 bytes, 28 places) and `func_002D53F0` (140 bytes, 28 places) | Question for maintainers | `func_L02_003FB000` is exact with its last store wrapped in `do { ... } while (0);`. The loop notes give the shared `jr $ra` retail's alignment `nop`, which cc1 adds only after a loop. `func_0033A520` needs a block boundary after its rounding of `D_001B23A4 * 300`; the wrapper gets that block right, and six allocation words remain. `func_002D53F0` is exact with its draw call wrapped and 329.0f held in a local; its rounding of `D_001B23A4 * 329.0f + 0.5f` is the same shape as `func_0033A520`'s. `func_L20_004450D8`, listed here before, now matches without a wrapper: written as the group-walk loop of its merged twins (`func_L02_003EAD38` and others), with their repeated `q != 0` test.<br><br>Two merged functions use the same wrapper as a documented scheduling barrier: `func_00124818` and `func_L23_0041C8C0` (ours). The repository check rejects only the empty form. Since AGENTS.md rules out artificial barriers, we have not landed more. **Question:** is a non-empty wrapper acceptable as a macro-shaped statement, or should the two precedents be revisited? |
| **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. |
| **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. |
Expand Down
5 changes: 0 additions & 5 deletions nonmatching/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,11 @@ the placement rules are in `tools/fcheck.py`'s docstring; its tests are
| [`func_001185E8`](core/func_001185E8.c) | 1496 | Candidate is 1508 bytes; the SDK vfprintf.obj float-to-string cvt, reconstructed from the assembly - its six-way Inf/-Inf/NaN/Ind dispatch matches instruction for instruction, and the twelve bytes left are register allocation (masked value in `$v1` against retail's `$s2`) plus the exponent tail |
| [`func_002A0488`](game/func_002A0488.c) | 48 | Non-volatile duplicate-arm form (as in func_002A0468) gives every retail word but the `nop` before `jr $ra` (blocker 4): 44/48, 0/28. An inline-asm `nop` probe matches 28/28 but is not acceptable. Neighbours reading the same word look handwritten; see retained source. |
| [`func_002CA070`](game/func_002CA070.c) | 1372 | 26 October 2026 (finisher F1, after big31): 343 of 343 instructions, all equal except five registers in case 8 (local-alloc priority order of the 0x18, init, upd, page and c values). Own unit -O2 -G16 -Wa,-G16. See the header. |
| [`func_002CDA20`](game/func_002CDA20.c) | 140 | 4 November 2026: US 140/140 bytes, 0/28, first +0x24 after removing the call wrapper used only as a scheduling barrier. US2/PAL canonical identities absent. Recover a real rounding/call block; the controlled historical result cannot qualify this source. Historical index notes (superseded where inconsistent): Retained wide-style near miss. PR #365 u32 trials used a false ABI: target unsigned long is 64 bits. Earlier candidate restored; no new match. |
| [`func_002CF2B8`](game/func_002CF2B8.c) | 136 | Retained wide-style near miss. PR #365 u32 trials used a false ABI: target unsigned long is 64 bits. Earlier candidate restored; no new match. |
| [`func_002859C8`](game/func_002859C8.c) | 3436 | 3436/3436 bytes, not exact. Restore func_0027FAA8's integer return and the 64-bit colour argument of func_0028F410. The remaining panel-call scheduling still differs. US 0/28, US2 absent, PAL absent. |
| [`func_002900B8`](game/func_002900B8.c) | 1388 | 1372 bytes; it belongs in `0028F948-g8.c` and returns `D_001A8D40`. Retail has seven `mfc1` directly followed by a reader of their target, where this assembler inserts a `nop` (docs/COMPILER-NOTES.md), and cc1 spreads the float calculations where retail keeps the source order |
| [`func_002B44C0`](game/func_002B44C0.c) | 1248 | 1192 bytes: the same folded pointer on the two copies at the top, which changes the saved registers holding `self + 0x10` and `target` and the order of the spills at 0xC8-0xEC |
| [`func_0033A520`](game/func_0033A520.c) | 360 | 28 October 2026 recheck: plain C is 360/360 bytes but 0/28 USA v1.01 placements; 26 words differ in the rounding/allocation block. A non-empty `do/while(0)` barrier reproduces the block boundary, but its admissibility remains pending maintainer review. See source and blocker #23. |
| [`func_L22_003F54B8`](levels/func_L22_003F54B8.c) | 196 | Candidate is 200 bytes; retail uses the annulled conditional store (`beql $v0,$zero,L; swc1` with the negative pi/2 above the branch), this cc1 emits the plain branch and both arms |
| [`func_L22_002A8B98`](levels/func_L22_002A8B98.c) | 172 | Recorded instruction mismatch at +0x4. |
| [`func_L22_003D0840`](levels/func_L22_003D0840.c) | 60 | 4; matches size and every other word, but the parameter copy is `daddu $v1,$a0,$zero` against retail's `$a3`. |
| [`func_00129410`](core/func_00129410.c) | 64 | 2 November 2026: VU0 sqc2 stores plus unmatched +0xA0 sp release and 12-byte CDCD fill. Corrected R5900 decode at +0x30 to ordinary sq; existing no-incoming-reference evidence, no C candidate. |
| [`func_L00_002EDC90`](levels/func_L00_002EDC90.c) | 40 | G8 candidate is 44 bytes; 0/27 places. Separate multiply/add does not reproduce the retail COP1 accumulation sequence. |
Expand Down Expand Up @@ -298,7 +295,6 @@ the placement rules are in `tools/fcheck.py`'s docstring; its tests are
| [`func_002FF0D8`](game/func_002FF0D8.c) | 428 | 3 November 2026: Real preview vectors belong to an incomplete resident byte owner, without a fake one-slot model bound. 428/428 bytes, 0/28 USA, first +0x90. Voice request/key scheduling remains; no artificial branch wrapper. |
| [`func_002CF5B0`](game/func_002CF5B0.c) | 576 | Near miss: 576/576 B, 10 words, base/status register swap plus item register; see the header. Profile `-O2 -G32 -Wa,-G32 -ffunction-sections`. |
| [`func_002D2188`](game/func_002D2188.c) | 592 | 2 November 2026: Matched0027FAA8 returns signed int. Correcting the ignored text-draw result ties at580/592,0/28. Missing retail unused saved s1/0x30 frame and FP rounding lifetimes remain. Not linked or counted. |
| [`func_L02_003FB000`](levels/func_L02_003FB000.c) | 288 | 284 bytes in a `-G8` unit: the only missing word is the `nop` that aligns the shared `jr $ra`, which final.c gives only to the first label after a loop start; exact with the last store in a non-empty `do { ... } while (0)`, not landed (a question on #157) |
| [`func_L02_003FF6E8`](levels/func_L02_003FF6E8.c) | 696 | 700 bytes: with `ctx` a pointer to the first buffer, retail's 0xF0 frame and register roles; retail rebuilds the address at each call and still saves an unused `$s2` (21 retail game functions save a register they never use, none in C) |
| [`func_L23_0042A018`](levels/func_L23_0042A018.c) | 1052 | 25 October 2026: 1052/1052 bytes. The 144-byte packet and matrix/axis extents are preserved. See retained source for profile, release coverage and reopening condition. |
| [`func_L23_0041A3B8`](levels/func_L23_0041A3B8.c) | 652 | 2 November 2026: Three canonical current helpers return pointers rather thanvoid. Correct declarations retained; output remains same known sound-tail branch mismatch. No fabricated single-pass scheduling loop added. Not linked or counted. |
Expand Down Expand Up @@ -336,7 +332,6 @@ the placement rules are in `tools/fcheck.py`'s docstring; its tests are
| [`func_L16_00410E40`](levels/func_L16_00410E40.c) | 356 | Near miss: 364 bytes against retail's 356. The table of the handle's index is at 0x447FE7C0 in level 16, the second argument of func_002ACBC8 is the result of func_L16_00417788 and not the table entry, and the first loop is a do-while whose index is incremented in the delay slot of the `bc1fl`. What is left is the register allocation (retail keeps that result in $fp across both loops, this source spills it) and the modulo of the second loop, which retail computes from the index it reloads from the frame |


| [`func_002D53F0`](game/func_002D53F0.c) | 140 | Retained wide-style near miss. PR #365 u32 trials used a false ABI: target unsigned long is 64 bits. Earlier candidate restored; no new match. |


| `func_0028A180` | 240 | One 228-byte plain-C draft matched 0/28 in US, PAL, and US2. It used guessed fixed data addresses; no safe shared-source data symbols were established, so the draft was discarded. Reopen with verified data-symbol mapping and retail loop/layout evidence. |
Expand Down
4 changes: 0 additions & 4 deletions nonmatching/functional.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@ func_L15_00437B68 nonmatching/functional/func_L15_00437B68.c
func_L16_003AA3F0 nonmatching/functional/func_L16_003AA3F0.c
func_L17_003E8A58 nonmatching/functional/func_L17_003E8A58.c
func_L18_003FAE50 nonmatching/functional/func_L18_003FAE50.c
func_L22_002A8B98 nonmatching/functional/func_L22_002A8B98.c
func_L22_003A15D0 nonmatching/functional/func_L22_003A15D0.c
func_L22_003D0840 nonmatching/functional/func_L22_003D0840.c
func_L22_003F54B8 nonmatching/functional/func_L22_003F54B8.c
Expand Down Expand Up @@ -287,7 +286,6 @@ func_002F93B0 nonmatching/functional/func_002F93B0.c
func_002F97A0 nonmatching/functional/func_002F97A0.c
func_002FB1A0 nonmatching/functional/func_002FB1A0.c
func_00281A08 nonmatching/functional/func_00281A08.c
func_002D53F0 nonmatching/functional/func_002D53F0.c
func_L00_00341FA8 nonmatching/functional/func_L00_00341FA8.c
func_L01_003801B8 nonmatching/functional/func_L01_003801B8.c
func_L22_003A5018 nonmatching/functional/func_L22_003A5018.c
Expand All @@ -304,7 +302,6 @@ func_L00_00316F60 nonmatching/functional/func_L00_00316F60.c
func_L00_002E0200 nonmatching/functional/func_L00_002E0200.c
func_002A3110 nonmatching/functional/func_002A3110.c
func_002CF5B0 nonmatching/functional/func_002CF5B0.c
func_002CF2B8 nonmatching/functional/func_002CF2B8.c
func_003166C8 nonmatching/functional/func_003166C8.c
func_L00_00423290 nonmatching/functional/func_L00_00423290.c
func_L00_002C40C0 nonmatching/functional/func_L00_002C40C0.c
Expand All @@ -316,7 +313,6 @@ func_L02_003C43F0 nonmatching/functional/func_L02_003C43F0.c
func_L00_003B84F0 nonmatching/functional/func_L00_003B84F0.c
func_L00_00395428 nonmatching/functional/func_L00_00395428.c
func_00282D48 nonmatching/functional/func_00282D48.c
func_002CDA20 nonmatching/functional/func_002CDA20.c
func_002900B8 nonmatching/functional/func_002900B8.c
func_002A3DB8 nonmatching/functional/func_002A3DB8.c
func_L00_00408E58 nonmatching/functional/func_L00_00408E58.c
Expand Down
25 changes: 0 additions & 25 deletions nonmatching/functional/func_002CDA20.c

This file was deleted.

Loading
Loading