From 1b987e862d8669e4218333c747a58c346e7f2c4d Mon Sep 17 00:00:00 2001 From: Pavel Durov Date: Tue, 12 May 2026 20:56:13 +0100 Subject: [PATCH 1/4] Fix panic in p_block when all control-point live values are globals When all live values at a control point are globals, their addresses are baked into the IR and nothing needs passing via registers or stack. This produces a trace block with no Arg instructions, which previously caused an unconditional panic in p_block. --- tests/c/guard_body_no_args.c | 43 +++++++++++++++++++++++++++++++ ykrt/src/compile/j2/hir_to_asm.rs | 8 +++--- 2 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 tests/c/guard_body_no_args.c diff --git a/tests/c/guard_body_no_args.c b/tests/c/guard_body_no_args.c new file mode 100644 index 000000000..7e3fad324 --- /dev/null +++ b/tests/c/guard_body_no_args.c @@ -0,0 +1,43 @@ +// Run-time: +// env-var: YKD_LOG=4 +// env-var: YKD_SERIALISE_COMPILATION=1 +// stderr: +// yk-tracing: start-tracing +// ... +// yk-tracing: stop-tracing +// ... +// yk-execution: enter-jit-code +// ... + +// Testing that when all interpreter state is in globals the JIT produces +// a trace block with no Arg instructions. +// Previously this caused an unconditional panic in p_block. + +#include +#include +#include + +long g_pc = 0; +YkMT *g_mt = NULL; +YkLocation i; + +__attribute__((yk_outline)) static void debug_fn(void) {} + +int main(void) { + g_mt = yk_mt_new(NULL); + yk_mt_hot_threshold_set(g_mt, 0); + yk_mt_sidetrace_threshold_set(g_mt, 4); + i = yk_location_new(); + + for (; g_pc < 20; g_pc++) { + yk_mt_control_point(g_mt, &i); + debug_fn(); + if (g_pc > 15) { + break; + } + } + + yk_location_drop(i); + yk_mt_shutdown(g_mt); + return EXIT_SUCCESS; +} diff --git a/ykrt/src/compile/j2/hir_to_asm.rs b/ykrt/src/compile/j2/hir_to_asm.rs index 7c1de6a61..36caa277f 100644 --- a/ykrt/src/compile/j2/hir_to_asm.rs +++ b/ykrt/src/compile/j2/hir_to_asm.rs @@ -860,11 +860,9 @@ impl<'a, AB: HirToAsmBackend> HirToAsm<'a, AB> { break; } let Some((iidx, hinst)) = insts_iter.next() else { - // By definition there must be no `Arg` instructions in this trace, which is only - // plausible in testing mode. - #[cfg(not(test))] - panic!(); - #[cfg(test)] + // No Arg instructions: Can happen when all live values at this control point are globals. + // Their addresses are baked into the IR, so nothing needs to be passed in + // via registers or stack at trace entry. break; }; match hinst { From 0699fb1f13a93e4bcb6d6285f63d619cf8fae108 Mon Sep 17 00:00:00 2001 From: Pavel Durov Date: Thu, 14 May 2026 10:33:44 +0100 Subject: [PATCH 2/4] Update test. --- tests/c/guard_body_no_args.c | 38 ++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/tests/c/guard_body_no_args.c b/tests/c/guard_body_no_args.c index 7e3fad324..1e10cf401 100644 --- a/tests/c/guard_body_no_args.c +++ b/tests/c/guard_body_no_args.c @@ -1,43 +1,55 @@ // Run-time: // env-var: YKD_LOG=4 +// env-var: YKD_LOG_IR=aot,hir // env-var: YKD_SERIALISE_COMPILATION=1 // stderr: // yk-tracing: start-tracing // ... // yk-tracing: stop-tracing // ... +// call llvm.experimental.patchpoint.void(0i64, 13i32, __ykrt_control_point, 3i32, %7_0, @location, 0i64) [safepoint: 0i64, ()] +// ... +// --- Begin hir --- +// ... +// term [] +// ; peel +// ... +// term [] +// --- End hir --- +// ... // yk-execution: enter-jit-code // ... // Testing that when all interpreter state is in globals the JIT produces // a trace block with no Arg instructions. -// Previously this caused an unconditional panic in p_block. +// +// The AOT patchpoint passes `@location` and `@global_var` as global address +// constants (not live locals). The empty safepoint `()` confirms this: live +// locals would appear there if interpreter state were in local variables. +// +// The HIR `term []` assert the absence of Arg instructions: the term +// list has a 1:1 correspondence with the Arg/Const instructions. + #include #include #include -long g_pc = 0; YkMT *g_mt = NULL; -YkLocation i; - -__attribute__((yk_outline)) static void debug_fn(void) {} +YkLocation location; +long global_var = 0; int main(void) { g_mt = yk_mt_new(NULL); yk_mt_hot_threshold_set(g_mt, 0); yk_mt_sidetrace_threshold_set(g_mt, 4); - i = yk_location_new(); + location = yk_location_new(); - for (; g_pc < 20; g_pc++) { - yk_mt_control_point(g_mt, &i); - debug_fn(); - if (g_pc > 15) { - break; - } + for (; global_var < 20; global_var++) { + yk_mt_control_point(g_mt, &location); } - yk_location_drop(i); + yk_location_drop(location); yk_mt_shutdown(g_mt); return EXIT_SUCCESS; } From 092ed866676b1c38902f6dcd148ea2e7d6997ea1 Mon Sep 17 00:00:00 2001 From: Pavel Durov Date: Sat, 16 May 2026 11:51:59 +0100 Subject: [PATCH 3/4] Update ykllvm. --- ykllvm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ykllvm b/ykllvm index 1d973b11d..456434e9e 160000 --- a/ykllvm +++ b/ykllvm @@ -1 +1 @@ -Subproject commit 1d973b11d6b6fb66356594f110d153c64792ddc5 +Subproject commit 456434e9ead2ff68e8d92083092913213c44fe82 From 947dc7b44dd79e2d0d995585a67acfd93af8cce2 Mon Sep 17 00:00:00 2001 From: Pavel Durov Date: Sat, 16 May 2026 12:46:37 +0100 Subject: [PATCH 4/4] Add assert on load const in trace. --- tests/c/guard_body_no_args.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/c/guard_body_no_args.c b/tests/c/guard_body_no_args.c index 1e10cf401..51119d527 100644 --- a/tests/c/guard_body_no_args.c +++ b/tests/c/guard_body_no_args.c @@ -11,10 +11,12 @@ // ... // --- Begin hir --- // ... -// term [] -// ; peel +// %0: ptr = 0x{{_}} ; @global_var +// %1: {{_}} = load %0 +// ... +// %0: ptr = 0x{{_}} ; @global_var +// %1: {{_}} = load %0 // ... -// term [] // --- End hir --- // ... // yk-execution: enter-jit-code @@ -26,9 +28,7 @@ // The AOT patchpoint passes `@location` and `@global_var` as global address // constants (not live locals). The empty safepoint `()` confirms this: live // locals would appear there if interpreter state were in local variables. -// -// The HIR `term []` assert the absence of Arg instructions: the term -// list has a 1:1 correspondence with the Arg/Const instructions. + #include