Skip to content

Commit 2ed74a9

Browse files
committed
docs: W25 §5.3 procedure correction 2 — function-target swap
Per theologian [chat 2026-04-22 19:29Z] second methodology catch on 93d60e8: hir_block_id has NO §1b TU callers (verified grep — func_type_checks_c.c §1a + w25_dual_include + hir_c_api.cpp impl only). Mutating hir_block_id with atomic .h+.cpp would prove §1a path is protected (Step A already gives that) but does NOT test §1b drift surface (which is what Step B closes). Function-target swap to hir_c_insert_before (§1b callers verified): - licm_c.c L17 extern + L246 caller (§1b TU) - pass_output_type_c.c L940 extern + L998/L1006/L1071 callers (§1b TU) Both are §1b per scripts/count_w25_b1b_tus.sh inventory. CHANGES (docs/w25-step-b-mutation-test.md): §1: function-target rationale + new mutation sigs (3-arg with void *unused_drift_param) §2: dual sed updated to mutate hir_c_insert_before in .h + .cpp §3: expected outcome reframed — BUILD_EXIT=0 = drift undetected = PASS for pre-Step-B baseline (theologian's acceptance flip [chat L2148]) §4: post-Step-B procedure also updated to hir_c_insert_before §5: acceptance criterion explicit per theologian flip NOTES on iteration: - Original e8b8c14 procedure: hir_block_id, .h-only sed - Correction 1 (93d60e8): atomic .h+.cpp (theologian L2137) - Correction 2 (this commit): function-target swap (theologian L2148) Per CLAUDE.md no-amend, evolution preserved across commits as methodology-debugging trail. Future readers see the iterative discovery, which is more honest than rewriting to hide the original errors. Authorization chain: - Theologian L2137: atomic .h+.cpp methodology - Theologian L2148: function-target with §1b callers - Generalist HALT before testkeeper baseline run: chat L2147 - Generalist verification grep on hir_c_insert_before §1b callers
1 parent 93d60e8 commit 2ed74a9

1 file changed

Lines changed: 71 additions & 50 deletions

File tree

docs/w25-step-b-mutation-test.md

Lines changed: 71 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -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
6881
cd /data/users/alexturner/phoenix/cpython
6982
git 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
8093
scripts/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
120140
git 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
124144
scripts/build_phoenix.sh > /tmp/w25-mutation-poststepb-stdout.log 2>&1
125145
echo "BUILD_EXIT=$?" >> /tmp/w25-mutation-poststepb-stdout.log
126146
git 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

Comments
 (0)