diff --git a/tests/c/early_return1.c b/tests/c/early_return1.c new file mode 100644 index 000000000..2e7346ea4 --- /dev/null +++ b/tests/c/early_return1.c @@ -0,0 +1,65 @@ +// Run-time: +// env-var: YKD_SERIALISE_COMPILATION=1 +// env-var: YKD_LOG_IR=jit-pre-opt +// env-var: YK_LOG=4 +// stderr: +// yk-jit-event: start-tracing +// early return +// 5 +// yk-jit-event: tracing-aborted +// 4 +// yk-jit-event: start-tracing +// 3 +// yk-jit-event: stop-tracing +// --- Begin jit-pre-opt --- +// ... +// --- End jit-pre-opt --- +// 2 +// yk-jit-event: enter-jit-code +// 1 +// yk-jit-event: deoptimise +// return +// exit + +// Check that early return from a recursive interpreter loop aborts tracing, +// but doesn't stop a location being retraced. + +#include +#include +#include +#include +#include +#include + +void loop(YkMT *, YkLocation *, int); + +void loop(YkMT *mt, YkLocation *loc, int i) { + NOOPT_VAL(i); + while (i > 0) { + yk_mt_control_point(mt, loc); + if (i == 6) { + loop(mt, loc, i - 1); + i--; + } else if (i == 5) { + fprintf(stderr, "early return\n"); + return; + } + fprintf(stderr, "%d\n", i); + i--; + } + fprintf(stderr, "return\n"); + return; +} + +int main(int argc, char **argv) { + YkMT *mt = yk_mt_new(NULL); + yk_mt_hot_threshold_set(mt, 1); + YkLocation loc = yk_location_new(); + + NOOPT_VAL(loc); + loop(mt, &loc, 6); + fprintf(stderr, "exit\n"); + yk_location_drop(loc); + yk_mt_shutdown(mt); + return (EXIT_SUCCESS); +} diff --git a/tests/c/early_return2.c b/tests/c/early_return2.c new file mode 100644 index 000000000..3b9062cea --- /dev/null +++ b/tests/c/early_return2.c @@ -0,0 +1,118 @@ + +// Run-time: +// env-var: YKD_SERIALISE_COMPILATION=1 +// env-var: YKD_LOG_IR=jit-pre-opt +// env-var: YK_LOG=4 +// stderr: +// yk-jit-event: start-tracing +// b3 +// 8 +// yk-jit-event: stop-tracing +// --- Begin jit-pre-opt --- +// ... +// --- End jit-pre-opt --- +// b1 +// 7 +// yk-jit-event: enter-jit-code +// yk-jit-event: deoptimise +// yk-jit-event: start-side-tracing +// b1 +// 9 +// yk-jit-event: tracing-aborted +// b3 +// 8 +// yk-jit-event: enter-jit-code +// yk-jit-event: deoptimise +// yk-jit-event: start-side-tracing +// b3 +// 7 +// yk-jit-event: stop-tracing +// --- Begin jit-pre-opt --- +// ... +// --- End jit-pre-opt --- +// b3 +// 6 +// yk-jit-event: enter-jit-code +// yk-jit-event: execute-side-trace +// yk-jit-event: deoptimise +// yk-jit-event: start-side-tracing +// b2 +// 5 +// yk-jit-event: stop-tracing +// --- Begin jit-pre-opt --- +// ... +// --- End jit-pre-opt --- +// b2 +// 4 +// yk-jit-event: enter-jit-code +// yk-jit-event: execute-side-trace +// yk-jit-event: execute-side-trace +// b2 +// 3 +// yk-jit-event: execute-side-trace +// yk-jit-event: execute-side-trace +// b2 +// 2 +// yk-jit-event: execute-side-trace +// yk-jit-event: execute-side-trace +// b2 +// 1 +// yk-jit-event: execute-side-trace +// yk-jit-event: execute-side-trace +// b2 +// 0 +// yk-jit-event: deoptimise +// yk-jit-event: start-side-tracing +// exit + +// Check that early return from a recursive interpreter loop aborts tracing, +// but doesn't stop a location being retraced. + +#include +#include +#include +#include +#include +#include +#include + +void loop(YkMT *, YkLocation *, int, bool); + +void loop(YkMT *mt, YkLocation *loc, int i, bool inner) { + NOOPT_VAL(i); + while (i > 0) { + yk_mt_control_point(mt, loc); + if (i == 10) { + loop(mt, loc, i - 1, true); + i--; + } else if (inner && i <= 8) { + fprintf(stderr, "b1\n"); + i--; + } else if (!inner && i <= 6) { + fprintf(stderr, "b2\n"); + i--; + } else { + fprintf(stderr, "b3\n"); + i--; + } + if (inner && i == 6) { + return; + } + fprintf(stderr, "%d\n", i); + } + return; +} + +int main(int argc, char **argv) { + YkMT *mt = yk_mt_new(NULL); + yk_mt_hot_threshold_set(mt, 1); + yk_mt_sidetrace_threshold_set(mt, 1); + YkLocation loc = yk_location_new(); + + NOOPT_VAL(loc); + loop(mt, &loc, 10, false); + fprintf(stderr, "exit\n"); + yk_location_drop(loc); + yk_mt_shutdown(mt); + return (EXIT_SUCCESS); +} diff --git a/ykrt/src/compile/jitc_yk/codegen/reg_alloc.rs b/ykrt/src/compile/jitc_yk/codegen/reg_alloc.rs index 55d206eff..7bbf8ec91 100644 --- a/ykrt/src/compile/jitc_yk/codegen/reg_alloc.rs +++ b/ykrt/src/compile/jitc_yk/codegen/reg_alloc.rs @@ -50,12 +50,6 @@ pub(crate) enum Register { FP(Rx), // floating point } -/// Indicates the direction of stack growth. -pub(crate) enum StackDirection { - GrowsUp, - GrowsDown, -} - #[cfg(target_arch = "x86_64")] impl VarLocation { pub(crate) fn from_yksmp_location(m: &Module, iidx: InstIdx, x: &yksmp::Location) -> Self { diff --git a/ykrt/src/compile/jitc_yk/codegen/x64/deopt.rs b/ykrt/src/compile/jitc_yk/codegen/x64/deopt.rs index af7562ca3..592d4beea 100644 --- a/ykrt/src/compile/jitc_yk/codegen/x64/deopt.rs +++ b/ykrt/src/compile/jitc_yk/codegen/x64/deopt.rs @@ -94,7 +94,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], @@ -353,13 +353,13 @@ pub(crate) extern "C" fn __yk_deopt( // The `clone` should really be `Arc::clone(&ctr)` but that doesn't play well with type // inference in this (unusual) case. - ctr.mt.guard_failure(ctr.clone(), gidx); + ctr.mt.guard_failure(ctr.clone(), gidx, frameaddr); // Since we won't return from this function, drop `ctr` manually. drop(ctr); // Now overwrite the existing stack with our newly recreated one. - unsafe { replace_stack(newframedst as *mut c_void, newstack, memsize) }; + unsafe { replace_stack(newframedst, newstack, memsize) }; } /// Writes the stack frames that we recreated in [__yk_deopt] onto the current stack, overwriting diff --git a/ykrt/src/compile/jitc_yk/codegen/x64/mod.rs b/ykrt/src/compile/jitc_yk/codegen/x64/mod.rs index 2f2936706..0ed3691d0 100644 --- a/ykrt/src/compile/jitc_yk/codegen/x64/mod.rs +++ b/ykrt/src/compile/jitc_yk/codegen/x64/mod.rs @@ -26,7 +26,7 @@ use super::{ jit_ir::{self, BinOp, FloatTy, Inst, InstIdx, Module, Operand, PtrAddInst, Ty}, CompilationError, }, - reg_alloc::{self, StackDirection, VarLocation}, + reg_alloc::{self, VarLocation}, CodeGen, }; #[cfg(any(debug_assertions, test))] @@ -140,9 +140,6 @@ static RBP_DWARF_NUM: u16 = 6; /// The x64 SysV ABI requires a 16-byte aligned stack prior to any call. const SYSV_CALL_STACK_ALIGN: usize = 16; -/// On x64 the stack grows down. -const STACK_DIRECTION: StackDirection = StackDirection::GrowsDown; - /// A function that we can put a debugger breakpoint on. /// FIXME: gross hack. #[cfg(debug_assertions)] diff --git a/ykrt/src/compile/jitc_yk/trace_builder.rs b/ykrt/src/compile/jitc_yk/trace_builder.rs index 3de0e13bf..8a63fc9b1 100644 --- a/ykrt/src/compile/jitc_yk/trace_builder.rs +++ b/ykrt/src/compile/jitc_yk/trace_builder.rs @@ -589,8 +589,10 @@ impl TraceBuilder { _aot_inst_idx: usize, val: &Option, ) -> Result<(), CompilationError> { - // FIXME: Map return value to AOT call instruction. + // If this `unwrap` fails, it means that early return detection in `mt.rs` is not working + // as expected. let frame = self.frames.pop().unwrap(); + // FIXME: Map return value to AOT call instruction. if let Some(val) = val { let op = self.handle_operand(val)?; self.local_map.insert(frame.callinst.unwrap(), op); diff --git a/ykrt/src/lib.rs b/ykrt/src/lib.rs index ee908075f..8d97fbb4f 100644 --- a/ykrt/src/lib.rs +++ b/ykrt/src/lib.rs @@ -15,6 +15,7 @@ mod location; mod log; pub(crate) mod mt; pub mod promote; +pub(crate) mod stack; pub(crate) mod thread_intercept; pub mod trace; diff --git a/ykrt/src/mt.rs b/ykrt/src/mt.rs index e86a2ce2e..355c089d8 100644 --- a/ykrt/src/mt.rs +++ b/ykrt/src/mt.rs @@ -28,6 +28,7 @@ use crate::{ stats::{Stats, TimingState}, Log, Verbosity, }, + stack::{StackDirection, STACK_DIRECTION}, trace::{default_tracer, AOTTraceIterator, TraceRecorder, Tracer}, }; @@ -401,8 +402,19 @@ impl MT { #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn control_point(self: &Arc, loc: &Location, frameaddr: *mut c_void, smid: u64) { - match self.transition_control_point(loc) { + match self.transition_control_point(loc, frameaddr) { TransitionControlPoint::NoAction => (), + TransitionControlPoint::AbortTracing => { + let thread_tracer = + MTThread::with( + |mtt| match mtt.tstate.replace(MTThreadState::Interpreting) { + MTThreadState::Tracing { thread_tracer, .. } => thread_tracer, + _ => unreachable!(), + }, + ); + thread_tracer.stop().ok(); + self.log.log(Verbosity::JITEvent, "tracing-aborted"); + } TransitionControlPoint::Execute(ctr) => { self.log.log(Verbosity::JITEvent, "enter-jit-code"); self.stats.trace_executed(); @@ -438,6 +450,7 @@ impl MT { hl, thread_tracer: tt, promotions: Vec::new(), + frameaddr, }; }), Err(e) => { @@ -474,7 +487,11 @@ impl MT { hl, thread_tracer, promotions, - } => (hl, thread_tracer, promotions), + frameaddr: tracing_frameaddr, + } => { + assert_eq!(frameaddr, tracing_frameaddr); + (hl, thread_tracer, promotions) + } _ => unreachable!(), }, ); @@ -506,7 +523,11 @@ impl MT { hl, thread_tracer, promotions, - } => (hl, thread_tracer, promotions), + frameaddr: tracing_frameaddr, + } => { + assert_eq!(frameaddr, tracing_frameaddr); + (hl, thread_tracer, promotions) + } _ => unreachable!(), }, ); @@ -536,7 +557,11 @@ impl MT { /// Perform the next step to `loc` in the `Location` state-machine for a control point. If /// `loc` moves to the Compiled state, return a pointer to a [CompiledTrace] object. - fn transition_control_point(self: &Arc, loc: &Location) -> TransitionControlPoint { + fn transition_control_point( + self: &Arc, + loc: &Location, + frameaddr: *mut c_void, + ) -> TransitionControlPoint { MTThread::with(|mtt| { let is_tracing = mtt.is_tracing(); match loc.hot_location() { @@ -599,16 +624,49 @@ impl MT { HotLocationKind::Tracing => { let hl = loc.hot_location_arc_clone().unwrap(); match &*mtt.tstate.borrow() { - MTThreadState::Tracing { hl: thread_hl, .. } => { + MTThreadState::Tracing { + hl: thread_hl, + frameaddr: tracing_frameaddr, + .. + } => { // This thread is tracing something... if !Arc::ptr_eq(thread_hl, &hl) { // ...but not this Location. TransitionControlPoint::NoAction } else { - // ...and it's this location: we have therefore finished - // tracing the loop. - lk.kind = HotLocationKind::Compiling; - TransitionControlPoint::StopTracing + // ...and it's this location... + match STACK_DIRECTION { + StackDirection::GrowsToHigherAddress => todo!(), + StackDirection::GrowsToLowerAddress => { + if frameaddr <= *tracing_frameaddr { + // We've completed a full iteration, either + // within the same frame, or in a recursive + // call. Either way, we do not want to unroll + // the loop / recursion! + lk.kind = HotLocationKind::Compiling; + TransitionControlPoint::StopTracing + } else { + debug_assert!(frameaddr > *tracing_frameaddr); + // We fell through to a caller frame. In other + // words, the frame that started tracing + // `return`ed before it stopped tracing. It's + // probably the case that we've unluckily + // started tracing at the point in a recursive + // algorithm where it's reached the culmination + // point and is now returning many levels deep. + // If we immediately restart tracing in such a + // case, we'll just end up aborting again and + // again. So we don't consider retrying and + // instead abort tracing, and hope we can start + // at a more propitious point in the future. + self.stats.trace_recorded_err(); + // FIXME: We will endless retry tracing this + // [HotLocation]. There should be a `Counting` + // state. + TransitionControlPoint::AbortTracing + } + } + } } } _ => { @@ -629,6 +687,7 @@ impl MT { TransitionControlPoint::StartTracing(hl) } TraceFailed::DontTrace => { + // FIXME: This is stupidly brutal. lk.kind = HotLocationKind::DontTrace; TransitionControlPoint::NoAction } @@ -647,22 +706,57 @@ impl MT { } => { let hl = loc.hot_location_arc_clone().unwrap(); match &*mtt.tstate.borrow() { - MTThreadState::Tracing { hl: thread_hl, .. } => { + MTThreadState::Tracing { + hl: thread_hl, + frameaddr: tracing_frameaddr, + .. + } => { // This thread is tracing something... if !Arc::ptr_eq(thread_hl, &hl) { // ...but not this Location. TransitionControlPoint::NoAction } else { - // ...and it's this location: we have therefore finished - // tracing the loop. - let parent_ctr = Arc::clone(parent_ctr); - let root_ctr_cl = Arc::clone(root_ctr); - lk.kind = HotLocationKind::Compiled(Arc::clone(root_ctr)); - drop(lk); - TransitionControlPoint::StopSideTracing { - gidx, - parent_ctr, - root_ctr: root_ctr_cl, + match STACK_DIRECTION { + StackDirection::GrowsToHigherAddress => todo!(), + StackDirection::GrowsToLowerAddress => { + if frameaddr <= *tracing_frameaddr { + // ...and it's this location: we have therefore + // finished tracing back to the control point, + // either within the same frame, or in a + // recursive call. Either way, we do not want + // to unroll the loop / recursion! + let parent_ctr = Arc::clone(parent_ctr); + let root_ctr_cl = Arc::clone(root_ctr); + lk.kind = HotLocationKind::Compiled( + Arc::clone(root_ctr), + ); + drop(lk); + TransitionControlPoint::StopSideTracing { + gidx, + parent_ctr, + root_ctr: root_ctr_cl, + } + } else { + debug_assert!(frameaddr > *tracing_frameaddr); + // We fell through to a caller frame. In other + // words, the frame that started tracing + // `return`ed before it stopped tracing. It's + // probably the case that we've unluckily + // started tracing at the point in a recursive + // algorithm where it's reached the culmination + // point and is now returning many levels deep. + // If we immediately restart tracing in such a + // case, we'll just end up aborting again and + // again. So we don't consider retrying and + // instead abort tracing, and hope we can start + // at a more propitious point in the future. + self.stats.trace_recorded_err(); + lk.kind = HotLocationKind::Compiled( + Arc::clone(root_ctr), + ); + TransitionControlPoint::AbortTracing + } + } } } } @@ -755,7 +849,12 @@ impl MT { // FIXME: Don't side trace the last guard of a side-trace as this guard always fails. // FIXME: Don't side-trace after switch instructions: not every guard failure is equal // and a trace compiled for case A won't work for case B. - pub(crate) fn guard_failure(self: &Arc, parent: Arc, gidx: GuardIdx) { + pub(crate) fn guard_failure( + self: &Arc, + parent: Arc, + gidx: GuardIdx, + frameaddr: *mut c_void, + ) { match self.transition_guard_failure(parent, gidx) { TransitionGuardFailure::NoAction => (), TransitionGuardFailure::StartSideTracing(hl) => { @@ -770,6 +869,7 @@ impl MT { hl, thread_tracer: tt, promotions: Vec::new(), + frameaddr, }; }), Err(e) => todo!("{e:?}"), @@ -832,6 +932,9 @@ enum MTThreadState { thread_tracer: Box, /// Records the content of data recorded via `yk_promote`. 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: *mut c_void, }, /// This thread is executing a trace. Note that the `dyn CompiledTrace` serves two different purposes: /// @@ -984,6 +1087,7 @@ impl MTThread { #[derive(Debug)] enum TransitionControlPoint { NoAction, + AbortTracing, Execute(Arc), StartTracing(Arc>), StopTracing, @@ -1009,7 +1113,7 @@ mod tests { compile::{CompiledTraceTestingBasicTransitions, CompiledTraceTestingMinimal}, trace::TraceRecorderError, }; - use std::hint::black_box; + use std::{hint::black_box, ptr}; use test::bench::Bencher; // We only implement enough of the equality function for the tests we have. @@ -1039,7 +1143,9 @@ mod tests { } fn expect_start_tracing(mt: &Arc, loc: &Location) { - let TransitionControlPoint::StartTracing(hl) = mt.transition_control_point(loc) else { + let TransitionControlPoint::StartTracing(hl) = + mt.transition_control_point(loc, ptr::null_mut() as *mut c_void) + else { panic!() }; MTThread::with(|mtt| { @@ -1047,12 +1153,15 @@ mod tests { hl, thread_tracer: Box::new(DummyTraceRecorder), promotions: Vec::new(), + frameaddr: ptr::null_mut() as *mut c_void, }; }); } fn expect_stop_tracing(mt: &Arc, loc: &Location) { - let TransitionControlPoint::StopTracing = mt.transition_control_point(loc) else { + let TransitionControlPoint::StopTracing = + mt.transition_control_point(loc, ptr::null_mut() as *mut c_void) + else { panic!() }; MTThread::with(|mtt| { @@ -1071,6 +1180,7 @@ mod tests { hl, thread_tracer: Box::new(DummyTraceRecorder), promotions: Vec::new(), + frameaddr: ptr::null_mut() as *mut c_void, }; }); } @@ -1084,7 +1194,7 @@ mod tests { let loc = Location::new(); for i in 0..mt.hot_threshold() { assert_eq!( - mt.transition_control_point(&loc), + mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); assert_eq!(loc.count(), Some(i + 1)); @@ -1104,12 +1214,12 @@ mod tests { ))); loc.hot_location().unwrap().lock().kind = HotLocationKind::Compiled(ctr.clone()); assert!(matches!( - mt.transition_control_point(&loc), + mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void), TransitionControlPoint::Execute(_) )); expect_start_side_tracing(&mt, ctr); - match mt.transition_control_point(&loc) { + match mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void) { TransitionControlPoint::StopSideTracing { .. } => { MTThread::with(|mtt| { *mtt.tstate.borrow_mut() = MTThreadState::Interpreting; @@ -1122,7 +1232,7 @@ mod tests { _ => unreachable!(), } assert!(matches!( - mt.transition_control_point(&loc), + mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void), TransitionControlPoint::Execute(_) )); } @@ -1146,25 +1256,25 @@ mod tests { // otherwise tracing will start, and the assertions will fail. for _ in 0..hot_thrsh / (num_threads * 4) { assert_eq!( - mt.transition_control_point(&loc), + mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); let c1 = loc.count(); assert!(c1.is_some()); assert_eq!( - mt.transition_control_point(&loc), + mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); let c2 = loc.count(); assert!(c2.is_some()); assert_eq!( - mt.transition_control_point(&loc), + mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); let c3 = loc.count(); assert!(c3.is_some()); assert_eq!( - mt.transition_control_point(&loc), + mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); let c4 = loc.count(); @@ -1184,7 +1294,7 @@ mod tests { // at or below the threshold: it could even be (although it's rather unlikely) 0! assert!(loc.count().is_some()); loop { - match mt.transition_control_point(&loc) { + match mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void) { TransitionControlPoint::NoAction => (), TransitionControlPoint::StartTracing(hl) => { MTThread::with(|mtt| { @@ -1192,6 +1302,7 @@ mod tests { hl, thread_tracer: Box::new(DummyTraceRecorder), promotions: Vec::new(), + frameaddr: ptr::null_mut() as *mut c_void, }; }); break; @@ -1216,7 +1327,7 @@ mod tests { // Get the location to the point of being hot. for _ in 0..THRESHOLD { assert_eq!( - mt.transition_control_point(&loc), + mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); } @@ -1248,7 +1359,7 @@ mod tests { HotLocationKind::Tracing )); assert_eq!( - mt.transition_control_point(&loc), + mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); assert!(matches!( @@ -1269,7 +1380,7 @@ mod tests { // Get the location to the point of being hot. for _ in 0..THRESHOLD { assert_eq!( - mt.transition_control_point(&loc), + mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); } @@ -1327,17 +1438,17 @@ mod tests { for _ in 0..THRESHOLD { assert_eq!( - mt.transition_control_point(&loc1), + mt.transition_control_point(&loc1, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); assert_eq!( - mt.transition_control_point(&loc2), + mt.transition_control_point(&loc2, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); } expect_start_tracing(&mt, &loc1); assert_eq!( - mt.transition_control_point(&loc2), + mt.transition_control_point(&loc2, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); assert!(matches!( @@ -1374,8 +1485,9 @@ mod tests { let num_starts = Arc::clone(&num_starts); thrs.push(thread::spawn(move || { for _ in 0..THRESHOLD { - match mt.transition_control_point(&loc) { + match mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void) { TransitionControlPoint::NoAction => (), + TransitionControlPoint::AbortTracing => panic!(), TransitionControlPoint::Execute(_) => (), TransitionControlPoint::StartTracing(hl) => { num_starts.fetch_add(1, Ordering::Relaxed); @@ -1384,6 +1496,7 @@ mod tests { hl, thread_tracer: Box::new(DummyTraceRecorder), promotions: Vec::new(), + frameaddr: ptr::null_mut() as *mut c_void, }; }); assert!(matches!( @@ -1396,7 +1509,7 @@ mod tests { HotLocationKind::Compiling )); assert_eq!( - mt.transition_control_point(&loc), + mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); assert!(matches!( @@ -1407,8 +1520,8 @@ mod tests { Arc::new(CompiledTraceTestingMinimal::new()), ); loop { - if let TransitionControlPoint::Execute(_) = - mt.transition_control_point(&loc) + if let TransitionControlPoint::Execute(_) = mt + .transition_control_point(&loc, ptr::null_mut() as *mut c_void) { break; } @@ -1443,11 +1556,11 @@ mod tests { for _ in 0..THRESHOLD { assert_eq!( - mt.transition_control_point(&loc1), + mt.transition_control_point(&loc1, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); assert_eq!( - mt.transition_control_point(&loc2), + mt.transition_control_point(&loc2, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); } @@ -1462,7 +1575,7 @@ mod tests { expect_start_tracing(&mt, &loc2); assert_eq!( - mt.transition_control_point(&loc1), + mt.transition_control_point(&loc1, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); expect_stop_tracing(&mt, &loc2); @@ -1484,7 +1597,7 @@ mod tests { fn to_compiled(mt: &Arc, loc: &Location) -> Arc { for _ in 0..THRESHOLD { assert_eq!( - mt.transition_control_point(loc), + mt.transition_control_point(loc, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction ); } @@ -1518,11 +1631,11 @@ mod tests { expect_start_side_tracing(&mt, ctr2); assert!(matches!( - dbg!(mt.transition_control_point(&loc1)), + dbg!(mt.transition_control_point(&loc1, ptr::null_mut() as *mut c_void)), TransitionControlPoint::NoAction )); assert!(matches!( - mt.transition_control_point(&loc2), + mt.transition_control_point(&loc2, ptr::null_mut() as *mut c_void), TransitionControlPoint::StopSideTracing { .. } )); } @@ -1533,7 +1646,7 @@ mod tests { let loc = Location::new(); b.iter(|| { for _ in 0..100000 { - black_box(mt.transition_control_point(&loc)); + black_box(mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void)); } }); } @@ -1549,7 +1662,9 @@ mod tests { let mt = Arc::clone(&mt); thrs.push(thread::spawn(move || { for _ in 0..100 { - black_box(mt.transition_control_point(&loc)); + black_box( + mt.transition_control_point(&loc, ptr::null_mut() as *mut c_void), + ); } })); } @@ -1580,14 +1695,14 @@ mod tests { // https://github.com/ykjit/yk/issues/519 expect_start_tracing(&mt, &loc2); assert!(matches!( - mt.transition_control_point(&loc1), + mt.transition_control_point(&loc1, ptr::null_mut() as *mut c_void), TransitionControlPoint::NoAction )); // But once we stop tracing for `loc2`, we should be able to execute the trace for `loc1`. expect_stop_tracing(&mt, &loc2); assert!(matches!( - mt.transition_control_point(&loc1), + mt.transition_control_point(&loc1, ptr::null_mut() as *mut c_void), TransitionControlPoint::Execute(_) )); } diff --git a/ykrt/src/stack.rs b/ykrt/src/stack.rs new file mode 100644 index 000000000..472225781 --- /dev/null +++ b/ykrt/src/stack.rs @@ -0,0 +1,12 @@ +/// Which direction does the CPU stack grow when more room is required? +pub(crate) enum StackDirection { + /// The CPU stack grows "downwards" i.e. growth changes it to a lower address. + #[allow(dead_code)] + GrowsToHigherAddress, + /// The CPU stack grows "upwards" i.e. growth changes it to a higher address. + #[allow(dead_code)] + GrowsToLowerAddress, +} + +#[cfg(target_arch = "x86_64")] +pub(crate) const STACK_DIRECTION: StackDirection = StackDirection::GrowsToLowerAddress;