Visitar URL original
builder: P5 type-level wrappers (BcByteOffset/BcInstrIndex) for bound… · SonicField/cpython@739bdc2 · GitHub
Skip to content

Commit 739bdc2

Browse files
committed
builder: P5 type-level wrappers (BcByteOffset/BcInstrIndex) for boundary domains
W-PROTOCOL-CODIFY Phase A per supervisor 20:03:34Z directive (post pythia python#138 python#3 + supervisor 19:37:39Z STRONG CONCUR + theologian 19:37:17Z spec). Replaces named-only conversions (post W-2B-RECONVERT 3-class fix at 8e0c73e) with single-field struct typedefs that the C compiler tracks distinctly. Inline arithmetic at the seam was the bug class, named conversions were the WEAKER form (HirType negative control: 14 reinterpret_casts shipped broken until 2026-04-15 because naming degrades to advisory). PhxMem has_base structural flag is the POSITIVE control: JIT_CHECK_C didn't fire on dual-arch gate; structural prevention worked. CHANGES (Python/jit/bytecode_c.h): - typedef struct { int v; } BcByteOffset; - typedef struct { int v; } BcInstrIndex; - phx_bc_offset_to_instr_index now takes BcByteOffset, returns BcInstrIndex - phx_bc_instr_index_to_offset now takes BcInstrIndex, returns BcByteOffset - Header comment cites P5 origin + paired empirical controls CHANGES (Python/jit/hir/builder_emit_c.c, build_inline_except_opcode_array_c): - Class A site: wrap except_body_offset in BcByteOffset, convert to BcInstrIndex, unwrap .v for jit_bc_instr_init (existing raw-int API) - Class C site: wrap jit_bc_instr_base_offset return in BcInstrIndex, convert to BcByteOffset, unwrap .v for entry->base_offset - Class B site: wrap jit_bc_instr_get_jump_target return in BcInstrIndex, convert to BcByteOffset, unwrap .v for phx_block_map_lookup_or_panic The wrap/unwrap dance localizes the .v access to exactly the boundary between raw-int existing APIs and typed conversion functions. Compiler will catch any future cross-domain assignment (e.g., assigning BcByteOffset to BcInstrIndex without conversion) as type error — the structural property the spec requires. Spec: docs/w-protocol-codify-spec.md P5 (lines 109-136). Behavior: ZERO change. The numeric values are identical to the named- conversion fix at 8e0c73e (same arithmetic, same call ordering). The 30/30 sentinels (test_exc_raise_catch + 3 W-2B-RECONVERT sentinels) + test_multiple_exceptions_in_loop must remain 30/30 PASS. OUT-OF-SCOPE for Phase A (per spec): - Other domains (cache-id, ref-kind, type-kind) — apply pattern as encountered, not pre-authored. - Existing C++ BCOffset/BCIndex code — already type-enforced in C++. - jit_bc_instr_* APIs — keep raw int signatures; conversion happens at call sites in helpers that cross domains. Future Phase: if a new helper repeatedly wraps/unwraps the same value, consider promoting the return type to BcInstrIndex / BcByteOffset. Phase B (gate integration) + Phase C (per-conversion P6/P7 application to emitAnyCall + emitLoadMethodStatic) queued post-Phase-A. Verification pending: testkeeper rebuild + 30x W-2B-RECONVERT sentinel suite + 30x test_multiple_exceptions_in_loop + full Phoenix gate + ABBA + dual-arch.
1 parent 6121ec4 commit 739bdc2

2 files changed

Lines changed: 51 additions & 37 deletions

File tree

‎Python/jit/bytecode_c.h‎

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,30 @@ _Py_CODEUNIT jit_bc_instr_word(JitBytecodeInstr *bci);
6262
* accessors and phx_block_map_lookup_or_panic argument). See
6363
* Python/jit/bytecode.cpp:8-14 for the boundary convention.
6464
*
65-
* Use these named helpers at every C/C++ seam that crosses byte ↔ index
66-
* units, per the boundary-domain rule (W-PROTOCOL-CODIFY, supervisor
67-
* 2026-04-25 19:01:19Z + theologian 19:01:17Z). Inline arithmetic at the
68-
* seam is the bug class: BCOffset/InstrIndex Class A + B + C mismatches
69-
* in build_inline_except_opcode_array_c (W-2B-RECONVERT investigation
70-
* found three boundary-domain bugs in a single helper across two HIR-diff
71-
* Phase 0 cycles). */
72-
static inline int phx_bc_offset_to_instr_index(int byte_off) {
73-
return byte_off / (int)sizeof(_Py_CODEUNIT);
65+
* W-PROTOCOL-CODIFY P5: TYPE-LEVEL DOMAIN ENFORCEMENT.
66+
* Single-field struct typedefs make BcByteOffset and BcInstrIndex
67+
* compile-time-distinct so the compiler catches accidental cross-domain
68+
* assignment. Conversion functions take/return the wrapper types; raw int
69+
* access is via the .v field. This is the STRUCTURAL form of the
70+
* boundary-domain rule (supervisor 19:37:39Z STRONG CONCUR per pythia
71+
* #138 #3, post W-2B-RECONVERT Class A→B→C cascade where naming-only
72+
* enforcement degraded as theologian's HirType negative control predicted).
73+
*
74+
* Paired empirical controls (per librarian 19:18:55Z + 19:43:28Z):
75+
* POSITIVE: PhxMem has_base structural flag — JIT_CHECK_C didn't fire
76+
* on dual-arch gate; structural prevention worked.
77+
* NEGATIVE: HirType named-conversion-only — 14 reinterpret_casts
78+
* shipped broken until 2026-04-15 (f59580a0e6); naming alone degrades. */
79+
typedef struct { int v; } BcByteOffset;
80+
typedef struct { int v; } BcInstrIndex;
81+
82+
static inline BcInstrIndex phx_bc_offset_to_instr_index(BcByteOffset off) {
83+
BcInstrIndex out = { off.v / (int)sizeof(_Py_CODEUNIT) };
84+
return out;
7485
}
75-
static inline int phx_bc_instr_index_to_offset(int instr_idx) {
76-
return instr_idx * (int)sizeof(_Py_CODEUNIT);
86+
static inline BcByteOffset phx_bc_instr_index_to_offset(BcInstrIndex idx) {
87+
BcByteOffset out = { idx.v * (int)sizeof(_Py_CODEUNIT) };
88+
return out;
7789
}
7890
#endif
7991

‎Python/jit/hir/builder_emit_c.c‎

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3237,16 +3237,21 @@ static void build_inline_except_opcode_array_c(
32373237
return;
32383238
}
32393239

3240-
/* except_body_offset is a BCOffset.value() (BYTE OFFSET, see
3241-
* Python/jit/bytecode.cpp:8-14 for the C/C++ boundary convention).
3242-
* jit_bc_instr_init expects an INSTRUCTION INDEX. Convert at the
3243-
* boundary via the named helper. Subsequent re-inits inside the loop
3244-
* use jit_bc_instr_next_offset which already returns an instruction
3245-
* index. (Class A fix: was passing BYTES → OOB read past end of
3246-
* bytecode → garbage opcode → corrupt deopt.) */
3240+
/* W-PROTOCOL-CODIFY P5: structural domain-typing via BcByteOffset
3241+
* and BcInstrIndex. Wrapping at the seam, unwrapping via .v at the
3242+
* boundary to existing raw-int APIs (jit_bc_instr_init).
3243+
*
3244+
* Class A: caller passed except_body_offset = BCOffset.value() (BYTES).
3245+
* jit_bc_instr_init expects INSTRUCTION INDEX. Pre-fix: bytes-as-index
3246+
* → OOB read → garbage opcode → corrupt deopt. Subsequent re-inits in
3247+
* the loop body use jit_bc_instr_next_offset's INDEX return value, so
3248+
* no further conversion needed there. */
32473249
JitBytecodeInstr ebc;
3248-
jit_bc_instr_init(&ebc, code,
3249-
phx_bc_offset_to_instr_index(except_body_offset));
3250+
{
3251+
BcByteOffset eb_off = { except_body_offset };
3252+
BcInstrIndex eb_idx = phx_bc_offset_to_instr_index(eb_off);
3253+
jit_bc_instr_init(&ebc, code, eb_idx.v);
3254+
}
32503255
int emitted_terminator = 0;
32513256
while (!emitted_terminator) {
32523257
if (n == cap) {
@@ -3265,15 +3270,13 @@ static void build_inline_except_opcode_array_c(
32653270

32663271
int op = jit_bc_instr_opcode(&ebc);
32673272
int oparg = jit_bc_instr_oparg(&ebc);
3268-
/* (Class C fix) jit_bc_instr_base_offset returns whatever was
3269-
* stored at init — INDEX after our Class A fix. But entry->base_offset
3270-
* is consumed downstream as BYTE OFFSET (cur_instr_offs assignment
3271-
* at line 3320 → BCOffset domain per phx_frame_state.h cur_instr_offs
3272-
* semantics, builder.cpp:1710/1774/4392). Convert at the WRITE site
3273-
* to preserve the original BYTES semantic. (Pre-Class-A fix this
3274-
* compensated by Class A's BYTES-as-INDEX init giving back BYTES
3275-
* here; Class C was dormant. Correct Class A exposed Class C.) */
3276-
int base_off = phx_bc_instr_index_to_offset(jit_bc_instr_base_offset(&ebc));
3273+
/* Class C: jit_bc_instr_base_offset returns whatever was stored at
3274+
* init — INDEX after Class A fix. entry->base_offset is consumed
3275+
* downstream as BYTE OFFSET (cur_instr_offs assignment below →
3276+
* BCOffset domain per phx_frame_state.h cur_instr_offs semantics,
3277+
* builder.cpp:1710/1774/4392). Wrap, convert, unwrap. */
3278+
BcInstrIndex base_idx = { jit_bc_instr_base_offset(&ebc) };
3279+
int base_off = phx_bc_instr_index_to_offset(base_idx).v;
32773280

32783281
OpcodeArrayEntry *entry = &arr[n++];
32793282
entry->opcode = op;
@@ -3286,15 +3289,14 @@ static void build_inline_except_opcode_array_c(
32863289
entry->const_obj = (void*)PyTuple_GET_ITEM(code->co_consts, oparg);
32873290
}
32883291
if (op == JUMP_BACKWARD || op == JUMP_BACKWARD_NO_INTERRUPT) {
3289-
/* (Class B fix) jit_bc_instr_get_jump_target returns INSTRUCTION
3290-
* INDEX (codeUnit[]); phx_block_map keys are BYTE OFFSETS
3291-
* (per builder.cpp:1235 phx_block_map_insert with
3292-
* BCOffset.value()). Convert at the boundary via the named
3293-
* helper. */
3294-
int target_idx = jit_bc_instr_get_jump_target(&ebc);
3295-
int target_off = phx_bc_instr_index_to_offset(target_idx);
3292+
/* Class B: jit_bc_instr_get_jump_target returns INSTRUCTION
3293+
* INDEX (codeUnit[]); phx_block_map keys are BYTE OFFSETS (per
3294+
* builder.cpp:1235 phx_block_map_insert with BCOffset.value()).
3295+
* Wrap, convert, unwrap. */
3296+
BcInstrIndex tgt_idx = { jit_bc_instr_get_jump_target(&ebc) };
3297+
BcByteOffset tgt_off = phx_bc_instr_index_to_offset(tgt_idx);
32963298
entry->jump_target_block = phx_block_map_lookup_or_panic(
3297-
&phx_hir_builder_state(builder)->block_map_phx, target_off);
3299+
&phx_hir_builder_state(builder)->block_map_phx, tgt_off.v);
32983300
}
32993301

33003302
if (op == RETURN_VALUE || op == RETURN_CONST

0 commit comments

Comments
 (0)