@@ -1326,10 +1326,18 @@ bool HIRBuilder::getSimpleExceptInfo(
13261326 return true ;
13271327}
13281328
1329- // W27c #2a + #2b: OpcodeArrayEntry C++/C bridge struct eliminated. The
1330- // pre-resolve loop now runs entirely C-side via
1331- // build_inline_except_opcode_array_c in builder_emit_c.c, sharing the
1332- // helper between emitInlineExceptionMatch and emitCallExceptionHandler.
1329+ // OpcodeArrayEntry — mirror of the C-side struct in builder_emit_c.c.
1330+ // 5 fields per entry. Layout MUST match (verified via static_assert).
1331+ struct OpcodeArrayEntry_CXX {
1332+ int opcode;
1333+ int oparg;
1334+ int base_offset;
1335+ void * const_obj;
1336+ void * jump_target_block;
1337+ };
1338+ static_assert (sizeof (OpcodeArrayEntry_CXX) <= 64 ,
1339+ " OpcodeArrayEntry size sanity" );
1340+
13331341extern " C" void hir_builder_emit_inline_exception_match_c (
13341342 void * tc, void * func, void * builder,
13351343 int bc_base_offset,
@@ -1383,7 +1391,9 @@ extern "C" void hir_builder_emit_call_exception_handler_c(
13831391 int except_body_offset,
13841392 HirType return_type,
13851393 void * result,
1386- void * match_and_clear_fn);
1394+ void * match_and_clear_fn,
1395+ const OpcodeArrayEntry_CXX* opcodes,
1396+ size_t opcode_count);
13871397
13881398void HIRBuilder::emitCallExceptionHandler (
13891399 CFG& /* cfg*/ ,
@@ -1399,12 +1409,46 @@ void HIRBuilder::emitCallExceptionHandler(
13991409 // preserved for Simplify, register allocation, other deopt paths.
14001410 // 2. Pop the result that emitAnyCall pushed onto the stack — the C
14011411 // body's deopt path expects a clean stack at the CALL offset.
1402- // W27c #2b: pre-resolve opcode array now built C-side via
1403- // build_inline_except_opcode_array_c (shared with W27c #2a
1404- // emitInlineExceptionMatch).
14051412 call_instr->setSuppressExceptionDeopt (true );
14061413 static_cast <Register*>(phx_ptr_arr_pop (&tc.frame .stack ));
14071414
1415+ // Build OpcodeArray identical-shape to emitInlineExceptionMatch — the
1416+ // dispatch loop in the shared C helper expects this layout. Pre-resolves
1417+ // const_obj (LOAD_CONST/RETURN_CONST) + jump_target_block (JUMP_BACKWARD*)
1418+ // to keep PyCodeObject + getBlockAtOff access on the C++ side.
1419+ std::vector<OpcodeArrayEntry_CXX> opcodes;
1420+ BytecodeInstruction ebc{code_, info.except_body };
1421+ bool emitted_terminator = false ;
1422+ while (!emitted_terminator) {
1423+ OpcodeArrayEntry_CXX entry{
1424+ ebc.opcode (),
1425+ ebc.oparg (),
1426+ ebc.baseOffset ().value (),
1427+ nullptr ,
1428+ nullptr };
1429+ int op = entry.opcode ;
1430+ if (op == LOAD_CONST || op == RETURN_CONST) {
1431+ entry.const_obj = PyTuple_GET_ITEM (code_->co_consts , entry.oparg );
1432+ }
1433+ if (op == JUMP_BACKWARD || op == JUMP_BACKWARD_NO_INTERRUPT) {
1434+ entry.jump_target_block =
1435+ static_cast <void *>(getBlockAtOff (ebc.getJumpTarget ()));
1436+ }
1437+ if (op == RETURN_VALUE || op == RETURN_CONST
1438+ || op == JUMP_BACKWARD || op == JUMP_BACKWARD_NO_INTERRUPT) {
1439+ emitted_terminator = true ;
1440+ } else if (op != POP_EXCEPT && op != POP_TOP && op != SWAP
1441+ && op != LOAD_FAST && op != LOAD_FAST_CHECK
1442+ && op != LOAD_FAST_AND_CLEAR && op != LOAD_CONST
1443+ && op != STORE_FAST && op != BINARY_OP) {
1444+ emitted_terminator = true ;
1445+ }
1446+ opcodes.push_back (entry);
1447+ if (!emitted_terminator) {
1448+ ebc = ebc.nextInstr ();
1449+ }
1450+ }
1451+
14081452 hir_builder_emit_call_exception_handler_c (
14091453 static_cast <void *>(&tc),
14101454 static_cast <void *>(current_func_),
@@ -1415,7 +1459,9 @@ void HIRBuilder::emitCallExceptionHandler(
14151459 info.except_body .value (),
14161460 Type::toHirType (preloader_.returnType ()),
14171461 static_cast <void *>(result),
1418- reinterpret_cast <void *>(JITRT_MatchAndClearException));
1462+ reinterpret_cast <void *>(JITRT_MatchAndClearException),
1463+ opcodes.data (),
1464+ opcodes.size ());
14191465}
14201466
14211467BasicBlock* HIRBuilder::getBlockAtOff (BCOffset off) {
0 commit comments