use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use yulang_runtime::runtime::bytes_tree::BytesTree;
use yulang_runtime::runtime::list_tree::{ListTree, ListView};
use yulang_runtime::runtime::string_tree::StringTree;
use yulang_typed_ir as typed_ir;
use super::{CpsRootDisplayHint, tag_hash};
type NativeCpsI64Continuation = extern "C" fn(*const i64, i64) -> i64;
type NativeCpsI64ThunkEntry = extern "C" fn(*const i64) -> i64;
type NativeCpsI64ClosureEntry = extern "C" fn(*const i64, i64) -> i64;
type NativeCpsI64HandlerSnapshot = Rc<[NativeCpsI64HandlerFrame]>;
type NativeCpsI64ReturnFrameSnapshot = Rc<[NativeCpsI64ReturnFrame]>;
type NativeCpsI64I64Snapshot = Rc<[i64]>;
fn native_cps_i64_snapshot<T>(items: Vec<T>) -> Rc<[T]> {
Rc::from(items.into_boxed_slice())
}
fn call_native_i64_continuation(continuation: usize, env: &[i64], value: i64) -> i64 {
let cont: NativeCpsI64Continuation = unsafe { std::mem::transmute(continuation) };
cont(env.as_ptr(), value)
}
#[derive(Debug, Clone, Copy)]
struct NativeCpsI64HandlerAnchor {
prompt: u64,
install_eval_id: u64,
}
#[derive(Debug, Clone, Copy)]
struct NativeCpsI64BlockedEffect {
guard_id: i64,
allowed_mask: i64,
active: bool,
}
#[repr(C)]
pub(super) struct NativeCpsI64Resumption {
code: NativeCpsI64Continuation,
env: Box<[i64]>,
handlers: NativeCpsI64HandlerSnapshot,
guard_stack: NativeCpsI64I64Snapshot,
return_frames: NativeCpsI64ReturnFrameSnapshot,
handled_anchor: Option<NativeCpsI64HandlerAnchor>,
debug_id: u64,
}
#[repr(C)]
struct NativeCpsI64Thunk {
code: NativeCpsI64ThunkEntry,
env: Box<[i64]>,
handlers: NativeCpsI64HandlerSnapshot,
guard_stack: NativeCpsI64I64Snapshot,
active_blocked: Box<[NativeCpsI64BlockedEffect]>,
}
#[repr(C)]
struct NativeCpsI64Closure {
code: NativeCpsI64ClosureEntry,
env: Box<[i64]>,
handlers: NativeCpsI64HandlerSnapshot,
guard_stack: NativeCpsI64I64Snapshot,
}
enum NativeCpsI64HeapValue {
Tuple(Box<[i64]>),
Record(Vec<(Box<str>, i64)>),
Variant { tag: i64, value: Option<i64> },
List(ListTree<i64>),
Bool(bool),
Unit,
Float(f64),
String(StringTree<Box<str>>),
Bytes(BytesTree),
Path(std::path::PathBuf),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum NativeCpsI64HandlerFrameOrigin {
RealInstall,
LegacyInstall,
PendingEnv,
ResumeWithHandler,
StaticFallback,
}
#[derive(Clone)]
struct NativeCpsI64HandlerFrame {
handler: i64,
consumes_mask: i64,
guard_stack: NativeCpsI64I64Snapshot,
envs: Vec<NativeCpsI64HandlerEnv>,
prompt: u64,
install_eval_id: u64,
escape_owner_function_id: u64,
threshold_owner_function_id: u64,
inherited: bool,
escape_continuation: usize,
escape_env: Box<[i64]>,
escape_env_targets: Box<[i64]>,
return_frame_threshold: usize,
return_frame_prefix: NativeCpsI64ReturnFrameSnapshot,
origin: NativeCpsI64HandlerFrameOrigin,
}
#[derive(Clone)]
struct NativeCpsI64HandlerEnv {
entry: i64,
env: i64,
slots: Vec<(i64, i64)>,
}
#[derive(Debug, Clone, Default)]
struct NativeCpsI64ScopeReturn {
active: bool,
prompt: u64,
target: i64,
value: i64,
}
#[derive(Clone, Default)]
enum NativeCpsI64Abort {
#[default]
None,
Global(i64),
#[allow(dead_code)]
Scoped {
value: i64,
return_frame_threshold: usize,
propagate_at_threshold: bool,
restore_frames: NativeCpsI64ReturnFrameSnapshot,
},
RoutedJump {
value: i64,
return_frame_threshold: usize,
continuation: usize,
env: Box<[i64]>,
handlers: Vec<NativeCpsI64HandlerFrame>,
guards: Vec<i64>,
return_frames: NativeCpsI64ReturnFrameSnapshot,
eval_context: NativeCpsI64EvalContext,
},
}
impl NativeCpsI64Abort {
fn is_active(&self) -> bool {
!matches!(self, NativeCpsI64Abort::None)
}
fn value_or_zero(&self) -> i64 {
match self {
NativeCpsI64Abort::None => 0,
NativeCpsI64Abort::Global(value) => *value,
NativeCpsI64Abort::Scoped { value, .. } => *value,
NativeCpsI64Abort::RoutedJump { value, .. } => *value,
}
}
fn mode_at_frame_len(&self, frame_len: usize) -> i64 {
match self {
NativeCpsI64Abort::None => 0,
NativeCpsI64Abort::Global(_) => 1,
NativeCpsI64Abort::Scoped {
return_frame_threshold,
propagate_at_threshold,
..
} => {
if frame_len > *return_frame_threshold
|| (*propagate_at_threshold && frame_len == *return_frame_threshold)
{
1
} else {
2
}
}
NativeCpsI64Abort::RoutedJump {
return_frame_threshold,
..
} => {
if frame_len > *return_frame_threshold {
1
} else {
2
}
}
}
}
fn is_unguarded_routed_jump(&self) -> bool {
matches!(
self,
NativeCpsI64Abort::RoutedJump { guards, .. } if guards.is_empty()
)
}
fn is_routed_jump(&self) -> bool {
matches!(self, NativeCpsI64Abort::RoutedJump { .. })
}
fn routed_jump_should_return(&self) -> bool {
matches!(
self,
NativeCpsI64Abort::RoutedJump {
return_frame_threshold,
return_frames,
..
} if *return_frame_threshold > 0 && !return_frames.is_empty()
)
}
}
#[derive(Clone)]
struct NativeCpsI64ReturnFrame {
prompt_exit: Option<NativeCpsI64PromptExitFrame>,
debug_id: u64,
continuation: usize,
continuation_id: u32,
env: NativeCpsI64I64Snapshot,
handlers: NativeCpsI64HandlerSnapshot,
guards: NativeCpsI64I64Snapshot,
owner_initial_frame_count: usize,
owner_eval_id: u64,
owner_function_id: u64,
immediately_forces_param: bool,
}
#[derive(Clone)]
struct NativeCpsI64PromptExitFrame {
prompt: u64,
}
#[derive(Default)]
struct NativeCpsI64HandlerArmReturnFrameSnapshot {
frames: Vec<NativeCpsI64ReturnFrame>,
consumed_start: usize,
}
#[derive(Debug, Clone, Copy, Default)]
struct NativeCpsI64EvalContext {
current_eval_id: u64,
initial_frame_count: usize,
}
#[derive(Clone, Default)]
struct NativeCpsI64PendingRoutedReturnFrames {
skip_initial_frame_count: usize,
frames: NativeCpsI64ReturnFrameSnapshot,
}
impl NativeCpsI64PendingRoutedReturnFrames {
fn new(skip_initial_frame_count: usize, frames: NativeCpsI64ReturnFrameSnapshot) -> Self {
Self {
skip_initial_frame_count,
frames,
}
}
fn should_restore_for_initial(&self, initial: usize) -> bool {
self.skip_initial_frame_count != initial
}
}
const NATIVE_CPS_I64_SYNTHETIC_EVAL_ID: u64 = u64::MAX;
#[allow(dead_code)]
const NATIVE_CPS_I64_EXIT_RWH_TARGET: i64 = -1;
pub(super) fn jit_trace_enabled() -> bool {
use std::sync::atomic::{AtomicU8, Ordering};
static STATE: AtomicU8 = AtomicU8::new(0); match STATE.load(Ordering::Relaxed) {
2 => true,
1 => false,
_ => {
let on = std::env::var("YULANG_CPS_JIT_TRACE")
.map(|v| v == "1")
.unwrap_or(false);
STATE.store(if on { 2 } else { 1 }, Ordering::Relaxed);
on
}
}
}
fn jit_force_frame_walk_sr() -> bool {
use std::sync::atomic::{AtomicU8, Ordering};
static STATE: AtomicU8 = AtomicU8::new(0);
match STATE.load(Ordering::Relaxed) {
2 => true,
1 => false,
_ => {
let on = std::env::var("YULANG_CPS_JIT_FORCE_FRAME_WALK_SR")
.map(|v| v == "1")
.unwrap_or(false);
STATE.store(if on { 2 } else { 1 }, Ordering::Relaxed);
on
}
}
}
thread_local! {
static NATIVE_CPS_I64_HEAP_VALUES: RefCell<HashSet<i64>> = RefCell::new(HashSet::new());
static NATIVE_CPS_I64_TAG_NAMES: RefCell<HashMap<i64, Box<str>>> = RefCell::new(HashMap::new());
static NATIVE_CPS_I64_THUNKS: RefCell<HashSet<usize>> = RefCell::new(HashSet::new());
static NATIVE_CPS_I64_CLOSURES: RefCell<HashSet<usize>> = RefCell::new(HashSet::new());
static NATIVE_CPS_I64_RESUMPTIONS: RefCell<HashSet<usize>> = RefCell::new(HashSet::new());
static NATIVE_CPS_I64_ROOT_FUNCTION_IDS: RefCell<Vec<u64>> = const { RefCell::new(Vec::new()) };
static NATIVE_CPS_I64_HANDLER_STACK: RefCell<Vec<NativeCpsI64HandlerFrame>> = const { RefCell::new(Vec::new()) };
static NATIVE_CPS_I64_GUARD_STACK: RefCell<Vec<i64>> = const { RefCell::new(Vec::new()) };
static NATIVE_CPS_I64_ACTIVE_BLOCKED: RefCell<Vec<NativeCpsI64BlockedEffect>> = const { RefCell::new(Vec::new()) };
static NATIVE_CPS_I64_NEXT_GUARD: RefCell<i64> = const { RefCell::new(0) };
static NATIVE_CPS_I64_NEXT_RESUMPTION_DEBUG_ID: RefCell<u64> = const { RefCell::new(1) };
static NATIVE_CPS_I64_NEXT_RETURN_FRAME_DEBUG_ID: RefCell<u64> = const { RefCell::new(1) };
static NATIVE_CPS_I64_PENDING_HANDLER_ENVS: RefCell<Vec<(i64, NativeCpsI64HandlerEnv)>> = const { RefCell::new(Vec::new()) };
static NATIVE_CPS_I64_PENDING_ESCAPE_ENV_TARGETS: RefCell<Vec<i64>> = const { RefCell::new(Vec::new()) };
static NATIVE_CPS_I64_SELECTED_HANDLER_ENVS: RefCell<Vec<NativeCpsI64HandlerEnv>> = const { RefCell::new(Vec::new()) };
static NATIVE_CPS_I64_SELECTED_HANDLER_ID: RefCell<i64> = const { RefCell::new(-1) };
static NATIVE_CPS_I64_SELECTED_HANDLER_OWNER_FUNCTION_ID: RefCell<u64> = const { RefCell::new(0) };
static NATIVE_CPS_I64_SELECTED_HANDLER_USED_RWH_ENV: RefCell<bool> = const { RefCell::new(false) };
static NATIVE_CPS_I64_RESUME_WITH_HANDLER_SIBLINGS: RefCell<Vec<NativeCpsI64HandlerFrame>> =
const { RefCell::new(Vec::new()) };
static NATIVE_CPS_I64_ABORT: RefCell<NativeCpsI64Abort> =
const { RefCell::new(NativeCpsI64Abort::None) };
static NATIVE_CPS_I64_HANDLER_THRESHOLD_OFFSET: RefCell<usize> = const { RefCell::new(0) };
static NATIVE_CPS_I64_SCOPE_RETURN: RefCell<NativeCpsI64ScopeReturn> =
const { RefCell::new(NativeCpsI64ScopeReturn {
active: false,
prompt: 0,
target: 0,
value: 0,
}) };
static NATIVE_CPS_I64_RETURN_FRAMES: RefCell<Vec<NativeCpsI64ReturnFrame>> =
const { RefCell::new(Vec::new()) };
static NATIVE_CPS_I64_PENDING_ROUTED_RETURN_FRAMES: RefCell<Option<NativeCpsI64PendingRoutedReturnFrames>> =
const { RefCell::new(None) };
static NATIVE_CPS_I64_RETURN_FRAMES_ROUTED: RefCell<bool> = const { RefCell::new(false) };
static NATIVE_CPS_I64_CONSUMED_RETURN_FRAME_IDS: RefCell<Vec<u64>> =
const { RefCell::new(Vec::new()) };
static NATIVE_CPS_I64_HANDLER_ARM_RETURN_FRAME_SNAPSHOTS: RefCell<Vec<NativeCpsI64HandlerArmReturnFrameSnapshot>> =
const { RefCell::new(Vec::new()) };
static NATIVE_CPS_I64_EVAL_CONTEXT: RefCell<NativeCpsI64EvalContext> =
const { RefCell::new(NativeCpsI64EvalContext {
current_eval_id: 0,
initial_frame_count: 0,
}) };
static NATIVE_CPS_I64_NEXT_EVAL_ID: RefCell<u64> = const { RefCell::new(0) };
static NATIVE_CPS_I64_NEXT_PROMPT: RefCell<u64> = const { RefCell::new(1) };
static NATIVE_CPS_I64_OUTER_HANDLER_SNAPSHOTS: RefCell<Vec<Vec<NativeCpsI64HandlerFrame>>> =
const { RefCell::new(Vec::new()) };
static NATIVE_CPS_I64_SELECTED_HANDLER_META_STACK: RefCell<Vec<NativeCpsI64SelectedHandlerMeta>> =
const { RefCell::new(Vec::new()) };
}
#[derive(Clone)]
#[allow(dead_code)]
struct NativeCpsI64SelectedHandlerMeta {
prompt: u64,
escape_continuation: usize,
escape_env: Box<[i64]>,
return_frame_threshold: usize,
install_eval_id: u64,
synthetic: bool,
}
#[derive(Clone)]
struct NativeCpsI64CurrentScopeHandlerMatch {
handler_index: usize,
handler: NativeCpsI64HandlerFrame,
}
#[derive(Clone)]
struct NativeCpsI64ReturnFrameScopeHandlerMatch {
return_frame_index: usize,
handler_index: usize,
return_frame: NativeCpsI64ReturnFrame,
handler: NativeCpsI64HandlerFrame,
}
pub(super) fn reset_native_i64_cps_state() {
NATIVE_CPS_I64_HEAP_VALUES.with(|values| values.borrow_mut().clear());
NATIVE_CPS_I64_TAG_NAMES.with(|names| names.borrow_mut().clear());
NATIVE_CPS_I64_THUNKS.with(|thunks| thunks.borrow_mut().clear());
NATIVE_CPS_I64_CLOSURES.with(|closures| closures.borrow_mut().clear());
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| stack.borrow_mut().clear());
NATIVE_CPS_I64_GUARD_STACK.with(|stack| stack.borrow_mut().clear());
NATIVE_CPS_I64_ACTIVE_BLOCKED.with(|stack| stack.borrow_mut().clear());
NATIVE_CPS_I64_NEXT_GUARD.with(|next| *next.borrow_mut() = 0);
NATIVE_CPS_I64_NEXT_RESUMPTION_DEBUG_ID.with(|next| *next.borrow_mut() = 1);
NATIVE_CPS_I64_NEXT_RETURN_FRAME_DEBUG_ID.with(|next| *next.borrow_mut() = 1);
NATIVE_CPS_I64_PENDING_HANDLER_ENVS.with(|envs| envs.borrow_mut().clear());
NATIVE_CPS_I64_PENDING_ESCAPE_ENV_TARGETS.with(|targets| targets.borrow_mut().clear());
NATIVE_CPS_I64_SELECTED_HANDLER_ENVS.with(|envs| envs.borrow_mut().clear());
NATIVE_CPS_I64_SELECTED_HANDLER_ID.with(|handler| *handler.borrow_mut() = -1);
NATIVE_CPS_I64_SELECTED_HANDLER_OWNER_FUNCTION_ID.with(|owner| *owner.borrow_mut() = 0);
NATIVE_CPS_I64_SELECTED_HANDLER_USED_RWH_ENV.with(|used| *used.borrow_mut() = false);
NATIVE_CPS_I64_RESUME_WITH_HANDLER_SIBLINGS.with(|handlers| handlers.borrow_mut().clear());
NATIVE_CPS_I64_ABORT.with(|slot| *slot.borrow_mut() = NativeCpsI64Abort::None);
NATIVE_CPS_I64_HANDLER_THRESHOLD_OFFSET.with(|slot| *slot.borrow_mut() = 0);
NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| {
*slot.borrow_mut() = NativeCpsI64ScopeReturn::default();
});
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow_mut().clear());
clear_pending_native_i64_routed_return_frames();
NATIVE_CPS_I64_RETURN_FRAMES_ROUTED.with(|routed| *routed.borrow_mut() = false);
NATIVE_CPS_I64_CONSUMED_RETURN_FRAME_IDS.with(|ids| ids.borrow_mut().clear());
NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| {
*ctx.borrow_mut() = NativeCpsI64EvalContext::default();
});
NATIVE_CPS_I64_NEXT_EVAL_ID.with(|next| *next.borrow_mut() = 0);
NATIVE_CPS_I64_NEXT_PROMPT.with(|next| *next.borrow_mut() = 1);
NATIVE_CPS_I64_OUTER_HANDLER_SNAPSHOTS.with(|snaps| snaps.borrow_mut().clear());
NATIVE_CPS_I64_SELECTED_HANDLER_META_STACK.with(|meta| meta.borrow_mut().clear());
NATIVE_CPS_I64_RESUMPTIONS.with(|s| s.borrow_mut().clear());
NATIVE_CPS_I64_ROOT_FUNCTION_IDS.with(|ids| ids.borrow_mut().clear());
}
pub(super) fn set_native_i64_root_function_ids(ids: &[u64]) {
NATIVE_CPS_I64_ROOT_FUNCTION_IDS.with(|slot| {
*slot.borrow_mut() = ids.to_vec();
});
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_reset_i64() {
reset_native_i64_cps_state();
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_set_root_function_ids_i64(ids: *const u64, len: usize) {
if ids.is_null() {
if len == 0 {
set_native_i64_root_function_ids(&[]);
}
return;
}
let ids = unsafe { std::slice::from_raw_parts(ids, len) };
set_native_i64_root_function_ids(ids);
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_take_root_result_i64(value: i64) -> i64 {
take_native_i64_root_result(value)
}
pub(super) fn take_native_i64_root_result(value: i64) -> i64 {
let mode = yulang_cps_abort_mode_i64();
if mode == 0 {
return value;
}
let abort = if mode == 2 {
yulang_cps_consume_abort_i64()
} else {
let value = yulang_cps_abort_value_i64();
let _ = yulang_cps_clear_abort_i64();
value
};
let _ = yulang_cps_clear_scope_return_i64();
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow_mut().clear());
force_native_i64_root_result(abort)
}
fn force_native_i64_root_result(value: i64) -> i64 {
let is_thunk = usize::try_from(value)
.ok()
.is_some_and(|value| NATIVE_CPS_I64_THUNKS.with(|thunks| thunks.borrow().contains(&value)));
if is_thunk {
yulang_cps_force_thunk_i64(value as usize)
} else {
value
}
}
fn native_i64_abort_is_routed_jump() -> bool {
NATIVE_CPS_I64_ABORT.with(|slot| slot.borrow().is_routed_jump())
}
fn native_i64_abort_is_unguarded_routed_jump() -> bool {
NATIVE_CPS_I64_ABORT.with(|slot| slot.borrow().is_unguarded_routed_jump())
}
fn native_i64_abort_should_consume_after_thunk_force() -> bool {
native_i64_abort_is_unguarded_routed_jump() && yulang_cps_abort_mode_i64() == 2
}
fn current_native_i64_guard_stack() -> Vec<i64> {
NATIVE_CPS_I64_GUARD_STACK.with(|stack| stack.borrow().clone())
}
fn current_native_i64_handler_stack_with_fallback(
fallback: i64,
effect_mask: i64,
) -> Vec<NativeCpsI64HandlerFrame> {
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| {
let stack = stack.borrow();
if stack.is_empty() && fallback >= 0 {
vec![NativeCpsI64HandlerFrame {
handler: fallback,
consumes_mask: effect_mask,
guard_stack: native_cps_i64_snapshot(current_native_i64_guard_stack()),
envs: Vec::new(),
prompt: 0,
install_eval_id: NATIVE_CPS_I64_SYNTHETIC_EVAL_ID,
escape_owner_function_id: 0,
threshold_owner_function_id: 0,
inherited: false,
escape_continuation: 0,
escape_env: Box::new([]),
escape_env_targets: Box::new([]),
return_frame_threshold: 0,
return_frame_prefix: native_cps_i64_snapshot(Vec::new()),
origin: NativeCpsI64HandlerFrameOrigin::StaticFallback,
}]
} else {
stack.clone()
}
})
}
fn take_pending_native_i64_handler_frames() -> Vec<NativeCpsI64HandlerFrame> {
let pending =
NATIVE_CPS_I64_PENDING_HANDLER_ENVS.with(|envs| std::mem::take(&mut *envs.borrow_mut()));
let mut frames: Vec<NativeCpsI64HandlerFrame> = Vec::new();
for (handler, env) in pending {
if let Some(frame) = frames.iter_mut().find(|frame| frame.handler == handler) {
frame.envs.push(env);
} else {
frames.push(NativeCpsI64HandlerFrame {
handler,
consumes_mask: -1,
guard_stack: native_cps_i64_snapshot(current_native_i64_guard_stack()),
envs: vec![env],
prompt: 0,
install_eval_id: NATIVE_CPS_I64_SYNTHETIC_EVAL_ID,
escape_owner_function_id: 0,
threshold_owner_function_id: 0,
inherited: false,
escape_continuation: 0,
escape_env: Box::new([]),
escape_env_targets: Box::new([]),
return_frame_threshold: 0,
return_frame_prefix: native_cps_i64_snapshot(Vec::new()),
origin: NativeCpsI64HandlerFrameOrigin::PendingEnv,
});
}
}
frames
}
fn take_pending_native_i64_handler_envs(handler: i64) -> Vec<NativeCpsI64HandlerEnv> {
NATIVE_CPS_I64_PENDING_HANDLER_ENVS.with(|envs| {
let mut envs = envs.borrow_mut();
let mut selected = Vec::new();
let mut index = 0;
while index < envs.len() {
if envs[index].0 == handler {
selected.push(envs.remove(index).1);
} else {
index += 1;
}
}
selected
})
}
fn take_pending_native_i64_escape_env_targets() -> Vec<i64> {
NATIVE_CPS_I64_PENDING_ESCAPE_ENV_TARGETS
.with(|targets| std::mem::take(&mut *targets.borrow_mut()))
}
fn with_native_i64_cps_state<T>(
handlers: Vec<NativeCpsI64HandlerFrame>,
guard_stack: Vec<i64>,
run: impl FnOnce() -> T,
) -> T {
let active_blocked = NATIVE_CPS_I64_ACTIVE_BLOCKED.with(|stack| stack.borrow().clone());
with_native_i64_cps_state_and_active(handlers, guard_stack, active_blocked, run)
}
fn with_native_i64_cps_state_and_active<T>(
handlers: Vec<NativeCpsI64HandlerFrame>,
guard_stack: Vec<i64>,
active_blocked: Vec<NativeCpsI64BlockedEffect>,
run: impl FnOnce() -> T,
) -> T {
let previous_handlers = NATIVE_CPS_I64_HANDLER_STACK
.with(|stack| std::mem::replace(&mut *stack.borrow_mut(), handlers));
let previous_guards = NATIVE_CPS_I64_GUARD_STACK
.with(|stack| std::mem::replace(&mut *stack.borrow_mut(), guard_stack));
let previous_active = NATIVE_CPS_I64_ACTIVE_BLOCKED
.with(|stack| std::mem::replace(&mut *stack.borrow_mut(), active_blocked));
let result = run();
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| *stack.borrow_mut() = previous_handlers);
NATIVE_CPS_I64_GUARD_STACK.with(|stack| *stack.borrow_mut() = previous_guards);
NATIVE_CPS_I64_ACTIVE_BLOCKED.with(|stack| *stack.borrow_mut() = previous_active);
result
}
#[allow(dead_code)]
fn native_i64_handler_stack_with_captured(
captured: &[NativeCpsI64HandlerFrame],
) -> Vec<NativeCpsI64HandlerFrame> {
let mut handlers = NATIVE_CPS_I64_HANDLER_STACK.with(|stack| stack.borrow().clone());
handlers.extend(captured.iter().cloned());
handlers
}
#[allow(dead_code)]
fn native_i64_handler_stack_for_force(
captured: &[NativeCpsI64HandlerFrame],
) -> Vec<NativeCpsI64HandlerFrame> {
let mut handlers = native_i64_handler_stack_with_captured(captured);
handlers.extend(take_pending_native_i64_handler_frames());
handlers
}
fn next_native_i64_resumption_debug_id() -> u64 {
NATIVE_CPS_I64_NEXT_RESUMPTION_DEBUG_ID.with(|next| {
let id = *next.borrow();
*next.borrow_mut() = id + 1;
id
})
}
fn next_native_i64_return_frame_debug_id() -> u64 {
NATIVE_CPS_I64_NEXT_RETURN_FRAME_DEBUG_ID.with(|next| {
let id = *next.borrow();
*next.borrow_mut() = id + 1;
id
})
}
fn make_native_i64_resumption(
code: usize,
fallback_handler: i64,
env: Vec<i64>,
) -> *mut NativeCpsI64Resumption {
let code = unsafe { std::mem::transmute::<usize, NativeCpsI64Continuation>(code) };
let return_frames = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow().clone());
let handlers = current_native_i64_handler_stack_with_fallback(fallback_handler, -1);
let debug_id = next_native_i64_resumption_debug_id();
let ptr = Box::into_raw(Box::new(NativeCpsI64Resumption {
code,
env: env.into_boxed_slice(),
handlers: native_cps_i64_snapshot(handlers.clone()),
guard_stack: native_cps_i64_snapshot(current_native_i64_guard_stack()),
return_frames: native_cps_i64_snapshot(return_frames),
handled_anchor: None,
debug_id,
}));
NATIVE_CPS_I64_RESUMPTIONS.with(|s| {
s.borrow_mut().insert(ptr as usize);
});
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] make_resumption: id={} ptr={:#x} handlers={} frames={}",
debug_id,
ptr as usize,
format_handler_stack(&handlers),
format_return_frames(unsafe { &(*ptr).return_frames }),
);
}
ptr
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_set_resumption_anchor_from_selected_i64(
resumption: *mut NativeCpsI64Resumption,
) -> i64 {
let meta = NATIVE_CPS_I64_SELECTED_HANDLER_META_STACK.with(|m| m.borrow().last().cloned());
if let Some(meta) = meta {
if !meta.synthetic && meta.escape_continuation != 0 {
unsafe {
(*resumption).handled_anchor = Some(NativeCpsI64HandlerAnchor {
prompt: meta.prompt,
install_eval_id: meta.install_eval_id,
});
let start = native_i64_prompt_frame_start(
&(*resumption).return_frames,
meta.prompt,
meta.return_frame_threshold,
);
(*resumption).return_frames = capture_native_i64_return_frames_from_start(
&(*resumption).return_frames,
start,
meta.install_eval_id,
);
rebase_native_i64_captured_handlers(
Rc::make_mut(&mut (*resumption).handlers),
start,
meta.install_eval_id,
);
recalibrate_inherited_handler_thresholds(
Rc::make_mut(&mut (*resumption).handlers),
&(*resumption).return_frames,
meta.install_eval_id,
);
let slice_snapshot = (*resumption).return_frames.clone();
for frame in Rc::make_mut(&mut (*resumption).return_frames) {
recalibrate_inherited_handler_thresholds(
Rc::make_mut(&mut frame.handlers),
&slice_snapshot,
meta.install_eval_id,
);
}
}
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] resumption_anchor: prompt={} install_eval={} frames={}",
meta.prompt,
meta.install_eval_id,
unsafe { format_return_frames(&(*resumption).return_frames) }
);
}
}
}
0
}
fn make_native_i64_thunk(code: usize, env: Vec<i64>) -> usize {
let code = unsafe { std::mem::transmute::<usize, NativeCpsI64ThunkEntry>(code) };
let mut handlers = NATIVE_CPS_I64_HANDLER_STACK.with(|stack| stack.borrow().clone());
handlers.extend(take_pending_native_i64_handler_frames());
let ptr = Box::into_raw(Box::new(NativeCpsI64Thunk {
code,
env: env.into_boxed_slice(),
handlers: native_cps_i64_snapshot(handlers),
guard_stack: native_cps_i64_snapshot(current_native_i64_guard_stack()),
active_blocked: Box::new([]),
})) as usize;
NATIVE_CPS_I64_THUNKS.with(|thunks| {
thunks.borrow_mut().insert(ptr);
});
ptr
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_add_thunk_boundary_i64(
value: usize,
guard_id: i64,
allowed_mask: i64,
active: i64,
) -> usize {
let is_thunk = NATIVE_CPS_I64_THUNKS.with(|thunks| thunks.borrow().contains(&value));
if !is_thunk {
return value;
}
let thunk = unsafe { &*(value as *const NativeCpsI64Thunk) };
let mut active_blocked = thunk.active_blocked.to_vec();
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] add_thunk_boundary: thunk={:#x} guard={} allowed_mask={} active={} existing={}",
value,
guard_id,
allowed_mask,
active,
thunk.active_blocked.len(),
);
}
active_blocked.push(NativeCpsI64BlockedEffect {
guard_id,
allowed_mask,
active: active != 0,
});
let ptr = Box::into_raw(Box::new(NativeCpsI64Thunk {
code: thunk.code,
env: thunk.env.clone(),
handlers: thunk.handlers.clone(),
guard_stack: thunk.guard_stack.clone(),
active_blocked: active_blocked.into_boxed_slice(),
})) as usize;
NATIVE_CPS_I64_THUNKS.with(|thunks| {
thunks.borrow_mut().insert(ptr);
});
ptr
}
fn make_native_i64_closure(code: usize, env: Vec<i64>) -> usize {
let code = unsafe { std::mem::transmute::<usize, NativeCpsI64ClosureEntry>(code) };
let mut handlers = NATIVE_CPS_I64_HANDLER_STACK.with(|stack| stack.borrow().clone());
handlers.extend(take_pending_native_i64_handler_frames());
let ptr = Box::into_raw(Box::new(NativeCpsI64Closure {
code,
env: env.into_boxed_slice(),
handlers: native_cps_i64_snapshot(handlers),
guard_stack: native_cps_i64_snapshot(current_native_i64_guard_stack()),
})) as usize;
NATIVE_CPS_I64_CLOSURES.with(|closures| {
closures.borrow_mut().insert(ptr);
});
ptr
}
fn make_native_i64_recursive_closure(code: usize, self_slot: usize, mut env: Vec<i64>) -> usize {
let code = unsafe { std::mem::transmute::<usize, NativeCpsI64ClosureEntry>(code) };
let closure = Box::into_raw(Box::new(NativeCpsI64Closure {
code,
env: Vec::new().into_boxed_slice(),
handlers: native_cps_i64_snapshot(Vec::new()),
guard_stack: native_cps_i64_snapshot(Vec::new()),
}));
let ptr = closure as usize;
if self_slot < env.len() {
env[self_slot] = ptr as i64;
}
unsafe {
(*closure).env = env.into_boxed_slice();
let mut handlers = NATIVE_CPS_I64_HANDLER_STACK.with(|stack| stack.borrow().clone());
handlers.extend(take_pending_native_i64_handler_frames());
(*closure).handlers = native_cps_i64_snapshot(handlers);
(*closure).guard_stack = native_cps_i64_snapshot(current_native_i64_guard_stack());
}
NATIVE_CPS_I64_CLOSURES.with(|closures| {
closures.borrow_mut().insert(ptr);
});
ptr
}
fn make_native_i64_env(env: Vec<i64>) -> *const i64 {
Box::leak(env.into_boxed_slice()).as_ptr()
}
unsafe fn native_i64_slice(ptr: *const i64, len: i64) -> Vec<i64> {
let Ok(len) = usize::try_from(len) else {
return Vec::new();
};
if len == 0 {
return Vec::new();
}
if ptr.is_null() {
return Vec::new();
}
unsafe { std::slice::from_raw_parts(ptr, len) }.to_vec()
}
fn native_cps_i64_heap(value: NativeCpsI64HeapValue) -> i64 {
let pointer = Box::into_raw(Box::new(value)) as i64;
NATIVE_CPS_I64_HEAP_VALUES.with(|values| {
values.borrow_mut().insert(pointer);
});
pointer
}
fn native_cps_i64_variant(tag: &str, value: Option<i64>) -> i64 {
let hash = tag_hash(&typed_ir::Name(tag.to_string()));
register_native_i64_tag_name(hash, tag);
native_cps_i64_heap(NativeCpsI64HeapValue::Variant { tag: hash, value })
}
fn register_native_i64_tag_name(tag: i64, name: &str) {
NATIVE_CPS_I64_TAG_NAMES.with(|names| {
names
.borrow_mut()
.entry(tag)
.or_insert_with(|| name.to_string().into_boxed_str());
});
}
fn native_i64_tag_name(tag: i64) -> String {
NATIVE_CPS_I64_TAG_NAMES.with(|names| {
names
.borrow()
.get(&tag)
.map(|name| name.to_string())
.unwrap_or_else(|| format!("#{tag}"))
})
}
fn native_cps_i64_string_from_raw(ptr: *const u8, len: i64) -> Option<String> {
if ptr.is_null() || len < 0 {
return None;
}
let len = usize::try_from(len).ok()?;
let bytes = unsafe { std::slice::from_raw_parts(ptr, len) };
std::str::from_utf8(bytes).ok().map(str::to_string)
}
pub(super) fn describe_cps_repr_i64_value(value: i64) -> String {
describe_native_i64_value(value)
}
pub(super) fn describe_cps_repr_i64_value_with_hint(
value: i64,
hint: &CpsRootDisplayHint,
) -> String {
match hint {
CpsRootDisplayHint::Unit if value == 0 => "()".to_string(),
CpsRootDisplayHint::Bool => {
if value == 0 {
"false".to_string()
} else {
"true".to_string()
}
}
CpsRootDisplayHint::Tuple(item_hints) => {
describe_native_i64_tuple_with_hints(value, item_hints)
.unwrap_or_else(|| describe_cps_repr_i64_value(value))
}
CpsRootDisplayHint::List(item_hint) => describe_native_i64_list_with_hint(value, item_hint)
.unwrap_or_else(|| describe_cps_repr_i64_value(value)),
_ => describe_cps_repr_i64_value(value),
}
}
fn describe_native_i64_tuple_with_hints(
value: i64,
item_hints: &[CpsRootDisplayHint],
) -> Option<String> {
let is_heap = NATIVE_CPS_I64_HEAP_VALUES.with(|values| values.borrow().contains(&value));
if !is_heap {
return None;
}
let heap = unsafe { &*(value as *const NativeCpsI64HeapValue) };
let NativeCpsI64HeapValue::Tuple(items) = heap else {
return None;
};
let rendered = items
.iter()
.enumerate()
.map(|(index, item)| match item_hints.get(index) {
Some(hint) => describe_cps_repr_i64_value_with_hint(*item, hint),
None => describe_native_i64_value_with_depth(*item, 1),
})
.collect::<Vec<_>>();
Some(match rendered.as_slice() {
[] => "()".to_string(),
[single] => format!("({single},)"),
_ => format!("({})", rendered.join(", ")),
})
}
fn describe_native_i64_list_with_hint(
value: i64,
item_hint: &CpsRootDisplayHint,
) -> Option<String> {
let is_heap = NATIVE_CPS_I64_HEAP_VALUES.with(|values| values.borrow().contains(&value));
if !is_heap {
return None;
}
let heap = unsafe { &*(value as *const NativeCpsI64HeapValue) };
let NativeCpsI64HeapValue::List(items) = heap else {
return None;
};
Some(format!(
"[{}]",
items
.to_vec()
.iter()
.map(|item| describe_cps_repr_i64_value_with_hint(*item, item_hint))
.collect::<Vec<_>>()
.join(", ")
))
}
pub(super) fn describe_native_i64_value(value: i64) -> String {
describe_native_i64_value_with_depth(value, 0)
}
fn describe_native_i64_debug_value(value: i64) -> String {
describe_native_i64_debug_value_with_depth(value, 0)
}
fn describe_native_i64_value_with_depth(value: i64, depth: usize) -> String {
if depth > 32 {
return "...".to_string();
}
let resumption_id = NATIVE_CPS_I64_RESUMPTIONS.with(|resumptions| {
if resumptions.borrow().contains(&(value as usize)) {
Some(unsafe { (*(value as *const NativeCpsI64Resumption)).debug_id })
} else {
None
}
});
if let Some(id) = resumption_id {
return format!("resumption#{id}@{value:#x}");
}
let is_thunk = NATIVE_CPS_I64_THUNKS.with(|thunks| thunks.borrow().contains(&(value as usize)));
if is_thunk {
return format!("thunk@{value:#x}");
}
let is_closure =
NATIVE_CPS_I64_CLOSURES.with(|closures| closures.borrow().contains(&(value as usize)));
if is_closure {
return format!("closure@{value:#x}");
}
let is_heap = NATIVE_CPS_I64_HEAP_VALUES.with(|values| values.borrow().contains(&value));
if !is_heap {
return value.to_string();
}
let heap = unsafe { &*(value as *const NativeCpsI64HeapValue) };
match heap {
NativeCpsI64HeapValue::Tuple(items) => {
let items = items
.iter()
.map(|item| describe_native_i64_value_with_depth(*item, depth + 1))
.collect::<Vec<_>>();
match items.as_slice() {
[] => "()".to_string(),
[single] => format!("({single},)"),
_ => format!("({})", items.join(", ")),
}
}
NativeCpsI64HeapValue::Record(fields) => format!(
"{{ {} }}",
fields
.iter()
.map(|(name, value)| format!(
"{name}: {}",
describe_native_i64_value_with_depth(*value, depth + 1)
))
.collect::<Vec<_>>()
.join(", ")
),
NativeCpsI64HeapValue::Variant { tag, value: None } => {
format!(":{}", native_i64_tag_name(*tag))
}
NativeCpsI64HeapValue::Variant {
tag,
value: Some(payload),
} => format!(
":{} {}",
native_i64_tag_name(*tag),
describe_native_i64_value_with_depth(*payload, depth + 1)
),
NativeCpsI64HeapValue::List(items) => format!(
"[{}]",
items
.to_vec()
.iter()
.map(|item| describe_native_i64_value_with_depth(*item, depth + 1))
.collect::<Vec<_>>()
.join(", ")
),
NativeCpsI64HeapValue::Bool(value) => value.to_string(),
NativeCpsI64HeapValue::Unit => "()".to_string(),
NativeCpsI64HeapValue::Float(value) => native_cps_format_float(*value),
NativeCpsI64HeapValue::String(text) => text.to_flat_string(),
NativeCpsI64HeapValue::Bytes(value) => format!("<bytes len={}>", value.len()),
NativeCpsI64HeapValue::Path(value) => value.display().to_string(),
}
}
fn describe_native_i64_debug_value_with_depth(value: i64, depth: usize) -> String {
if depth > 32 {
return "...".to_string();
}
let is_heap = NATIVE_CPS_I64_HEAP_VALUES.with(|values| values.borrow().contains(&value));
if !is_heap {
return value.to_string();
}
let heap = unsafe { &*(value as *const NativeCpsI64HeapValue) };
match heap {
NativeCpsI64HeapValue::Tuple(items) => {
let items = items
.iter()
.map(|item| describe_native_i64_debug_value_with_depth(*item, depth + 1))
.collect::<Vec<_>>();
match items.as_slice() {
[] => "()".to_string(),
[single] => format!("({single},)"),
_ => format!("({})", items.join(", ")),
}
}
NativeCpsI64HeapValue::Record(fields) => format!(
"{{{}}}",
fields
.iter()
.map(|(name, value)| format!(
"{name}: {}",
describe_native_i64_debug_value_with_depth(*value, depth + 1)
))
.collect::<Vec<_>>()
.join(", ")
),
NativeCpsI64HeapValue::Variant { tag, value: None } => native_i64_tag_name(*tag),
NativeCpsI64HeapValue::Variant {
tag,
value: Some(payload),
} => format!(
"{} {}",
native_i64_tag_name(*tag),
describe_native_i64_debug_value_with_depth(*payload, depth + 1)
),
NativeCpsI64HeapValue::List(items) => format!(
"[{}]",
items
.to_vec()
.iter()
.map(|item| describe_native_i64_debug_value_with_depth(*item, depth + 1))
.collect::<Vec<_>>()
.join(", ")
),
NativeCpsI64HeapValue::Bool(value) => value.to_string(),
NativeCpsI64HeapValue::Unit => "()".to_string(),
NativeCpsI64HeapValue::Float(value) => native_cps_format_float(*value),
NativeCpsI64HeapValue::String(text) => format!("{:?}", text.to_flat_string()),
NativeCpsI64HeapValue::Bytes(value) => format!("<bytes len={}>", value.len()),
NativeCpsI64HeapValue::Path(value) => format!("{:?}", value.display().to_string()),
}
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_resumption_i64_0(
code: usize,
handler: i64,
) -> *mut NativeCpsI64Resumption {
make_native_i64_resumption(code, handler, Vec::new())
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_resumption_i64_1(
code: usize,
handler: i64,
a: i64,
) -> *mut NativeCpsI64Resumption {
make_native_i64_resumption(code, handler, vec![a])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_resumption_i64_2(
code: usize,
handler: i64,
a: i64,
b: i64,
) -> *mut NativeCpsI64Resumption {
make_native_i64_resumption(code, handler, vec![a, b])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_resumption_i64_3(
code: usize,
handler: i64,
a: i64,
b: i64,
c: i64,
) -> *mut NativeCpsI64Resumption {
make_native_i64_resumption(code, handler, vec![a, b, c])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_resumption_i64_4(
code: usize,
handler: i64,
a: i64,
b: i64,
c: i64,
d: i64,
) -> *mut NativeCpsI64Resumption {
make_native_i64_resumption(code, handler, vec![a, b, c, d])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_resumption_i64_many(
code: usize,
handler: i64,
ptr: *const i64,
len: i64,
) -> *mut NativeCpsI64Resumption {
make_native_i64_resumption(code, handler, unsafe { native_i64_slice(ptr, len) })
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_env_i64_0() -> *const i64 {
make_native_i64_env(Vec::new())
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_env_i64_1(a: i64) -> *const i64 {
make_native_i64_env(vec![a])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_env_i64_2(a: i64, b: i64) -> *const i64 {
make_native_i64_env(vec![a, b])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_env_i64_3(a: i64, b: i64, c: i64) -> *const i64 {
make_native_i64_env(vec![a, b, c])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_env_i64_4(a: i64, b: i64, c: i64, d: i64) -> *const i64 {
make_native_i64_env(vec![a, b, c, d])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_env_i64_many(ptr: *const i64, len: i64) -> *const i64 {
make_native_i64_env(unsafe { native_i64_slice(ptr, len) })
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_closure_i64_0(code: usize) -> usize {
make_native_i64_closure(code, Vec::new())
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_closure_i64_1(code: usize, a: i64) -> usize {
make_native_i64_closure(code, vec![a])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_closure_i64_2(code: usize, a: i64, b: i64) -> usize {
make_native_i64_closure(code, vec![a, b])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_closure_i64_3(
code: usize,
a: i64,
b: i64,
c: i64,
) -> usize {
make_native_i64_closure(code, vec![a, b, c])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_closure_i64_4(
code: usize,
a: i64,
b: i64,
c: i64,
d: i64,
) -> usize {
make_native_i64_closure(code, vec![a, b, c, d])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_closure_i64_many(
code: usize,
ptr: *const i64,
len: i64,
) -> usize {
make_native_i64_closure(code, unsafe { native_i64_slice(ptr, len) })
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_recursive_closure_i64_0(
code: usize,
self_slot: i64,
) -> usize {
make_native_i64_recursive_closure(code, self_slot as usize, Vec::new())
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_recursive_closure_i64_1(
code: usize,
self_slot: i64,
a: i64,
) -> usize {
make_native_i64_recursive_closure(code, self_slot as usize, vec![a])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_recursive_closure_i64_2(
code: usize,
self_slot: i64,
a: i64,
b: i64,
) -> usize {
make_native_i64_recursive_closure(code, self_slot as usize, vec![a, b])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_recursive_closure_i64_3(
code: usize,
self_slot: i64,
a: i64,
b: i64,
c: i64,
) -> usize {
make_native_i64_recursive_closure(code, self_slot as usize, vec![a, b, c])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_recursive_closure_i64_4(
code: usize,
self_slot: i64,
a: i64,
b: i64,
c: i64,
d: i64,
) -> usize {
make_native_i64_recursive_closure(code, self_slot as usize, vec![a, b, c, d])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_recursive_closure_i64_many(
code: usize,
self_slot: i64,
ptr: *const i64,
len: i64,
) -> usize {
make_native_i64_recursive_closure(code, self_slot as usize, unsafe {
native_i64_slice(ptr, len)
})
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_apply_closure_i64(value: usize, arg: i64) -> i64 {
let is_resumption = NATIVE_CPS_I64_RESUMPTIONS.with(|s| s.borrow().contains(&value));
if is_resumption {
let result = yulang_cps_resume_i64(value as *const NativeCpsI64Resumption, arg);
return yulang_cps_force_thunk_i64(result as usize);
}
let closure = unsafe { &*(value as *const NativeCpsI64Closure) };
if jit_trace_enabled() {
let frames = NATIVE_CPS_I64_RETURN_FRAMES.with(|f| f.borrow().clone());
let eval = NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| *ctx.borrow());
eprintln!(
"[JIT-CPS] apply_closure: closure={:#x} arg={} eval={} initial={} frames={}",
value,
describe_native_i64_value(arg),
eval.current_eval_id,
eval.initial_frame_count,
format_return_frames(&frames),
);
}
let result = (closure.code)(closure.env.as_ptr(), arg);
if jit_trace_enabled() {
let frames = NATIVE_CPS_I64_RETURN_FRAMES.with(|f| f.borrow().clone());
let eval = NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| *ctx.borrow());
eprintln!(
"[JIT-CPS] apply_closure.out: closure={:#x} result={} eval={} initial={} frames={}",
value,
describe_native_i64_value(result),
eval.current_eval_id,
eval.initial_frame_count,
format_return_frames(&frames),
);
}
yulang_cps_force_thunk_i64(result as usize)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_tuple_i64_0() -> i64 {
native_cps_i64_heap(NativeCpsI64HeapValue::Tuple(Vec::new().into_boxed_slice()))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_tuple_i64_1(a: i64) -> i64 {
native_cps_i64_heap(NativeCpsI64HeapValue::Tuple(vec![a].into_boxed_slice()))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_tuple_i64_2(a: i64, b: i64) -> i64 {
native_cps_i64_heap(NativeCpsI64HeapValue::Tuple(vec![a, b].into_boxed_slice()))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_tuple_i64_3(a: i64, b: i64, c: i64) -> i64 {
native_cps_i64_heap(NativeCpsI64HeapValue::Tuple(
vec![a, b, c].into_boxed_slice(),
))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_tuple_i64_4(a: i64, b: i64, c: i64, d: i64) -> i64 {
native_cps_i64_heap(NativeCpsI64HeapValue::Tuple(
vec![a, b, c, d].into_boxed_slice(),
))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_tuple_get_i64(value: i64, index: i64) -> i64 {
let value = unsafe { &*(value as *const NativeCpsI64HeapValue) };
let NativeCpsI64HeapValue::Tuple(items) = value else {
return 0;
};
items.get(index as usize).copied().unwrap_or(0)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_record_empty_i64() -> i64 {
native_cps_i64_heap(NativeCpsI64HeapValue::Record(Vec::new()))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_record_insert_i64(
record: i64,
field_ptr: *const u8,
field_len: i64,
value: i64,
) -> i64 {
let Some(field) = native_cps_i64_string_from_raw(field_ptr, field_len) else {
return record;
};
let record = unsafe { &*(record as *const NativeCpsI64HeapValue) };
let NativeCpsI64HeapValue::Record(fields) = record else {
return yulang_cps_record_empty_i64();
};
let mut fields = fields.clone();
if let Some((_, slot)) = fields
.iter_mut()
.find(|(existing, _)| existing.as_ref() == field.as_str())
{
*slot = value;
} else {
fields.push((field.into_boxed_str(), value));
}
native_cps_i64_heap(NativeCpsI64HeapValue::Record(fields))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_record_select_i64(
record: i64,
field_ptr: *const u8,
field_len: i64,
) -> i64 {
let Some(field) = native_cps_i64_string_from_raw(field_ptr, field_len) else {
return 0;
};
let record = unsafe { &*(record as *const NativeCpsI64HeapValue) };
let NativeCpsI64HeapValue::Record(fields) = record else {
return 0;
};
fields
.iter()
.find_map(|(name, value)| (name.as_ref() == field.as_str()).then_some(*value))
.unwrap_or(0)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_record_select_or_default_i64(
record: i64,
field_ptr: *const u8,
field_len: i64,
default: i64,
) -> i64 {
let Some(field) = native_cps_i64_string_from_raw(field_ptr, field_len) else {
return default;
};
let record = unsafe { &*(record as *const NativeCpsI64HeapValue) };
let NativeCpsI64HeapValue::Record(fields) = record else {
return default;
};
fields
.iter()
.find_map(|(name, value)| (name.as_ref() == field.as_str()).then_some(*value))
.unwrap_or(default)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_record_has_field_i64(
record: i64,
field_ptr: *const u8,
field_len: i64,
) -> i64 {
let Some(field) = native_cps_i64_string_from_raw(field_ptr, field_len) else {
return 0;
};
let record = unsafe { &*(record as *const NativeCpsI64HeapValue) };
let NativeCpsI64HeapValue::Record(fields) = record else {
return 0;
};
fields
.iter()
.any(|(name, _)| name.as_ref() == field.as_str()) as i64
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_record_without_field_i64(
record: i64,
field_ptr: *const u8,
field_len: i64,
) -> i64 {
let Some(field) = native_cps_i64_string_from_raw(field_ptr, field_len) else {
return record;
};
let record = unsafe { &*(record as *const NativeCpsI64HeapValue) };
let NativeCpsI64HeapValue::Record(fields) = record else {
return yulang_cps_record_empty_i64();
};
let fields = fields
.iter()
.filter(|(name, _)| name.as_ref() != field.as_str())
.cloned()
.collect();
native_cps_i64_heap(NativeCpsI64HeapValue::Record(fields))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_variant_i64_0(tag: i64) -> i64 {
let result = native_cps_i64_heap(NativeCpsI64HeapValue::Variant { tag, value: None });
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] variant_new: tag={} payload=none result={}",
native_i64_tag_name(tag),
describe_native_i64_value(result)
);
}
result
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_variant_i64_1(tag: i64, value: i64) -> i64 {
let result = native_cps_i64_heap(NativeCpsI64HeapValue::Variant {
tag,
value: Some(value),
});
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] variant_new: tag={} payload={} result={}",
native_i64_tag_name(tag),
describe_native_i64_value(value),
describe_native_i64_value(result)
);
}
result
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_variant_tag_eq_i64(value: i64, tag: i64) -> i64 {
let value = unsafe { &*(value as *const NativeCpsI64HeapValue) };
let result = match value {
NativeCpsI64HeapValue::Variant { tag: actual, .. } => i64::from(*actual == tag),
_ => 0,
};
if jit_trace_enabled() {
let actual = match value {
NativeCpsI64HeapValue::Variant { tag: actual, .. } => native_i64_tag_name(*actual),
_ => "non_variant".to_string(),
};
eprintln!(
"[JIT-CPS] variant_tag_eq: expected={} actual={} result={}",
native_i64_tag_name(tag),
actual,
result
);
}
result
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_variant_payload_i64(value: i64) -> i64 {
let value = unsafe { &*(value as *const NativeCpsI64HeapValue) };
let result = match value {
NativeCpsI64HeapValue::Variant {
value: Some(value), ..
} => *value,
_ => 0,
};
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] variant_payload: payload={}",
describe_native_i64_value(result)
);
}
result
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_register_tag_i64(tag: i64, ptr: *const u8, len: i64) -> i64 {
if ptr.is_null() || len < 0 {
return 0;
}
let Ok(len) = usize::try_from(len) else {
return 0;
};
let bytes = unsafe { std::slice::from_raw_parts(ptr, len) };
let Ok(name) = std::str::from_utf8(bytes) else {
return 0;
};
register_native_i64_tag_name(tag, name);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_print_i64(value: i64) {
print!("{}", describe_native_i64_value(value));
let mut stdout = std::io::stdout();
let _ = std::io::Write::flush(&mut stdout);
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_print_debug_i64(value: i64) {
print!("{}", describe_native_i64_debug_value(value));
let mut stdout = std::io::stdout();
let _ = std::io::Write::flush(&mut stdout);
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_debug_i64(value: i64) -> i64 {
native_cps_i64_string_heap(&describe_native_i64_debug_value(value))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_out_write_i64(value: i64) -> i64 {
print!("{}", describe_native_i64_value(value));
let mut stdout = std::io::stdout();
let _ = std::io::Write::flush(&mut stdout);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_err_write_i64(value: i64) -> i64 {
eprint!("{}", describe_native_i64_value(value));
let mut stderr = std::io::stderr();
let _ = std::io::Write::flush(&mut stderr);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_warn_write_i64(value: i64) -> i64 {
eprintln!("warning: {}", describe_native_i64_value(value));
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_die_i64(value: i64) -> i64 {
eprintln!("die: {}", describe_native_i64_value(value));
std::process::exit(1);
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_list_empty_i64() -> i64 {
let result = native_cps_i64_heap(NativeCpsI64HeapValue::List(ListTree::empty()));
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] list_empty: result={}",
describe_native_i64_value(result)
);
}
result
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_list_singleton_i64(value: i64) -> i64 {
let result = native_cps_i64_heap(NativeCpsI64HeapValue::List(ListTree::singleton(value)));
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] list_singleton: item={} result={}",
describe_native_i64_value(value),
describe_native_i64_value(result)
);
}
result
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_list_merge_i64(left: i64, right: i64) -> i64 {
let left = unsafe { &*(left as *const NativeCpsI64HeapValue) };
let right = unsafe { &*(right as *const NativeCpsI64HeapValue) };
let (NativeCpsI64HeapValue::List(left), NativeCpsI64HeapValue::List(right)) = (left, right)
else {
return yulang_cps_list_empty_i64();
};
let merged = ListTree::concat(left.clone(), right.clone());
let result = native_cps_i64_heap(NativeCpsI64HeapValue::List(merged));
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] list_merge: left_len={} right_len={} result={}",
left.len(),
right.len(),
describe_native_i64_value(result)
);
}
result
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_list_len_i64(value: i64) -> i64 {
let value = unsafe { &*(value as *const NativeCpsI64HeapValue) };
match value {
NativeCpsI64HeapValue::List(items) => items.len() as i64,
_ => 0,
}
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_list_index_i64(value: i64, index: i64) -> i64 {
let value = unsafe { &*(value as *const NativeCpsI64HeapValue) };
let NativeCpsI64HeapValue::List(items) = value else {
return 0;
};
let Ok(index) = usize::try_from(index) else {
return 0;
};
items.index(index).unwrap_or(0)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_list_index_range_raw_i64(
value: i64,
start: i64,
end: i64,
) -> i64 {
let value = unsafe { &*(value as *const NativeCpsI64HeapValue) };
let NativeCpsI64HeapValue::List(items) = value else {
return yulang_cps_list_empty_i64();
};
let Ok(start) = usize::try_from(start) else {
return yulang_cps_list_empty_i64();
};
let Ok(end) = usize::try_from(end) else {
return yulang_cps_list_empty_i64();
};
if start > end || end > items.len() {
return yulang_cps_list_empty_i64();
}
let Some(range) = items.index_range(start, end) else {
return yulang_cps_list_empty_i64();
};
native_cps_i64_heap(NativeCpsI64HeapValue::List(range))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_list_index_range_i64(value: i64, range: i64) -> i64 {
let value = unsafe { &*(value as *const NativeCpsI64HeapValue) };
let NativeCpsI64HeapValue::List(items) = value else {
return yulang_cps_list_empty_i64();
};
let Some((start, end)) = native_cps_i64_normalized_int_range(range, items.len()) else {
return yulang_cps_list_empty_i64();
};
let Some(range) = items.index_range(start, end) else {
return yulang_cps_list_empty_i64();
};
native_cps_i64_heap(NativeCpsI64HeapValue::List(range))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_list_splice_raw_i64(
value: i64,
start: i64,
end: i64,
insert: i64,
) -> i64 {
let value = unsafe { &*(value as *const NativeCpsI64HeapValue) };
let insert = unsafe { &*(insert as *const NativeCpsI64HeapValue) };
let (NativeCpsI64HeapValue::List(items), NativeCpsI64HeapValue::List(insert)) = (value, insert)
else {
return yulang_cps_list_empty_i64();
};
let Ok(start) = usize::try_from(start) else {
return yulang_cps_list_empty_i64();
};
let Ok(end) = usize::try_from(end) else {
return yulang_cps_list_empty_i64();
};
if start > end || end > items.len() {
return yulang_cps_list_empty_i64();
}
let Some(result) = items.splice(start, end, insert.clone()) else {
return yulang_cps_list_empty_i64();
};
native_cps_i64_heap(NativeCpsI64HeapValue::List(result))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_list_splice_i64(value: i64, range: i64, insert: i64) -> i64 {
let value = unsafe { &*(value as *const NativeCpsI64HeapValue) };
let insert = unsafe { &*(insert as *const NativeCpsI64HeapValue) };
let (NativeCpsI64HeapValue::List(items), NativeCpsI64HeapValue::List(insert)) = (value, insert)
else {
return yulang_cps_list_empty_i64();
};
let Some((start, end)) = native_cps_i64_normalized_int_range(range, items.len()) else {
return yulang_cps_list_empty_i64();
};
let Some(result) = items.splice(start, end, insert.clone()) else {
return yulang_cps_list_empty_i64();
};
native_cps_i64_heap(NativeCpsI64HeapValue::List(result))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_list_view_raw_i64(value: i64) -> i64 {
let value = unsafe { &*(value as *const NativeCpsI64HeapValue) };
let NativeCpsI64HeapValue::List(items) = value else {
return native_cps_i64_variant("empty", None);
};
match items.view() {
ListView::Empty => {
let result = native_cps_i64_variant("empty", None);
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] list_view: len=0 result={}",
describe_native_i64_value(result)
);
}
result
}
ListView::Leaf(head) => {
let result = native_cps_i64_variant("leaf", Some(head));
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] list_view: len=1 head={} result={}",
describe_native_i64_value(head),
describe_native_i64_value(result)
);
}
result
}
ListView::Node {
len, left, right, ..
} => {
let left = native_cps_i64_heap(NativeCpsI64HeapValue::List(left));
let right = native_cps_i64_heap(NativeCpsI64HeapValue::List(right));
let tuple = native_cps_i64_heap(NativeCpsI64HeapValue::Tuple(
vec![left, right].into_boxed_slice(),
));
let result = native_cps_i64_variant("node", Some(tuple));
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] list_view: len={} left={} right={} result={}",
len,
describe_native_i64_value(left),
describe_native_i64_value(right),
describe_native_i64_value(result)
);
}
result
}
}
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_int_to_string_i64(value: i64) -> i64 {
native_cps_i64_string_heap(&value.to_string())
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_int_to_hex_i64(value: i64) -> i64 {
native_cps_i64_string_heap(&format!("{value:x}"))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_int_to_upper_hex_i64(value: i64) -> i64 {
native_cps_i64_string_heap(&format!("{value:X}"))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_bool_to_string_i64(value: i64) -> i64 {
let text = if value == 0 { "false" } else { "true" };
native_cps_i64_string_heap(text)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_box_bool_i64(value: i64) -> i64 {
native_cps_i64_heap(NativeCpsI64HeapValue::Bool(value != 0))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_unit_i64() -> i64 {
native_cps_i64_heap(NativeCpsI64HeapValue::Unit)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_float_to_string_f64(value: f64) -> i64 {
native_cps_i64_string_heap(&native_cps_format_float(value))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_box_float_f64(value: f64) -> i64 {
native_cps_i64_heap(NativeCpsI64HeapValue::Float(value))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_unbox_float_i64(value: i64) -> f64 {
let Some(NativeCpsI64HeapValue::Float(value)) = native_cps_i64_heap_value(value) else {
return value as f64;
};
*value
}
fn native_cps_format_float(value: f64) -> String {
let mut rendered = value.to_string();
if !rendered.contains('.') && !rendered.contains('e') && !rendered.contains('E') {
rendered.push_str(".0");
}
rendered
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_string_literal_i64(ptr: *const u8, len: i64) -> i64 {
let Some(text) = native_cps_i64_string_from_bytes(ptr, len) else {
return native_cps_i64_string_empty();
};
native_cps_i64_string_heap(&text)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_string_concat_i64(left: i64, right: i64) -> i64 {
let Some(left) = native_cps_i64_string_value(left) else {
return native_cps_i64_string_empty();
};
let Some(right) = native_cps_i64_string_value(right) else {
return native_cps_i64_string_empty();
};
native_cps_i64_heap(NativeCpsI64HeapValue::String(StringTree::concat(
left.clone(),
right.clone(),
)))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_string_eq_i64(left: i64, right: i64) -> i64 {
let Some(left) = native_cps_i64_string_value(left) else {
return 0;
};
let Some(right) = native_cps_i64_string_value(right) else {
return 0;
};
i64::from(left.to_flat_string() == right.to_flat_string())
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_string_len_i64(value: i64) -> i64 {
native_cps_i64_string_value(value)
.map(|text| text.len() as i64)
.unwrap_or(0)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_string_index_i64(value: i64, index: i64) -> i64 {
let Some(text) = native_cps_i64_string_value(value) else {
return native_cps_i64_string_empty();
};
let Ok(index) = usize::try_from(index) else {
return native_cps_i64_string_empty();
};
let Some(ch) = text.index(index) else {
return native_cps_i64_string_empty();
};
native_cps_i64_string_heap(&ch.to_string())
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_string_index_range_raw_i64(
value: i64,
start: i64,
end: i64,
) -> i64 {
let Some(text) = native_cps_i64_string_value(value) else {
return native_cps_i64_string_empty();
};
let Some((start, end)) = native_cps_i64_string_index_range(text, start, end) else {
return native_cps_i64_string_empty();
};
let Some(slice) = text.index_range(start, end) else {
return native_cps_i64_string_empty();
};
native_cps_i64_heap(NativeCpsI64HeapValue::String(slice))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_string_index_range_i64(value: i64, range: i64) -> i64 {
let Some(text) = native_cps_i64_string_value(value) else {
return native_cps_i64_string_empty();
};
let Some((start, end)) = native_cps_i64_normalized_int_range(range, text.len()) else {
return native_cps_i64_string_empty();
};
let Some(slice) = text.index_range(start, end) else {
return native_cps_i64_string_empty();
};
native_cps_i64_heap(NativeCpsI64HeapValue::String(slice))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_string_splice_raw_i64(
value: i64,
start: i64,
end: i64,
insert: i64,
) -> i64 {
let Some(text) = native_cps_i64_string_value(value) else {
return native_cps_i64_string_empty();
};
let Some(insert) = native_cps_i64_string_value(insert) else {
return native_cps_i64_string_empty();
};
let Some((start, end)) = native_cps_i64_string_index_range(text, start, end) else {
return native_cps_i64_string_empty();
};
let Some(result) = text.splice(start, end, insert.clone()) else {
return native_cps_i64_string_empty();
};
native_cps_i64_heap(NativeCpsI64HeapValue::String(result))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_string_splice_i64(value: i64, range: i64, insert: i64) -> i64 {
let Some(text) = native_cps_i64_string_value(value) else {
return native_cps_i64_string_empty();
};
let Some(insert) = native_cps_i64_string_value(insert) else {
return native_cps_i64_string_empty();
};
let Some((start, end)) = native_cps_i64_normalized_int_range(range, text.len()) else {
return native_cps_i64_string_empty();
};
let Some(result) = text.splice(start, end, insert.clone()) else {
return native_cps_i64_string_empty();
};
native_cps_i64_heap(NativeCpsI64HeapValue::String(result))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_string_to_bytes_i64(value: i64) -> i64 {
let Some(text) = native_cps_i64_string_value(value) else {
return native_cps_i64_heap(NativeCpsI64HeapValue::Bytes(BytesTree::empty()));
};
native_cps_i64_heap(NativeCpsI64HeapValue::Bytes(BytesTree::from_bytes(
text.to_flat_string().as_bytes(),
)))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_bytes_len_i64(value: i64) -> i64 {
native_cps_i64_bytes_value(value)
.map(|value| value.len() as i64)
.unwrap_or(0)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_bytes_eq_i64(left: i64, right: i64) -> i64 {
let Some(left) = native_cps_i64_bytes_value(left) else {
return 0;
};
let Some(right) = native_cps_i64_bytes_value(right) else {
return 0;
};
i64::from(left.to_flat_vec() == right.to_flat_vec())
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_bytes_concat_i64(left: i64, right: i64) -> i64 {
let Some(left) = native_cps_i64_bytes_value(left) else {
return native_cps_i64_heap(NativeCpsI64HeapValue::Bytes(BytesTree::empty()));
};
let Some(right) = native_cps_i64_bytes_value(right) else {
return native_cps_i64_heap(NativeCpsI64HeapValue::Bytes(BytesTree::empty()));
};
native_cps_i64_heap(NativeCpsI64HeapValue::Bytes(BytesTree::concat(
left.clone(),
right.clone(),
)))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_bytes_index_i64(value: i64, index: i64) -> i64 {
let Some(value) = native_cps_i64_bytes_value(value) else {
return 0;
};
let Ok(index) = usize::try_from(index) else {
return 0;
};
value.index(index).map(i64::from).unwrap_or(0)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_bytes_index_range_i64(value: i64, range: i64) -> i64 {
let Some(value) = native_cps_i64_bytes_value(value) else {
return native_cps_i64_heap(NativeCpsI64HeapValue::Bytes(BytesTree::empty()));
};
let Some((start, end)) = native_cps_i64_normalized_int_range(range, value.len()) else {
return native_cps_i64_heap(NativeCpsI64HeapValue::Bytes(BytesTree::empty()));
};
native_cps_i64_heap(NativeCpsI64HeapValue::Bytes(
value
.index_range(start, end)
.unwrap_or_else(BytesTree::empty),
))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_bytes_to_utf8_raw_i64(value: i64) -> i64 {
let Some(value) = native_cps_i64_bytes_value(value) else {
return native_cps_i64_utf8_raw_result("", 0);
};
let bytes = value.to_flat_vec();
match std::str::from_utf8(&bytes) {
Ok(text) => native_cps_i64_utf8_raw_result(text, bytes.len()),
Err(error) => {
let valid = error.valid_up_to();
let text = std::str::from_utf8(&bytes[..valid]).unwrap_or("");
native_cps_i64_utf8_raw_result(text, valid)
}
}
}
fn native_cps_i64_utf8_raw_result(text: &str, valid: usize) -> i64 {
native_cps_i64_heap(NativeCpsI64HeapValue::Tuple(Box::new([
native_cps_i64_string_heap(text),
valid as i64,
])))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_bytes_to_path_i64(value: i64) -> i64 {
let Some(value) = native_cps_i64_bytes_value(value) else {
return native_cps_i64_heap(NativeCpsI64HeapValue::Path(path_buf_from_bytes(&[])));
};
native_cps_i64_heap(NativeCpsI64HeapValue::Path(path_buf_from_bytes(
&value.to_flat_vec(),
)))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_path_to_bytes_i64(value: i64) -> i64 {
let Some(value) = native_cps_i64_path_value(value) else {
return native_cps_i64_heap(NativeCpsI64HeapValue::Bytes(BytesTree::empty()));
};
native_cps_i64_heap(NativeCpsI64HeapValue::Bytes(BytesTree::from_bytes(
&path_buf_bytes(value),
)))
}
fn native_cps_i64_string_from_bytes(ptr: *const u8, len: i64) -> Option<String> {
if ptr.is_null() || len < 0 {
return None;
}
let len = usize::try_from(len).ok()?;
let bytes = unsafe { std::slice::from_raw_parts(ptr, len) };
std::str::from_utf8(bytes).ok().map(str::to_string)
}
fn native_cps_i64_string_heap(text: &str) -> i64 {
native_cps_i64_heap(NativeCpsI64HeapValue::String(StringTree::from_str(text)))
}
fn native_cps_i64_string_empty() -> i64 {
native_cps_i64_heap(NativeCpsI64HeapValue::String(StringTree::empty()))
}
fn native_cps_i64_string_value(value: i64) -> Option<&'static StringTree<Box<str>>> {
let is_heap = NATIVE_CPS_I64_HEAP_VALUES.with(|values| values.borrow().contains(&value));
if !is_heap {
return None;
}
let value = unsafe { &*(value as *const NativeCpsI64HeapValue) };
match value {
NativeCpsI64HeapValue::String(text) => Some(text),
_ => None,
}
}
fn native_cps_i64_bytes_value(value: i64) -> Option<&'static BytesTree> {
let is_heap = NATIVE_CPS_I64_HEAP_VALUES.with(|values| values.borrow().contains(&value));
if !is_heap {
return None;
}
let value = unsafe { &*(value as *const NativeCpsI64HeapValue) };
match value {
NativeCpsI64HeapValue::Bytes(value) => Some(value),
_ => None,
}
}
fn native_cps_i64_path_value(value: i64) -> Option<&'static std::path::PathBuf> {
let is_heap = NATIVE_CPS_I64_HEAP_VALUES.with(|values| values.borrow().contains(&value));
if !is_heap {
return None;
}
let value = unsafe { &*(value as *const NativeCpsI64HeapValue) };
match value {
NativeCpsI64HeapValue::Path(value) => Some(value),
_ => None,
}
}
#[cfg(unix)]
fn path_buf_from_bytes(bytes: &[u8]) -> std::path::PathBuf {
use std::ffi::OsString;
use std::os::unix::ffi::OsStringExt;
std::path::PathBuf::from(OsString::from_vec(bytes.to_vec()))
}
#[cfg(not(unix))]
fn path_buf_from_bytes(bytes: &[u8]) -> std::path::PathBuf {
std::path::PathBuf::from(String::from_utf8_lossy(bytes).into_owned())
}
#[cfg(unix)]
fn path_buf_bytes(path: &std::path::PathBuf) -> Vec<u8> {
use std::os::unix::ffi::OsStrExt;
path.as_os_str().as_bytes().to_vec()
}
#[cfg(not(unix))]
fn path_buf_bytes(path: &std::path::PathBuf) -> Vec<u8> {
path.to_string_lossy().as_bytes().to_vec()
}
fn native_cps_i64_string_index_range(
text: &StringTree<Box<str>>,
start: i64,
end: i64,
) -> Option<(usize, usize)> {
let start = usize::try_from(start).ok()?;
let end = usize::try_from(end).ok()?;
if start > end || end > text.len() {
return None;
}
Some((start, end))
}
fn native_cps_i64_normalized_int_range(value: i64, len: usize) -> Option<(usize, usize)> {
let value = native_cps_i64_heap_value(value)?;
let NativeCpsI64HeapValue::Variant {
tag,
value: Some(payload),
} = value
else {
return None;
};
if *tag != tag_hash(&typed_ir::Name("within".to_string())) {
return None;
}
let NativeCpsI64HeapValue::Tuple(items) = native_cps_i64_heap_value(*payload)? else {
return None;
};
let [start, end] = items.as_ref() else {
return None;
};
let start = native_cps_i64_normalized_start_bound(*start)?;
let end = native_cps_i64_normalized_end_bound(*end, len)?;
(start <= end && end <= len).then_some((start, end))
}
fn native_cps_i64_normalized_start_bound(value: i64) -> Option<usize> {
let NativeCpsI64HeapValue::Variant { tag, value } = native_cps_i64_heap_value(value)? else {
return None;
};
let tag = native_i64_tag_name(*tag);
match tag.as_str() {
"unbounded" => Some(0),
"included" => usize::try_from(value.as_ref().copied()?).ok(),
"excluded" => usize::try_from(value.as_ref().copied()? + 1).ok(),
_ => None,
}
}
fn native_cps_i64_normalized_end_bound(value: i64, len: usize) -> Option<usize> {
let NativeCpsI64HeapValue::Variant { tag, value } = native_cps_i64_heap_value(value)? else {
return None;
};
let tag = native_i64_tag_name(*tag);
match tag.as_str() {
"unbounded" => Some(len),
"included" => usize::try_from(value.as_ref().copied()? + 1).ok(),
"excluded" => usize::try_from(value.as_ref().copied()?).ok(),
_ => None,
}
}
fn native_cps_i64_heap_value(value: i64) -> Option<&'static NativeCpsI64HeapValue> {
let is_heap = NATIVE_CPS_I64_HEAP_VALUES.with(|values| values.borrow().contains(&value));
is_heap.then(|| unsafe { &*(value as *const NativeCpsI64HeapValue) })
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_resume_i64(
resumption: *const NativeCpsI64Resumption,
arg: i64,
) -> i64 {
let resumption = unsafe { &*resumption };
let current_handlers = NATIVE_CPS_I64_HANDLER_STACK.with(|s| s.borrow().clone());
let anchor = resumption.handled_anchor;
let resumed_handlers =
merge_resumption_handlers_native(&resumption.handlers, ¤t_handlers, anchor);
let adjusted_frames =
merge_extras_into_frames_native(&resumption.return_frames, ¤t_handlers, anchor);
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] resume: anchor={:?} captured={} current={} resumed={} adjusted_frames={}",
anchor,
format_handler_stack(&resumption.handlers),
format_handler_stack(¤t_handlers),
format_handler_stack(&resumed_handlers),
adjusted_frames.len(),
);
}
let saved_frames = NATIVE_CPS_I64_RETURN_FRAMES
.with(|f| std::mem::replace(&mut *f.borrow_mut(), adjusted_frames));
let fresh_eval = NATIVE_CPS_I64_NEXT_EVAL_ID.with(|next| {
let id = *next.borrow();
*next.borrow_mut() = id + 1;
id
});
let saved_eval_ctx = NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| {
std::mem::replace(
&mut *ctx.borrow_mut(),
NativeCpsI64EvalContext {
current_eval_id: fresh_eval,
initial_frame_count: 0,
},
)
});
let mut result =
with_native_i64_cps_state(resumed_handlers, resumption.guard_stack.to_vec(), || {
(resumption.code)(resumption.env.as_ptr(), arg)
});
if yulang_cps_scope_return_active_i64() != 0 {
result = yulang_cps_route_scope_return_i64(result);
}
restore_native_i64_return_frames_after_resume(saved_frames, &resumption.return_frames);
NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| *ctx.borrow_mut() = saved_eval_ctx);
result
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_resume_with_handler_i64(
resumption: *const NativeCpsI64Resumption,
arg: i64,
handler: i64,
consumes_mask: i64,
owner_function: i64,
updates_existing_handler_env: i64,
) -> i64 {
let resumption = unsafe { &*resumption };
let prompt = yulang_cps_fresh_prompt_i64() as u64;
let install_eval_id = yulang_cps_current_eval_id_i64() as u64;
let return_frame_threshold = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow().len());
let updates_existing_handler_env = updates_existing_handler_env != 0;
let rebases_existing_handler = updates_existing_handler_env
&& resumption
.handlers
.iter()
.any(|frame| frame.handler == handler);
let guard_stack = if updates_existing_handler_env || rebases_existing_handler {
native_cps_i64_snapshot(Vec::new())
} else {
native_cps_i64_snapshot(current_native_i64_guard_stack())
};
let outer_handler = NativeCpsI64HandlerFrame {
handler,
consumes_mask,
guard_stack,
envs: take_pending_native_i64_handler_envs(handler),
prompt,
install_eval_id,
escape_owner_function_id: owner_function.max(0) as u64,
threshold_owner_function_id: owner_function.max(0) as u64,
inherited: false,
escape_continuation: 0,
escape_env: Box::new([]),
escape_env_targets: Box::new([]),
return_frame_threshold,
return_frame_prefix: native_cps_i64_snapshot(Vec::new()),
origin: NativeCpsI64HandlerFrameOrigin::ResumeWithHandler,
};
let mut inherited_handler = outer_handler.clone();
inherited_handler.inherited = true;
push_resume_with_handler_sibling(outer_handler.clone());
let mut handlers = resumption.handlers.to_vec();
if !rebases_existing_handler {
handlers.push(inherited_handler.clone());
}
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] resume_with_handler: rid={} handler={} rebased={} envs={} captured_frames={} handlers={}",
resumption.debug_id,
handler,
rebases_existing_handler,
format_handler_envs(&outer_handler.envs),
format_return_frames(&resumption.return_frames),
format_handler_stack(&handlers),
);
}
let mut resumed_frames = resumption.return_frames.to_vec();
if rebases_existing_handler {
own_native_i64_captured_return_frames(&mut resumed_frames);
} else {
for frame in &mut resumed_frames {
if !frame
.handlers
.iter()
.any(|existing| same_handler_frame_native(existing, &inherited_handler))
{
let mut handlers = frame.handlers.to_vec();
handlers.push(inherited_handler.clone());
frame.handlers = native_cps_i64_snapshot(handlers);
}
}
}
let mut saved_frames = NATIVE_CPS_I64_RETURN_FRAMES
.with(|frames| std::mem::replace(&mut *frames.borrow_mut(), resumed_frames));
let mut previous_handlers = NATIVE_CPS_I64_HANDLER_STACK.with(|stack| {
let previous = stack.borrow().clone();
*stack.borrow_mut() = handlers;
previous
});
let previous_guards = NATIVE_CPS_I64_GUARD_STACK
.with(|stack| std::mem::replace(&mut *stack.borrow_mut(), resumption.guard_stack.to_vec()));
let fresh_eval = NATIVE_CPS_I64_NEXT_EVAL_ID.with(|next| {
let id = *next.borrow();
*next.borrow_mut() = id + 1;
id
});
let saved_eval_ctx = NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| {
std::mem::replace(
&mut *ctx.borrow_mut(),
NativeCpsI64EvalContext {
current_eval_id: fresh_eval,
initial_frame_count: 0,
},
)
});
let result = (resumption.code)(resumption.env.as_ptr(), arg);
let scope_return_active = NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| slot.borrow().active);
let abort_active = NATIVE_CPS_I64_ABORT.with(|slot| slot.borrow().is_active());
if !rebases_existing_handler {
append_resume_handler_to_frames(&mut saved_frames, &outer_handler);
append_resume_handler_to_handler_prefixes(&mut previous_handlers, &outer_handler);
}
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| {
let mut outer = previous_handlers;
if !rebases_existing_handler {
outer.push(outer_handler);
}
*stack.borrow_mut() = outer;
});
NATIVE_CPS_I64_GUARD_STACK.with(|stack| *stack.borrow_mut() = previous_guards);
if abort_active && !scope_return_active {
restore_native_i64_return_frames_after_resume(saved_frames, &resumption.return_frames);
} else {
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| *frames.borrow_mut() = saved_frames);
}
NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| *ctx.borrow_mut() = saved_eval_ctx);
result
}
fn same_handler_frame_native(a: &NativeCpsI64HandlerFrame, b: &NativeCpsI64HandlerFrame) -> bool {
a.prompt == b.prompt && a.install_eval_id == b.install_eval_id
}
fn merge_resumption_handlers_native(
captured: &[NativeCpsI64HandlerFrame],
current: &[NativeCpsI64HandlerFrame],
anchor: Option<NativeCpsI64HandlerAnchor>,
) -> Vec<NativeCpsI64HandlerFrame> {
if let Some(anchor) = anchor {
if let Some(anchor_index) = captured
.iter()
.position(|f| f.prompt == anchor.prompt && f.install_eval_id == anchor.install_eval_id)
{
let mut merged = Vec::with_capacity(captured.len() + current.len());
merged.extend(captured[..=anchor_index].iter().cloned());
for frame in current {
let in_prefix = merged.iter().any(|m| same_handler_frame_native(m, frame));
let in_tail = captured[anchor_index + 1..]
.iter()
.any(|c| same_handler_frame_native(c, frame));
if !in_prefix && !in_tail {
merged.push(frame.clone());
}
}
merged.extend(captured[anchor_index + 1..].iter().cloned());
return merged;
}
}
let mut shared = 0;
while shared < captured.len()
&& shared < current.len()
&& same_handler_frame_native(&captured[shared], ¤t[shared])
{
shared += 1;
}
let mut merged = Vec::with_capacity(captured.len() + current.len());
merged.extend(captured[..shared].iter().cloned());
for frame in ¤t[shared..] {
if !captured.iter().any(|c| same_handler_frame_native(c, frame)) {
merged.push(frame.clone());
}
}
merged.extend(captured[shared..].iter().cloned());
merged
}
fn merge_extras_into_frames_native(
frames: &[NativeCpsI64ReturnFrame],
current: &[NativeCpsI64HandlerFrame],
anchor: Option<NativeCpsI64HandlerAnchor>,
) -> Vec<NativeCpsI64ReturnFrame> {
frames
.iter()
.map(|frame| {
let merged = merge_resumption_handlers_native(&frame.handlers, current, anchor);
NativeCpsI64ReturnFrame {
prompt_exit: frame.prompt_exit.clone(),
debug_id: frame.debug_id,
continuation: frame.continuation,
continuation_id: frame.continuation_id,
env: frame.env.clone(),
handlers: native_cps_i64_snapshot(merged),
guards: frame.guards.clone(),
owner_initial_frame_count: frame.owner_initial_frame_count,
owner_eval_id: frame.owner_eval_id,
owner_function_id: frame.owner_function_id,
immediately_forces_param: frame.immediately_forces_param,
}
})
.collect()
}
fn capture_native_i64_return_frames_from_start(
frames: &[NativeCpsI64ReturnFrame],
start: usize,
handled_install_eval_id: u64,
) -> NativeCpsI64ReturnFrameSnapshot {
let mut captured = frames[start..]
.iter()
.cloned()
.map(|frame| rebase_native_i64_captured_return_frame(frame, start, handled_install_eval_id))
.collect::<Vec<_>>();
own_native_i64_captured_return_frames(&mut captured);
native_cps_i64_snapshot(captured)
}
fn native_i64_prompt_frame_start(
frames: &[NativeCpsI64ReturnFrame],
prompt: u64,
_fallback_threshold: usize,
) -> usize {
frames
.iter()
.rposition(|frame| {
frame
.prompt_exit
.as_ref()
.is_some_and(|exit| exit.prompt == prompt)
})
.map(|index| index + 1)
.unwrap_or(0)
.min(frames.len())
}
fn rebase_native_i64_captured_return_frame(
mut frame: NativeCpsI64ReturnFrame,
dropped_frames: usize,
handled_install_eval_id: u64,
) -> NativeCpsI64ReturnFrame {
frame.owner_initial_frame_count = frame
.owner_initial_frame_count
.saturating_sub(dropped_frames);
for handler in Rc::make_mut(&mut frame.handlers) {
if handler.install_eval_id >= handled_install_eval_id {
handler.return_frame_threshold = handler
.return_frame_threshold
.saturating_sub(dropped_frames);
}
}
frame
}
fn rebase_native_i64_captured_handlers(
handlers: &mut [NativeCpsI64HandlerFrame],
dropped_frames: usize,
handled_install_eval_id: u64,
) {
for handler in handlers {
if handler.install_eval_id >= handled_install_eval_id {
handler.return_frame_threshold = handler
.return_frame_threshold
.saturating_sub(dropped_frames);
}
}
}
fn recalibrate_inherited_handler_thresholds(
handlers: &mut [NativeCpsI64HandlerFrame],
slice: &[NativeCpsI64ReturnFrame],
handled_install_eval_id: u64,
) {
for handler in handlers {
if handler.install_eval_id >= handled_install_eval_id {
continue;
}
let prompt = handler.prompt;
let marker = slice.iter().position(|frame| {
frame
.prompt_exit
.as_ref()
.is_some_and(|exit| exit.prompt == prompt)
});
handler.return_frame_threshold = marker.unwrap_or(0);
}
}
fn own_native_i64_captured_return_frames(frames: &mut [NativeCpsI64ReturnFrame]) {
for frame in frames {
frame.owner_initial_frame_count = 0;
}
}
fn append_resume_handler_to_frames(
frames: &mut [NativeCpsI64ReturnFrame],
handler: &NativeCpsI64HandlerFrame,
) {
for frame in frames {
if !frame
.handlers
.iter()
.any(|existing| same_handler_frame_native(existing, handler))
{
let mut handlers = frame.handlers.to_vec();
handlers.push(handler.clone());
frame.handlers = native_cps_i64_snapshot(handlers);
}
}
}
fn append_resume_handler_to_handler_prefixes(
handlers: &mut [NativeCpsI64HandlerFrame],
handler: &NativeCpsI64HandlerFrame,
) {
for active in handlers {
if active.return_frame_prefix.is_empty() {
continue;
}
let mut prefix = active.return_frame_prefix.to_vec();
append_resume_handler_to_frames(&mut prefix, handler);
active.return_frame_prefix = native_cps_i64_snapshot(prefix);
}
}
fn push_resume_with_handler_sibling(handler: NativeCpsI64HandlerFrame) {
NATIVE_CPS_I64_RESUME_WITH_HANDLER_SIBLINGS.with(|siblings| {
let mut siblings = siblings.borrow_mut();
if !siblings
.iter()
.any(|existing| same_handler_frame_native(existing, &handler))
{
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] push_rwh_sibling: handler={} envs={}",
handler.handler,
format_handler_envs(&handler.envs)
);
}
siblings.push(handler);
}
});
}
fn append_resume_with_handler_siblings(handlers: &mut Vec<NativeCpsI64HandlerFrame>) {
NATIVE_CPS_I64_RESUME_WITH_HANDLER_SIBLINGS.with(|siblings| {
for sibling in siblings.borrow().iter() {
if !handlers
.iter()
.any(|existing| same_handler_frame_native(existing, sibling))
{
handlers.push(sibling.clone());
}
}
});
}
fn restore_native_i64_return_frames_after_resume(
saved_frames: Vec<NativeCpsI64ReturnFrame>,
_resumed_frames: &[NativeCpsI64ReturnFrame],
) {
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| {
let current = frames.borrow().clone();
let mut restored = Vec::new();
let mut used_current = HashSet::new();
for saved in saved_frames {
if let Some((index, current_frame)) = current
.iter()
.enumerate()
.find(|(_, frame)| frame.debug_id == saved.debug_id)
{
restored.push(current_frame.clone());
used_current.insert(index);
} else {
restored.push(saved);
}
}
for (index, current_frame) in current.into_iter().enumerate() {
if !used_current.contains(&index) {
restored.push(current_frame);
}
}
*frames.borrow_mut() = restored;
});
}
fn format_handler_stack(stack: &[NativeCpsI64HandlerFrame]) -> String {
let mut s = String::from("[");
for (i, frame) in stack.iter().enumerate() {
if i > 0 {
s.push_str(", ");
}
s.push_str(&format!(
"h{}(p={},ev={},owner_fn={},thr_owner={},{:?},inh={},thr={},guards={:?})",
frame.handler,
frame.prompt,
if frame.install_eval_id == NATIVE_CPS_I64_SYNTHETIC_EVAL_ID {
"SYN".to_string()
} else {
frame.install_eval_id.to_string()
},
frame.escape_owner_function_id,
frame.threshold_owner_function_id,
frame.origin,
frame.inherited,
frame.return_frame_threshold,
frame.guard_stack,
));
}
s.push(']');
s
}
fn format_return_frames(frames: &[NativeCpsI64ReturnFrame]) -> String {
const EDGE_FRAMES: usize = 4;
let mut s = String::from("[");
let mut wrote = 0usize;
for (i, frame) in frames.iter().enumerate() {
if frames.len() > EDGE_FRAMES * 2 && i == EDGE_FRAMES {
if wrote > 0 {
s.push_str(", ");
}
s.push_str(&format!("... {} more ...", frames.len() - EDGE_FRAMES * 2));
wrote += 1;
continue;
}
if frames.len() > EDGE_FRAMES * 2 && i >= EDGE_FRAMES && i < frames.len() - EDGE_FRAMES {
continue;
}
if i > 0 {
s.push_str(", ");
}
let prompt_exit = frame
.prompt_exit
.as_ref()
.map(|exit| format!(",prompt_exit={}", exit.prompt))
.unwrap_or_default();
s.push_str(&format!(
"F#{}/id{}:k{}(owner_eval={},owner_fn={},init={}{} ,handlers={})",
i,
frame.debug_id,
frame.continuation_id,
frame.owner_eval_id,
frame.owner_function_id,
frame.owner_initial_frame_count,
prompt_exit,
format_handler_stack(&frame.handlers),
));
wrote += 1;
}
s.push(']');
s
}
fn format_handler_envs(envs: &[NativeCpsI64HandlerEnv]) -> String {
let parts = envs
.iter()
.map(|env| {
let slots = if env.slots.is_empty() {
String::new()
} else {
let slots = env
.slots
.iter()
.map(|(target, value)| {
format!("{}:{}", target, describe_native_i64_value(*value))
})
.collect::<Vec<_>>()
.join(",");
format!(" {{{}}}", slots)
};
format!(
"{}={}{}",
env.entry,
describe_native_i64_value(env.env),
slots
)
})
.collect::<Vec<_>>();
format!("[{}]", parts.join(", "))
}
fn refreshed_escape_env(frame: &NativeCpsI64HandlerFrame) -> Box<[i64]> {
if frame.escape_env_targets.is_empty() {
return frame.escape_env.clone();
}
let mut env = frame.escape_env.to_vec();
for (index, target) in frame.escape_env_targets.iter().copied().enumerate() {
let Some(slot) = env.get_mut(index) else {
continue;
};
if let Some(value) = latest_handler_slot_value(frame.handler, target) {
*slot = value;
}
}
env.into_boxed_slice()
}
fn latest_handler_slot_value(handler: i64, target: i64) -> Option<i64> {
NATIVE_CPS_I64_RESUME_WITH_HANDLER_SIBLINGS.with(|siblings| {
siblings
.borrow()
.iter()
.rev()
.filter(|frame| frame.handler == handler)
.flat_map(|frame| frame.envs.iter().rev())
.flat_map(|env| env.slots.iter().rev())
.find_map(|(slot_target, value)| (*slot_target == target).then_some(*value))
})
}
fn append_distinct_return_frames(
out: &mut Vec<NativeCpsI64ReturnFrame>,
frames: impl IntoIterator<Item = NativeCpsI64ReturnFrame>,
) {
for frame in frames {
if out
.iter()
.any(|existing| existing.debug_id == frame.debug_id)
{
if jit_trace_enabled() {
eprintln!("[JIT-CPS] return_frame_dedup: skip id={}", frame.debug_id);
}
continue;
}
out.push(frame);
}
}
fn is_captured_handler_key(
handler: &NativeCpsI64HandlerFrame,
captured: &[NativeCpsI64HandlerFrame],
) -> bool {
captured.iter().any(|candidate| {
candidate.prompt == handler.prompt && candidate.install_eval_id == handler.install_eval_id
})
}
fn rebase_captured_return_frame_threshold(
old_threshold: usize,
captured_frames: &[NativeCpsI64ReturnFrame],
combined_prefix: &[NativeCpsI64ReturnFrame],
) -> usize {
let mut rebased = combined_prefix.len();
let captured_prefix_len = old_threshold.min(captured_frames.len());
for captured in &captured_frames[..captured_prefix_len] {
if !combined_prefix
.iter()
.any(|existing| existing.debug_id == captured.debug_id)
{
rebased += 1;
}
}
rebased
}
fn rebase_captured_handler_thresholds(
frames: &mut [NativeCpsI64ReturnFrame],
captured_handlers: &[NativeCpsI64HandlerFrame],
captured_frames: &[NativeCpsI64ReturnFrame],
combined_prefix: &[NativeCpsI64ReturnFrame],
) {
for frame in frames {
for handler in Rc::make_mut(&mut frame.handlers) {
if is_captured_handler_key(handler, captured_handlers) {
handler.return_frame_threshold = rebase_captured_return_frame_threshold(
handler.return_frame_threshold,
captured_frames,
combined_prefix,
);
}
}
}
}
fn effectful_apply_resumption_native(
resumption: *const NativeCpsI64Resumption,
arg: i64,
post_cont: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: bool,
env: Vec<i64>,
) -> i64 {
let resumption = unsafe { &*resumption };
let current_handlers = NATIVE_CPS_I64_HANDLER_STACK.with(|s| s.borrow().clone());
let current_guards = NATIVE_CPS_I64_GUARD_STACK.with(|s| s.borrow().clone());
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] effectful_apply_resumption.in: rid={} anchor={:?} captured={} captured_frames={} current={}",
resumption.debug_id,
resumption.handled_anchor,
format_handler_stack(&resumption.handlers),
format_return_frames(&resumption.return_frames),
format_handler_stack(¤t_handlers),
);
}
let f_post = NativeCpsI64ReturnFrame {
prompt_exit: None,
debug_id: next_native_i64_return_frame_debug_id(),
continuation: post_cont as usize,
continuation_id: 0,
env: native_cps_i64_snapshot(env),
handlers: native_cps_i64_snapshot(current_handlers.clone()),
guards: native_cps_i64_snapshot(current_guards.clone()),
owner_initial_frame_count: owner_initial.max(0) as usize,
owner_eval_id: owner_eval as u64,
owner_function_id: owner_function.max(0) as u64,
immediately_forces_param: immediately_forces,
};
let mut new_frames = NATIVE_CPS_I64_RETURN_FRAMES.with(|f| f.borrow().clone());
new_frames.push(f_post);
let anchor = resumption.handled_anchor;
let resumed_handlers =
merge_resumption_handlers_native(&resumption.handlers, ¤t_handlers, anchor);
let mut adjusted_res =
merge_extras_into_frames_native(&resumption.return_frames, ¤t_handlers, anchor);
rebase_captured_handler_thresholds(
&mut adjusted_res,
&resumption.handlers,
&resumption.return_frames,
&new_frames,
);
append_distinct_return_frames(&mut new_frames, adjusted_res);
let combined_len = new_frames.len();
let resumed_len = resumed_handlers.len();
NATIVE_CPS_I64_RETURN_FRAMES.with(|f| *f.borrow_mut() = new_frames.clone());
NATIVE_CPS_I64_HANDLER_STACK.with(|s| *s.borrow_mut() = resumed_handlers.clone());
NATIVE_CPS_I64_GUARD_STACK.with(|s| *s.borrow_mut() = resumption.guard_stack.to_vec());
let fresh_eval = NATIVE_CPS_I64_NEXT_EVAL_ID.with(|next| {
let id = *next.borrow();
*next.borrow_mut() = id + 1;
id
});
NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| {
*ctx.borrow_mut() = NativeCpsI64EvalContext {
current_eval_id: fresh_eval,
initial_frame_count: 0,
};
});
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] effectful_apply_resumption.out: rid={} anchor={:?} fresh_eval={} combined_len={} resumed={} resumed_handlers={} new_frames={}",
resumption.debug_id,
anchor,
fresh_eval,
combined_len,
resumed_len,
format_handler_stack(&resumed_handlers),
format_return_frames(&new_frames),
);
}
(resumption.code)(resumption.env.as_ptr(), arg)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_effectful_apply_resumption_i64_0(
resumption: i64,
arg: i64,
post_cont: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: i64,
) -> i64 {
effectful_apply_resumption_native(
resumption as *const NativeCpsI64Resumption,
arg,
post_cont,
owner_initial,
owner_eval,
owner_function,
immediately_forces != 0,
Vec::new(),
)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_effectful_apply_resumption_i64_1(
resumption: i64,
arg: i64,
post_cont: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: i64,
a: i64,
) -> i64 {
effectful_apply_resumption_native(
resumption as *const NativeCpsI64Resumption,
arg,
post_cont,
owner_initial,
owner_eval,
owner_function,
immediately_forces != 0,
vec![a],
)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_effectful_apply_resumption_i64_2(
resumption: i64,
arg: i64,
post_cont: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: i64,
a: i64,
b: i64,
) -> i64 {
effectful_apply_resumption_native(
resumption as *const NativeCpsI64Resumption,
arg,
post_cont,
owner_initial,
owner_eval,
owner_function,
immediately_forces != 0,
vec![a, b],
)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_effectful_apply_resumption_i64_3(
resumption: i64,
arg: i64,
post_cont: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: i64,
a: i64,
b: i64,
c: i64,
) -> i64 {
effectful_apply_resumption_native(
resumption as *const NativeCpsI64Resumption,
arg,
post_cont,
owner_initial,
owner_eval,
owner_function,
immediately_forces != 0,
vec![a, b, c],
)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_effectful_apply_resumption_i64_4(
resumption: i64,
arg: i64,
post_cont: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: i64,
a: i64,
b: i64,
c: i64,
d: i64,
) -> i64 {
effectful_apply_resumption_native(
resumption as *const NativeCpsI64Resumption,
arg,
post_cont,
owner_initial,
owner_eval,
owner_function,
immediately_forces != 0,
vec![a, b, c, d],
)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_effectful_apply_resumption_i64_many(
resumption: i64,
arg: i64,
post_cont: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: i64,
ptr: *const i64,
len: i64,
) -> i64 {
effectful_apply_resumption_native(
resumption as *const NativeCpsI64Resumption,
arg,
post_cont,
owner_initial,
owner_eval,
owner_function,
immediately_forces != 0,
unsafe { native_i64_slice(ptr, len) },
)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_is_resumption_i64(value: i64) -> i64 {
let is = NATIVE_CPS_I64_RESUMPTIONS.with(|s| s.borrow().contains(&(value as usize)));
i64::from(is)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_select_handler_i64(
fallback_handler: i64,
effect_mask: i64,
blocked: i64,
) -> i64 {
let stack = current_native_i64_handler_stack_with_fallback(fallback_handler, effect_mask);
let is_preferred_origin = |origin: NativeCpsI64HandlerFrameOrigin| {
!matches!(origin, NativeCpsI64HandlerFrameOrigin::PendingEnv)
};
let frame_allowed = |frame: &NativeCpsI64HandlerFrame| {
let allowed = (frame.consumes_mask & effect_mask) != 0;
if !allowed {
return false;
}
if blocked >= 0 && frame.guard_stack.contains(&blocked) {
return false;
}
true
};
let chosen = stack
.iter()
.enumerate()
.rev()
.find(|(_, frame)| frame_allowed(frame) && is_preferred_origin(frame.origin))
.or_else(|| {
stack
.iter()
.enumerate()
.rev()
.find(|(_, frame)| frame_allowed(frame))
});
if let Some((index, frame)) = chosen {
NATIVE_CPS_I64_OUTER_HANDLER_SNAPSHOTS.with(|snaps| {
snaps.borrow_mut().push(stack.clone());
});
NATIVE_CPS_I64_SELECTED_HANDLER_META_STACK.with(|meta| {
meta.borrow_mut().push(NativeCpsI64SelectedHandlerMeta {
prompt: frame.prompt,
escape_continuation: frame.escape_continuation,
escape_env: frame.escape_env.clone(),
return_frame_threshold: frame.return_frame_threshold,
install_eval_id: frame.install_eval_id,
synthetic: frame.install_eval_id == NATIVE_CPS_I64_SYNTHETIC_EVAL_ID
|| frame.escape_continuation == 0,
});
});
NATIVE_CPS_I64_HANDLER_STACK.with(|active| {
*active.borrow_mut() = stack[..index].to_vec();
});
NATIVE_CPS_I64_SELECTED_HANDLER_ENVS.with(|envs| {
*envs.borrow_mut() = frame.envs.to_vec();
});
NATIVE_CPS_I64_SELECTED_HANDLER_ID.with(|handler| *handler.borrow_mut() = frame.handler);
NATIVE_CPS_I64_SELECTED_HANDLER_OWNER_FUNCTION_ID
.with(|owner| *owner.borrow_mut() = frame.escape_owner_function_id);
NATIVE_CPS_I64_SELECTED_HANDLER_USED_RWH_ENV.with(|used| *used.borrow_mut() = false);
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] perform_select: handler={} prompt={} install_eval={} owner_fn={} synthetic={} threshold={} idx={} origin={:?} envs={}",
frame.handler,
frame.prompt,
frame.install_eval_id,
frame.escape_owner_function_id,
frame.install_eval_id == NATIVE_CPS_I64_SYNTHETIC_EVAL_ID,
frame.return_frame_threshold,
index,
frame.origin,
format_handler_envs(&frame.envs),
);
}
return frame.handler;
}
NATIVE_CPS_I64_SELECTED_HANDLER_ENVS.with(|envs| envs.borrow_mut().clear());
NATIVE_CPS_I64_SELECTED_HANDLER_ID.with(|handler| *handler.borrow_mut() = -1);
NATIVE_CPS_I64_SELECTED_HANDLER_OWNER_FUNCTION_ID.with(|owner| *owner.borrow_mut() = 0);
NATIVE_CPS_I64_SELECTED_HANDLER_USED_RWH_ENV.with(|used| *used.borrow_mut() = false);
-1
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_active_blocked_guard_i64(effect_mask: i64) -> i64 {
NATIVE_CPS_I64_ACTIVE_BLOCKED.with(|stack| {
let stack = stack.borrow();
let mut peeled = HashSet::new();
let mut selected = -1;
for blocked in stack.iter().rev() {
if peeled.contains(&blocked.guard_id) {
continue;
}
if !blocked.active {
peeled.insert(blocked.guard_id);
continue;
}
if (blocked.allowed_mask & effect_mask) == 0 {
selected = blocked.guard_id;
break;
}
}
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] active_blocked: effect_mask={} selected={} stack={:?}",
effect_mask,
selected,
stack
.iter()
.map(|entry| (entry.guard_id, entry.allowed_mask, entry.active))
.collect::<Vec<_>>()
);
}
selected
})
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_restore_outer_handler_stack_i64() -> i64 {
NATIVE_CPS_I64_OUTER_HANDLER_SNAPSHOTS.with(|snaps| {
if let Some(snap) = snaps.borrow_mut().pop() {
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| *stack.borrow_mut() = snap);
}
});
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_perform_finish_i64(value: i64) -> i64 {
NATIVE_CPS_I64_OUTER_HANDLER_SNAPSHOTS.with(|snaps| {
if let Some(snap) = snaps.borrow_mut().pop() {
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| *stack.borrow_mut() = snap);
}
});
let meta = NATIVE_CPS_I64_SELECTED_HANDLER_META_STACK.with(|m| m.borrow_mut().pop());
let is_real = meta
.as_ref()
.map(|m| !m.synthetic && m.escape_continuation != 0)
.unwrap_or(false);
let already_active = NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| slot.borrow().active);
if is_real && !already_active {
let meta = meta.as_ref().expect("is_real implies meta");
NATIVE_CPS_I64_ABORT.with(|slot| *slot.borrow_mut() = NativeCpsI64Abort::None);
NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| {
*slot.borrow_mut() = NativeCpsI64ScopeReturn {
active: true,
prompt: meta.prompt,
target: meta.escape_continuation as i64,
value,
};
});
if jit_trace_enabled() {
let current_eval = NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| ctx.borrow().current_eval_id);
let current_initial =
NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| ctx.borrow().initial_frame_count);
let stack = NATIVE_CPS_I64_HANDLER_STACK.with(|s| s.borrow().clone());
let frames = NATIVE_CPS_I64_RETURN_FRAMES.with(|f| f.borrow().clone());
eprintln!(
"[JIT-CPS] scope_return_set (perform_finish): prompt={} target={:#x} value={} current_eval={} initial={} stack={} frames={}",
meta.prompt,
meta.escape_continuation,
describe_native_i64_value(value),
current_eval,
current_initial,
format_handler_stack(&stack),
format_return_frames(&frames),
);
}
}
let routed = yulang_cps_route_scope_return_i64(value);
if !is_real {
let abort_already = NATIVE_CPS_I64_ABORT.with(|slot| slot.borrow().is_active());
if !abort_already {
NATIVE_CPS_I64_ABORT
.with(|slot| *slot.borrow_mut() = NativeCpsI64Abort::Global(routed));
}
}
routed
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_perform_finish_escaped_i64(value: i64) -> i64 {
NATIVE_CPS_I64_OUTER_HANDLER_SNAPSHOTS.with(|snaps| {
if let Some(snap) = snaps.borrow_mut().pop() {
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| *stack.borrow_mut() = snap);
}
});
let meta = NATIVE_CPS_I64_SELECTED_HANDLER_META_STACK.with(|meta| meta.borrow_mut().pop());
if NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| slot.borrow().active) {
return yulang_cps_route_scope_return_i64(value);
}
let mut abort_outer_eval = false;
if let Some(meta) = meta {
if meta.synthetic || meta.escape_continuation == 0 {
return value;
}
let current_eval = NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| ctx.borrow().current_eval_id);
let used_rwh_env = NATIVE_CPS_I64_SELECTED_HANDLER_USED_RWH_ENV.with(|used| *used.borrow());
if meta.install_eval_id != current_eval && used_rwh_env {
return value;
}
if meta.install_eval_id != current_eval {
NATIVE_CPS_I64_ABORT.with(|slot| *slot.borrow_mut() = NativeCpsI64Abort::None);
NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| {
*slot.borrow_mut() = NativeCpsI64ScopeReturn {
active: true,
prompt: meta.prompt,
target: meta.escape_continuation as i64,
value,
};
});
return yulang_cps_route_scope_return_i64(value);
}
abort_outer_eval = meta.return_frame_threshold == 0;
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| {
let mut frames = frames.borrow_mut();
if frames.len() > meta.return_frame_threshold {
frames.truncate(meta.return_frame_threshold);
}
});
}
let frame_len = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow().len());
if frame_len == 0 {
let current_initial =
NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| ctx.borrow().initial_frame_count);
if abort_outer_eval && current_initial > 0 {
NATIVE_CPS_I64_ABORT.with(|slot| *slot.borrow_mut() = NativeCpsI64Abort::Global(value));
}
return value;
}
let result = yulang_cps_continue_return_frame_i64(value);
let current_initial = NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| ctx.borrow().initial_frame_count);
if abort_outer_eval && current_initial > 0 {
NATIVE_CPS_I64_ABORT.with(|slot| *slot.borrow_mut() = NativeCpsI64Abort::Global(result));
}
result
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_scope_return_from_selected_handler_i64(value: i64) -> i64 {
let already_active = NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| slot.borrow().active);
if already_active {
return value;
}
let meta = NATIVE_CPS_I64_SELECTED_HANDLER_META_STACK.with(|m| m.borrow().last().cloned());
let Some(meta) = meta else {
return value;
};
if meta.synthetic || meta.escape_continuation == 0 {
return value;
}
NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| {
*slot.borrow_mut() = NativeCpsI64ScopeReturn {
active: true,
prompt: meta.prompt,
target: meta.escape_continuation as i64,
value,
};
});
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] scope_return_set (from selected): prompt={} target={:#x} value={}",
meta.prompt, meta.escape_continuation, value
);
}
value
}
fn find_current_native_i64_scope_handler(
prompt: u64,
current_eval: u64,
skip_current: bool,
) -> Option<NativeCpsI64CurrentScopeHandlerMatch> {
if skip_current {
return None;
}
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| {
let stack = stack.borrow();
stack
.iter()
.rposition(|frame| frame.prompt == prompt && frame.install_eval_id == current_eval)
.map(|handler_index| NativeCpsI64CurrentScopeHandlerMatch {
handler_index,
handler: stack[handler_index].clone(),
})
})
}
fn find_native_i64_scope_handler_in_return_frames(
prompt: u64,
) -> Option<NativeCpsI64ReturnFrameScopeHandlerMatch> {
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| {
let frames = frames.borrow();
for return_frame_index in (0..frames.len()).rev() {
let return_frame = &frames[return_frame_index];
for (handler_index, handler) in return_frame.handlers.iter().enumerate().rev() {
if handler.prompt == prompt
&& handler.install_eval_id == return_frame.owner_eval_id
&& handler.escape_owner_function_id == return_frame.owner_function_id
{
return Some(NativeCpsI64ReturnFrameScopeHandlerMatch {
return_frame_index,
handler_index,
return_frame: return_frame.clone(),
handler: handler.clone(),
});
}
}
}
None
})
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_route_scope_return_i64(fallback_value: i64) -> i64 {
let sr = NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| slot.borrow().clone());
if !sr.active {
return fallback_value;
}
let prompt = sr.prompt;
let value = fallback_value;
NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| {
let mut slot = slot.borrow_mut();
if slot.active && slot.prompt == prompt {
slot.value = value;
}
});
let current_eval = NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| ctx.borrow().current_eval_id);
let current_initial = NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| ctx.borrow().initial_frame_count);
if jit_trace_enabled() {
let stack = NATIVE_CPS_I64_HANDLER_STACK.with(|s| s.borrow().clone());
let frames = NATIVE_CPS_I64_RETURN_FRAMES.with(|f| f.borrow().clone());
eprintln!(
"[JIT-CPS] route_scope_return.scan: prompt={} value={} current_eval={} initial={} force_frame_walk={} stack={} frames={}",
prompt,
describe_native_i64_value(value),
current_eval,
current_initial,
jit_force_frame_walk_sr(),
format_handler_stack(&stack),
format_return_frames(&frames),
);
}
let skip_current = jit_force_frame_walk_sr();
if let Some(scope_match) =
find_current_native_i64_scope_handler(prompt, current_eval, skip_current)
{
let handler_index = scope_match.handler_index;
let frame = scope_match.handler;
let mut post_handlers = NATIVE_CPS_I64_HANDLER_STACK.with(|stack| stack.borrow().clone());
post_handlers.truncate(handler_index);
let mut post_frames = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow().clone());
let truncate_at = frame.return_frame_threshold;
if post_frames.len() > truncate_at {
post_frames.truncate(truncate_at);
}
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| *stack.borrow_mut() = post_handlers.clone());
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| *frames.borrow_mut() = post_frames.clone());
NATIVE_CPS_I64_SCOPE_RETURN
.with(|slot| *slot.borrow_mut() = NativeCpsI64ScopeReturn::default());
NATIVE_CPS_I64_RETURN_FRAMES_ROUTED.with(|routed| *routed.borrow_mut() = true);
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] route_scope_return: prompt={} current_eval={} initial={} action=current_handler value={}",
prompt,
current_eval,
current_initial,
describe_native_i64_value(value)
);
}
if frame.escape_continuation == 0 {
if current_initial > 0 && post_handlers.is_empty() {
NATIVE_CPS_I64_ABORT
.with(|slot| *slot.borrow_mut() = NativeCpsI64Abort::Global(value));
}
return value;
}
let escape_env = refreshed_escape_env(&frame);
if current_initial > 0 && post_handlers.is_empty() && frame.guard_stack.is_empty() {
let result =
call_native_i64_continuation(frame.escape_continuation, &escape_env, value);
NATIVE_CPS_I64_ABORT
.with(|slot| *slot.borrow_mut() = NativeCpsI64Abort::Global(result));
return result;
}
if current_initial > 0 {
NATIVE_CPS_I64_ABORT.with(|slot| {
*slot.borrow_mut() = routed_jump_abort(
value,
truncate_at,
frame.escape_continuation,
escape_env,
post_handlers,
frame.guard_stack.to_vec(),
post_frames,
NativeCpsI64EvalContext {
current_eval_id: frame.install_eval_id,
initial_frame_count: truncate_at,
},
);
});
return value;
}
let result = call_native_i64_continuation(frame.escape_continuation, &escape_env, value);
return result;
}
if let Some(scope_match) = find_native_i64_scope_handler_in_return_frames(prompt) {
let return_frame_index = scope_match.return_frame_index;
let handler_index = scope_match.handler_index;
let frame = scope_match.return_frame;
let handler = scope_match.handler;
let mut post_handlers = frame.handlers.to_vec();
post_handlers.truncate(handler_index);
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| *stack.borrow_mut() = post_handlers.clone());
NATIVE_CPS_I64_GUARD_STACK.with(|stack| *stack.borrow_mut() = frame.guards.to_vec());
let mut post_frames = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow().clone());
post_frames.truncate(return_frame_index);
let truncate_at = handler.return_frame_threshold;
if post_frames.len() > truncate_at {
post_frames.truncate(truncate_at);
}
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| *frames.borrow_mut() = post_frames.clone());
let rest_len = post_frames.len();
let owner_initial = frame.owner_initial_frame_count.min(rest_len);
let post_eval_context = NativeCpsI64EvalContext {
current_eval_id: frame.owner_eval_id,
initial_frame_count: owner_initial,
};
NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| *ctx.borrow_mut() = post_eval_context);
NATIVE_CPS_I64_SCOPE_RETURN
.with(|slot| *slot.borrow_mut() = NativeCpsI64ScopeReturn::default());
NATIVE_CPS_I64_RETURN_FRAMES_ROUTED.with(|routed| *routed.borrow_mut() = true);
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] route_scope_return: prompt={} current_eval={} initial={} action=frame_walk fi={} hi={} value={}",
prompt,
current_eval,
current_initial,
return_frame_index,
handler_index,
describe_native_i64_value(value)
);
}
if handler.escape_continuation == 0 {
if current_initial > 0 && post_handlers.is_empty() {
NATIVE_CPS_I64_ABORT
.with(|slot| *slot.borrow_mut() = NativeCpsI64Abort::Global(value));
}
return value;
}
let escape_env = refreshed_escape_env(&handler);
if current_initial > 0 && post_handlers.is_empty() && handler.guard_stack.is_empty() {
let result =
call_native_i64_continuation(handler.escape_continuation, &escape_env, value);
NATIVE_CPS_I64_ABORT
.with(|slot| *slot.borrow_mut() = NativeCpsI64Abort::Global(result));
return result;
}
if current_initial > 0 {
NATIVE_CPS_I64_ABORT.with(|slot| {
*slot.borrow_mut() = routed_jump_abort(
value,
truncate_at,
handler.escape_continuation,
escape_env,
post_handlers,
frame.guards.to_vec(),
post_frames,
post_eval_context,
);
});
return value;
}
let result = call_native_i64_continuation(handler.escape_continuation, &escape_env, value);
return result;
}
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] route_scope_return: prompt={} current_eval={} initial={} action=propagate value={}",
prompt,
current_eval,
current_initial,
describe_native_i64_value(value)
);
}
fallback_value
}
#[allow(dead_code)]
fn routed_scope_return_abort(
value: i64,
return_frame_threshold: usize,
restore_frames: NativeCpsI64ReturnFrameSnapshot,
) -> NativeCpsI64Abort {
NativeCpsI64Abort::Scoped {
value,
return_frame_threshold,
propagate_at_threshold: false,
restore_frames,
}
}
fn routed_jump_abort(
value: i64,
return_frame_threshold: usize,
continuation: usize,
env: Box<[i64]>,
handlers: Vec<NativeCpsI64HandlerFrame>,
guards: Vec<i64>,
return_frames: Vec<NativeCpsI64ReturnFrame>,
eval_context: NativeCpsI64EvalContext,
) -> NativeCpsI64Abort {
NativeCpsI64Abort::RoutedJump {
value,
return_frame_threshold,
continuation,
env,
handlers,
guards,
return_frames: native_cps_i64_snapshot(return_frames),
eval_context,
}
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_capture_handler_env_i64(
handler: i64,
entry: i64,
env: i64,
) -> i64 {
NATIVE_CPS_I64_PENDING_HANDLER_ENVS.with(|envs| {
envs.borrow_mut().push((
handler,
NativeCpsI64HandlerEnv {
entry,
env,
slots: Vec::new(),
},
));
});
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_capture_handler_env_mapped_i64(
handler: i64,
entry: i64,
env: i64,
target_ptr: *const i64,
value_ptr: *const i64,
target_len: i64,
value_len: i64,
) -> i64 {
let len = target_len.min(value_len).max(0) as usize;
let targets = unsafe { native_i64_slice(target_ptr, len as i64) };
let values = unsafe { native_i64_slice(value_ptr, len as i64) };
let slots = targets
.iter()
.copied()
.zip(values.iter().copied())
.collect::<Vec<_>>();
NATIVE_CPS_I64_PENDING_HANDLER_ENVS.with(|envs| {
envs.borrow_mut()
.push((handler, NativeCpsI64HandlerEnv { entry, env, slots }));
});
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_set_pending_escape_env_targets_i64(
ptr: *const i64,
len: i64,
) -> i64 {
let targets = unsafe { native_i64_slice(ptr, len) };
NATIVE_CPS_I64_PENDING_ESCAPE_ENV_TARGETS.with(|slot| {
*slot.borrow_mut() = targets.to_vec();
});
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_install_handler_i64(handler: i64, consumes_mask: i64) -> i64 {
let envs = take_pending_native_i64_handler_envs(handler);
let frame = NativeCpsI64HandlerFrame {
handler,
consumes_mask,
guard_stack: native_cps_i64_snapshot(current_native_i64_guard_stack()),
envs,
prompt: 0,
install_eval_id: NATIVE_CPS_I64_SYNTHETIC_EVAL_ID,
escape_owner_function_id: 0,
threshold_owner_function_id: 0,
inherited: false,
escape_continuation: 0,
escape_env: Box::new([]),
escape_env_targets: Box::new([]),
return_frame_threshold: 0,
return_frame_prefix: native_cps_i64_snapshot(Vec::new()),
origin: NativeCpsI64HandlerFrameOrigin::LegacyInstall,
};
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| {
stack.borrow_mut().push(frame);
});
if jit_trace_enabled() {
eprintln!("[JIT-CPS] install_handler (legacy): handler={}", handler);
}
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_uninstall_handler_i64(handler: i64) -> i64 {
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| {
let mut stack = stack.borrow_mut();
if let Some(pos) = stack.iter().rposition(|frame| frame.handler == handler) {
stack.remove(pos);
}
});
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_fresh_prompt_i64() -> i64 {
NATIVE_CPS_I64_NEXT_PROMPT.with(|next| {
let id = *next.borrow();
*next.borrow_mut() = id.wrapping_add(1);
id as i64
})
}
fn install_native_i64_handler_full(
handler: i64,
consumes_mask: i64,
escape_continuation: i64,
return_frame_threshold: i64,
prompt: i64,
install_eval_id: i64,
escape_owner_function_id: i64,
inherited: i64,
escape_env: Vec<i64>,
) {
let envs = take_pending_native_i64_handler_envs(handler);
let trace_envs = jit_trace_enabled().then(|| format_handler_envs(&envs));
let threshold = return_frame_threshold.max(0) as usize;
let escape_owner = escape_owner_function_id.max(0) as u64;
let threshold_owner_function_id = if threshold == 0 {
escape_owner
} else {
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| {
frames
.borrow()
.get(threshold - 1)
.map(|frame| frame.owner_function_id)
.unwrap_or(escape_owner)
})
};
let return_frame_prefix = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| {
frames
.borrow()
.iter()
.take(threshold)
.cloned()
.collect::<Vec<_>>()
});
let frame = NativeCpsI64HandlerFrame {
handler,
consumes_mask,
guard_stack: native_cps_i64_snapshot(current_native_i64_guard_stack()),
envs,
prompt: prompt as u64,
install_eval_id: install_eval_id as u64,
escape_owner_function_id: escape_owner,
threshold_owner_function_id,
inherited: inherited != 0,
escape_continuation: escape_continuation as usize,
escape_env: escape_env.into_boxed_slice(),
escape_env_targets: take_pending_native_i64_escape_env_targets().into_boxed_slice(),
return_frame_threshold: threshold,
return_frame_prefix: native_cps_i64_snapshot(return_frame_prefix),
origin: NativeCpsI64HandlerFrameOrigin::RealInstall,
};
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| {
stack.borrow_mut().push(frame);
});
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] install_handler_full: handler={} prompt={} install_eval={} owner_fn={} consumes={} threshold={} threshold_owner={} escape={:#x} envs={}",
handler,
prompt,
install_eval_id,
escape_owner_function_id,
consumes_mask,
return_frame_threshold,
threshold_owner_function_id,
escape_continuation as usize,
trace_envs.as_deref().unwrap_or("[]")
);
}
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_install_handler_full_i64_0(
handler: i64,
consumes_mask: i64,
escape_continuation: i64,
return_frame_threshold: i64,
prompt: i64,
install_eval_id: i64,
escape_owner_function_id: i64,
inherited: i64,
) -> i64 {
install_native_i64_handler_full(
handler,
consumes_mask,
escape_continuation,
return_frame_threshold,
prompt,
install_eval_id,
escape_owner_function_id,
inherited,
Vec::new(),
);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_install_handler_full_i64_1(
handler: i64,
consumes_mask: i64,
escape_continuation: i64,
return_frame_threshold: i64,
prompt: i64,
install_eval_id: i64,
escape_owner_function_id: i64,
inherited: i64,
a: i64,
) -> i64 {
install_native_i64_handler_full(
handler,
consumes_mask,
escape_continuation,
return_frame_threshold,
prompt,
install_eval_id,
escape_owner_function_id,
inherited,
vec![a],
);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_install_handler_full_i64_2(
handler: i64,
consumes_mask: i64,
escape_continuation: i64,
return_frame_threshold: i64,
prompt: i64,
install_eval_id: i64,
escape_owner_function_id: i64,
inherited: i64,
a: i64,
b: i64,
) -> i64 {
install_native_i64_handler_full(
handler,
consumes_mask,
escape_continuation,
return_frame_threshold,
prompt,
install_eval_id,
escape_owner_function_id,
inherited,
vec![a, b],
);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_install_handler_full_i64_3(
handler: i64,
consumes_mask: i64,
escape_continuation: i64,
return_frame_threshold: i64,
prompt: i64,
install_eval_id: i64,
escape_owner_function_id: i64,
inherited: i64,
a: i64,
b: i64,
c: i64,
) -> i64 {
install_native_i64_handler_full(
handler,
consumes_mask,
escape_continuation,
return_frame_threshold,
prompt,
install_eval_id,
escape_owner_function_id,
inherited,
vec![a, b, c],
);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_install_handler_full_i64_4(
handler: i64,
consumes_mask: i64,
escape_continuation: i64,
return_frame_threshold: i64,
prompt: i64,
install_eval_id: i64,
escape_owner_function_id: i64,
inherited: i64,
a: i64,
b: i64,
c: i64,
d: i64,
) -> i64 {
install_native_i64_handler_full(
handler,
consumes_mask,
escape_continuation,
return_frame_threshold,
prompt,
install_eval_id,
escape_owner_function_id,
inherited,
vec![a, b, c, d],
);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_abort_i64(value: i64) -> i64 {
NATIVE_CPS_I64_ABORT.with(|slot| {
*slot.borrow_mut() = NativeCpsI64Abort::Global(value);
});
value
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_abort_active_i64() -> i64 {
NATIVE_CPS_I64_ABORT.with(|slot| {
let value = slot.borrow().clone();
let active = value.is_active();
if jit_trace_enabled() && active {
eprintln!(
"[JIT-CPS] abort_active: value={}",
describe_native_i64_value(value.value_or_zero())
);
}
i64::from(active)
})
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_abort_should_return_i64() -> i64 {
i64::from(yulang_cps_abort_mode_i64() == 1)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_routed_jump_should_return_i64() -> i64 {
NATIVE_CPS_I64_ABORT.with(|slot| i64::from(slot.borrow().routed_jump_should_return()))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_abort_mode_i64() -> i64 {
NATIVE_CPS_I64_ABORT.with(|slot| {
let abort = slot.borrow().clone();
let frame_len = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow().len());
let mode = abort.mode_at_frame_len(frame_len);
if jit_trace_enabled() && mode != 0 {
eprintln!(
"[JIT-CPS] abort_mode: mode={} frame_len={} value={}",
mode,
frame_len,
describe_native_i64_value(abort.value_or_zero())
);
}
mode
})
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_abort_value_i64() -> i64 {
NATIVE_CPS_I64_ABORT.with(|slot| slot.borrow().value_or_zero())
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_consume_abort_i64() -> i64 {
consume_native_i64_abort()
}
fn consume_native_i64_abort() -> i64 {
let cleared_abort = NATIVE_CPS_I64_ABORT.with(|slot| {
let mut slot = slot.borrow_mut();
let cleared_abort = std::mem::take(&mut *slot);
*slot = NativeCpsI64Abort::None;
cleared_abort
});
match cleared_abort {
NativeCpsI64Abort::None => 0,
NativeCpsI64Abort::Global(value) => value,
NativeCpsI64Abort::Scoped {
value,
return_frame_threshold,
restore_frames,
propagate_at_threshold: false,
} => {
restore_native_i64_return_frame_prefix(&restore_frames);
let frame_len = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow().len());
let offset = return_frame_threshold.saturating_sub(frame_len);
if offset > 0 {
NATIVE_CPS_I64_HANDLER_THRESHOLD_OFFSET.with(|slot| {
let mut slot = slot.borrow_mut();
*slot = (*slot).max(offset);
});
}
value
}
NativeCpsI64Abort::Scoped { value, .. } => value,
NativeCpsI64Abort::RoutedJump {
value,
continuation,
env,
handlers,
guards,
return_frames,
eval_context,
..
} => resume_native_i64_routed_jump(
value,
continuation,
env,
handlers,
guards,
return_frames,
eval_context,
),
}
}
fn resume_native_i64_routed_jump(
value: i64,
continuation: usize,
env: Box<[i64]>,
handlers: Vec<NativeCpsI64HandlerFrame>,
guards: Vec<i64>,
return_frames: NativeCpsI64ReturnFrameSnapshot,
eval_context: NativeCpsI64EvalContext,
) -> i64 {
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| *stack.borrow_mut() = handlers);
NATIVE_CPS_I64_GUARD_STACK.with(|stack| *stack.borrow_mut() = guards);
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow_mut().clear());
park_native_i64_routed_return_frames(eval_context, return_frames);
NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| *ctx.borrow_mut() = eval_context);
call_native_i64_continuation(continuation, &env, value)
}
fn park_native_i64_routed_return_frames(
eval_context: NativeCpsI64EvalContext,
frames: NativeCpsI64ReturnFrameSnapshot,
) {
NATIVE_CPS_I64_PENDING_ROUTED_RETURN_FRAMES.with(|pending| {
*pending.borrow_mut() = Some(NativeCpsI64PendingRoutedReturnFrames::new(
eval_context.initial_frame_count,
frames,
));
});
}
fn clear_pending_native_i64_routed_return_frames() {
NATIVE_CPS_I64_PENDING_ROUTED_RETURN_FRAMES.with(|pending| *pending.borrow_mut() = None);
}
fn take_pending_native_i64_routed_return_frames(
initial: usize,
) -> Option<NativeCpsI64PendingRoutedReturnFrames> {
NATIVE_CPS_I64_PENDING_ROUTED_RETURN_FRAMES.with(|pending| {
let mut pending = pending.borrow_mut();
let should_restore = pending
.as_ref()
.is_some_and(|frames| frames.should_restore_for_initial(initial));
if should_restore { pending.take() } else { None }
})
}
fn restore_pending_routed_return_frames_for_normal_return(initial: usize) {
let pending = take_pending_native_i64_routed_return_frames(initial);
if let Some(pending) = pending {
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| {
let mut frames = frames.borrow_mut();
if frames.is_empty() {
*frames = pending.frames.to_vec();
}
});
}
}
fn clear_native_i64_abort() {
let cleared_abort = NATIVE_CPS_I64_ABORT.with(|slot| {
let mut slot = slot.borrow_mut();
let cleared_abort = std::mem::take(&mut *slot);
*slot = NativeCpsI64Abort::None;
cleared_abort
});
let restore_consumed_frames = matches!(
cleared_abort,
NativeCpsI64Abort::Scoped {
propagate_at_threshold: false,
..
}
);
if restore_consumed_frames {
if let NativeCpsI64Abort::Scoped {
return_frame_threshold,
restore_frames,
..
} = cleared_abort.clone()
{
restore_native_i64_return_frame_prefix(&restore_frames);
let frame_len = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow().len());
let offset = return_frame_threshold.saturating_sub(frame_len);
if offset > 0 {
NATIVE_CPS_I64_HANDLER_THRESHOLD_OFFSET.with(|slot| {
let mut slot = slot.borrow_mut();
*slot = (*slot).max(offset);
});
}
}
}
}
fn restore_native_i64_return_frame_prefix(prefix: &[NativeCpsI64ReturnFrame]) {
if prefix.is_empty() {
return;
}
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| {
let mut frames = frames.borrow_mut();
let current = frames
.iter()
.map(|frame| (frame.debug_id, frame.clone()))
.collect::<HashMap<_, _>>();
*frames = prefix
.iter()
.map(|frame| {
current
.get(&frame.debug_id)
.cloned()
.unwrap_or_else(|| frame.clone())
})
.collect();
});
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_clear_abort_i64() -> i64 {
clear_native_i64_abort();
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_scope_return_i64(prompt: i64, target: i64, value: i64) -> i64 {
NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| {
*slot.borrow_mut() = NativeCpsI64ScopeReturn {
active: true,
prompt: prompt as u64,
target,
value,
};
});
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] scope_return_set: prompt={} target={} value={}",
prompt, target, value
);
}
value
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_scope_return_active_i64() -> i64 {
NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| i64::from(slot.borrow().active))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_scope_return_prompt_i64() -> i64 {
NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| slot.borrow().prompt as i64)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_scope_return_target_i64() -> i64 {
NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| slot.borrow().target)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_scope_return_value_i64() -> i64 {
NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| slot.borrow().value)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_clear_scope_return_i64() -> i64 {
NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| {
*slot.borrow_mut() = NativeCpsI64ScopeReturn::default();
});
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_fresh_eval_id_i64() -> i64 {
NATIVE_CPS_I64_NEXT_EVAL_ID.with(|next| {
let id = *next.borrow();
*next.borrow_mut() = id + 1;
id as i64
})
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_current_eval_id_i64() -> i64 {
NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| ctx.borrow().current_eval_id as i64)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_current_initial_frame_count_i64() -> i64 {
NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| ctx.borrow().initial_frame_count as i64)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_set_eval_context_i64(
eval_id: i64,
initial_frame_count: i64,
) -> i64 {
NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| {
*ctx.borrow_mut() = NativeCpsI64EvalContext {
current_eval_id: eval_id as u64,
initial_frame_count: initial_frame_count.max(0) as usize,
};
});
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] set_eval_context: eval={} initial={}",
eval_id, initial_frame_count
);
}
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_return_frame_len_i64() -> i64 {
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow().len() as i64)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_handler_return_frame_threshold_i64() -> i64 {
let frame_len = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow().len());
let offset = NATIVE_CPS_I64_HANDLER_THRESHOLD_OFFSET
.with(|slot| std::mem::take(&mut *slot.borrow_mut()));
(frame_len + offset) as i64
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_enter_handler_arm_i64() -> i64 {
let saved =
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| std::mem::take(&mut *frames.borrow_mut()));
let consumed_start = NATIVE_CPS_I64_CONSUMED_RETURN_FRAME_IDS.with(|ids| ids.borrow().len());
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] enter_handler_arm: saved_frames={}",
format_return_frames(&saved)
);
}
NATIVE_CPS_I64_HANDLER_ARM_RETURN_FRAME_SNAPSHOTS.with(|snapshots| {
snapshots
.borrow_mut()
.push(NativeCpsI64HandlerArmReturnFrameSnapshot {
frames: saved,
consumed_start,
});
});
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_exit_handler_arm_i64() -> i64 {
let snapshot = NATIVE_CPS_I64_HANDLER_ARM_RETURN_FRAME_SNAPSHOTS
.with(|snapshots| snapshots.borrow_mut().pop().unwrap_or_default());
let consumed_since = NATIVE_CPS_I64_CONSUMED_RETURN_FRAME_IDS.with(|ids| {
ids.borrow()
.iter()
.skip(snapshot.consumed_start)
.copied()
.collect::<HashSet<_>>()
});
let restored: Vec<NativeCpsI64ReturnFrame> = snapshot
.frames
.into_iter()
.filter(|frame| !consumed_since.contains(&frame.debug_id))
.collect();
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] exit_handler_arm: restored_frames={}",
format_return_frames(&restored)
);
}
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| *frames.borrow_mut() = restored);
0
}
fn push_native_i64_return_frame_with_env(
prompt_exit: Option<NativeCpsI64PromptExitFrame>,
continuation: i64,
continuation_id: i64,
env: Vec<i64>,
owner_initial_frame_count: i64,
owner_eval_id: i64,
owner_function_id: i64,
immediately_forces_param: i64,
) {
let handlers = NATIVE_CPS_I64_HANDLER_STACK.with(|stack| stack.borrow().clone());
let guards = NATIVE_CPS_I64_GUARD_STACK.with(|stack| stack.borrow().clone());
let env_len = env.len();
let len_before = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow().len());
let debug_id = next_native_i64_return_frame_debug_id();
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| {
frames.borrow_mut().push(NativeCpsI64ReturnFrame {
prompt_exit,
debug_id,
continuation: continuation as usize,
continuation_id: continuation_id.max(0) as u32,
env: native_cps_i64_snapshot(env),
handlers: native_cps_i64_snapshot(handlers),
guards: native_cps_i64_snapshot(guards),
owner_initial_frame_count: owner_initial_frame_count.max(0) as usize,
owner_eval_id: owner_eval_id as u64,
owner_function_id: owner_function_id.max(0) as u64,
immediately_forces_param: immediately_forces_param != 0,
});
});
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] push_return_frame: id={} k={} len_before={} len_after={} cont={:#x} env_len={} owner_initial={} owner_eval={} owner_fn={} immediate_force={}",
debug_id,
continuation_id,
len_before,
len_before + 1,
continuation as usize,
env_len,
owner_initial_frame_count,
owner_eval_id,
owner_function_id,
immediately_forces_param != 0,
);
}
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_push_return_frame_i64_0(
continuation: i64,
continuation_id: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: i64,
) -> i64 {
push_native_i64_return_frame_with_env(
None,
continuation,
continuation_id,
Vec::new(),
owner_initial,
owner_eval,
owner_function,
immediately_forces,
);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_push_return_frame_i64_1(
continuation: i64,
continuation_id: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: i64,
a: i64,
) -> i64 {
push_native_i64_return_frame_with_env(
None,
continuation,
continuation_id,
vec![a],
owner_initial,
owner_eval,
owner_function,
immediately_forces,
);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_push_return_frame_i64_2(
continuation: i64,
continuation_id: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: i64,
a: i64,
b: i64,
) -> i64 {
push_native_i64_return_frame_with_env(
None,
continuation,
continuation_id,
vec![a, b],
owner_initial,
owner_eval,
owner_function,
immediately_forces,
);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_push_return_frame_i64_3(
continuation: i64,
continuation_id: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: i64,
a: i64,
b: i64,
c: i64,
) -> i64 {
push_native_i64_return_frame_with_env(
None,
continuation,
continuation_id,
vec![a, b, c],
owner_initial,
owner_eval,
owner_function,
immediately_forces,
);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_push_return_frame_i64_4(
continuation: i64,
continuation_id: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: i64,
a: i64,
b: i64,
c: i64,
d: i64,
) -> i64 {
push_native_i64_return_frame_with_env(
None,
continuation,
continuation_id,
vec![a, b, c, d],
owner_initial,
owner_eval,
owner_function,
immediately_forces,
);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_push_return_frame_i64_many(
continuation: i64,
continuation_id: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: i64,
ptr: *const i64,
len: i64,
) -> i64 {
push_native_i64_return_frame_with_env(
None,
continuation,
continuation_id,
unsafe { native_i64_slice(ptr, len) },
owner_initial,
owner_eval,
owner_function,
immediately_forces,
);
0
}
fn push_native_i64_prompt_exit_frame_with_env(
prompt: i64,
continuation: i64,
continuation_id: i64,
env: Vec<i64>,
owner_initial_frame_count: i64,
owner_eval_id: i64,
owner_function_id: i64,
immediately_forces_param: i64,
) {
push_native_i64_return_frame_with_env(
Some(NativeCpsI64PromptExitFrame {
prompt: prompt as u64,
}),
continuation,
continuation_id,
env,
owner_initial_frame_count,
owner_eval_id,
owner_function_id,
immediately_forces_param,
);
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_push_prompt_exit_frame_i64_0(
prompt: i64,
continuation: i64,
continuation_id: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: i64,
) -> i64 {
push_native_i64_prompt_exit_frame_with_env(
prompt,
continuation,
continuation_id,
Vec::new(),
owner_initial,
owner_eval,
owner_function,
immediately_forces,
);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_push_prompt_exit_frame_i64_many(
prompt: i64,
continuation: i64,
continuation_id: i64,
owner_initial: i64,
owner_eval: i64,
owner_function: i64,
immediately_forces: i64,
ptr: *const i64,
len: i64,
) -> i64 {
push_native_i64_prompt_exit_frame_with_env(
prompt,
continuation,
continuation_id,
unsafe { native_i64_slice(ptr, len) },
owner_initial,
owner_eval,
owner_function,
immediately_forces,
);
0
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_continue_return_frame_i64(value: i64) -> i64 {
let frame = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow_mut().pop());
let Some(frame) = frame else {
return value;
};
NATIVE_CPS_I64_CONSUMED_RETURN_FRAME_IDS.with(|ids| {
let mut ids = ids.borrow_mut();
if !ids.contains(&frame.debug_id) {
ids.push(frame.debug_id);
}
});
let mut restored_handlers = frame.handlers.to_vec();
append_resume_with_handler_siblings(&mut restored_handlers);
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] continue_return_frame: id={} owner_eval={} owner_initial={} restored_handlers_len={} restored_guards_len={} value={}",
frame.debug_id,
frame.owner_eval_id,
frame.owner_initial_frame_count,
restored_handlers.len(),
frame.guards.len(),
describe_native_i64_value(value),
);
}
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| *stack.borrow_mut() = restored_handlers);
NATIVE_CPS_I64_GUARD_STACK.with(|stack| *stack.borrow_mut() = frame.guards.to_vec());
NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| {
*ctx.borrow_mut() = NativeCpsI64EvalContext {
current_eval_id: frame.owner_eval_id,
initial_frame_count: frame.owner_initial_frame_count,
};
});
call_native_i64_continuation(frame.continuation, &frame.env, value)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_top_return_frame_pre_force_i64() -> i64 {
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| {
frames
.borrow()
.last()
.map(|frame| i64::from(frame.immediately_forces_param))
.unwrap_or(0)
})
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_pre_force_top_frame_i64(value: i64) -> i64 {
let saved_eval_ctx = NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| *ctx.borrow());
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| {
let frames = frames.borrow();
let top = frames.last().expect("pre-force called with no frame");
NATIVE_CPS_I64_HANDLER_STACK.with(|stack| *stack.borrow_mut() = top.handlers.to_vec());
NATIVE_CPS_I64_GUARD_STACK.with(|stack| *stack.borrow_mut() = top.guards.to_vec());
NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| {
*ctx.borrow_mut() = NativeCpsI64EvalContext {
current_eval_id: top.owner_eval_id,
initial_frame_count: saved_eval_ctx.initial_frame_count,
};
});
});
let forced = yulang_cps_force_thunk_i64(value as usize);
match yulang_cps_abort_mode_i64() {
1 => {
return yulang_cps_abort_value_i64();
}
2 => {
if native_i64_abort_is_routed_jump() {
return yulang_cps_abort_value_i64();
}
return yulang_cps_consume_abort_i64();
}
_ => {}
}
if yulang_cps_abort_active_i64() != 0 {
return yulang_cps_abort_value_i64();
}
if yulang_cps_scope_return_active_i64() != 0 {
return yulang_cps_route_scope_return_i64(forced);
}
yulang_cps_continue_return_frame_i64(forced)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_return_i64(value: i64) -> i64 {
let mut value = value;
match yulang_cps_abort_mode_i64() {
1 => {
return yulang_cps_abort_value_i64();
}
2 => {
if native_i64_abort_is_routed_jump() {
return yulang_cps_abort_value_i64();
}
value = yulang_cps_consume_abort_i64();
}
_ => {}
}
let frame_len = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow().len());
let initial = NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| ctx.borrow().initial_frame_count);
if frame_len == 0 {
restore_pending_routed_return_frames_for_normal_return(initial);
}
let frame_len = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow().len());
if frame_len <= initial {
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] return_i64: value={} frame_len={} initial={} action=noop",
describe_native_i64_value(value),
frame_len,
initial
);
}
return value;
}
let is_thunk = NATIVE_CPS_I64_THUNKS.with(|thunks| thunks.borrow().contains(&(value as usize)));
let top_forces = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| {
frames
.borrow()
.last()
.map(|frame| frame.immediately_forces_param)
.unwrap_or(false)
});
if is_thunk && top_forces {
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] return_i64: value={} frame_len={} initial={} action=pre_force",
describe_native_i64_value(value),
frame_len,
initial
);
}
return yulang_cps_pre_force_top_frame_i64(value);
}
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] return_i64: value={} frame_len={} initial={} action=continue",
describe_native_i64_value(value),
frame_len,
initial
);
}
yulang_cps_continue_return_frame_i64(value)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_selected_handler_env_or_i64(entry: i64, fallback: i64) -> i64 {
let selected_handler = NATIVE_CPS_I64_SELECTED_HANDLER_ID.with(|handler| *handler.borrow());
if let Some(value) = NATIVE_CPS_I64_RESUME_WITH_HANDLER_SIBLINGS.with(|siblings| {
siblings
.borrow()
.iter()
.rev()
.filter(|handler| handler.handler == selected_handler)
.flat_map(|handler| handler.envs.iter().rev())
.find(|env| env.entry == entry)
.map(|env| env.env)
}) {
NATIVE_CPS_I64_SELECTED_HANDLER_USED_RWH_ENV.with(|used| *used.borrow_mut() = true);
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] selected_handler_env: entry={} fallback={} value={} source=rwh_sibling",
entry,
describe_native_i64_value(fallback),
describe_native_i64_value(value)
);
}
return value;
}
let value = NATIVE_CPS_I64_SELECTED_HANDLER_ENVS.with(|envs| {
envs.borrow()
.iter()
.rev()
.find(|env| env.entry == entry)
.map(|env| env.env)
.unwrap_or(fallback)
});
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] selected_handler_env: entry={} fallback={} value={}",
entry,
describe_native_i64_value(fallback),
describe_native_i64_value(value)
);
}
value
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_selected_handler_owner_function_i64() -> i64 {
NATIVE_CPS_I64_SELECTED_HANDLER_OWNER_FUNCTION_ID.with(|owner| *owner.borrow() as i64)
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_thunk_i64_0(code: usize) -> usize {
make_native_i64_thunk(code, Vec::new())
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_thunk_i64_1(code: usize, a: i64) -> usize {
make_native_i64_thunk(code, vec![a])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_thunk_i64_2(code: usize, a: i64, b: i64) -> usize {
make_native_i64_thunk(code, vec![a, b])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_thunk_i64_3(code: usize, a: i64, b: i64, c: i64) -> usize {
make_native_i64_thunk(code, vec![a, b, c])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_thunk_i64_4(
code: usize,
a: i64,
b: i64,
c: i64,
d: i64,
) -> usize {
make_native_i64_thunk(code, vec![a, b, c, d])
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_make_thunk_i64_many(
code: usize,
ptr: *const i64,
len: i64,
) -> usize {
make_native_i64_thunk(code, unsafe { native_i64_slice(ptr, len) })
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_is_thunk_i64(value: i64) -> i64 {
usize::try_from(value)
.ok()
.is_some_and(|value| NATIVE_CPS_I64_THUNKS.with(|thunks| thunks.borrow().contains(&value)))
.into()
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_force_thunk_i64(value: usize) -> i64 {
let mut value = value;
loop {
let is_thunk = NATIVE_CPS_I64_THUNKS.with(|thunks| thunks.borrow().contains(&value));
if !is_thunk {
if jit_trace_enabled() {
eprintln!(
"[JIT-CPS] force_thunk.out: value={}",
describe_native_i64_value(value as i64)
);
}
return value as i64;
}
let thunk = unsafe { &*(value as *const NativeCpsI64Thunk) };
if jit_trace_enabled() {
let frames = NATIVE_CPS_I64_RETURN_FRAMES.with(|f| f.borrow().clone());
let eval = NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| *ctx.borrow());
eprintln!(
"[JIT-CPS] force_thunk: thunk={:#x} eval={} initial={} frames={}",
value,
eval.current_eval_id,
eval.initial_frame_count,
format_return_frames(&frames),
);
}
let current_handlers_empty = NATIVE_CPS_I64_HANDLER_STACK.with(|s| s.borrow().is_empty());
let current_guards_empty = NATIVE_CPS_I64_GUARD_STACK.with(|s| s.borrow().is_empty());
let handlers = if current_handlers_empty {
thunk.handlers.to_vec()
} else {
NATIVE_CPS_I64_HANDLER_STACK.with(|s| s.borrow().clone())
};
let guards = if current_guards_empty {
thunk.guard_stack.to_vec()
} else {
NATIVE_CPS_I64_GUARD_STACK.with(|s| s.borrow().clone())
};
let mut active_blocked = NATIVE_CPS_I64_ACTIVE_BLOCKED.with(|stack| stack.borrow().clone());
active_blocked.extend(thunk.active_blocked.iter().copied());
let saved_frames = NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| frames.borrow().clone());
let consumed_start =
NATIVE_CPS_I64_CONSUMED_RETURN_FRAME_IDS.with(|ids| ids.borrow().len());
let saved_eval_ctx = NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| *ctx.borrow());
let result = with_native_i64_cps_state_and_active(handlers, guards, active_blocked, || {
(thunk.code)(thunk.env.as_ptr())
});
if native_i64_abort_should_consume_after_thunk_force() {
return yulang_cps_consume_abort_i64();
}
let routed_frames = NATIVE_CPS_I64_RETURN_FRAMES_ROUTED
.with(|routed| std::mem::take(&mut *routed.borrow_mut()));
let active_abort = NATIVE_CPS_I64_ABORT.with(|slot| slot.borrow().clone());
let active_scope_return = NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| slot.borrow().active);
let propagating_non_local = active_scope_return || active_abort.is_active();
if propagating_non_local && (!routed_frames || active_scope_return) {
let consumed_since = NATIVE_CPS_I64_CONSUMED_RETURN_FRAME_IDS.with(|ids| {
ids.borrow()
.iter()
.skip(consumed_start)
.copied()
.collect::<HashSet<_>>()
});
let restore_consumed = matches!(
active_abort,
NativeCpsI64Abort::Scoped {
propagate_at_threshold: false,
..
}
) || active_scope_return;
let restored = if restore_consumed {
saved_frames.clone()
} else {
saved_frames
.clone()
.into_iter()
.filter(|frame| !consumed_since.contains(&frame.debug_id))
.collect()
};
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| {
*frames.borrow_mut() = restored;
});
} else if !routed_frames {
NATIVE_CPS_I64_RETURN_FRAMES.with(|frames| {
let mut frames = frames.borrow_mut();
let keep_len = frames
.iter()
.zip(saved_frames.iter())
.take_while(|(current, saved)| current.debug_id == saved.debug_id)
.count();
frames.truncate(keep_len);
});
}
NATIVE_CPS_I64_EVAL_CONTEXT.with(|ctx| *ctx.borrow_mut() = saved_eval_ctx);
NATIVE_CPS_I64_SCOPE_RETURN.with(|slot| {
let mut slot = slot.borrow_mut();
if slot.active && slot.value == value as i64 {
slot.value = result;
}
});
if yulang_cps_abort_active_i64() != 0 || yulang_cps_scope_return_active_i64() != 0 {
return result;
}
value = result as usize;
}
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_fresh_guard_i64() -> i64 {
let id = NATIVE_CPS_I64_NEXT_GUARD.with(|next| {
let mut next = next.borrow_mut();
let id = *next;
*next += 1;
id
});
NATIVE_CPS_I64_GUARD_STACK.with(|stack| {
stack.borrow_mut().push(id);
});
id
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_peek_guard_i64() -> i64 {
NATIVE_CPS_I64_GUARD_STACK.with(|stack| stack.borrow().last().copied().unwrap_or(0))
}
#[unsafe(no_mangle)]
pub(super) extern "C" fn yulang_cps_find_guard_i64(id: i64) -> i64 {
NATIVE_CPS_I64_GUARD_STACK.with(|stack| i64::from(stack.borrow().contains(&id)))
}