Files
BFM-decomp/cookbook/C0380.md
T

1.8 KiB

§339 — A 2-CASE SWITCH OMITS THE LOW-BOUND RANGE TEST, SO THE PRESENCE OF slti/bnez BETWEEN THE beqs IS A COUNT TELL FOR A THIRD CASE NODE (P31 S67; byte-proven ov_SC02_005/func_80190538, 197 ins)

stmt.c's emit_case_nodes carries the rule in its own comment — "omit the conditional branch to default if we avoid only one right child" — so a switch with TWO case nodes emits no low-bound check, while THREE emits one. That makes the range test a structural count you can read off the target, not a detail to reproduce by luck:

beq  $v1, 1, ...        <- case 1
slti $v0, $v1, 2        <- THIS PAIR ONLY EXISTS IF THERE ARE >=3 CASE NODES
bnez $v0, default
beq  $v1, 2, ...        <- case 2

Seeing slti/bnez sitting BETWEEN two beqs means a case node is missing from your draft. Adding case 0: return; supplied exactly the missing 2 instructions here. Read the case count off the range test before you start rewriting arms.

Three more dials byte-proven on the same function, all reusable:

  • A frame's "holes" can be struct padding, not scalars. Slots 0x20/0x24/0x28 + 0x30/0x38 with gaps at 0x2C/0x34/0x3C are TWO 16-byte VECTOR-shaped structs whose pad members are the holes — not five independent scalars. Cf. §333: frame arithmetic is evidence about DECLARATIONS.
  • A - (B + C) tree-folds to (A - C) - B, which flips an addiu sign. An explicit temp for B + C blocks the fold.
  • Declaring temps INSIDE each if-arm instead of at function scope is a REGISTER dial. A function-scope pseudo spans both arms (ptr=$a2 / dx=$a1 / dz=$a0); block-scope gives the target's ptr=$a1, first-computed=$a0, second=$v1. Paired lever: putting v.vy = 0; at the END of each arm rather than after the join keeps the join block's lw $a2,0x24($sp) a REAL load instead of letting cse turn it into move $a2,$zero.