@@ -29,34 +29,47 @@ With dual-mutation (.h + .cpp consistent on new sig):
2929 resolves to the new-signature impl → silent runtime UB (PROVES §1b
3030 is the unprotected drift surface — exactly what Step B closes)
3131
32- Mutation function choice: ` hir_block_id ` .
32+ ** Function-target correction per theologian [ chat 2026-04-22 19:29Z] :**
33+ the original ` hir_block_id ` choice has NO §1b TU callers (only
34+ ` func_type_checks_c.c ` which is §1a). Mutating it would catch the
35+ §1a-path drift (proves Step A is tight) but NOT the §1b drift surface
36+ (which is what Step B closes). To test the §1b surface, choose a
37+ function called from §1b TUs via local extern.
3338
34- ** Original signature** (post-Step-A at 3404a81192):
39+ Mutation function choice: ` hir_c_insert_before ` (§1b callers verified
40+ via grep — ` licm_c.c:17 ` extern + ` :246 ` call, `pass_output_type_c.c
41+ :940` extern + ` :998/1006/1071` calls; both are §1b TUs per
42+ ` scripts/count_w25_b1b_tus.sh ` inventory).
43+
44+ ** Original signature** (in hir_c_api.h:726 + hir_c_api.cpp:2349):
3545
3646``` c
3747/* hir_c_api.h */
38- int hir_block_id (struct HirBasicBlock * block );
48+ void hir_c_insert_before (HirInstr new_instr, HirInstr before );
3949/* hir_c_api.cpp * /
40- int hir_block_id(struct HirBasicBlock * block ) { ... }
50+ void hir_c_insert_before(HirInstr new_instr, HirInstr before ) { ... }
4151```
4252
4353**Mutated signature** (W25 §5.3 baseline mutation, BOTH files):
4454
4555```c
4656/* hir_c_api.h */
47- int hir_block_id(struct HirBasicBlock *block, int unused_drift_param);
57+ void hir_c_insert_before(HirInstr new_instr, HirInstr before, void * unused_drift_param);
4858/* hir_c_api.cpp */
49- int hir_block_id(struct HirBasicBlock *block, int unused_drift_param) { ... }
59+ void hir_c_insert_before(HirInstr new_instr, HirInstr before, void * unused_drift_param) { ... }
5060```
5161
52- Why ` hir_block_id ` :
53- - Called from at least 1 §1b TU (per ` count_w25_b1b_tus.sh ` survey).
54- - Simple int parameter — no implicit-conversion masking.
55- - ABI-incompatible mutation (extra arg) — caller pushes wrong arg
56- count vs callee expects new arg → silent UB at runtime.
57- - In C, calling a function with a different signature than its
58- declaration is undefined behavior, NOT a hard link error. §5.3
59- answers empirically what this UB looks like in our build.
62+ Why ` hir_c_insert_before ` :
63+ - §1b callers exist (licm_c.c + pass_output_type_c.c), so the §1b
64+ drift surface is exercised.
65+ - Simple void* parameter — no implicit-conversion masking, no struct
66+ layout dependency.
67+ - ABI-incompatible mutation (extra arg) — §1b callers' local externs
68+ stay 2-arg, callers pass 2 args, linker resolves to 3-arg impl →
69+ silent runtime UB.
70+ - Predictable: §1a path (none for this function) and §1b path
71+ (licm_c.c + pass_output_type_c.c local externs) are visibly
72+ identifiable in the build output.
6073
6174## 2. Baseline procedure (pre-Step-B, run at HEAD e8b8c149fe or later)
6275
@@ -68,13 +81,13 @@ git rev-parse HEAD # expect: e8b8c149fe... (this commit) or its descendant
6881cd /data/users/alexturner/phoenix/cpython
6982git stash --include-untracked # stash any working-tree changes
7083# Mutate header decl
71- sed -i ' s|int hir_block_id(struct HirBasicBlock \*block );|int hir_block_id(struct HirBasicBlock *block, int unused_drift_param);|' Python/jit/hir/hir_c_api.h
84+ sed -i ' s|void hir_c_insert_before(HirInstr new_instr, HirInstr before );|void hir_c_insert_before(HirInstr new_instr, HirInstr before, void * unused_drift_param);|' Python/jit/hir/hir_c_api.h
7285# Mutate cpp impl signature line (preserves brace-on-same-line)
73- sed -i ' s|int hir_block_id(struct HirBasicBlock \*block ) {|int hir_block_id(struct HirBasicBlock *block, int unused_drift_param) {|' Python/jit/hir/hir_c_api.cpp
86+ sed -i ' s|void hir_c_insert_before(HirInstr new_instr, HirInstr before ) {|void hir_c_insert_before(HirInstr new_instr, HirInstr before, void * unused_drift_param) {|' Python/jit/hir/hir_c_api.cpp
7487
7588# Verify both mutations applied
76- grep -A1 " hir_block_id " Python/jit/hir/hir_c_api.h | head -5
77- grep -A1 " ^int hir_block_id " Python/jit/hir/hir_c_api.cpp | head -3
89+ grep -A1 " hir_c_insert_before " Python/jit/hir/hir_c_api.h | head -5
90+ grep -A1 " ^void hir_c_insert_before " Python/jit/hir/hir_c_api.cpp | head -3
7891
7992# Step 3: try to build
8093scripts/build_phoenix.sh > /tmp/w25-mutation-baseline-stdout.log 2>&1
@@ -95,20 +108,27 @@ git diff Python/jit/hir/hir_c_api.h Python/jit/hir/hir_c_api.cpp # expect: empt
95108
96109## 3. Expected baseline outcome
97110
98- ** Hypothesis (theoretical):** the mutation will be silently linkable
99- because §1b TUs use local extern decls (their stale signature matches
100- itself; linker sees only function name).
101-
102- ** To-verify:** the build either:
103- - (a) Succeeds at link time — confirms drift surface exists. Record
104- the finding as PRE-STEP-B BASELINE.
105- - (b) Fails at compile time — surprising; hir_c_api.h consumers (the
106- §1a TUs) would catch the mismatch. Record what those compile errors
107- look like.
108- - (c) Succeeds at compile but fails at runtime — also possible if some
109- caller ends up with corrupted-stack behavior.
110-
111- The actual outcome is the empirical baseline. Document it.
111+ ** Hypothesis (theoretical):** with corrected target (hir_c_insert_before
112+ which has §1b callers) AND atomic .h+.cpp mutation, the build will
113+ succeed (BUILD_EXIT=0) because:
114+ - hir_c_api.cpp compiles fine (header + impl agree on new sig)
115+ - §1a TUs that include hir_c_api.h see new sig — but hir_c_insert_before
116+ has no §1a callers, so no §1a-side compile error
117+ - §1b TUs (licm_c.c + pass_output_type_c.c) use local extern (2-arg)
118+ for hir_c_insert_before; their callers compile fine against local
119+ extern; linker resolves to 3-arg impl by name → silent runtime UB
120+
121+ ** Acceptance flip per theologian [ chat 2026-04-22 19:29Z] :** PRE-STEP-B
122+ baseline PASSES when BUILD_EXIT=0 (drift undetected at compile time).
123+ This proves the §1b drift surface exists.
124+
125+ ** Surprising outcomes to investigate:**
126+ - BUILD_EXIT≠0 with errors in licm_c.c / pass_output_type_c.c: the §1b
127+ protection might already be in place via some other mechanism
128+ (unexpected — would re-frame Step B's value).
129+ - BUILD_EXIT≠0 with errors elsewhere: a different §1a or non-§1b TU
130+ has a hir_c_insert_before caller my grep missed. Document the
131+ unexpected callsite + adjust scope of finding.
112132
113133## 4. Post-Step-B procedure
114134
@@ -119,32 +139,33 @@ hir_c_api.h):
119139# At post-Step-B HEAD
120140git rev-parse HEAD # expect: <Step B's last commit hash>
121141# Same DUAL mutation as §2 — keep .h + .cpp consistent
122- sed -i ' s|int hir_block_id(struct HirBasicBlock \*block );|int hir_block_id(struct HirBasicBlock *block, int unused_drift_param);|' Python/jit/hir/hir_c_api.h
123- sed -i ' s|int hir_block_id(struct HirBasicBlock \*block ) {|int hir_block_id(struct HirBasicBlock *block, int unused_drift_param) {|' Python/jit/hir/hir_c_api.cpp
142+ sed -i ' s|void hir_c_insert_before(HirInstr new_instr, HirInstr before );|void hir_c_insert_before(HirInstr new_instr, HirInstr before, void * unused_drift_param);|' Python/jit/hir/hir_c_api.h
143+ sed -i ' s|void hir_c_insert_before(HirInstr new_instr, HirInstr before ) {|void hir_c_insert_before(HirInstr new_instr, HirInstr before, void * unused_drift_param) {|' Python/jit/hir/hir_c_api.cpp
124144scripts/build_phoenix.sh > /tmp/w25-mutation-poststepb-stdout.log 2>&1
125145echo " BUILD_EXIT=$? " >> /tmp/w25-mutation-poststepb-stdout.log
126146git checkout -- Python/jit/hir/hir_c_api.h Python/jit/hir/hir_c_api.cpp
127147```
128148
129- ** Expected post-Step-B outcome:** compile FAILS in every consuming TU
130- that calls ` hir_block_id ` (the linker doesn't get a chance — compiler
131- catches the mismatch at parse time because all consumers see the
132- canonical signature from hir_c_api.h).
149+ ** Expected post-Step-B outcome:** compile FAILS in licm_c.c +
150+ pass_output_type_c.c (the §1b TUs that called ` hir_c_insert_before `
151+ via local extern). Step B deleted those externs and added
152+ ` #include hir_c_api.h ` , so post-Step-B these TUs see the new 3-arg
153+ signature directly — their 2-arg callers fail to compile with clear
154+ "too few arguments" errors.
133155
134156## 5. Acceptance criterion
135157
136- §5.3 falsification PASSES when the pre-Step-B baseline shows DRIFT GOES
137- UNDETECTED (or weakly detected) AND the post-Step-B run shows DRIFT
138- CAUGHT AT COMPILE TIME with clear errors at every call site.
139-
140- If pre-Step-B baseline shows the drift is ALREADY caught at compile time
141- (unexpected outcome (b) above), then either:
142- - The drift surface was always smaller than Step B's framing assumed
143- (and Step B's value is reduced — not invalidated, just reframed).
144- - Or my mutation choice doesn't actually exercise the §1b drift surface
145- (need a different mutation).
146-
147- Either way, the empirical finding informs Step B's framing.
158+ §5.3 falsification PASSES when:
159+ - PRE-STEP-B baseline: BUILD_EXIT=0 (drift undetected at compile time
160+ — §1b drift surface exists per hypothesis)
161+ - POST-STEP-B re-run: BUILD_EXIT≠0 with compile errors in §1b TU
162+ callers (§1b drift surface closed by Step B)
163+
164+ If PRE-STEP-B baseline shows BUILD_EXIT≠0 (drift caught somewhere),
165+ investigate: the protection might already be in place via a different
166+ mechanism, or my mutation choice doesn't actually exercise the §1b
167+ drift surface as predicted. Either way, the empirical finding informs
168+ Step B's framing.
148169
149170---
150171
0 commit comments