Skip to content

Commit c905827

Browse files
committed
Phase 3 Batch 1: PhxHirBuilderState foundation + parseExceptionTable port
Phase 3 Batch 1 (Y) per docs/tier7-phase3-hirbuilder-state-extraction-spec.md: foundation + first-method atomic. Establishes the state-extraction pattern for subsequent Phase 3 batches. NEW: PhxHirBuilderState struct holding HIRBuilder Class A members (code, preloader, current_func, func, kwnames). Class B members (block_map_, exception_table_, etc.) remain on the C++ side; per-batch migration per spec §5 python#1. NEW: pure-C body hir_builder_state_parse_exception_table_c reads the 3.12+ varint exception table via state->code, pushing entries through the C++-side bridge hir_builder_state_exception_table_push_cpp (which accesses HIRBuilder.exception_table_ as friend). C++ shim: HIRBuilder::parseExceptionTable becomes a 1-line delegation to the C body. C++ ctor calls hir_builder_state_init to populate Class A fields. Bridges added (3, well under W25b ≤5/batch): hir_builder_state_init(state, code, preloader) hir_builder_state_parse_exception_table_c(state, builder) hir_builder_state_exception_table_push_cpp(builder, start, end, target, depth, lasti) 5-arg flat push_cpp signature avoids struct-on-stack ABI complexity (theologian 23:05:15Z (d)). end is pre-computed as (start+size)*scale in the C body. W45 fixtures: 3 new fixtures (state_init, parse_exception_table_c, exception_table_push_cpp) added to scripts/w45_bridge_drift_falsifier.sh per shepard 22:46:33Z same-commit discipline. W45 script extended to mutate 4 source files (added builder_state_c.h + builder_state_c.c). Dry-run: 9/9 fixtures stage cleanly. Numstat (vs HEAD e05a358): Python/jit/hir/builder.cpp +17 -38 (-21 NET) Python/jit/hir/builder.h +15 -1 (+14 NET) Python/jit/hir/builder_state_c.h +68/0 (NEW) Python/jit/hir/builder_state_c.c +64/0 (NEW) scripts/w45_bridge_drift_falsifier.sh +34 -19 (+15 NET) TOTAL: NET +140L (forecast +95L; +45L variance one-time foundation cost — W45 script extension + ctor wiring + friend-decl plumbing reusable across future batches; theologian 23:12:04Z normalization). Pre-commit compile-check: testkeeper 23:13:20Z BUILD_EXIT=0, 3-test sanity green (partial_conversions + W22 main + W44 gate). W44 gate: PASS (2 markers, 0 production callers). Authorization: theologian 23:05:15Z + supervisor 23:02:34Z (Y) atomic. Cross-post discipline: cross-post handled per librarian 23:05:04Z (supervisor disposition + theologian methodology cross-check both PASS for (Y)).
1 parent e05a358 commit c905827

5 files changed

Lines changed: 198 additions & 58 deletions

File tree

Python/jit/hir/builder.cpp

Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,45 +1227,24 @@ HIRBuilder::BlockMap HIRBuilder::createBlocks(
12271227
}
12281228

12291229

1230-
void HIRBuilder::parseExceptionTable() {
1231-
PyObject* table_obj = code_->co_exceptiontable;
1232-
if (table_obj == nullptr || !PyBytes_Check(table_obj)) {
1233-
return;
1234-
}
1235-
const uint8_t* table =
1236-
reinterpret_cast<const uint8_t*>(PyBytes_AS_STRING(table_obj));
1237-
Py_ssize_t length = PyBytes_GET_SIZE(table_obj);
1238-
Py_ssize_t pos = 0;
1230+
extern "C" void hir_builder_state_exception_table_push_cpp(
1231+
void *builder,
1232+
int start,
1233+
int end,
1234+
int target,
1235+
int depth,
1236+
int lasti) {
1237+
HIRBuilder *self = static_cast<HIRBuilder*>(builder);
1238+
self->exception_table_.push_back(HIRBuilder::ExceptionTableEntry{
1239+
BCOffset{start},
1240+
BCOffset{end},
1241+
BCOffset{target},
1242+
depth,
1243+
lasti != 0});
1244+
}
12391245

1240-
// Variable-length integer decoder matching CPython 3.12 format (MSB first).
1241-
// Each byte: bits 0-5 = payload, bit 6 = continuation.
1242-
// First byte holds the most-significant bits of the value.
1243-
auto parse_varint = [&]() -> int {
1244-
int val = 0;
1245-
uint8_t b;
1246-
do {
1247-
JIT_DCHECK(pos < length, "Truncated exception table");
1248-
b = table[pos++];
1249-
val = (val << 6) | (b & 0x3F);
1250-
} while (b & 0x40);
1251-
return val;
1252-
};
1253-
while (pos < length) {
1254-
int start = parse_varint();
1255-
int size = parse_varint();
1256-
int target = parse_varint();
1257-
int depth_lasti = parse_varint();
1258-
1259-
// Exception table values are in instruction units (word offsets).
1260-
// Convert to byte offsets by multiplying by sizeof(_Py_CODEUNIT).
1261-
constexpr int scale = sizeof(_Py_CODEUNIT);
1262-
exception_table_.push_back(ExceptionTableEntry{
1263-
BCOffset{start * scale},
1264-
BCOffset{(start + size) * scale},
1265-
BCOffset{target * scale},
1266-
depth_lasti >> 1,
1267-
(depth_lasti & 1) != 0});
1268-
}
1246+
void HIRBuilder::parseExceptionTable() {
1247+
hir_builder_state_parse_exception_table_c(&state_, this);
12691248
}
12701249

12711250
const HIRBuilder::ExceptionTableEntry* HIRBuilder::findExceptionHandler(

Python/jit/hir/builder.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "cinderx/Common/util.h"
88
#include "cinderx/Jit/bytecode.h"
99
#include "cinderx/Jit/bytecode_offsets.h"
10+
#include "cinderx/Jit/hir/builder_state_c.h"
1011
#include "cinderx/Jit/hir/hir.h"
1112
#include "cinderx/Jit/hir/preload.h"
1213

@@ -207,10 +208,17 @@ class HIRBuilder {
207208
void*, void*, void*, int, void*, void*);
208209
// W27d #1 (theologian L2544): grants C body access to private Register* func_.
209210
friend void* ::hir_builder_func_register_c(void*);
211+
// Phase 3 Batch 1 (theologian 23:05:15Z): grants C-side bridge access to
212+
// private std::vector<ExceptionTableEntry> exception_table_.
213+
friend void ::hir_builder_state_exception_table_push_cpp(
214+
void*, int, int, int, int, int);
210215
public:
211216
const Preloader& preloader() const { return preloader_; }
212217
explicit HIRBuilder(const Preloader& preloader)
213-
: code_(preloader.code()), preloader_(preloader) {}
218+
: code_(preloader.code()), preloader_(preloader) {
219+
hir_builder_state_init(
220+
&state_, static_cast<void*>(code_), static_cast<const void*>(&preloader_));
221+
}
214222

215223
// Translate the bytecode for code_ into HIR, in the context of the preloaded
216224
// globals and classloader lookups from preloader_.
@@ -679,6 +687,12 @@ class HIRBuilder {
679687
Register* kwnames_{nullptr};
680688

681689
OperandStack static_method_stack_;
690+
691+
// Phase 3 Batch 1: Class A state mirror (code_, preloader_, current_func_,
692+
// func_, kwnames_). Initialized in ctor; subsequent batches migrate
693+
// mutator sites to update state_ in lockstep + remove duplicate C++
694+
// members. C-body bridges (hir_builder_state_*_c) read state via this.
695+
PhxHirBuilderState state_{};
682696
};
683697

684698
} // namespace jit::hir

Python/jit/hir/builder_state_c.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/* Copyright (c) Meta Platforms, Inc. and affiliates.
2+
*
3+
* Pure-C port of HIRBuilder Class A state initialization +
4+
* parseExceptionTable algorithm. Phase 3 Batch 1 per
5+
* docs/tier7-phase3-hirbuilder-state-extraction-spec.md.
6+
*/
7+
8+
#include "cinderx/Jit/hir/builder_state_c.h"
9+
#include "Python.h"
10+
11+
#include <stdint.h>
12+
13+
void hir_builder_state_init(
14+
PhxHirBuilderState *state,
15+
void *code,
16+
const void *preloader) {
17+
state->code = code;
18+
state->preloader = preloader;
19+
state->current_func = NULL;
20+
state->func = NULL;
21+
state->kwnames = NULL;
22+
}
23+
24+
void hir_builder_state_parse_exception_table_c(
25+
PhxHirBuilderState *state,
26+
void *builder) {
27+
PyCodeObject *code = (PyCodeObject*)state->code;
28+
PyObject *table_obj = code->co_exceptiontable;
29+
if (table_obj == NULL || !PyBytes_Check(table_obj)) {
30+
return;
31+
}
32+
const uint8_t *table = (const uint8_t*)PyBytes_AS_STRING(table_obj);
33+
Py_ssize_t length = PyBytes_GET_SIZE(table_obj);
34+
Py_ssize_t pos = 0;
35+
36+
/* CPython 3.12 varint: 6 payload bits per byte, bit 6 = continuation,
37+
* MSB first. */
38+
while (pos < length) {
39+
int vals[4];
40+
for (int i = 0; i < 4; i++) {
41+
int val = 0;
42+
uint8_t b;
43+
do {
44+
b = table[pos++];
45+
val = (val << 6) | (b & 0x3F);
46+
} while (b & 0x40);
47+
vals[i] = val;
48+
}
49+
int start = vals[0];
50+
int size = vals[1];
51+
int target = vals[2];
52+
int depth_lasti = vals[3];
53+
54+
/* Convert instruction units to byte offsets. */
55+
const int scale = (int)sizeof(_Py_CODEUNIT);
56+
hir_builder_state_exception_table_push_cpp(
57+
builder,
58+
start * scale,
59+
(start + size) * scale,
60+
target * scale,
61+
depth_lasti >> 1,
62+
depth_lasti & 1);
63+
}
64+
}

Python/jit/hir/builder_state_c.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/* Copyright (c) Meta Platforms, Inc. and affiliates.
2+
*
3+
* PhxHirBuilderState — Phase 3 Batch 1 foundation per
4+
* docs/tier7-phase3-hirbuilder-state-extraction-spec.md §3.1.
5+
*
6+
* Class A members (5 immutable + nullable opaque pointers) extracted
7+
* from HIRBuilder. Class B members (block_map_, exception_table_, etc.)
8+
* remain C++-side and access via _cpp suffix bridges (push/clear/iterate)
9+
* until per-batch migration per spec §5 #1.
10+
*
11+
* Authorization: theologian 23:05:15Z + supervisor 23:02:34Z (Y) atomic.
12+
*/
13+
14+
#ifndef PHX_BUILDER_STATE_C_H
15+
#define PHX_BUILDER_STATE_C_H
16+
17+
#include "Python.h"
18+
19+
#ifdef __cplusplus
20+
extern "C" {
21+
#endif
22+
23+
/* PhxHirBuilderState — opaque holder for HIRBuilder Class A state.
24+
* code + preloader are immutable post-ctor; current_func/func/kwnames
25+
* mutate during translate()/emit. Subsequent batches add Class B
26+
* opaque pointers (block_map, exception_table, pending_b2_blocks,
27+
* temps, static_method_stack). */
28+
typedef struct PhxHirBuilderState {
29+
void *code; /* PyCodeObject* (ctor, immutable) */
30+
const void *preloader; /* const Preloader& (ctor, immutable) */
31+
void *current_func; /* Function* (mutable, nullable) */
32+
void *func; /* Register* (mutable, nullable) */
33+
void *kwnames; /* Register* (mutable, nullable) */
34+
} PhxHirBuilderState;
35+
36+
/* Initialize state_ Class A fields from HIRBuilder ctor args. Mutable
37+
* fields (current_func, func, kwnames) are NULL-initialized matching
38+
* the existing C++ default-member-initialization. */
39+
void hir_builder_state_init(
40+
PhxHirBuilderState *state,
41+
void *code,
42+
const void *preloader);
43+
44+
/* Pure-C port of HIRBuilder::parseExceptionTable. Reads
45+
* state->code's co_exceptiontable, decodes 3.12+ varint format, and
46+
* pushes ExceptionTableEntry records via the _cpp bridge below. */
47+
void hir_builder_state_parse_exception_table_c(
48+
PhxHirBuilderState *state,
49+
void *builder);
50+
51+
/* C++-side bridge: pushes a single entry onto HIRBuilder.exception_table_
52+
* (a std::vector<ExceptionTableEntry>). 5-arg flat signature avoids
53+
* struct-on-stack ABI complexity (theologian 23:05:15Z (d)). 'end' is the
54+
* absolute byte offset after the try range (start + size * sizeof(_Py_CODEUNIT))
55+
* — pre-computed by the C body. lasti is 0 or 1 (decoded as bit). */
56+
void hir_builder_state_exception_table_push_cpp(
57+
void *builder,
58+
int start,
59+
int end,
60+
int target,
61+
int depth,
62+
int lasti);
63+
64+
#ifdef __cplusplus
65+
} /* extern "C" */
66+
#endif
67+
68+
#endif /* PHX_BUILDER_STATE_C_H */

scripts/w45_bridge_drift_falsifier.sh

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,16 @@ done
4848

4949
BUILDER_CPP="Python/jit/hir/builder.cpp"
5050
BUILDER_EMIT_C="Python/jit/hir/builder_emit_c.c"
51+
BUILDER_STATE_C_H="Python/jit/hir/builder_state_c.h"
52+
BUILDER_STATE_C="Python/jit/hir/builder_state_c.c"
5153
CMAKE_BUILD_DIR="Python/jit_build/build"
5254

53-
if [ ! -f "$BUILDER_CPP" ] || [ ! -f "$BUILDER_EMIT_C" ]; then
54-
echo "ERROR: missing $BUILDER_CPP or $BUILDER_EMIT_C — wrong CWD?" >&2
55-
exit 1
56-
fi
55+
for f in "$BUILDER_CPP" "$BUILDER_EMIT_C" "$BUILDER_STATE_C_H" "$BUILDER_STATE_C"; do
56+
if [ ! -f "$f" ]; then
57+
echo "ERROR: missing $f — wrong CWD?" >&2
58+
exit 1
59+
fi
60+
done
5761

5862
echo "=== w45_bridge_drift_falsifier.sh — W45 gate ==="
5963
echo "HEAD: $(git rev-parse HEAD)"
@@ -63,13 +67,19 @@ echo ""
6367
# Backup files atomically; restore on any exit.
6468
BACKUP_CPP=$(mktemp /tmp/w45_builder_cpp.XXXXXX)
6569
BACKUP_C=$(mktemp /tmp/w45_builder_emit_c.XXXXXX)
70+
BACKUP_STATE_H=$(mktemp /tmp/w45_builder_state_h.XXXXXX)
71+
BACKUP_STATE_C=$(mktemp /tmp/w45_builder_state_c.XXXXXX)
6672
cp "$BUILDER_CPP" "$BACKUP_CPP"
6773
cp "$BUILDER_EMIT_C" "$BACKUP_C"
74+
cp "$BUILDER_STATE_C_H" "$BACKUP_STATE_H"
75+
cp "$BUILDER_STATE_C" "$BACKUP_STATE_C"
6876

6977
restore_files() {
7078
cp "$BACKUP_CPP" "$BUILDER_CPP" 2>/dev/null || true
7179
cp "$BACKUP_C" "$BUILDER_EMIT_C" 2>/dev/null || true
72-
rm -f "$BACKUP_CPP" "$BACKUP_C" 2>/dev/null || true
80+
cp "$BACKUP_STATE_H" "$BUILDER_STATE_C_H" 2>/dev/null || true
81+
cp "$BACKUP_STATE_C" "$BUILDER_STATE_C" 2>/dev/null || true
82+
rm -f "$BACKUP_CPP" "$BACKUP_C" "$BACKUP_STATE_H" "$BACKUP_STATE_C" 2>/dev/null || true
7383
}
7484
trap restore_files EXIT
7585

@@ -83,6 +93,9 @@ FIXTURES=(
8393
"hir_builder_emit_copy_free_vars_c|Phase 1 #4 emitCopyFreeVars delegation (5 args)"
8494
"hir_builder_emit_get_yield_from_iter_c|Phase 1 #4 emitGetYieldFromIter delegation (5 args)"
8595
"hir_builder_emit_primitive_load_const_c|Phase 1 #5 emitPrimitiveLoadConst delegation (5 args)"
96+
"hir_builder_state_init|Phase 3 Batch 1 state init (3 args)"
97+
"hir_builder_state_parse_exception_table_c|Phase 3 Batch 1 parseExceptionTable C body (2 args)"
98+
"hir_builder_state_exception_table_push_cpp|Phase 3 Batch 1 exception_table push bridge (6 args)"
8699
)
87100

88101
# Mutation: append ', int phx_w45_drift' before the closing paren of the
@@ -91,24 +104,20 @@ FIXTURES=(
91104
mutate_bridge() {
92105
local symbol="$1"
93106
# Match: void <symbol>( ... ) — capture inner contents, append drift param.
94-
# Multi-line via perl -0777.
107+
# Multi-line via perl -0777. Apply across all source files containing
108+
# bridge decls / defs.
95109
perl -i -0777 -pe \
96110
"s/(void\s+${symbol}\s*\([^)]*?)\)/\${1}, int phx_w45_drift)/g" \
97-
"$BUILDER_CPP" "$BUILDER_EMIT_C"
111+
"$BUILDER_CPP" "$BUILDER_EMIT_C" "$BUILDER_STATE_C_H" "$BUILDER_STATE_C"
98112
}
99113

100-
# Verify mutation actually applied (perl substitution silently no-ops if
101-
# pattern doesn't match — guard against false-positive PASS where the
102-
# mutation didn't happen and build "passing" is meaningless).
114+
# Verify mutation actually applied somewhere (perl substitution silently
115+
# no-ops if pattern doesn't match — guard against false-positive PASS).
103116
verify_mutated() {
104117
local symbol="$1"
105-
if ! grep -q "phx_w45_drift" "$BUILDER_CPP" 2>/dev/null; then
106-
return 1
107-
fi
108-
if ! grep -q "phx_w45_drift" "$BUILDER_EMIT_C" 2>/dev/null; then
109-
return 1
110-
fi
111-
return 0
118+
grep -l "phx_w45_drift" \
119+
"$BUILDER_CPP" "$BUILDER_EMIT_C" "$BUILDER_STATE_C_H" "$BUILDER_STATE_C" \
120+
2>/dev/null | grep -q .
112121
}
113122

114123
PASS=0
@@ -125,6 +134,8 @@ for fixture in "${FIXTURES[@]}"; do
125134
# Restore before each fixture (defensive).
126135
cp "$BACKUP_CPP" "$BUILDER_CPP"
127136
cp "$BACKUP_C" "$BUILDER_EMIT_C"
137+
cp "$BACKUP_STATE_H" "$BUILDER_STATE_C_H"
138+
cp "$BACKUP_STATE_C" "$BUILDER_STATE_C"
128139

129140
# Mutate.
130141
mutate_bridge "$SYMBOL"
@@ -138,8 +149,10 @@ for fixture in "${FIXTURES[@]}"; do
138149

139150
if [ "$DRY_RUN" -eq 1 ]; then
140151
echo " [DRY] mutation applied; mutation-marker sites (build skipped):"
141-
grep -n "phx_w45_drift" "$BUILDER_CPP" | sed 's/^/ cpp: /'
142-
grep -n "phx_w45_drift" "$BUILDER_EMIT_C" | sed 's/^/ c: /'
152+
{ grep -n "phx_w45_drift" "$BUILDER_CPP" 2>/dev/null || true; } | sed 's/^/ cpp: /'
153+
{ grep -n "phx_w45_drift" "$BUILDER_EMIT_C" 2>/dev/null || true; } | sed 's/^/ emit_c: /'
154+
{ grep -n "phx_w45_drift" "$BUILDER_STATE_C_H" 2>/dev/null || true; } | sed 's/^/ state_h: /'
155+
{ grep -n "phx_w45_drift" "$BUILDER_STATE_C" 2>/dev/null || true; } | sed 's/^/ state_c: /'
143156
SKIP=$((SKIP+1))
144157
echo ""
145158
continue
@@ -184,6 +197,8 @@ done
184197
echo "--- Restore + verify clean rebuild"
185198
cp "$BACKUP_CPP" "$BUILDER_CPP"
186199
cp "$BACKUP_C" "$BUILDER_EMIT_C"
200+
cp "$BACKUP_STATE_H" "$BUILDER_STATE_C_H"
201+
cp "$BACKUP_STATE_C" "$BUILDER_STATE_C"
187202

188203
if [ "$DRY_RUN" -eq 0 ]; then
189204
BUILD_OUT=$(mktemp /tmp/w45_build_restore.XXXXXX)

0 commit comments

Comments
 (0)