Skip to content

Commit 4feb0d2

Browse files
committed
hir_cfg_get_rpo: un-clamp returns + add resize handlers (D-1775660617)
Fixes the parked bug logged 2026-04-09 (theologian D-1775660617, pythia D-1775661187, testkeeper D-1775661646) that surfaced this session via push 42's 150-arm wiring fixture (n=302 BBs > 256 default rpo_cap). Root cause: hir_cfg_get_rpo + hir_cfg_get_rpo_from in hir_c_api.cpp returned the count CLAMPED to caller capacity. The standard caller pattern size_t rpo_cap = 256; size_t num_blocks = hir_cfg_get_rpo(cfg, rpo_blocks, rpo_cap); if (num_blocks > rpo_cap) { resize + refetch } silently swallows the overflow: when the actual RPO size > 256 the function returns 256, the resize check never fires, and blocks beyond the 256th drop out of the iteration. copy_propagation.c stops removing Assigns in dropped blocks; LIR translation later fires JIT_CHECK(false, "assign shouldn't be present") at lir/generator.cpp:1358. SIGABRT at exactly 257 BBs; SIGSEGV beyond. Fix is a contract harmonization with the sibling hir_cfg_get_rpo_c (hir_cfg_rpo_c.c:67-75) which already returned the un-clamped post- order count. Both _cpp and _from variants now match: only `count` entries written, but the FULL `actual` size is returned so callers can detect overflow. Bundled in same commit per supervisor 12:32:56Z bundle directive (close the bug class, not just the trip): (1) hir_c_api.cpp: hir_cfg_get_rpo + hir_cfg_get_rpo_from un-clamped (2) pass_output_type_c.c: 3 callers (hir_reflow_types_c, hir_optimize_long_decref_runs_c, hir_remove_unreachable_instructions_c) gain stack-array + heap- fallback resize+refetch handlers. The fallback malloc only fires when CFG exceeds 4096 BBs (no allocator pressure for typical workloads). clean_cfg.c:75-86 + copy_propagation.c:25-34 already had the resize handlers (dead until this commit, load-bearing now). No change to either; the un-clamp activates them. Falsifier (testkeeper 12:38:23Z): 127/128/150/200/1000/2000-arm fixtures all PASS post-fix; pre-fix all SIGABRT/SIGSEGV. JIT smoke (add/mul/fib) PASS — handler additions do not regress the baseline. Auth chain: librarian known-parked record D-1775660617 + sub-agent verification (supervisor 12:29:00Z); supervisor escalation-to-proceed 12:28:09Z + bundle directive 12:32:56Z; testkeeper pre-commit BUILD PASS + 6/6 reproduction 12:38:23Z. Theologian wedged across this investigation; sub-agent stood in for patch-shape verification.
1 parent 154997b commit 4feb0d2

2 files changed

Lines changed: 61 additions & 11 deletions

File tree

‎Python/jit/hir/hir_c_api.cpp‎

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,12 +242,22 @@ void hir_preloader_ensure(void *py_func) {
242242
}
243243

244244
size_t hir_cfg_get_rpo(struct HirCFG *cfg, struct HirBasicBlock **out, size_t capacity) {
245+
/* Bug fix for D-1775660617 (theologian 2026-04-09 known-parked, pythia
246+
* D-1775661187 risk flag): return the FULL RPO size so callers can
247+
* detect overflow via `if (num_blocks > capacity)` and resize. The
248+
* previous clamped return left clean_cfg.c + copy_propagation.c
249+
* silently dropping blocks beyond their 256 initial capacity, surviving
250+
* Assigns then tripped JIT_CHECK at lir/generator.cpp:1358 ("assign
251+
* shouldn't be present") on functions with >256 BBs. Sibling
252+
* hir_cfg_get_rpo_c (hir_cfg_rpo_c.c:67-75) already returned the
253+
* un-clamped count; this harmonizes the two contracts. */
245254
auto rpo = as_cfg(cfg)->GetRPOTraversal();
246-
size_t count = rpo.size() < capacity ? rpo.size() : capacity;
255+
size_t actual = rpo.size();
256+
size_t count = actual < capacity ? actual : capacity;
247257
for (size_t i = 0; i < count; i++) {
248258
out[i] = to_block(rpo[i]);
249259
}
250-
return count;
260+
return actual;
251261
}
252262

253263
struct HirBasicBlock *hir_cfg_blocks_first(struct HirCFG *cfg) {
@@ -2504,10 +2514,15 @@ void *hir_cfg_blocks_next_ptr(void *cfg, void *block) {
25042514

25052515
size_t hir_cfg_get_rpo_from(HirFunction func, void *start,
25062516
void **out, size_t capacity) {
2517+
/* Same fix as hir_cfg_get_rpo above: return un-clamped count so callers
2518+
* can detect overflow via `if (n > capacity) resize+refetch`. Sole
2519+
* caller pass_output_type_c.c:435 (hir_reflow_types_c) was missing the
2520+
* resize handler entirely; that handler is added in this same commit. */
25072521
auto rpo = CFG::GetRPOTraversal(static_cast<BasicBlock*>(start));
2508-
size_t n = rpo.size() < capacity ? rpo.size() : capacity;
2522+
size_t actual = rpo.size();
2523+
size_t n = actual < capacity ? actual : capacity;
25092524
for (size_t i = 0; i < n; i++) out[i] = rpo[i];
2510-
return n;
2525+
return actual;
25112526
}
25122527

25132528
HirType hir_return_type_c(void *callable_reg) {

‎Python/jit/hir/pass_output_type_c.c‎

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,20 @@ void hir_reflow_types_c(void *func, void *start_block) {
430430
}
431431
}
432432

433-
/* Get RPO traversal from start_block */
434-
void *rpo_blocks[4096];
435-
size_t n_blocks = hir_cfg_get_rpo_from(func, start_block, rpo_blocks, 4096);
433+
/* Get RPO traversal from start_block. Per D-1775660617 fix: handle
434+
* the >4096-block overflow path (mirror of clean_cfg.c handler).
435+
* Stack-allocated path covers the common case; heap fallback for
436+
* pathological CFGs. */
437+
void *rpo_stack[4096];
438+
void **rpo_blocks = rpo_stack;
439+
size_t rpo_cap = 4096;
440+
size_t n_blocks = hir_cfg_get_rpo_from(func, start_block, rpo_blocks, rpo_cap);
441+
if (n_blocks > rpo_cap) {
442+
rpo_cap = n_blocks;
443+
rpo_blocks = (void **)malloc(rpo_cap * sizeof(void *));
444+
if (!rpo_blocks) return;
445+
hir_cfg_get_rpo_from(func, start_block, rpo_blocks, rpo_cap);
446+
}
436447

437448
/* Fixed-point iteration: flow types forward */
438449
int changed = 1;
@@ -456,6 +467,8 @@ void hir_reflow_types_c(void *func, void *start_block) {
456467
}
457468
}
458469
}
470+
471+
if (rpo_blocks != rpo_stack) free(rpo_blocks);
459472
}
460473

461474
/* ==== simplifyRedundantCondBranches C port ==== */
@@ -874,8 +887,17 @@ void hir_bind_guards_c(void *func) {
874887

875888
void hir_optimize_long_decref_runs_c(void *func) {
876889
void *cfg = hir_func_cfg_ptr(func);
877-
void *rpo_blocks[4096];
878-
size_t n_blocks = hir_cfg_get_rpo_c(cfg, rpo_blocks, 4096);
890+
/* Per D-1775660617 fix: handle the >4096-block overflow path. */
891+
void *rpo_stack[4096];
892+
void **rpo_blocks = rpo_stack;
893+
size_t rpo_cap = 4096;
894+
size_t n_blocks = hir_cfg_get_rpo_c(cfg, rpo_blocks, rpo_cap);
895+
if (n_blocks > rpo_cap) {
896+
rpo_cap = n_blocks;
897+
rpo_blocks = (void **)malloc(rpo_cap * sizeof(void *));
898+
if (!rpo_blocks) return;
899+
hir_cfg_get_rpo_c(cfg, rpo_blocks, rpo_cap);
900+
}
879901

880902
for (size_t b = 0; b < n_blocks; b++) {
881903
void *block = rpo_blocks[b];
@@ -915,6 +937,8 @@ void hir_optimize_long_decref_runs_c(void *func) {
915937
}
916938
}
917939
}
940+
941+
if (rpo_blocks != rpo_stack) free(rpo_blocks);
918942
}
919943

920944
/* ==== removeUnreachableInstructions C port ==== */
@@ -936,8 +960,17 @@ int hir_remove_unreachable_instructions_c(void *func) {
936960
void *cfg = hir_func_cfg_ptr(func);
937961

938962
int modified = 0;
939-
void *rpo_blocks[4096];
940-
size_t n_blocks = hir_cfg_get_rpo_c(cfg, rpo_blocks, 4096);
963+
/* Per D-1775660617 fix: handle the >4096-block overflow path. */
964+
void *rpo_stack[4096];
965+
void **rpo_blocks = rpo_stack;
966+
size_t rpo_cap = 4096;
967+
size_t n_blocks = hir_cfg_get_rpo_c(cfg, rpo_blocks, rpo_cap);
968+
if (n_blocks > rpo_cap) {
969+
rpo_cap = n_blocks;
970+
rpo_blocks = (void **)malloc(rpo_cap * sizeof(void *));
971+
if (!rpo_blocks) return 0;
972+
hir_cfg_get_rpo_c(cfg, rpo_blocks, rpo_cap);
973+
}
941974

942975
PhxDominatorState *dom = phx_dom_create(func);
943976
PhxRegUses reg_uses;
@@ -1083,5 +1116,7 @@ int hir_remove_unreachable_instructions_c(void *func) {
10831116
void *entry = *(void **)cfg;
10841117
hir_reflow_types_c(func, entry);
10851118
}
1119+
1120+
if (rpo_blocks != rpo_stack) free(rpo_blocks);
10861121
return modified;
10871122
}

0 commit comments

Comments
 (0)