From b2b96eef25db5a93c163ab9314d7f7f543d17b27 Mon Sep 17 00:00:00 2001 From: Laurence Tratt Date: Fri, 29 Nov 2024 16:27:31 +0000 Subject: [PATCH] Add early return and recursive tests. --- tests/c/early_return.c | 63 +++++++++++++++++++ tests/c/fcmp_double.c | 1 - tests/c/fcmp_float.c | 1 - tests/c/recursive.c | 61 ++++++++++++++++++ ykcapi/src/lib.rs | 7 +++ ykcapi/yk.h | 16 +++++ ykrt/src/compile/jitc_yk/codegen/x64/deopt.rs | 2 +- ykrt/src/mt.rs | 50 ++++++++++++--- 8 files changed, 190 insertions(+), 11 deletions(-) create mode 100644 tests/c/early_return.c create mode 100644 tests/c/recursive.c diff --git a/tests/c/early_return.c b/tests/c/early_return.c new file mode 100644 index 000000000..0b3e99020 --- /dev/null +++ b/tests/c/early_return.c @@ -0,0 +1,63 @@ +// Run-time: +// env-var: YKD_LOG_IR=-:jit-post-opt +// env-var: YKD_SERIALISE_COMPILATION=1 +// env-var: YK_LOG=4 +// stderr: +// 3 +// 2 +// yk-jit-event: start-tracing +// 1 +// yk-jit-event: stop-tracing-early-return +// return +// yk-jit-event: start-tracing +// 3 +// yk-jit-event: stop-tracing +// --- Begin jit-pre-opt --- +// ... +// --- End jit-pre-opt --- +// exit + +// Check that basic trace compilation works. + +#include +#include +#include +#include +#include +#include + +void loop(YkMT *, YkLocation *); + +void loop(YkMT *mt, YkLocation *loc) { + int res = 9998; + int i = 3; + NOOPT_VAL(res); + NOOPT_VAL(i); + while (i > 0) { + yk_mt_control_point(mt, loc); + fprintf(stderr, "%d\n", i); + i--; + } + yk_mt_early_return(mt); + fprintf(stderr, "return\n"); + NOOPT_VAL(res); +} + +int main(int argc, char **argv) { + YkMT *mt = yk_mt_new(NULL); + yk_mt_hot_threshold_set(mt, 2); + YkLocation loc = yk_location_new(); + + int res = 9998; + int i = 4; + NOOPT_VAL(loc); + NOOPT_VAL(res); + NOOPT_VAL(i); + loop(mt, &loc); + loop(mt, &loc); + fprintf(stderr, "exit\n"); + NOOPT_VAL(res); + yk_location_drop(loc); + yk_mt_shutdown(mt); + return (EXIT_SUCCESS); +} diff --git a/tests/c/fcmp_double.c b/tests/c/fcmp_double.c index 1fd425a85..30a6063f1 100644 --- a/tests/c/fcmp_double.c +++ b/tests/c/fcmp_double.c @@ -1,5 +1,4 @@ // Run-time: -// env-var: YKD_LOG_IR=aot,jit-pre-opt,jit-asm // env-var: YKD_SERIALISE_COMPILATION=1 // env-var: YK_LOG=4 // stderr: diff --git a/tests/c/fcmp_float.c b/tests/c/fcmp_float.c index 7089b0516..ef8b9e9e2 100644 --- a/tests/c/fcmp_float.c +++ b/tests/c/fcmp_float.c @@ -1,5 +1,4 @@ // Run-time: -// env-var: YKD_LOG_IR=aot,jit-pre-opt,jit-asm // env-var: YKD_SERIALISE_COMPILATION=1 // env-var: YK_LOG=4 // stderr: diff --git a/tests/c/recursive.c b/tests/c/recursive.c new file mode 100644 index 000000000..4bee91bdf --- /dev/null +++ b/tests/c/recursive.c @@ -0,0 +1,61 @@ +// Run-time: +// env-var: YKD_LOG_IR=-:jit-post-opt +// env-var: YKD_SERIALISE_COMPILATION=1 +// env-var: YK_LOG=4 +// stderr: +// 3 +// 2 +// yk-jit-event: start-tracing +// 1 +// yk-jit-event: stop-tracing-early-return +// return +// yk-jit-event: start-tracing +// 3 +// yk-jit-event: stop-tracing +// --- Begin jit-pre-opt --- +// ... +// --- End jit-pre-opt --- +// exit + +// Check that basic trace compilation works. + +#include +#include +#include +#include +#include +#include + +int loop(YkMT *, YkLocation *, int); + +int loop(YkMT *mt, YkLocation *loc, int i) { + int res = 9998; + NOOPT_VAL(res); + NOOPT_VAL(i); + while (i > 0) { + yk_mt_control_point(mt, loc); + loop(mt, loc, i - 1); + fprintf(stderr, "%d\n", i); + i--; + } + yk_mt_early_return(mt); + fprintf(stderr, "return\n"); + NOOPT_VAL(res); + return i; +} + +int main(int argc, char **argv) { + YkMT *mt = yk_mt_new(NULL); + yk_mt_hot_threshold_set(mt, 2); + YkLocation loc = yk_location_new(); + + int res = 9998; + NOOPT_VAL(loc); + NOOPT_VAL(res); + loop(mt, &loc, 3); + fprintf(stderr, "exit\n"); + NOOPT_VAL(res); + yk_location_drop(loc); + yk_mt_shutdown(mt); + return (EXIT_SUCCESS); +} diff --git a/ykcapi/src/lib.rs b/ykcapi/src/lib.rs index ba06dcc3f..22220ca58 100644 --- a/ykcapi/src/lib.rs +++ b/ykcapi/src/lib.rs @@ -50,6 +50,13 @@ pub extern "C" fn yk_mt_control_point(_mt: *mut MT, _loc: *mut Location) { // Intentionally empty. } +#[no_mangle] +pub extern "C" fn __yk_mt_early_return(mt: *mut MT, frameaddr: *mut c_void) { + let mt = unsafe { &*mt }; + let arc = unsafe { Arc::from_raw(mt) }; + arc.early_return(frameaddr); +} + // The new control point called after the interpreter has been patched by ykllvm. #[cfg(target_arch = "x86_64")] #[naked] diff --git a/ykcapi/yk.h b/ykcapi/yk.h index fad7223ca..244ccc4ed 100644 --- a/ykcapi/yk.h +++ b/ykcapi/yk.h @@ -49,6 +49,22 @@ void yk_mt_shutdown(YkMT *); // execute JITted code. void yk_mt_control_point(YkMT *, YkLocation *); +// At each point a function containing a control point can exit "early" this +// function must be called. "Early" includes, but is not +// limited to, the following: +// +// 1. Immediately after a non-infinite loop containing a call to +// `yk_mt_control_point`. +// 2. Immediately before `return` statements in code reachable from a +// `yk_mt_control_point`. +// +// Failure to call this function will lead to undefined behaviour. +# define yk_mt_early_return(mt) __yk_mt_early_return(mt, __builtin_frame_address(0)) + +// This is an internal function to yk: calling it directly leads to undefined +// behaviour. +void __yk_mt_early_return(YkMT *, void *); + // Set the threshold at which `YkLocation`'s are considered hot. void yk_mt_hot_threshold_set(YkMT *, YkHotThreshold); diff --git a/ykrt/src/compile/jitc_yk/codegen/x64/deopt.rs b/ykrt/src/compile/jitc_yk/codegen/x64/deopt.rs index 554ff5921..5da02ce74 100644 --- a/ykrt/src/compile/jitc_yk/codegen/x64/deopt.rs +++ b/ykrt/src/compile/jitc_yk/codegen/x64/deopt.rs @@ -92,7 +92,7 @@ fn running_trace(gidxs: &[usize]) -> Arc { /// * glen - Length for list in `gptr`. #[no_mangle] pub(crate) extern "C" fn __yk_deopt( - frameaddr: *const c_void, + frameaddr: *mut c_void, gidx: u64, gp_regs: &[u64; 16], fp_regs: &[u64; 16], diff --git a/ykrt/src/mt.rs b/ykrt/src/mt.rs index 699532414..faaa9b3f3 100644 --- a/ykrt/src/mt.rs +++ b/ykrt/src/mt.rs @@ -456,7 +456,7 @@ impl MT { promotions, frameaddr: tracing_frameaddr, } => { - assert_eq!(frameaddr as *const c_void, tracing_frameaddr); + assert_eq!(frameaddr, tracing_frameaddr); (hl, thread_tracer, promotions) } _ => unreachable!(), @@ -492,7 +492,7 @@ impl MT { promotions, frameaddr: tracing_frameaddr, } => { - assert_eq!(frameaddr as *const c_void, tracing_frameaddr); + assert_eq!(frameaddr, tracing_frameaddr); (hl, thread_tracer, promotions) } _ => unreachable!(), @@ -746,7 +746,7 @@ impl MT { self: &Arc, parent: Arc, gidx: GuardIdx, - frameaddr: *const c_void, + frameaddr: *mut c_void, ) { match self.transition_guard_failure(parent, gidx) { TransitionGuardFailure::NoAction => (), @@ -770,6 +770,40 @@ impl MT { } } } + + pub fn early_return(self: &Arc, frameaddr: *mut c_void) { + MTThread::with(|mtt| { + let mut abort = false; + if let MTThreadState::Tracing { + frameaddr: tracing_frameaddr, + .. + } = &*mtt.tstate.borrow() + { + if frameaddr == *tracing_frameaddr { + abort = true; + } + } + + if abort { + match mtt.tstate.replace(MTThreadState::Interpreting) { + MTThreadState::Tracing { + thread_tracer, hl, .. + } => { + // We don't care if the thread tracer went wrong: we're not going to use + // its result anyway. + thread_tracer.stop().unwrap(); + let mut lk = hl.lock(); + lk.tracecompilation_error(self); + drop(lk); + self.stats.timing_state(TimingState::None); + self.log + .log(Verbosity::JITEvent, "stop-tracing-early-return"); + } + _ => unreachable!(), + } + } + }); + } } #[cfg(target_arch = "x86_64")] @@ -826,7 +860,7 @@ enum MTThreadState { promotions: Vec, /// The `frameaddr` when tracing started. This allows us to tell if we're finishing tracing /// at the same point that we started. - frameaddr: *const c_void, + frameaddr: *mut c_void, }, /// This thread is executing a trace. Note that the `dyn CompiledTrace` serves two different purposes: /// @@ -1041,7 +1075,7 @@ mod tests { hl, thread_tracer: Box::new(DummyTraceRecorder), promotions: Vec::new(), - frameaddr: 0 as *const c_void, + frameaddr: 0 as *mut c_void, }; }); } @@ -1066,7 +1100,7 @@ mod tests { hl, thread_tracer: Box::new(DummyTraceRecorder), promotions: Vec::new(), - frameaddr: 0 as *const c_void, + frameaddr: 0 as *mut c_void, }; }); } @@ -1188,7 +1222,7 @@ mod tests { hl, thread_tracer: Box::new(DummyTraceRecorder), promotions: Vec::new(), - frameaddr: 0 as *const c_void, + frameaddr: 0 as *mut c_void, }; }); break; @@ -1381,7 +1415,7 @@ mod tests { hl, thread_tracer: Box::new(DummyTraceRecorder), promotions: Vec::new(), - frameaddr: 0 as *const c_void, + frameaddr: 0 as *mut c_void, }; }); assert!(matches!(