Skip to content

Commit 0810be4

Browse files
committed
builder: emitAnyCall full PURE conversion (W27c) + Phase A.5 wrap
Per supervisor 21:23:07Z + theologian 21:23:08Z + theologian P7 audit 20:53:51Z. Moves emitAnyCall from PARTIAL Cat-B to PURE-CONVERTED (Phase 3D 98/100 → 99/100). One PARTIAL Cat-B remains: emitLoadMethodStatic (blocked on VTableByteOffset wrapper, landed in push 58). CHANGES: C body (Python/jit/hir/builder_emit_c.c hir_builder_emit_any_call_c): - New signature: drop call_kind/is_kw_arg/is_awaited/const_arg params; add opcode (raw int) and BcByteOffset base_offset (P5 wrapper). - Move opcode→PhxCallKind switch into C body. CALL/CALL_FUNCTION_EX from Include/opcode.h; CALL_FUNCTION/_KW/CALL_KW/CALL_METHOD from Python/jit_common/opcode_stubs.h (stubbed >255 in 3.12 but kept for forward compat). - INVOKE_FUNCTION/NATIVE/METHOD opcode constants local-#define'd in C body (209/216/185 from Python/jit_interp/3.12/cinder_opcode_ids.h). Local-define avoids pulling cinder_opcode.h which would shadow Include/opcode.h's BINARY_OP_ADD_INT and break BINARY_OP specialization #ifdef (per existing comment at builder_emit_c.c:30). Guarded with #ifndef so future direct cinder_opcode_ids.h include doesn't conflict. - is_awaited hardcoded 0 for 3.12 with #if PY_VERSION_HEX guard + #error fallback for older Python (Phoenix targets 3.12 exclusively). Older-Python path would need C-side bc_it/bc_instr accessors (out- of-scope; covered by existing C++ stub history in git log). - const_arg = PyTuple_GET_ITEM(code->co_consts, oparg) for INVOKE_*. Per HIRBuilder::constArg @ builder.cpp pre-conversion: borrowed ref, no GC trigger, no refcount. Lifetime = code object = compile duration. Theologian 20:53:51Z P7 audit confirmed safe to extract on C side. - base_offset.v unwrap at the one boundary (hir_builder_emit_call_method_exception_handler_inline_c, raw-int API). C++ stub (Python/jit/hir/builder.cpp HIRBuilder::emitAnyCall): - Shrinks from 67 lines to 12 lines: pure type marshaling delegation. - Wraps base_offset via bc_byte_offset_from_int factory (Phase A.5). - Added bytecode_c.h include for BcByteOffset visibility. P6 EXECUTION-PATH COVERAGE: - PHX_CALL_KIND_CALL_METHOD (CALL/CALL_METHOD/CALL_KW): EXERCISED by test_exc_raise_catch (CALL ValueError(...)) + 4 W-2A-DISPATCH-COVERAGE sentinels (try/else, try/finally with raise, nested try, raise-in-handler) + 4 W-2B-RECONVERT sentinels (raise/catch, BINARY_SUBSCR_DICT-in-try, continue-in-loop, multi-except-in-loop) + test_multiple_exceptions_in_loop. - PHX_CALL_KIND_CALL_EX (CALL_FUNCTION_EX): EXERCISED indirectly by any test using *args/**kwargs unpacking; covered by Phoenix CPython suite. - PHX_CALL_KIND_VECTOR_CALL (CALL_FUNCTION/_KW): DEAD in 3.12 (stubbed >255). Switch case kept for forward compat with older Python builds. - PHX_CALL_KIND_INVOKE_FUNCTION/NATIVE/METHOD: COVERAGE GAP. INVOKE_* opcodes are Cinder static-Python only; vanilla CPython 3.12 compiler does NOT emit them. Pure Python sentinel cannot exercise these branches without Cinder static-Python infrastructure (not available in this build per theologian 21:32:27Z + supervisor 21:32:42Z fallback). Conversion is mechanical (PyTuple_GET_ITEM extraction + delegate to existing emit functions); structural test exists at Lib/test/test_phoenix_partial_conversions.py:52. Execution-level coverage queued as W-CINDERX-INVOKE-COVERAGE workstream (cinderx_dev integration post-Phoenix-integration). Phase 0' HIR-DIFF planned (theologian step 4): test_exc_raise_catch + 4 W-2A sentinels + Phoenix suite — pre/post conversion HIR comparison to detect any divergence. Verification pending: testkeeper rebuild + 30x all 8 W-2A+W-2B-RECONVERT sentinels + 30x test_multiple_exceptions_in_loop + Phoenix suite + ABBA + dual-arch. Per supervisor 21:32:42Z (post pythia python#140 python#2): full 24-bench ABBA between push 59 (this) and push 60 (emitLoadMethodStatic conversion) — single-commit bisect window for cap-clock at 73 commits since last 24-bench (cbb9453 2026-04-23).
1 parent c5d2edd commit 0810be4

2 files changed

Lines changed: 98 additions & 73 deletions

File tree

Python/jit/hir/builder.cpp

Lines changed: 10 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "cinderx/Jit/hir/hir_instr_c.h"
55
#include "cinderx/Jit/hir/hir_c_api.h"
66
#include "cinderx/Jit/hir/hir_type_c.h"
7+
#include "cinderx/Jit/bytecode_c.h" /* BcByteOffset wrapper for emitAnyCall seam */
78

89
extern "C" int hir_remove_trampoline_blocks_c(void *cfg);
910
extern "C" int hir_remove_unreachable_blocks_c(void *func);
@@ -2852,77 +2853,26 @@ extern "C" int hir_builder_bc_it_oparg_c(void *bc_it) {
28522853
extern "C" void hir_builder_emit_any_call_c(
28532854
void *tc, void *cfg, void *func, void *builder,
28542855
void *bc_instrs, void *bc_it,
2855-
int call_kind, int oparg, int base_offset,
2856-
int is_awaited, int is_kw_arg,
2857-
void *code, int code_flags,
2858-
PyObject *const_arg);
2856+
int opcode, int oparg, BcByteOffset base_offset,
2857+
void *code, int code_flags);
28592858

2859+
// W27c full PURE conversion: opcode→PhxCallKind switch + is_awaited check
2860+
// + const_arg extraction all moved to C body. C++ stub is now pure type
2861+
// marshaling delegation; counts as PURE-CONVERTED per W27c #1 emitLoadAttr
2862+
// precedent (98/100 → 99/100 + 1 PARTIAL Cat-B remaining = emitLoadMethodStatic).
28602863
void HIRBuilder::emitAnyCall(
28612864
CFG& cfg,
28622865
TranslationContext& tc,
28632866
jit::BytecodeInstructionBlock::Iterator& bc_it,
28642867
const jit::BytecodeInstructionBlock& bc_instrs) {
28652868
BytecodeInstruction bc_instr = *bc_it;
2866-
int opcode = bc_instr.opcode();
2867-
int oparg = bc_instr.oparg();
2868-
int base_offset = bc_instr.baseOffset().value();
2869-
2870-
// Map opcode → PhxCallKind (C body switches on enum to avoid pulling
2871-
// opcode constants into builder_emit_c.c, where cinder_opcode.h's
2872-
// Py_OPCODE_H header guard would shadow Include/opcode.h's
2873-
// BINARY_OP_ADD_INT define + break #ifdef in BINARY_OP specialization).
2874-
int call_kind;
2875-
int is_kw_arg = 0;
2876-
switch (opcode) {
2877-
case CALL_FUNCTION:
2878-
call_kind = PHX_CALL_KIND_VECTOR_CALL; break;
2879-
case CALL_FUNCTION_KW:
2880-
call_kind = PHX_CALL_KIND_VECTOR_CALL; is_kw_arg = 1; break;
2881-
case CALL_FUNCTION_EX:
2882-
call_kind = PHX_CALL_KIND_CALL_EX; break;
2883-
case CALL:
2884-
case CALL_METHOD:
2885-
call_kind = PHX_CALL_KIND_CALL_METHOD; break;
2886-
case CALL_KW:
2887-
call_kind = PHX_CALL_KIND_CALL_METHOD; is_kw_arg = 1; break;
2888-
case INVOKE_FUNCTION:
2889-
call_kind = PHX_CALL_KIND_INVOKE_FUNCTION; break;
2890-
case INVOKE_NATIVE:
2891-
call_kind = PHX_CALL_KIND_INVOKE_NATIVE; break;
2892-
case INVOKE_METHOD:
2893-
call_kind = PHX_CALL_KIND_INVOKE_METHOD; break;
2894-
default:
2895-
JIT_ABORT("Unhandled call opcode {} ({})", opcode, opcodeName(opcode));
2896-
}
2897-
2898-
int is_awaited;
2899-
if constexpr (PY_VERSION_HEX >= 0x030C0000) {
2900-
is_awaited = 0;
2901-
} else {
2902-
is_awaited = (code_->co_flags & CO_COROUTINE) &&
2903-
// We only need to be followed by GET_AWAITABLE to know we are awaited,
2904-
// but we also need to ensure the following LOAD_CONST and YIELD_FROM
2905-
// are inside this BytecodeInstructionBlock. This may not be the case if
2906-
// the 'await' is shared as in 'await (x if y else z)'.
2907-
bc_it.remainingIndices() >= 3 &&
2908-
bc_instr.nextInstr().opcode() == GET_AWAITABLE ? 1 : 0;
2909-
}
2910-
2911-
// Pre-extract const_arg for INVOKE_* paths — only used if opcode is one
2912-
// of INVOKE_FUNCTION/NATIVE/METHOD; NULL otherwise.
2913-
PyObject *const_arg = nullptr;
2914-
if (opcode == INVOKE_FUNCTION || opcode == INVOKE_NATIVE
2915-
|| opcode == INVOKE_METHOD) {
2916-
const_arg = constArg(bc_instr);
2917-
}
2918-
29192869
hir_builder_emit_any_call_c(
29202870
&tc, &cfg, current_func_, this,
29212871
const_cast<void*>(static_cast<const void*>(&bc_instrs)),
29222872
&bc_it,
2923-
call_kind, oparg, base_offset, is_awaited, is_kw_arg,
2924-
code_, static_cast<int>(code_->co_flags),
2925-
const_arg);
2873+
bc_instr.opcode(), bc_instr.oparg(),
2874+
bc_byte_offset_from_int(bc_instr.baseOffset().value()),
2875+
code_, static_cast<int>(code_->co_flags));
29262876
}
29272877

29282878
extern "C" void hir_builder_emit_call_intrinsic_c(void *tc, void *func, int opcode, int oparg);

Python/jit/hir/builder_emit_c.c

Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3695,20 +3695,92 @@ void hir_builder_emit_any_call_c(
36953695
void *builder,
36963696
void *bc_instrs,
36973697
void *bc_it,
3698-
int call_kind, /* PhxCallKind enum (mapped from opcode by C++ stub) */
3698+
int opcode, /* W27c full conversion: opcode dispatch C-side */
36993699
int oparg,
3700-
int base_offset,
3701-
int is_awaited,
3702-
int is_kw_arg, /* set for CALL_FUNCTION_KW + CALL_KW (kwnames flag) */
3700+
BcByteOffset base_offset, /* W27c P5: BcByteOffset wrapper (Phase A.5) */
37033701
void *code,
3704-
int code_flags,
3705-
PyObject *const_arg) {
3706-
/* CallFlags: None=0, KwArgs=1<<0=1, Awaited=1<<1=2, Static=1<<2=4.
3702+
int code_flags) {
3703+
/* W27c full PURE conversion (post Phase A.5): opcode→PhxCallKind switch,
3704+
* is_awaited check, and const_arg extraction all moved C-side from the
3705+
* former C++ stub. base_offset is now BcByteOffset per Phase A.5 P5
3706+
* wrapper (theologian P7 audit 20:53:51Z). C++ stub at builder.cpp
3707+
* HIRBuilder::emitAnyCall is now pure type-marshaling delegation
3708+
* (~12 lines), counts as PURE-CONVERTED per W27c #1 emitLoadAttr
3709+
* precedent.
3710+
*
3711+
* CallFlags: None=0, KwArgs=1<<0=1, Awaited=1<<1=2, Static=1<<2=4.
3712+
* PHX_CALL_KIND_* values defined in builder.h. */
3713+
3714+
/* Map opcode → (call_kind, is_kw_arg). CALL + CALL_FUNCTION_EX from
3715+
* Include/opcode.h (already included). CALL_FUNCTION/_KW/CALL_KW/
3716+
* CALL_METHOD from Python/jit_common/opcode_stubs.h (already included,
3717+
* stubbed >255 in 3.12 but kept in switch for forward compat).
37073718
*
3708-
* call_kind dispatch (set by C++ stub) replaces direct opcode switching
3709-
* here so this C body does not need to import opcode constants. The
3710-
* opcode switch in the C++ stub maps each call-class opcode to one of
3711-
* the PHX_CALL_KIND_* values (defined in builder.h). */
3719+
* INVOKE_FUNCTION/NATIVE/METHOD are Cinder-specific opcodes from
3720+
* Python/jit_interp/3.12/cinder_opcode_ids.h. Local-defined here to
3721+
* avoid pulling cinder_opcode.h which would shadow Include/opcode.h's
3722+
* BINARY_OP_ADD_INT define + break #ifdef in BINARY_OP specialization
3723+
* (per existing comment at line 30). Values (209/216/185) verified
3724+
* against cinder_opcode_ids.h for 3.12; if cinder INVOKE_* values
3725+
* change, propagate here. */
3726+
#ifndef INVOKE_FUNCTION
3727+
#define INVOKE_FUNCTION 209
3728+
#endif
3729+
#ifndef INVOKE_NATIVE
3730+
#define INVOKE_NATIVE 216
3731+
#endif
3732+
#ifndef INVOKE_METHOD
3733+
#define INVOKE_METHOD 185
3734+
#endif
3735+
3736+
int call_kind;
3737+
int is_kw_arg = 0;
3738+
switch (opcode) {
3739+
case CALL_FUNCTION:
3740+
call_kind = PHX_CALL_KIND_VECTOR_CALL; break;
3741+
case CALL_FUNCTION_KW:
3742+
call_kind = PHX_CALL_KIND_VECTOR_CALL; is_kw_arg = 1; break;
3743+
case CALL_FUNCTION_EX:
3744+
call_kind = PHX_CALL_KIND_CALL_EX; break;
3745+
case CALL:
3746+
case CALL_METHOD:
3747+
call_kind = PHX_CALL_KIND_CALL_METHOD; break;
3748+
case CALL_KW:
3749+
call_kind = PHX_CALL_KIND_CALL_METHOD; is_kw_arg = 1; break;
3750+
case INVOKE_FUNCTION:
3751+
call_kind = PHX_CALL_KIND_INVOKE_FUNCTION; break;
3752+
case INVOKE_NATIVE:
3753+
call_kind = PHX_CALL_KIND_INVOKE_NATIVE; break;
3754+
case INVOKE_METHOD:
3755+
call_kind = PHX_CALL_KIND_INVOKE_METHOD; break;
3756+
default:
3757+
JIT_CHECK_C(0, "Unhandled call opcode %d", opcode);
3758+
call_kind = PHX_CALL_KIND_VECTOR_CALL; /* unreachable; silence warn */
3759+
}
3760+
3761+
/* is_awaited: GET_AWAITABLE-tail handling. Per HIRBuilder::emitAnyCall
3762+
* pre-W27c-PURE C++ stub: 3.12+ doesn't compute this here (returns 0);
3763+
* older Python checked code->co_flags & CO_COROUTINE + bc_it.nextInstr()
3764+
* == GET_AWAITABLE. Phoenix targets 3.12 exclusively per memory;
3765+
* forward-compat for older Python would need C-side bc_it/bc_instr
3766+
* accessors (out-of-scope for this conversion). */
3767+
#if PY_VERSION_HEX >= 0x030C0000
3768+
int is_awaited = 0;
3769+
#else
3770+
# error "is_awaited check needs C-side bc_it accessor for Python < 3.12; \
3771+
not implemented per current 3.12-only target"
3772+
#endif
3773+
3774+
/* const_arg = PyTuple_GET_ITEM(code->co_consts, oparg) per
3775+
* HIRBuilder::constArg @ builder.cpp pre-W27c-PURE. Borrowed ref to
3776+
* a co_consts entry; no refcount, no GC trigger. Lifetime = code
3777+
* object = compile duration. Only used for INVOKE_* paths. */
3778+
PyObject *const_arg = NULL;
3779+
if (opcode == INVOKE_FUNCTION || opcode == INVOKE_NATIVE
3780+
|| opcode == INVOKE_METHOD) {
3781+
const_arg = PyTuple_GET_ITEM(((PyCodeObject*)code)->co_consts, oparg);
3782+
}
3783+
37123784
uint32_t flags = is_awaited ? 2u : 0u;
37133785
int call_used_is_awaited = 1;
37143786

@@ -3773,9 +3845,12 @@ void hir_builder_emit_any_call_c(
37733845
/* B2: If this CALL is inside a try block with a simple except pattern,
37743846
* inline the exception handler instead of deopting on exception.
37753847
* Combined per W26 (B) decision: NULL-safe internally — does nothing
3776-
* if no handler matches OR not simple-pattern. */
3848+
* if no handler matches OR not simple-pattern.
3849+
*
3850+
* base_offset is BcByteOffset (P5 wrapper); unwrap .v at the
3851+
* boundary to existing raw-int hir_builder_emit_call_method_exception_handler_inline_c API. */
37773852
hir_builder_emit_call_method_exception_handler_inline_c(
3778-
builder, tc, cfg, base_offset, call, out);
3853+
builder, tc, cfg, base_offset.v, call, out);
37793854
break;
37803855
}
37813856
case PHX_CALL_KIND_INVOKE_FUNCTION: {

0 commit comments

Comments
 (0)