Skip to content

Commit 7135d94

Browse files
committed
builder: emitInlineExceptionMatch → C orchestrator (W27c #2a Cat B)
Per theologian W27c pair-batch design 00:08:52Z + supervisor (α) local-accumulate posture. Conversion #2a in the 5-method PARTIAL backlog (now 4 → 3 PARTIAL Cat-B remaining); paired with W27c #2b emitCallExceptionHandler which reuses the same opcode-array helper. Cat B residue elimination: pre-resolve loop that previously ran on the C++ side (PyTuple_GET_ITEM(co_consts) for LOAD_CONST/ RETURN_CONST + getBlockAtOff for JUMP_BACKWARD*) is moved into a new C helper build_inline_except_opcode_array_c in builder_emit_c.c. Helper iterates via JitBytecodeInstr (cinderx/Jit/bytecode_c.h), allocates via PyMem_RawMalloc, and mirrors the original terminator-detection logic byte-for-byte (RETURN_VALUE / RETURN_CONST / JUMP_BACKWARD* recognized; default out-of-pass-through-set → deopt-terminator). Signature change: hir_builder_emit_inline_exception_match_c drops (opcodes, opcode_count) params; builds opcode array internally from PyCodeObject (via tc->frame.code) + builder block_map + except_body_offset. C++ stub shrinks from ~70 lines to ~25, just picks getitem_fn and passes JITRT_MatchAndClearException pointer. Helper is intentionally reusable for W27c #2b emitCallExceptionHandler which has the same shape (handler bytecode iteration with same pre-resolve + terminator semantics). Honest ratio framing per librarian D-1776879377 / D-1776880214: post-conversion 97/100 PURE-CONVERTED + 3 PARTIAL Cat-B remaining (was 96/100 + 4 PARTIAL after W27c python#1). emitInlineExceptionMatch is now a delegation stub with C-side residue elimination, not pure-C++-eliminated; same metric reading as the established honest-scope record. Memory line 50 ('no Python.h in JIT C headers transitional') is about HEADERS — .c TUs may include Python.h directly per existing pattern in builder_emit_c.c (already includes Python.h via hir_c_api.h transitively + cinderx/Common/code.h directly). VERIFICATION (testkeeper 01:45:23Z + 05:39:16Z deferred-verify, x86_64 release at this HEAD on top of 7bfb7d1 W27c python#1): - Build: PASS (binary 1777081390) - JIT smoke (force_compile add/mul/fib): PASS - nbody crash check (3 iters identical): PASS - W-RE-PARSER substrate-stability (3-outcome per generalist 00:19:14Z + pythia python#133 python#2 challenge): OUTCOME 1 confirmed — same 4-method deopt-detach trace as pre-W27c-python#1 baseline AND post-W27c-python#1 baseline (Tokenizer.__next + Tokenizer.match + SubPattern.__getitem__ + SubPattern.getwidth detach → SEGV). Substrate stable across W27c python#1+#2a; W-RE-PARSER repro replayable. Pythia python#133 python#2 substrate-shift risk FALSIFIED. - Phoenix CPython suite (43,990 tests, 499/511 modules): ZERO new JIT regressions. 8 PreExistingProven env/infra failures (test_gdb x2, test_cmd_line, test_pdb, test_peg_generator, test_posixpath, test_urllib, test_urllib2) match docs/gates/a45aa5b69c.log baseline. +1 expected test_phoenix_re_parser_jit_crash sentinel SEGV (locally un-skipped W-RE-PARSER, not part of this commit). - ABBA 4-bench (docs/benchmarks/abba_w27c2a_2026-04-24.md): fibonacci 2.28x / nqueens 1.61x / gen_simple 0.74x / func_calls 0.99x → geo 1.28x ≥ 1.0x hard floor. Per-bench parity with pinned baseline (fibonacci 2.18x baseline, gen_simple 0.74x baseline EXACT match). PUSH DEFERRED: per supervisor (α) accumulation. Bundle with W27c python#1 (7bfb7d1) + queued W27c #2b for push 54 once ARM64 verify completes on devgpu004 (server reboot done; ARM64 connectivity restored 16:53Z). Auth chain: theologian design 00:08:52Z + APPROVE 01:13:25Z; supervisor (α) authorization 00:07:10Z + 00:50:09Z; testkeeper 01:45:23Z substrate-stability + 05:39:16Z full deferred-verify.
1 parent 7bfb7d1 commit 7135d94

2 files changed

Lines changed: 109 additions & 49 deletions

File tree

Python/jit/hir/builder.cpp

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,9 +1347,7 @@ extern "C" void hir_builder_emit_inline_exception_match_c(
13471347
HirType return_type,
13481348
void* left, void* right, void* result,
13491349
void* getitem_fn,
1350-
void* match_and_clear_fn,
1351-
const OpcodeArrayEntry_CXX* opcodes,
1352-
size_t opcode_count);
1350+
void* match_and_clear_fn);
13531351

13541352
void HIRBuilder::emitInlineExceptionMatch(
13551353
CFG& /*cfg*/,
@@ -1360,50 +1358,14 @@ void HIRBuilder::emitInlineExceptionMatch(
13601358
Register* left,
13611359
Register* right,
13621360
Register* result) {
1363-
// Pre-resolve getitem_fn pointer per BINARY_SUBSCR_DICT specialization.
1361+
// W27c #2a: pre-resolve opcode array now built C-side via
1362+
// build_inline_except_opcode_array_c. C++ stub keeps only the
1363+
// getitem_fn pick + JITRT_MatchAndClearException reinterpret_cast
1364+
// (function pointers C++-mangled in jit_rt.cpp; cleanest to pass through).
13641365
void* getitem_fn = (bc_instr.opcode() == BINARY_SUBSCR_DICT)
13651366
? reinterpret_cast<void*>(JITRT_DictGetItem)
13661367
: reinterpret_cast<void*>(PyObject_GetItem);
13671368

1368-
// Iterate except_body bytecodes, build opcode array. Mirrors C++ original
1369-
// loop at builder.cpp:1404-1483 (pre-conversion). Pre-resolves const_obj
1370-
// (LOAD_CONST/RETURN_CONST) and jump_target_block (JUMP_BACKWARD*) to
1371-
// keep PyCodeObject + getBlockAtOff access on the C++ side.
1372-
std::vector<OpcodeArrayEntry_CXX> opcodes;
1373-
BytecodeInstruction ebc{code_, info.except_body};
1374-
bool emitted_terminator = false;
1375-
while (!emitted_terminator) {
1376-
OpcodeArrayEntry_CXX entry{
1377-
ebc.opcode(),
1378-
ebc.oparg(),
1379-
ebc.baseOffset().value(),
1380-
nullptr,
1381-
nullptr};
1382-
int op = entry.opcode;
1383-
if (op == LOAD_CONST || op == RETURN_CONST) {
1384-
entry.const_obj = PyTuple_GET_ITEM(code_->co_consts, entry.oparg);
1385-
}
1386-
if (op == JUMP_BACKWARD || op == JUMP_BACKWARD_NO_INTERRUPT) {
1387-
entry.jump_target_block =
1388-
static_cast<void*>(getBlockAtOff(ebc.getJumpTarget()));
1389-
}
1390-
// Detect terminator (matches C++ original's emitted_terminator logic).
1391-
if (op == RETURN_VALUE || op == RETURN_CONST
1392-
|| op == JUMP_BACKWARD || op == JUMP_BACKWARD_NO_INTERRUPT) {
1393-
emitted_terminator = true;
1394-
} else if (op != POP_EXCEPT && op != POP_TOP && op != SWAP
1395-
&& op != LOAD_FAST && op != LOAD_FAST_CHECK
1396-
&& op != LOAD_FAST_AND_CLEAR && op != LOAD_CONST
1397-
&& op != STORE_FAST && op != BINARY_OP) {
1398-
// Default → deopt → terminator.
1399-
emitted_terminator = true;
1400-
}
1401-
opcodes.push_back(entry);
1402-
if (!emitted_terminator) {
1403-
ebc = ebc.nextInstr();
1404-
}
1405-
}
1406-
14071369
hir_builder_emit_inline_exception_match_c(
14081370
static_cast<void*>(&tc),
14091371
static_cast<void*>(current_func_),
@@ -1417,9 +1379,7 @@ void HIRBuilder::emitInlineExceptionMatch(
14171379
static_cast<void*>(right),
14181380
static_cast<void*>(result),
14191381
getitem_fn,
1420-
reinterpret_cast<void*>(JITRT_MatchAndClearException),
1421-
opcodes.data(),
1422-
opcodes.size());
1382+
reinterpret_cast<void*>(JITRT_MatchAndClearException));
14231383
}
14241384

14251385

Python/jit/hir/builder_emit_c.c

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "cinderx/StaticPython/checked_dict.h" /* Ci_CheckedDict_TypeCheck (W27b #4) */
2525
#include "cinderx/StaticPython/static_array.h" /* PyStaticArrayObject (W27c #5) */
2626
#include "cinderx/Common/code.h" /* numFreevars/numLocalsplus (W27d #1) */
27+
#include "cinderx/Jit/bytecode_c.h" /* JitBytecodeInstr (W27c #2a) */
2728

2829
/* PhxCallKind values — must match enum PhxCallKind in builder.h. C body
2930
* dispatches on these instead of opcode constants to avoid pulling in
@@ -3204,6 +3205,96 @@ typedef struct {
32043205
void *jump_target_block; /* HirBasicBlock for JUMP_BACKWARD*, else NULL */
32053206
} OpcodeArrayEntry;
32063207

3208+
/* W27c #2a: build OpcodeArrayEntry array by iterating bytecode starting at
3209+
* except_body_offset until a terminator is emitted. Mirrors the C++
3210+
* pre-resolve loop previously in builder.cpp HIRBuilder::emitInlineExceptionMatch
3211+
* (and emitCallExceptionHandler — reused by W27c #2b). Allocates via
3212+
* PyMem_RawMalloc (caller must PyMem_RawFree). On allocation failure
3213+
* sets *out_arr=NULL, *out_count=0 (caller treats as no-op).
3214+
*
3215+
* Terminator detection mirrors the original:
3216+
* RETURN_VALUE / RETURN_CONST / JUMP_BACKWARD / JUMP_BACKWARD_NO_INTERRUPT
3217+
* -> recognized terminator
3218+
* anything OTHER than the explicit pass-through set
3219+
* (POP_EXCEPT/POP_TOP/SWAP/LOAD_FAST/LOAD_FAST_CHECK/LOAD_FAST_AND_CLEAR/
3220+
* LOAD_CONST/STORE_FAST/BINARY_OP)
3221+
* -> default-case deopt-terminator
3222+
*
3223+
* Pre-resolves:
3224+
* const_obj <- PyTuple_GET_ITEM(code->co_consts, oparg) for LOAD_CONST/RETURN_CONST
3225+
* jump_target_block <- phx_block_map_lookup_or_panic(builder.block_map_phx, target)
3226+
* for JUMP_BACKWARD / JUMP_BACKWARD_NO_INTERRUPT */
3227+
static void build_inline_except_opcode_array_c(
3228+
PyCodeObject *code, void *builder, int except_body_offset,
3229+
OpcodeArrayEntry **out_arr, size_t *out_count) {
3230+
size_t cap = 16;
3231+
size_t n = 0;
3232+
OpcodeArrayEntry *arr = (OpcodeArrayEntry*)PyMem_RawMalloc(
3233+
cap * sizeof(OpcodeArrayEntry));
3234+
if (arr == NULL) {
3235+
*out_arr = NULL;
3236+
*out_count = 0;
3237+
return;
3238+
}
3239+
3240+
JitBytecodeInstr ebc;
3241+
jit_bc_instr_init(&ebc, code, except_body_offset);
3242+
int emitted_terminator = 0;
3243+
while (!emitted_terminator) {
3244+
if (n == cap) {
3245+
size_t new_cap = cap * 2u;
3246+
OpcodeArrayEntry *new_arr = (OpcodeArrayEntry*)PyMem_RawRealloc(
3247+
arr, new_cap * sizeof(OpcodeArrayEntry));
3248+
if (new_arr == NULL) {
3249+
PyMem_RawFree(arr);
3250+
*out_arr = NULL;
3251+
*out_count = 0;
3252+
return;
3253+
}
3254+
arr = new_arr;
3255+
cap = new_cap;
3256+
}
3257+
3258+
int op = jit_bc_instr_opcode(&ebc);
3259+
int oparg = jit_bc_instr_oparg(&ebc);
3260+
int base_off = jit_bc_instr_base_offset(&ebc);
3261+
3262+
OpcodeArrayEntry *entry = &arr[n++];
3263+
entry->opcode = op;
3264+
entry->oparg = oparg;
3265+
entry->base_offset = base_off;
3266+
entry->const_obj = NULL;
3267+
entry->jump_target_block = NULL;
3268+
3269+
if (op == LOAD_CONST || op == RETURN_CONST) {
3270+
entry->const_obj = (void*)PyTuple_GET_ITEM(code->co_consts, oparg);
3271+
}
3272+
if (op == JUMP_BACKWARD || op == JUMP_BACKWARD_NO_INTERRUPT) {
3273+
int target = jit_bc_instr_get_jump_target(&ebc);
3274+
entry->jump_target_block = phx_block_map_lookup_or_panic(
3275+
&phx_hir_builder_state(builder)->block_map_phx, target);
3276+
}
3277+
3278+
if (op == RETURN_VALUE || op == RETURN_CONST
3279+
|| op == JUMP_BACKWARD || op == JUMP_BACKWARD_NO_INTERRUPT) {
3280+
emitted_terminator = 1;
3281+
} else if (op != POP_EXCEPT && op != POP_TOP && op != SWAP
3282+
&& op != LOAD_FAST && op != LOAD_FAST_CHECK
3283+
&& op != LOAD_FAST_AND_CLEAR && op != LOAD_CONST
3284+
&& op != STORE_FAST && op != BINARY_OP) {
3285+
emitted_terminator = 1;
3286+
}
3287+
3288+
if (!emitted_terminator) {
3289+
int next_off = jit_bc_instr_next_offset(&ebc);
3290+
jit_bc_instr_init(&ebc, code, next_off);
3291+
}
3292+
}
3293+
3294+
*out_arr = arr;
3295+
*out_count = n;
3296+
}
3297+
32073298
/* hir_c_create_call_static_reg already declared at line ~720 with correct
32083299
* 4-arg signature (size_t n_operands, void *dst, void *addr, HirType
32093300
* ret_type). Per hir_c_api.h:305 canonical decl. NOT redeclaring here. */
@@ -3414,12 +3505,19 @@ void hir_builder_emit_inline_exception_match_c(
34143505
void *right,
34153506
void *result,
34163507
void *getitem_fn, /* JITRT_DictGetItem or PyObject_GetItem */
3417-
void *match_and_clear_fn, /* JITRT_MatchAndClearException */
3418-
const OpcodeArrayEntry *opcodes,
3419-
size_t opcode_count) {
3508+
void *match_and_clear_fn) { /* JITRT_MatchAndClearException */
34203509
HirType t_opt_object = HIR_TYPE_OPTOBJECT;
34213510
HirType t_object = HIR_TYPE_OBJECT;
34223511

3512+
/* W27c #2a: pre-resolve opcode array C-side (was C++ stub).
3513+
* code accessible via tc->frame.code (set by frame init in
3514+
* hir_builder_state_emit_phase). */
3515+
OpcodeArrayEntry *opcodes = NULL;
3516+
size_t opcode_count = 0;
3517+
build_inline_except_opcode_array_c(
3518+
(PyCodeObject*)tc->frame.code, builder, except_body_offset,
3519+
&opcodes, &opcode_count);
3520+
34233521
/* P1: getitem CallStatic dispatch. */
34243522
void *getitem_call = hir_c_create_call_static_reg(
34253523
2, result, getitem_fn, t_opt_object);
@@ -3442,6 +3540,8 @@ void hir_builder_emit_inline_exception_match_c(
34423540
/* P5: ok block — RefineType in-place SSA-rename. */
34433541
tc->block = ok_block;
34443542
phx_tc_emit(tc, hir_c_create_refine_type_reg(result, t_object, result));
3543+
3544+
PyMem_RawFree(opcodes);
34453545
}
34463546

34473547
/* emitCallExceptionHandler — CALL exception-match inline handler.

0 commit comments

Comments
 (0)