Skip to main content

yulang_native/
cps_repr_cranelift.rs

1use std::collections::{HashMap, HashSet};
2use std::fmt;
3
4use cranelift_codegen::ir::{self, AbiParam, InstBuilder, types};
5use cranelift_codegen::settings;
6use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
7use cranelift_jit::{JITBuilder, JITModule};
8use cranelift_module::{DataDescription, FuncId, Linkage, Module};
9use cranelift_object::{ObjectBuilder, ObjectModule};
10use yulang_runtime as runtime;
11use yulang_typed_ir as typed_ir;
12
13use crate::cps_ir::{
14    CpsContinuationId, CpsHandlerId, CpsLiteral, CpsRecordField, CpsStmt, CpsTerminator, CpsValueId,
15};
16use crate::cps_lower::{CpsLowerError, lower_cps_module};
17use crate::cps_optimize::{
18    CpsOptimizationOutput, CpsOptimizationProfile, optimize_cps_repr_abi_module,
19};
20use crate::cps_repr::CpsReprAbiLane;
21use crate::cps_repr_abi::{
22    CpsReprAbiContinuation, CpsReprAbiFunction, CpsReprAbiModule, CpsReprAbiValue,
23    lower_cps_repr_abi_module,
24};
25use crate::cps_validate::{CpsValidateError, validate_cps_module};
26
27mod helper_arity;
28mod runtime_i64;
29mod runtime_symbols;
30
31#[cfg(test)]
32use runtime_i64::describe_native_i64_value;
33use runtime_i64::{
34    describe_cps_repr_i64_value_with_hint, jit_trace_enabled, reset_native_i64_cps_state,
35    set_native_i64_root_function_ids, take_native_i64_root_result,
36};
37
38pub type CpsReprCraneliftResult<T> = Result<T, CpsReprCraneliftError>;
39
40#[derive(Debug, Clone, PartialEq)]
41pub enum CpsReprCraneliftError {
42    Lower(CpsLowerError),
43    Validate(CpsValidateError),
44    UnsupportedFunction {
45        function: String,
46        reason: &'static str,
47    },
48    UnsupportedLane {
49        function: String,
50        value: CpsValueId,
51        lane: CpsReprAbiLane,
52    },
53    UnsupportedReturnLane {
54        function: String,
55        continuation: CpsContinuationId,
56        lane: CpsReprAbiLane,
57    },
58    UnsupportedLiteral {
59        function: String,
60        literal: CpsLiteral,
61    },
62    UnsupportedPrimitive {
63        function: String,
64        op: typed_ir::PrimitiveOp,
65    },
66    UnsupportedStmt {
67        function: String,
68        kind: &'static str,
69    },
70    UnsupportedTerminator {
71        function: String,
72        kind: &'static str,
73    },
74    MissingFunction {
75        name: String,
76    },
77    MissingContinuation {
78        function: String,
79        continuation: CpsContinuationId,
80    },
81    MissingValue {
82        function: String,
83        value: CpsValueId,
84    },
85    InvalidReturnArity {
86        function: String,
87        arity: usize,
88    },
89    Cranelift(String),
90}
91
92impl fmt::Display for CpsReprCraneliftError {
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        match self {
95            CpsReprCraneliftError::Lower(error) => write!(f, "{error}"),
96            CpsReprCraneliftError::Validate(error) => write!(f, "{error}"),
97            CpsReprCraneliftError::UnsupportedFunction { function, reason } => write!(
98                f,
99                "CPS repr Cranelift prototype does not support `{function}` yet: {reason}"
100            ),
101            CpsReprCraneliftError::UnsupportedLane {
102                function,
103                value,
104                lane,
105            } => write!(
106                f,
107                "CPS repr Cranelift prototype cannot lower value {value:?} with lane {lane:?} in `{function}`"
108            ),
109            CpsReprCraneliftError::UnsupportedReturnLane {
110                function,
111                continuation,
112                lane,
113            } => write!(
114                f,
115                "CPS repr Cranelift prototype cannot lower continuation {function}::{continuation:?} with return lane {lane:?}"
116            ),
117            CpsReprCraneliftError::UnsupportedLiteral { function, literal } => write!(
118                f,
119                "CPS repr Cranelift prototype does not support literal {literal:?} in `{function}` yet"
120            ),
121            CpsReprCraneliftError::UnsupportedPrimitive { function, op } => write!(
122                f,
123                "CPS repr Cranelift prototype does not support primitive {op:?} in `{function}` yet"
124            ),
125            CpsReprCraneliftError::UnsupportedStmt { function, kind } => write!(
126                f,
127                "CPS repr Cranelift prototype does not support {kind} statements in `{function}` yet"
128            ),
129            CpsReprCraneliftError::UnsupportedTerminator { function, kind } => write!(
130                f,
131                "CPS repr Cranelift prototype does not support {kind} terminators in `{function}` yet"
132            ),
133            CpsReprCraneliftError::MissingFunction { name } => {
134                write!(f, "CPS repr Cranelift function `{name}` is missing")
135            }
136            CpsReprCraneliftError::MissingContinuation {
137                function,
138                continuation,
139            } => write!(
140                f,
141                "CPS repr Cranelift continuation {continuation:?} is missing in `{function}`"
142            ),
143            CpsReprCraneliftError::MissingValue { function, value } => write!(
144                f,
145                "CPS repr Cranelift value {value:?} is missing in `{function}`"
146            ),
147            CpsReprCraneliftError::InvalidReturnArity { function, arity } => write!(
148                f,
149                "CPS repr Cranelift function `{function}` has {arity} return values"
150            ),
151            CpsReprCraneliftError::Cranelift(error) => write!(f, "{error}"),
152        }
153    }
154}
155
156impl std::error::Error for CpsReprCraneliftError {}
157
158impl From<CpsLowerError> for CpsReprCraneliftError {
159    fn from(error: CpsLowerError) -> Self {
160        Self::Lower(error)
161    }
162}
163
164impl From<CpsValidateError> for CpsReprCraneliftError {
165    fn from(error: CpsValidateError) -> Self {
166        Self::Validate(error)
167    }
168}
169
170pub struct CpsReprJitModule {
171    module: JITModule,
172    roots: Vec<FuncId>,
173    root_display_hints: Vec<CpsRootDisplayHint>,
174    root_function_ids: Vec<u64>,
175    cranelift_ir: Vec<String>,
176    optimization_profile: CpsOptimizationProfile,
177    _strings: Vec<Box<str>>,
178}
179
180impl CpsReprJitModule {
181    pub fn cranelift_ir(&self) -> &[String] {
182        &self.cranelift_ir
183    }
184
185    pub fn optimization_profile(&self) -> CpsOptimizationProfile {
186        self.optimization_profile
187    }
188
189    pub fn run_roots_display(&mut self) -> CpsReprCraneliftResult<Vec<String>> {
190        self.module
191            .finalize_definitions()
192            .map_err(cranelift_error)?;
193        self.roots
194            .iter()
195            .zip(self.root_display_hints.iter())
196            .map(|(root, hint)| {
197                reset_native_i64_cps_state();
198                set_native_i64_root_function_ids(&self.root_function_ids);
199                let ptr = self.module.get_finalized_function(*root);
200                let call = unsafe { std::mem::transmute::<_, extern "C" fn() -> i64>(ptr) };
201                let value = take_native_i64_root_result(call());
202                Ok(describe_cps_repr_i64_value_with_hint(value, hint))
203            })
204            .collect()
205    }
206
207    pub fn run_roots_i64(&mut self) -> CpsReprCraneliftResult<Vec<i64>> {
208        self.module
209            .finalize_definitions()
210            .map_err(cranelift_error)?;
211        self.roots
212            .iter()
213            .map(|root| {
214                reset_native_i64_cps_state();
215                set_native_i64_root_function_ids(&self.root_function_ids);
216                let ptr = self.module.get_finalized_function(*root);
217                let call = unsafe { std::mem::transmute::<_, extern "C" fn() -> i64>(ptr) };
218                let value = call();
219                Ok(take_native_i64_root_result(value))
220            })
221            .collect()
222    }
223}
224
225#[derive(Debug, Clone, PartialEq, Eq)]
226pub(super) enum CpsRootDisplayHint {
227    Plain,
228    Unit,
229    Bool,
230    Tuple(Vec<CpsRootDisplayHint>),
231    List(Box<CpsRootDisplayHint>),
232}
233
234#[derive(Debug, Clone, PartialEq, Eq)]
235pub struct CpsReprObjectModule {
236    bytes: Vec<u8>,
237    roots: Vec<String>,
238    root_lanes: Vec<CpsReprAbiLane>,
239    root_function_ids: Vec<u64>,
240    optimization_profile: CpsOptimizationProfile,
241}
242
243impl CpsReprObjectModule {
244    pub fn bytes(&self) -> &[u8] {
245        &self.bytes
246    }
247
248    pub fn roots(&self) -> &[String] {
249        &self.roots
250    }
251
252    pub fn root_lanes(&self) -> &[CpsReprAbiLane] {
253        &self.root_lanes
254    }
255
256    pub fn root_function_ids(&self) -> &[u64] {
257        &self.root_function_ids
258    }
259
260    pub fn optimization_profile(&self) -> CpsOptimizationProfile {
261        self.optimization_profile
262    }
263
264    pub fn into_bytes(self) -> Vec<u8> {
265        self.bytes
266    }
267}
268
269pub fn compile_cps_repr_abi_module(
270    module: &CpsReprAbiModule,
271) -> CpsReprCraneliftResult<CpsReprJitModule> {
272    compile_cps_repr_abi_module_with_root_hints(module, Vec::new())
273}
274
275fn compile_cps_repr_abi_module_with_root_hints(
276    module: &CpsReprAbiModule,
277    root_display_hints: Vec<CpsRootDisplayHint>,
278) -> CpsReprCraneliftResult<CpsReprJitModule> {
279    let optimized = if std::env::var_os("YULANG_CPS_JIT_DISABLE_OPT").is_some() {
280        CpsOptimizationOutput {
281            module: module.clone(),
282            profile: CpsOptimizationProfile::default(),
283        }
284    } else {
285        optimize_cps_repr_abi_module(module)
286    };
287    let module = &optimized.module;
288    validate_scalar_subset(module)?;
289
290    let mut builder =
291        JITBuilder::new(cranelift_module::default_libcall_names()).map_err(cranelift_error)?;
292    runtime_symbols::register_cps_runtime_symbols(&mut builder);
293    let mut jit = JITModule::new(builder);
294    let functions = declare_functions(&mut jit, module)?;
295    let mut strings = Vec::new();
296    let mut literals = HostLiteralStore {
297        strings: &mut strings,
298    };
299    let cranelift_ir = define_functions(&mut jit, module, &functions, &mut literals)?;
300    let roots = module
301        .roots
302        .iter()
303        .map(|root| {
304            functions.functions.get(&root.name).copied().ok_or_else(|| {
305                CpsReprCraneliftError::MissingFunction {
306                    name: root.name.clone(),
307                }
308            })
309        })
310        .collect::<CpsReprCraneliftResult<Vec<_>>>()?;
311    let root_display_hints = root_display_hints_for_len(root_display_hints, roots.len());
312    let root_function_ids = module
313        .roots
314        .iter()
315        .filter_map(|root| functions.function_ids.get(&root.name).copied())
316        .collect::<Vec<_>>();
317    Ok(CpsReprJitModule {
318        module: jit,
319        roots,
320        root_display_hints,
321        root_function_ids,
322        cranelift_ir,
323        optimization_profile: optimized.profile,
324        _strings: strings,
325    })
326}
327
328pub fn compile_cps_repr_abi_module_to_object(
329    module: &CpsReprAbiModule,
330) -> CpsReprCraneliftResult<CpsReprObjectModule> {
331    let optimized = if std::env::var_os("YULANG_CPS_JIT_DISABLE_OPT").is_some() {
332        CpsOptimizationOutput {
333            module: module.clone(),
334            profile: CpsOptimizationProfile::default(),
335        }
336    } else {
337        optimize_cps_repr_abi_module(module)
338    };
339    let module = &optimized.module;
340    validate_scalar_subset(module)?;
341
342    let isa_builder = cranelift_native::builder().map_err(cranelift_error)?;
343    let flags = settings::Flags::new(settings::builder());
344    let isa = isa_builder.finish(flags).map_err(cranelift_error)?;
345    let builder = ObjectBuilder::new(
346        isa,
347        "yulang_cps_repr_object".to_string(),
348        cranelift_module::default_libcall_names(),
349    )
350    .map_err(cranelift_error)?;
351    let mut object = ObjectModule::new(builder);
352    let functions = declare_functions(&mut object, module)?;
353    let mut literals = ObjectLiteralStore::default();
354    let _ = define_functions(&mut object, module, &functions, &mut literals)?;
355    let roots = module
356        .roots
357        .iter()
358        .map(|root| root.name.clone())
359        .collect::<Vec<_>>();
360    let root_lanes = module
361        .roots
362        .iter()
363        .map(|root| {
364            functions
365                .function_returns
366                .get(&root.name)
367                .copied()
368                .unwrap_or(CpsReprAbiLane::Unknown)
369        })
370        .collect::<Vec<_>>();
371    let root_function_ids = module
372        .roots
373        .iter()
374        .filter_map(|root| functions.function_ids.get(&root.name).copied())
375        .collect::<Vec<_>>();
376    let product = object.finish();
377    let bytes = product.emit().map_err(cranelift_error)?;
378    Ok(CpsReprObjectModule {
379        bytes,
380        roots,
381        root_lanes,
382        root_function_ids,
383        optimization_profile: optimized.profile,
384    })
385}
386
387pub fn compile_runtime_module_to_cps_repr_jit(
388    module: &runtime::Module,
389) -> CpsReprCraneliftResult<CpsReprJitModule> {
390    let root_display_hints = runtime_root_display_hints(module);
391    let cps = lower_cps_module(module)?;
392    validate_cps_module(&cps)?;
393    let repr = crate::cps_repr::lower_cps_repr_module(&cps);
394    let abi = lower_cps_repr_abi_module(&repr);
395    compile_cps_repr_abi_module_with_root_hints(&abi, root_display_hints)
396}
397
398pub fn compile_runtime_module_to_cps_repr_object(
399    module: &runtime::Module,
400) -> CpsReprCraneliftResult<CpsReprObjectModule> {
401    let cps = lower_cps_module(module)?;
402    validate_cps_module(&cps)?;
403    let repr = crate::cps_repr::lower_cps_repr_module(&cps);
404    let abi = lower_cps_repr_abi_module(&repr);
405    compile_cps_repr_abi_module_to_object(&abi)
406}
407
408fn declare_functions<M: Module>(
409    module_backend: &mut M,
410    module: &CpsReprAbiModule,
411) -> CpsReprCraneliftResult<DeclaredFunctions> {
412    let mut functions = HashMap::new();
413    let mut continuations = HashMap::new();
414    let mut function_ids = HashMap::new();
415    let mut function_returns = HashMap::new();
416    let mut function_params = HashMap::new();
417    let mut function_effect_flow = HashMap::new();
418    for (index, function) in module.functions.iter().chain(&module.roots).enumerate() {
419        let sig = function_signature(module_backend, function);
420        let id = module_backend
421            .declare_function(&function.name, Linkage::Export, &sig)
422            .map_err(cranelift_error)?;
423        functions.insert(function.name.clone(), id);
424        let function_id = (index + 1) as u64;
425        function_ids.insert(function.name.clone(), function_id);
426        if jit_trace_enabled() {
427            eprintln!("[JIT-CPS] function_id: {} {}", function_id, function.name);
428        }
429        let has_effect_flow = function_has_effect_flow(function);
430        function_effect_flow.insert(function.name.clone(), has_effect_flow);
431        function_returns.insert(
432            function.name.clone(),
433            continuation(function, function.entry)
434                .map(|entry| effective_continuation_return_lane(function, entry))
435                .unwrap_or(CpsReprAbiLane::Unknown),
436        );
437        function_params.insert(
438            function.name.clone(),
439            effective_function_param_lanes(function),
440        );
441        if has_effect_flow {
442            for continuation in &function.continuations {
443                let name = continuation_symbol(function, continuation.id);
444                let sig = continuation_signature(module_backend, function, continuation);
445                let id = module_backend
446                    .declare_function(&name, Linkage::Local, &sig)
447                    .map_err(cranelift_error)?;
448                continuations.insert((function.name.clone(), continuation.id), id);
449            }
450        }
451    }
452    Ok(DeclaredFunctions {
453        functions,
454        continuations,
455        function_ids,
456        function_returns,
457        function_params,
458        function_effect_flow,
459    })
460}
461
462fn root_display_hints_for_len(
463    mut hints: Vec<CpsRootDisplayHint>,
464    root_len: usize,
465) -> Vec<CpsRootDisplayHint> {
466    hints.resize(root_len, CpsRootDisplayHint::Plain);
467    hints.truncate(root_len);
468    hints
469}
470
471fn runtime_root_display_hints(module: &runtime::Module) -> Vec<CpsRootDisplayHint> {
472    module
473        .roots
474        .iter()
475        .map(|root| match root {
476            runtime::Root::Expr(index) => module
477                .root_exprs
478                .get(*index)
479                .map(|expr| root_display_hint_for_runtime_type(&expr.ty))
480                .unwrap_or(CpsRootDisplayHint::Plain),
481            runtime::Root::Binding(_) => CpsRootDisplayHint::Plain,
482        })
483        .collect()
484}
485
486fn root_display_hint_for_runtime_type(ty: &runtime::Type) -> CpsRootDisplayHint {
487    match ty {
488        runtime::Type::Core(core) => root_display_hint_for_core_type(core),
489        runtime::Type::Thunk { value, .. } => root_display_hint_for_runtime_type(value),
490        _ => CpsRootDisplayHint::Plain,
491    }
492}
493
494fn root_display_hint_for_core_type(ty: &typed_ir::Type) -> CpsRootDisplayHint {
495    match ty {
496        typed_ir::Type::Named { path, args }
497            if args.is_empty() && core_type_path_is(path, "unit") =>
498        {
499            CpsRootDisplayHint::Unit
500        }
501        typed_ir::Type::Named { path, args }
502            if args.is_empty() && core_type_path_is(path, "bool") =>
503        {
504            CpsRootDisplayHint::Bool
505        }
506        typed_ir::Type::Named { path, args } if core_type_path_is(path, "list") => args
507            .iter()
508            .find_map(root_display_hint_for_type_arg)
509            .map(|item| CpsRootDisplayHint::List(Box::new(item)))
510            .unwrap_or(CpsRootDisplayHint::Plain),
511        typed_ir::Type::Tuple(items) => {
512            CpsRootDisplayHint::Tuple(items.iter().map(root_display_hint_for_core_type).collect())
513        }
514        _ => CpsRootDisplayHint::Plain,
515    }
516}
517
518fn root_display_hint_for_type_arg(arg: &typed_ir::TypeArg) -> Option<CpsRootDisplayHint> {
519    match arg {
520        typed_ir::TypeArg::Type(ty) => Some(root_display_hint_for_core_type(ty)),
521        typed_ir::TypeArg::Bounds(bounds) => bounds
522            .lower
523            .as_deref()
524            .or(bounds.upper.as_deref())
525            .map(root_display_hint_for_core_type),
526    }
527}
528
529fn core_type_path_is(path: &typed_ir::Path, name: &str) -> bool {
530    match path.segments.as_slice() {
531        [single] => single.0 == name,
532        [std, _, last] => std.0 == "std" && last.0 == name,
533        _ => false,
534    }
535}
536
537fn define_functions<M: Module, L: CpsLiteralStore>(
538    module_backend: &mut M,
539    module: &CpsReprAbiModule,
540    functions: &DeclaredFunctions,
541    literals: &mut L,
542) -> CpsReprCraneliftResult<Vec<String>> {
543    let mut cranelift_ir = Vec::new();
544    let handlers = HandlerRegistry::new(module);
545    for function in module.functions.iter().chain(&module.roots) {
546        let id = functions
547            .functions
548            .get(&function.name)
549            .copied()
550            .ok_or_else(|| CpsReprCraneliftError::MissingFunction {
551                name: function.name.clone(),
552            })?;
553        if function_has_effect_flow(function) {
554            cranelift_ir.extend(define_effectful_function(
555                module_backend,
556                function,
557                functions,
558                &handlers,
559                literals,
560            )?);
561        }
562        let mut ctx = module_backend.make_context();
563        ctx.func.signature = function_signature(module_backend, function);
564        if function_has_effect_flow(function) {
565            lower_effectful_function_wrapper(module_backend, &mut ctx, function, functions)?;
566        } else {
567            lower_function(module_backend, &mut ctx, function, functions, literals)?;
568        }
569        let ir_dump = format!(
570            ";; cps-repr function {}\n{}",
571            function.name,
572            ctx.func.display()
573        );
574        if std::env::var_os("YULANG_DUMP_CRANELIFT_IR").is_some() {
575            eprintln!("{ir_dump}");
576        }
577        cranelift_ir.push(ir_dump);
578        module_backend
579            .define_function(id, &mut ctx)
580            .map_err(cranelift_error)?;
581        module_backend.clear_context(&mut ctx);
582    }
583    Ok(cranelift_ir)
584}
585
586#[derive(Debug, Default)]
587struct DeclaredFunctions {
588    functions: HashMap<String, FuncId>,
589    continuations: HashMap<(String, CpsContinuationId), FuncId>,
590    function_ids: HashMap<String, u64>,
591    function_returns: HashMap<String, CpsReprAbiLane>,
592    function_params: HashMap<String, Vec<CpsReprAbiLane>>,
593    function_effect_flow: HashMap<String, bool>,
594}
595
596#[derive(Debug, Clone)]
597struct HandlerCandidate {
598    handler: CpsHandlerId,
599    function: String,
600    entry: CpsContinuationId,
601    continues_to_installed_escape: bool,
602}
603
604#[derive(Debug, Clone, Default)]
605struct HandlerRegistry {
606    candidates: Vec<(typed_ir::Path, HandlerCandidate)>,
607    effects: Vec<RegisteredEffect>,
608}
609
610#[derive(Debug, Default)]
611struct LocalValueCache {
612    native_float: HashMap<CpsValueId, ir::Value>,
613}
614
615use self::helper_arity::{
616    EFFECTFUL_APPLY_RESUMPTION_HELPERS, INSTALL_HANDLER_FULL_HELPERS, MAKE_CLOSURE_HELPERS,
617    MAKE_ENV_HELPERS, MAKE_RECURSIVE_CLOSURE_HELPERS, MAKE_RESUMPTION_HELPERS, MAKE_THUNK_HELPERS,
618    PUSH_RETURN_FRAME_HELPERS, TUPLE_HELPERS,
619};
620
621struct CpsCraneliftLowerCx<'a, 'builder, M: Module, L: CpsLiteralStore> {
622    module_backend: &'a mut M,
623    builder: &'a mut FunctionBuilder<'builder>,
624    function: &'a CpsReprAbiFunction,
625    functions: &'a DeclaredFunctions,
626    handlers: &'a HandlerRegistry,
627    literals: &'a mut L,
628    values: &'a mut LocalValueCache,
629}
630
631struct PerformTerminatorCase<'a> {
632    effect: &'a typed_ir::Path,
633    payload: CpsValueId,
634    resume: CpsContinuationId,
635    handler: CpsHandlerId,
636    blocked: Option<CpsValueId>,
637}
638
639struct EffectfulCallTerminatorCase<'a> {
640    target: &'a str,
641    args: &'a [CpsValueId],
642    resume: CpsContinuationId,
643}
644
645struct EffectfulForceTerminatorCase {
646    thunk: CpsValueId,
647    resume: CpsContinuationId,
648}
649
650struct EffectfulApplyTerminatorCase {
651    closure: CpsValueId,
652    arg: CpsValueId,
653    resume: CpsContinuationId,
654}
655
656#[derive(Debug, Clone)]
657struct RegisteredEffect {
658    function: String,
659    path: typed_ir::Path,
660}
661
662impl HandlerRegistry {
663    fn new(module: &CpsReprAbiModule) -> Self {
664        let candidates = module
665            .functions
666            .iter()
667            .chain(&module.roots)
668            .flat_map(|function| {
669                function.handlers.iter().flat_map(|handler| {
670                    handler.arms.iter().map(|arm| {
671                        (
672                            arm.effect.clone(),
673                            HandlerCandidate {
674                                handler: handler.id,
675                                function: function.name.clone(),
676                                entry: arm.entry,
677                                continues_to_installed_escape:
678                                    handler_arm_continues_to_installed_escape(
679                                        function, handler.id, arm.entry,
680                                    ) || handler_arm_uses_resume_with_handler(function, arm.entry),
681                            },
682                        )
683                    })
684                })
685            })
686            .collect::<Vec<_>>();
687        let mut effects = Vec::new();
688        for (effect, candidate) in &candidates {
689            if !effects.iter().any(|existing: &RegisteredEffect| {
690                existing.function == candidate.function && existing.path == *effect
691            }) {
692                effects.push(RegisteredEffect {
693                    function: candidate.function.clone(),
694                    path: effect.clone(),
695                });
696            }
697        }
698        if jit_trace_enabled() {
699            for (index, effect) in effects.iter().enumerate() {
700                eprintln!(
701                    "[JIT-CPS] registered_effect: bit={} function={} path={}",
702                    index,
703                    effect.function,
704                    format_typed_path(&effect.path),
705                );
706            }
707            for (effect, candidate) in &candidates {
708                eprintln!(
709                    "[JIT-CPS] handler_candidate: handler={} function={} entry={} effect={} escape={}",
710                    candidate.handler.0,
711                    candidate.function,
712                    candidate.entry.0,
713                    format_typed_path(effect),
714                    candidate.continues_to_installed_escape
715                );
716            }
717        }
718        Self {
719            candidates,
720            effects,
721        }
722    }
723
724    fn candidates_for_effect(&self, effect: &typed_ir::Path) -> Vec<HandlerCandidate> {
725        self.candidates
726            .iter()
727            .filter(|(expected, _)| effect_matches(expected, effect))
728            .map(|(_, candidate)| candidate.clone())
729            .collect()
730    }
731
732    fn effect_mask(
733        &self,
734        function: &CpsReprAbiFunction,
735        effect: &typed_ir::Path,
736    ) -> CpsReprCraneliftResult<i64> {
737        let mut mask = 0_i64;
738        for (index, registered) in self.effects.iter().enumerate() {
739            if index >= 62 {
740                return Err(CpsReprCraneliftError::UnsupportedFunction {
741                    function: function.name.clone(),
742                    reason: "effect id outside scalar boundary mask",
743                });
744            }
745            if effect_matches(&registered.path, effect) {
746                mask |= 1_i64 << index;
747            }
748        }
749        Ok(mask)
750    }
751
752    fn handler_consumes_mask(
753        &self,
754        function: &CpsReprAbiFunction,
755        handler: CpsHandlerId,
756    ) -> CpsReprCraneliftResult<i64> {
757        let mut mask = 0_i64;
758        for effect in self
759            .candidates
760            .iter()
761            .filter(|(_, candidate)| {
762                candidate.function == function.name && candidate.handler == handler
763            })
764            .map(|(effect, _)| effect)
765        {
766            mask |= self.registered_effect_exact_mask(function, effect)?;
767        }
768        Ok(mask)
769    }
770
771    fn registered_effect_exact_mask(
772        &self,
773        function: &CpsReprAbiFunction,
774        effect: &typed_ir::Path,
775    ) -> CpsReprCraneliftResult<i64> {
776        let mut mask = 0_i64;
777        for (index, registered) in self.effects.iter().enumerate() {
778            if index >= 62 {
779                return Err(CpsReprCraneliftError::UnsupportedFunction {
780                    function: function.name.clone(),
781                    reason: "effect id outside scalar boundary mask",
782                });
783            }
784            if registered.function == function.name && registered.path == *effect {
785                mask |= 1_i64 << index;
786            }
787        }
788        Ok(mask)
789    }
790
791    fn allowed_mask(
792        &self,
793        function: &CpsReprAbiFunction,
794        allowed: &typed_ir::Type,
795    ) -> CpsReprCraneliftResult<i64> {
796        if matches!(allowed, typed_ir::Type::Any) {
797            return Ok(-1);
798        }
799        let mut mask = 0_i64;
800        for (index, effect) in self.effects.iter().enumerate() {
801            if index >= 62 {
802                return Err(CpsReprCraneliftError::UnsupportedFunction {
803                    function: function.name.clone(),
804                    reason: "effect id outside scalar boundary mask",
805                });
806            }
807            if effect_allowed_by_type_for_registered(allowed, &effect.function, &effect.path) {
808                mask |= 1_i64 << index;
809            }
810        }
811        Ok(mask)
812    }
813}
814
815fn format_typed_path(path: &typed_ir::Path) -> String {
816    path.segments
817        .iter()
818        .map(|segment| segment.0.as_str())
819        .collect::<Vec<_>>()
820        .join("::")
821}
822
823fn define_effectful_function<M: Module, L: CpsLiteralStore>(
824    module_backend: &mut M,
825    function: &CpsReprAbiFunction,
826    functions: &DeclaredFunctions,
827    handlers: &HandlerRegistry,
828    literals: &mut L,
829) -> CpsReprCraneliftResult<Vec<String>> {
830    let mut cranelift_ir = Vec::new();
831    for continuation in &function.continuations {
832        let id = functions
833            .continuations
834            .get(&(function.name.clone(), continuation.id))
835            .copied()
836            .ok_or_else(|| CpsReprCraneliftError::MissingContinuation {
837                function: function.name.clone(),
838                continuation: continuation.id,
839            })?;
840        let mut ctx = module_backend.make_context();
841        ctx.func.signature = continuation_signature(module_backend, function, continuation);
842        lower_continuation_function(
843            module_backend,
844            &mut ctx,
845            function,
846            continuation,
847            functions,
848            handlers,
849            literals,
850        )?;
851        let ir_dump = format!(
852            ";; cps-repr continuation {}::{:?}\n{}",
853            function.name,
854            continuation.id,
855            ctx.func.display()
856        );
857        if std::env::var_os("YULANG_DUMP_CRANELIFT_IR").is_some() {
858            eprintln!("{ir_dump}");
859        }
860        cranelift_ir.push(ir_dump);
861        module_backend
862            .define_function(id, &mut ctx)
863            .map_err(cranelift_error)?;
864        module_backend.clear_context(&mut ctx);
865    }
866    Ok(cranelift_ir)
867}
868
869fn lower_effectful_function_wrapper<M: Module>(
870    module_backend: &mut M,
871    ctx: &mut cranelift_codegen::Context,
872    function: &CpsReprAbiFunction,
873    functions: &DeclaredFunctions,
874) -> CpsReprCraneliftResult<()> {
875    let mut builder_context = FunctionBuilderContext::new();
876    let mut builder = FunctionBuilder::new(&mut ctx.func, &mut builder_context);
877    let block = builder.create_block();
878    builder.append_block_params_for_function_params(block);
879    builder.switch_to_block(block);
880
881    let entry = continuation_func_ref(
882        module_backend,
883        &mut builder,
884        function,
885        function.entry,
886        functions,
887    )?;
888    let null_env = builder.ins().iconst(types::I64, 0);
889    let mut args = vec![null_env];
890    args.extend(builder.block_params(block).iter().copied());
891    let call = builder.ins().call(entry, &args);
892    let result = match builder.inst_results(call) {
893        [result] => *result,
894        results => {
895            return Err(CpsReprCraneliftError::InvalidReturnArity {
896                function: function.name.clone(),
897                arity: results.len(),
898            });
899        }
900    };
901    let result = force_function_result_if_thunk(module_backend, &mut builder, function, result)?;
902    builder.ins().return_(&[result]);
903    builder.seal_all_blocks();
904    builder.finalize();
905    Ok(())
906}
907
908fn force_function_result_if_thunk<M: Module>(
909    module_backend: &mut M,
910    builder: &mut FunctionBuilder<'_>,
911    function: &CpsReprAbiFunction,
912    result: ir::Value,
913) -> CpsReprCraneliftResult<ir::Value> {
914    let entry = continuation(function, function.entry)?;
915    if entry.return_lane != CpsReprAbiLane::ThunkPtr {
916        return Ok(result);
917    }
918    let helper = declare_import(
919        module_backend,
920        builder,
921        "yulang_cps_force_thunk_i64",
922        &[types::I64],
923        types::I64,
924    )?;
925    let call = builder.ins().call(helper, &[result]);
926    Ok(builder.inst_results(call)[0])
927}
928
929fn function_has_effect_flow(function: &CpsReprAbiFunction) -> bool {
930    !function.handlers.is_empty()
931        || function.continuations.iter().any(|continuation| {
932            !continuation.environment.is_empty()
933                || matches!(
934                    continuation.terminator,
935                    CpsTerminator::Perform { .. }
936                        | CpsTerminator::EffectfulCall { .. }
937                        | CpsTerminator::EffectfulApply { .. }
938                        | CpsTerminator::EffectfulForce { .. }
939                )
940                || continuation.stmts.iter().any(|stmt| {
941                    matches!(
942                        stmt,
943                        CpsStmt::MakeClosure { .. }
944                            | CpsStmt::MakeRecursiveClosure { .. }
945                            | CpsStmt::Resume { .. }
946                            | CpsStmt::ResumeWithHandler { .. }
947                            | CpsStmt::InstallHandler { .. }
948                            | CpsStmt::UninstallHandler { .. }
949                            // Thunks introduce additional continuations
950                            // (the thunk body) that the scalar path cannot
951                            // discover from a single Cranelift block.
952                            | CpsStmt::MakeThunk { .. }
953                            | CpsStmt::AddThunkBoundary { .. }
954                            | CpsStmt::ForceThunk { .. }
955                    )
956                })
957        })
958}
959
960fn continuation_signature<M: Module>(
961    module_backend: &M,
962    function: &CpsReprAbiFunction,
963    continuation: &CpsReprAbiContinuation,
964) -> ir::Signature {
965    let mut sig = module_backend.make_signature();
966    sig.params.push(AbiParam::new(types::I64));
967    sig.params.extend(
968        continuation
969            .params
970            .iter()
971            .map(|param| AbiParam::new(lane_type(effective_value_lane(function, param.value)))),
972    );
973    sig.returns.push(AbiParam::new(lane_type(
974        effective_continuation_return_lane(function, continuation),
975    )));
976    sig
977}
978
979fn continuation_symbol(function: &CpsReprAbiFunction, id: CpsContinuationId) -> String {
980    format!("{}$k{}", function.name, id.0)
981}
982
983fn continuation_func_ref<M: Module>(
984    module_backend: &mut M,
985    builder: &mut FunctionBuilder<'_>,
986    function: &CpsReprAbiFunction,
987    id: CpsContinuationId,
988    functions: &DeclaredFunctions,
989) -> CpsReprCraneliftResult<ir::FuncRef> {
990    continuation_func_ref_by_name(module_backend, builder, &function.name, id, functions).map_err(
991        |error| match error {
992            CpsReprCraneliftError::MissingContinuation { continuation, .. } => {
993                CpsReprCraneliftError::MissingContinuation {
994                    function: function.name.clone(),
995                    continuation,
996                }
997            }
998            error => error,
999        },
1000    )
1001}
1002
1003fn function_runtime_id(
1004    function: &CpsReprAbiFunction,
1005    functions: &DeclaredFunctions,
1006) -> CpsReprCraneliftResult<u64> {
1007    functions
1008        .function_ids
1009        .get(&function.name)
1010        .copied()
1011        .ok_or_else(|| CpsReprCraneliftError::MissingFunction {
1012            name: function.name.clone(),
1013        })
1014}
1015
1016fn continuation_func_ref_by_name<M: Module>(
1017    module_backend: &mut M,
1018    builder: &mut FunctionBuilder<'_>,
1019    function: &str,
1020    id: CpsContinuationId,
1021    functions: &DeclaredFunctions,
1022) -> CpsReprCraneliftResult<ir::FuncRef> {
1023    let id = functions
1024        .continuations
1025        .get(&(function.to_string(), id))
1026        .copied()
1027        .ok_or_else(|| CpsReprCraneliftError::MissingContinuation {
1028            function: function.to_string(),
1029            continuation: id,
1030        })?;
1031    Ok(module_backend.declare_func_in_func(id, builder.func))
1032}
1033
1034fn lower_continuation_function<M: Module, L: CpsLiteralStore>(
1035    module_backend: &mut M,
1036    ctx: &mut cranelift_codegen::Context,
1037    function: &CpsReprAbiFunction,
1038    continuation: &CpsReprAbiContinuation,
1039    functions: &DeclaredFunctions,
1040    handlers: &HandlerRegistry,
1041    literals: &mut L,
1042) -> CpsReprCraneliftResult<()> {
1043    let direct_island = direct_style_island_from(function, continuation.id);
1044    if direct_island.len() > 1 {
1045        return lower_direct_style_continuation_island(
1046            module_backend,
1047            ctx,
1048            function,
1049            continuation,
1050            functions,
1051            literals,
1052            &direct_island,
1053        );
1054    }
1055
1056    let mut builder_context = FunctionBuilderContext::new();
1057    let mut builder = FunctionBuilder::new(&mut ctx.func, &mut builder_context);
1058    let block = builder.create_block();
1059    builder.append_block_params_for_function_params(block);
1060    builder.switch_to_block(block);
1061    declare_variables(&mut builder, function);
1062    let mut values = LocalValueCache::default();
1063
1064    let params = builder.block_params(block).to_vec();
1065    let Some(env_ptr) = params.first().copied() else {
1066        return Err(CpsReprCraneliftError::UnsupportedFunction {
1067            function: function.name.clone(),
1068            reason: "continuation function without environment pointer",
1069        });
1070    };
1071    bind_environment_slots(&mut builder, function, continuation, env_ptr)?;
1072    for (param, value) in continuation.params.iter().zip(params.iter().skip(1)) {
1073        define_value_as_lane(
1074            &mut builder,
1075            &mut values,
1076            param.value,
1077            effective_value_lane(function, param.value),
1078            *value,
1079        );
1080    }
1081
1082    {
1083        let mut defined_values = continuation
1084            .environment
1085            .iter()
1086            .map(|slot| slot.value)
1087            .chain(continuation.params.iter().map(|param| param.value))
1088            .collect::<HashSet<_>>();
1089        let mut lower_cx = CpsCraneliftLowerCx {
1090            module_backend,
1091            builder: &mut builder,
1092            function,
1093            functions,
1094            handlers,
1095            literals,
1096            values: &mut values,
1097        };
1098        for stmt in &continuation.stmts {
1099            capture_handler_envs_for_stmt(
1100                lower_cx.module_backend,
1101                lower_cx.builder,
1102                function,
1103                stmt,
1104                &defined_values,
1105            )?;
1106            lower_effect_stmt(&mut lower_cx, stmt)?;
1107            if let Some(dest) = stmt_dest(stmt) {
1108                defined_values.insert(dest);
1109            }
1110        }
1111        lower_effect_terminator(&mut lower_cx, continuation)?;
1112        lower_cx.builder.seal_all_blocks();
1113    }
1114    builder.finalize();
1115    Ok(())
1116}
1117
1118fn lower_direct_style_continuation_island<M: Module, L: CpsLiteralStore>(
1119    module_backend: &mut M,
1120    ctx: &mut cranelift_codegen::Context,
1121    function: &CpsReprAbiFunction,
1122    entry_continuation: &CpsReprAbiContinuation,
1123    functions: &DeclaredFunctions,
1124    literals: &mut L,
1125    island: &HashSet<CpsContinuationId>,
1126) -> CpsReprCraneliftResult<()> {
1127    let mut builder_context = FunctionBuilderContext::new();
1128    let mut builder = FunctionBuilder::new(&mut ctx.func, &mut builder_context);
1129    let blocks =
1130        create_direct_style_island_blocks(&mut builder, function, entry_continuation, island);
1131    declare_variables(&mut builder, function);
1132    let mut values = LocalValueCache::default();
1133
1134    let entry_block = continuation_block(function, &blocks, entry_continuation.id)?;
1135    builder.append_block_params_for_function_params(entry_block);
1136    builder.switch_to_block(entry_block);
1137    let params = builder.block_params(entry_block).to_vec();
1138    for (param, value) in entry_continuation.params.iter().zip(params.iter().skip(1)) {
1139        define_value_as_lane(
1140            &mut builder,
1141            &mut values,
1142            param.value,
1143            effective_value_lane(function, param.value),
1144            *value,
1145        );
1146    }
1147
1148    for continuation in function
1149        .continuations
1150        .iter()
1151        .filter(|continuation| island.contains(&continuation.id))
1152    {
1153        let block = continuation_block(function, &blocks, continuation.id)?;
1154        builder.switch_to_block(block);
1155        if continuation.id != entry_continuation.id {
1156            bind_continuation_params(&mut builder, function, continuation, block, &mut values)?;
1157        }
1158        for stmt in &continuation.stmts {
1159            lower_stmt(
1160                module_backend,
1161                &mut builder,
1162                function,
1163                stmt,
1164                functions,
1165                literals,
1166                &mut values,
1167            )?;
1168        }
1169        lower_direct_style_island_terminator(
1170            module_backend,
1171            &mut builder,
1172            function,
1173            &blocks,
1174            continuation,
1175            &continuation.terminator,
1176            functions,
1177            literals,
1178            &mut values,
1179            island,
1180        )?;
1181    }
1182
1183    builder.seal_all_blocks();
1184    builder.finalize();
1185    Ok(())
1186}
1187
1188fn create_direct_style_island_blocks(
1189    builder: &mut FunctionBuilder<'_>,
1190    function: &CpsReprAbiFunction,
1191    entry_continuation: &CpsReprAbiContinuation,
1192    island: &HashSet<CpsContinuationId>,
1193) -> HashMap<CpsContinuationId, ir::Block> {
1194    function
1195        .continuations
1196        .iter()
1197        .filter(|continuation| island.contains(&continuation.id))
1198        .map(|continuation| {
1199            let block = builder.create_block();
1200            if continuation.id != entry_continuation.id {
1201                for param in &continuation.params {
1202                    builder.append_block_param(
1203                        block,
1204                        lane_type(effective_value_lane(function, param.value)),
1205                    );
1206                }
1207            }
1208            (continuation.id, block)
1209        })
1210        .collect()
1211}
1212
1213fn lower_direct_style_island_terminator<M: Module, L: CpsLiteralStore>(
1214    module_backend: &mut M,
1215    builder: &mut FunctionBuilder<'_>,
1216    function: &CpsReprAbiFunction,
1217    blocks: &HashMap<CpsContinuationId, ir::Block>,
1218    continuation: &CpsReprAbiContinuation,
1219    terminator: &CpsTerminator,
1220    functions: &DeclaredFunctions,
1221    literals: &mut L,
1222    values: &mut LocalValueCache,
1223    island: &HashSet<CpsContinuationId>,
1224) -> CpsReprCraneliftResult<()> {
1225    match terminator {
1226        CpsTerminator::Continue { target, args } if island.contains(target) => {
1227            let target_continuation = lookup_continuation(function, *target)?;
1228            let target = continuation_block(function, blocks, *target)?;
1229            let args = read_block_args(builder, function, values, target_continuation, args)?;
1230            builder.ins().jump(target, &args);
1231            Ok(())
1232        }
1233        CpsTerminator::Branch {
1234            cond,
1235            then_cont,
1236            else_cont,
1237        } if island.contains(then_cont) && island.contains(else_cont) => {
1238            let cond = read_value(builder, function, *cond)?;
1239            let cond = builder
1240                .ins()
1241                .icmp_imm(ir::condcodes::IntCC::NotEqual, cond, 0);
1242            let then_cont = continuation_block(function, blocks, *then_cont)?;
1243            let else_cont = continuation_block(function, blocks, *else_cont)?;
1244            builder.ins().brif(cond, then_cont, &[], else_cont, &[]);
1245            Ok(())
1246        }
1247        _ => {
1248            let handlers = HandlerRegistry::default();
1249            let mut cx = CpsCraneliftLowerCx {
1250                module_backend,
1251                builder,
1252                function,
1253                functions,
1254                handlers: &handlers,
1255                literals,
1256                values,
1257            };
1258            lower_effect_terminator(&mut cx, continuation)
1259        }
1260    }
1261}
1262
1263fn direct_style_island_from(
1264    function: &CpsReprAbiFunction,
1265    start: CpsContinuationId,
1266) -> HashSet<CpsContinuationId> {
1267    let candidates = function
1268        .continuations
1269        .iter()
1270        .filter(|continuation| direct_style_codegen_candidate(continuation))
1271        .map(|continuation| continuation.id)
1272        .collect::<HashSet<_>>();
1273    if !candidates.contains(&start) {
1274        return HashSet::new();
1275    }
1276
1277    let continuations = function
1278        .continuations
1279        .iter()
1280        .map(|continuation| (continuation.id, continuation))
1281        .collect::<HashMap<_, _>>();
1282    let mut island = HashSet::new();
1283    let mut work = vec![start];
1284    island.insert(start);
1285
1286    while let Some(id) = work.pop() {
1287        let Some(continuation) = continuations.get(&id) else {
1288            continue;
1289        };
1290        for successor in direct_style_codegen_successors(&continuation.terminator) {
1291            if candidates.contains(&successor) && island.insert(successor) {
1292                work.push(successor);
1293            }
1294        }
1295    }
1296
1297    island
1298}
1299
1300fn direct_style_codegen_candidate(continuation: &CpsReprAbiContinuation) -> bool {
1301    continuation.environment.is_empty()
1302        && continuation.stmts.iter().all(direct_style_codegen_stmt)
1303        && matches!(
1304            continuation.terminator,
1305            CpsTerminator::Return(_)
1306                | CpsTerminator::Continue { .. }
1307                | CpsTerminator::Branch { .. }
1308        )
1309}
1310
1311fn direct_style_codegen_stmt(stmt: &CpsStmt) -> bool {
1312    matches!(
1313        stmt,
1314        CpsStmt::Literal { .. }
1315            | CpsStmt::Tuple { .. }
1316            | CpsStmt::Record { .. }
1317            | CpsStmt::RecordWithoutFields { .. }
1318            | CpsStmt::Variant { .. }
1319            | CpsStmt::Select { .. }
1320            | CpsStmt::SelectWithDefault { .. }
1321            | CpsStmt::RecordHasField { .. }
1322            | CpsStmt::TupleGet { .. }
1323            | CpsStmt::VariantTagEq { .. }
1324            | CpsStmt::VariantPayload { .. }
1325            | CpsStmt::Primitive { .. }
1326            | CpsStmt::DirectCall { .. }
1327    )
1328}
1329
1330fn direct_style_codegen_successors(terminator: &CpsTerminator) -> Vec<CpsContinuationId> {
1331    match terminator {
1332        CpsTerminator::Continue { target, .. } => vec![*target],
1333        CpsTerminator::Branch {
1334            then_cont,
1335            else_cont,
1336            ..
1337        } => vec![*then_cont, *else_cont],
1338        CpsTerminator::Return(_)
1339        | CpsTerminator::Perform { .. }
1340        | CpsTerminator::EffectfulCall { .. }
1341        | CpsTerminator::EffectfulApply { .. }
1342        | CpsTerminator::EffectfulForce { .. } => Vec::new(),
1343    }
1344}
1345
1346fn bind_environment_slots(
1347    builder: &mut FunctionBuilder<'_>,
1348    function: &CpsReprAbiFunction,
1349    continuation: &CpsReprAbiContinuation,
1350    env_ptr: ir::Value,
1351) -> CpsReprCraneliftResult<()> {
1352    for slot in &continuation.environment {
1353        validate_environment_lane(function, slot.value, slot.lane)?;
1354        let offset = i32::try_from(slot.index * 8).map_err(|_| {
1355            CpsReprCraneliftError::UnsupportedFunction {
1356                function: function.name.clone(),
1357                reason: "continuation environment offset",
1358            }
1359        })?;
1360        let value = builder
1361            .ins()
1362            .load(types::I64, ir::MemFlags::new(), env_ptr, offset);
1363        builder.def_var(variable(slot.value), value);
1364    }
1365    Ok(())
1366}
1367
1368fn capture_handler_envs_for_stmt<M: Module>(
1369    module_backend: &mut M,
1370    builder: &mut FunctionBuilder<'_>,
1371    function: &CpsReprAbiFunction,
1372    stmt: &CpsStmt,
1373    defined_values: &HashSet<CpsValueId>,
1374) -> CpsReprCraneliftResult<()> {
1375    if !matches!(
1376        stmt,
1377        CpsStmt::MakeThunk { .. }
1378            | CpsStmt::AddThunkBoundary { .. }
1379            | CpsStmt::MakeClosure { .. }
1380            | CpsStmt::MakeRecursiveClosure { .. }
1381            | CpsStmt::ForceThunk { .. }
1382    ) || function.handlers.is_empty()
1383    {
1384        return Ok(());
1385    }
1386
1387    for handler in &function.handlers {
1388        for arm in &handler.arms {
1389            let entry = continuation(function, arm.entry)?;
1390            if !entry
1391                .environment
1392                .iter()
1393                .all(|slot| defined_values.contains(&slot.value))
1394            {
1395                continue;
1396            }
1397            let entry = continuation(function, arm.entry)?;
1398            let env =
1399                continuation_environment_argument(module_backend, builder, function, arm.entry)?;
1400            let slots = entry
1401                .environment
1402                .iter()
1403                .map(|slot| {
1404                    validate_environment_lane(function, slot.value, slot.lane)?;
1405                    Ok((slot.value, read_value(builder, function, slot.value)?))
1406                })
1407                .collect::<CpsReprCraneliftResult<Vec<_>>>()?;
1408            let handler = builder.ins().iconst(types::I64, handler.id.0 as i64);
1409            let entry = builder.ins().iconst(types::I64, arm.entry.0 as i64);
1410            capture_handler_env(module_backend, builder, handler, entry, env, &slots)?;
1411        }
1412    }
1413    Ok(())
1414}
1415
1416fn capture_handler_envs<M: Module>(
1417    module_backend: &mut M,
1418    builder: &mut FunctionBuilder<'_>,
1419    function: &CpsReprAbiFunction,
1420    handler: CpsHandlerId,
1421    envs: &[crate::cps_ir::CpsHandlerEnv],
1422) -> CpsReprCraneliftResult<()> {
1423    for env in envs {
1424        let (env_ptr, slots) = handler_env_argument(module_backend, builder, function, env)?;
1425        let handler = builder.ins().iconst(types::I64, handler.0 as i64);
1426        let entry = builder.ins().iconst(types::I64, env.entry.0 as i64);
1427        capture_handler_env(module_backend, builder, handler, entry, env_ptr, &slots)?;
1428    }
1429    Ok(())
1430}
1431
1432fn handler_env_argument<M: Module>(
1433    module_backend: &mut M,
1434    builder: &mut FunctionBuilder<'_>,
1435    function: &CpsReprAbiFunction,
1436    env: &crate::cps_ir::CpsHandlerEnv,
1437) -> CpsReprCraneliftResult<(ir::Value, Vec<(CpsValueId, ir::Value)>)> {
1438    let mut slots = Vec::with_capacity(env.values.len());
1439    for (index, value) in env.values.iter().enumerate() {
1440        let target = env.targets.get(index).copied().unwrap_or(*value);
1441        slots.push((target, read_value(builder, function, *value)?));
1442    }
1443
1444    if env.targets.is_empty() {
1445        if env.values.len() > 4 {
1446            return Err(CpsReprCraneliftError::UnsupportedStmt {
1447                function: function.name.clone(),
1448                kind: "handler environment larger than four slots",
1449            });
1450        }
1451        let values = read_values(builder, function, &env.values)?;
1452        let env = make_env(module_backend, builder, function, &values)?;
1453        return Ok((env, slots));
1454    }
1455
1456    if env.values.len() != env.targets.len() {
1457        return Err(CpsReprCraneliftError::UnsupportedStmt {
1458            function: function.name.clone(),
1459            kind: "handler environment target/value arity mismatch",
1460        });
1461    }
1462    let target = continuation(function, env.entry)?;
1463    if target.environment.len() > 4 {
1464        return Err(CpsReprCraneliftError::UnsupportedStmt {
1465            function: function.name.clone(),
1466            kind: "handler environment larger than four slots",
1467        });
1468    }
1469
1470    let mut values = Vec::with_capacity(target.environment.len());
1471    for slot in &target.environment {
1472        validate_environment_lane(function, slot.value, slot.lane)?;
1473        let source = env
1474            .targets
1475            .iter()
1476            .position(|target| *target == slot.value)
1477            .map(|index| env.values[index])
1478            .unwrap_or(slot.value);
1479        values.push(read_value(builder, function, source)?);
1480    }
1481    let env = make_env(module_backend, builder, function, &values)?;
1482    Ok((env, slots))
1483}
1484
1485fn capture_handler_env<M: Module>(
1486    module_backend: &mut M,
1487    builder: &mut FunctionBuilder<'_>,
1488    handler: ir::Value,
1489    entry: ir::Value,
1490    env: ir::Value,
1491    slots: &[(CpsValueId, ir::Value)],
1492) -> CpsReprCraneliftResult<()> {
1493    let targets = slots
1494        .iter()
1495        .map(|(target, _)| builder.ins().iconst(types::I64, target.0 as i64))
1496        .collect::<Vec<_>>();
1497    let values = slots.iter().map(|(_, value)| *value).collect::<Vec<_>>();
1498    let (target_ptr, target_len) = stack_i64_slice(builder, &targets)?;
1499    let (value_ptr, value_len) = stack_i64_slice(builder, &values)?;
1500    let _ = call_i64_helper(
1501        module_backend,
1502        builder,
1503        "yulang_cps_capture_handler_env_mapped_i64",
1504        &[
1505            handler, entry, env, target_ptr, value_ptr, target_len, value_len,
1506        ],
1507    )?;
1508    Ok(())
1509}
1510
1511fn lower_effect_stmt<M: Module, L: CpsLiteralStore>(
1512    cx: &mut CpsCraneliftLowerCx<'_, '_, M, L>,
1513    stmt: &CpsStmt,
1514) -> CpsReprCraneliftResult<()> {
1515    match stmt {
1516        CpsStmt::Literal { .. }
1517        | CpsStmt::Tuple { .. }
1518        | CpsStmt::Record { .. }
1519        | CpsStmt::RecordWithoutFields { .. }
1520        | CpsStmt::Select { .. }
1521        | CpsStmt::SelectWithDefault { .. }
1522        | CpsStmt::RecordHasField { .. }
1523        | CpsStmt::Variant { .. }
1524        | CpsStmt::TupleGet { .. }
1525        | CpsStmt::VariantTagEq { .. }
1526        | CpsStmt::VariantPayload { .. }
1527        | CpsStmt::Primitive { .. } => lower_value_stmt(cx, stmt),
1528        CpsStmt::FreshGuard { .. }
1529        | CpsStmt::PeekGuard { .. }
1530        | CpsStmt::FindGuard { .. }
1531        | CpsStmt::MakeThunk { .. }
1532        | CpsStmt::AddThunkBoundary { .. }
1533        | CpsStmt::MakeClosure { .. }
1534        | CpsStmt::MakeRecursiveClosure { .. }
1535        | CpsStmt::ForceThunk { .. } => lower_runtime_value_stmt(cx, stmt),
1536        CpsStmt::DirectCall { .. }
1537        | CpsStmt::ApplyClosure { .. }
1538        | CpsStmt::CloneContinuation { .. }
1539        | CpsStmt::Resume { .. }
1540        | CpsStmt::ResumeWithHandler { .. } => lower_call_stmt_case(cx, stmt),
1541        CpsStmt::InstallHandler { .. } | CpsStmt::UninstallHandler { .. } => {
1542            lower_handler_stmt_case(cx, stmt)
1543        }
1544    }
1545}
1546
1547fn lower_value_stmt<M: Module, L: CpsLiteralStore>(
1548    cx: &mut CpsCraneliftLowerCx<'_, '_, M, L>,
1549    stmt: &CpsStmt,
1550) -> CpsReprCraneliftResult<()> {
1551    lower_value_stmt_parts(
1552        cx.module_backend,
1553        cx.builder,
1554        cx.function,
1555        stmt,
1556        cx.literals,
1557        cx.values,
1558    )
1559}
1560
1561fn lower_value_stmt_parts<M: Module, L: CpsLiteralStore>(
1562    module_backend: &mut M,
1563    builder: &mut FunctionBuilder<'_>,
1564    function: &CpsReprAbiFunction,
1565    stmt: &CpsStmt,
1566    literals: &mut L,
1567    values: &mut LocalValueCache,
1568) -> CpsReprCraneliftResult<()> {
1569    match stmt {
1570        CpsStmt::Literal { dest, literal } => {
1571            let value = lower_literal(module_backend, builder, function, literal, literals)?;
1572            if matches!(literal, CpsLiteral::Float(_)) {
1573                define_value_as_lane(builder, values, *dest, CpsReprAbiLane::NativeFloat, value);
1574                let boxed = call_helper(
1575                    module_backend,
1576                    builder,
1577                    "yulang_cps_box_float_f64",
1578                    &[types::F64],
1579                    types::I64,
1580                    &[value],
1581                )?;
1582                builder.def_var(variable(*dest), boxed);
1583                return Ok(());
1584            }
1585            define_value_as_lane(builder, values, *dest, literal_lane(literal), value);
1586        }
1587        CpsStmt::Tuple { dest, items } => {
1588            let items = read_runtime_values_i64(module_backend, builder, function, values, items)?;
1589            let value = make_tuple_value(module_backend, builder, &items)?;
1590            builder.def_var(variable(*dest), value);
1591        }
1592        CpsStmt::Record { dest, base, fields } => {
1593            let value = make_record_value(
1594                module_backend,
1595                builder,
1596                function,
1597                *base,
1598                fields,
1599                literals,
1600                values,
1601            )?;
1602            builder.def_var(variable(*dest), value);
1603        }
1604        CpsStmt::RecordWithoutFields { dest, base, fields } => {
1605            let value = make_record_without_fields_value(
1606                module_backend,
1607                builder,
1608                function,
1609                *base,
1610                fields,
1611                literals,
1612            )?;
1613            builder.def_var(variable(*dest), value);
1614        }
1615        CpsStmt::Select { dest, base, field } => {
1616            let base = read_value(builder, function, *base)?;
1617            let (field_ptr, field_len) =
1618                literals.literal_bytes(module_backend, builder, field.0.as_bytes())?;
1619            let value = call_i64_helper(
1620                module_backend,
1621                builder,
1622                "yulang_cps_record_select_i64",
1623                &[base, field_ptr, field_len],
1624            )?;
1625            builder.def_var(variable(*dest), value);
1626        }
1627        CpsStmt::SelectWithDefault {
1628            dest,
1629            base,
1630            field,
1631            default,
1632        } => {
1633            let base = read_value(builder, function, *base)?;
1634            let default = read_value(builder, function, *default)?;
1635            let (field_ptr, field_len) =
1636                literals.literal_bytes(module_backend, builder, field.0.as_bytes())?;
1637            let value = call_i64_helper(
1638                module_backend,
1639                builder,
1640                "yulang_cps_record_select_or_default_i64",
1641                &[base, field_ptr, field_len, default],
1642            )?;
1643            builder.def_var(variable(*dest), value);
1644        }
1645        CpsStmt::RecordHasField { dest, base, field } => {
1646            let base = read_value(builder, function, *base)?;
1647            let (field_ptr, field_len) =
1648                literals.literal_bytes(module_backend, builder, field.0.as_bytes())?;
1649            let value = call_i64_helper(
1650                module_backend,
1651                builder,
1652                "yulang_cps_record_has_field_i64",
1653                &[base, field_ptr, field_len],
1654            )?;
1655            builder.def_var(variable(*dest), value);
1656        }
1657        CpsStmt::Variant { dest, tag, value } => {
1658            let value = value
1659                .map(|value| {
1660                    read_runtime_value_i64(module_backend, builder, function, values, value)
1661                })
1662                .transpose()?;
1663            let tag = register_variant_tag(module_backend, builder, tag, literals)?;
1664            let result = if let Some(value) = value {
1665                call_i64_helper(
1666                    module_backend,
1667                    builder,
1668                    "yulang_cps_variant_i64_1",
1669                    &[tag, value],
1670                )?
1671            } else {
1672                call_i64_helper(module_backend, builder, "yulang_cps_variant_i64_0", &[tag])?
1673            };
1674            builder.def_var(variable(*dest), result);
1675        }
1676        CpsStmt::TupleGet { dest, tuple, index } => {
1677            let tuple = read_value(builder, function, *tuple)?;
1678            let index = builder.ins().iconst(types::I64, *index as i64);
1679            let value = call_i64_helper(
1680                module_backend,
1681                builder,
1682                "yulang_cps_tuple_get_i64",
1683                &[tuple, index],
1684            )?;
1685            builder.def_var(variable(*dest), value);
1686        }
1687        CpsStmt::VariantTagEq { dest, variant, tag } => {
1688            let variant = read_value(builder, function, *variant)?;
1689            let tag = builder.ins().iconst(types::I64, tag_hash(tag));
1690            let value = call_i64_helper(
1691                module_backend,
1692                builder,
1693                "yulang_cps_variant_tag_eq_i64",
1694                &[variant, tag],
1695            )?;
1696            builder.def_var(variable(*dest), value);
1697        }
1698        CpsStmt::VariantPayload { dest, variant } => {
1699            let variant = read_value(builder, function, *variant)?;
1700            let value = call_i64_helper(
1701                module_backend,
1702                builder,
1703                "yulang_cps_variant_payload_i64",
1704                &[variant],
1705            )?;
1706            builder.def_var(variable(*dest), value);
1707        }
1708        CpsStmt::Primitive { dest, op, args } => {
1709            let args = read_primitive_args(module_backend, builder, function, values, *op, args)?;
1710            let value = lower_primitive(module_backend, builder, function, *op, &args)?;
1711            define_value_as_lane(builder, values, *dest, primitive_result_lane(*op), value);
1712        }
1713        _ => unreachable!("lower_value_stmt called with non-value statement"),
1714    }
1715    Ok(())
1716}
1717
1718fn lower_runtime_value_stmt<M: Module, L: CpsLiteralStore>(
1719    cx: &mut CpsCraneliftLowerCx<'_, '_, M, L>,
1720    stmt: &CpsStmt,
1721) -> CpsReprCraneliftResult<()> {
1722    lower_runtime_value_stmt_parts(
1723        cx.module_backend,
1724        cx.builder,
1725        cx.function,
1726        stmt,
1727        cx.functions,
1728        cx.handlers,
1729        cx.values,
1730    )
1731}
1732
1733fn lower_runtime_value_stmt_parts<M: Module>(
1734    module_backend: &mut M,
1735    builder: &mut FunctionBuilder<'_>,
1736    function: &CpsReprAbiFunction,
1737    stmt: &CpsStmt,
1738    functions: &DeclaredFunctions,
1739    handlers: &HandlerRegistry,
1740    values: &mut LocalValueCache,
1741) -> CpsReprCraneliftResult<()> {
1742    match stmt {
1743        CpsStmt::FreshGuard { dest, .. } => {
1744            let value =
1745                call_i64_helper(module_backend, builder, "yulang_cps_fresh_guard_i64", &[])?;
1746            builder.def_var(variable(*dest), value);
1747        }
1748        CpsStmt::PeekGuard { dest } => {
1749            let value = call_i64_helper(module_backend, builder, "yulang_cps_peek_guard_i64", &[])?;
1750            builder.def_var(variable(*dest), value);
1751        }
1752        CpsStmt::FindGuard { dest, guard } => {
1753            let guard = read_value(builder, function, *guard)?;
1754            let value = call_i64_helper(
1755                module_backend,
1756                builder,
1757                "yulang_cps_find_guard_i64",
1758                &[guard],
1759            )?;
1760            builder.def_var(variable(*dest), value);
1761        }
1762        CpsStmt::MakeThunk { dest, entry } => {
1763            let value = make_thunk(module_backend, builder, function, *entry, functions)?;
1764            builder.def_var(variable(*dest), value);
1765        }
1766        CpsStmt::AddThunkBoundary {
1767            dest,
1768            thunk,
1769            guard,
1770            allowed,
1771            active,
1772        } => {
1773            let value = read_value(builder, function, *thunk)?;
1774            let guard = read_value(builder, function, *guard)?;
1775            let allowed_mask = handlers.allowed_mask(function, allowed)?;
1776            let allowed_mask = builder.ins().iconst(types::I64, allowed_mask);
1777            let active = builder.ins().iconst(types::I64, i64::from(*active));
1778            let value = call_i64_helper(
1779                module_backend,
1780                builder,
1781                "yulang_cps_add_thunk_boundary_i64",
1782                &[value, guard, allowed_mask, active],
1783            )?;
1784            builder.def_var(variable(*dest), value);
1785        }
1786        CpsStmt::MakeClosure { dest, entry } => {
1787            let value = make_closure(module_backend, builder, function, *entry, functions)?;
1788            builder.def_var(variable(*dest), value);
1789        }
1790        CpsStmt::MakeRecursiveClosure { dest, entry } => {
1791            let value = make_recursive_closure(
1792                module_backend,
1793                builder,
1794                function,
1795                *dest,
1796                *entry,
1797                functions,
1798            )?;
1799            builder.def_var(variable(*dest), value);
1800        }
1801        CpsStmt::ForceThunk { dest, thunk } => {
1802            if force_thunk_passes_native_float(function, values, *thunk) {
1803                let value = read_value_as_lane(
1804                    builder,
1805                    function,
1806                    values,
1807                    *thunk,
1808                    CpsReprAbiLane::NativeFloat,
1809                )?;
1810                define_value_as_lane(builder, values, *dest, CpsReprAbiLane::NativeFloat, value);
1811                return Ok(());
1812            }
1813            let helper = declare_import(
1814                module_backend,
1815                builder,
1816                "yulang_cps_force_thunk_i64",
1817                &[types::I64],
1818                types::I64,
1819            )?;
1820            let thunk = read_value(builder, function, *thunk)?;
1821            // write27-d d5: fresh eval context for the sync force.
1822            let (saved_eval, saved_initial) = enter_callee_eval_context(module_backend, builder)?;
1823            let call = builder.ins().call(helper, &[thunk]);
1824            let results = builder.inst_results(call);
1825            let result = results[0];
1826            restore_caller_eval_context(module_backend, builder, saved_eval, saved_initial)?;
1827            let result = abort_result_or_return(module_backend, builder, result)?;
1828            let scope_fallback = scope_return_fallback_value(builder, function, *dest, result);
1829            return_if_scope_return_active(module_backend, builder, scope_fallback)?;
1830            builder.def_var(variable(*dest), result);
1831        }
1832        _ => unreachable!("lower_runtime_value_stmt called with non-runtime-value statement"),
1833    }
1834    Ok(())
1835}
1836
1837fn lower_call_stmt_case<M: Module, L: CpsLiteralStore>(
1838    cx: &mut CpsCraneliftLowerCx<'_, '_, M, L>,
1839    stmt: &CpsStmt,
1840) -> CpsReprCraneliftResult<()> {
1841    lower_call_stmt(
1842        cx.module_backend,
1843        cx.builder,
1844        cx.function,
1845        stmt,
1846        cx.functions,
1847        cx.handlers,
1848        cx.values,
1849    )
1850}
1851
1852fn lower_handler_stmt_case<M: Module, L: CpsLiteralStore>(
1853    cx: &mut CpsCraneliftLowerCx<'_, '_, M, L>,
1854    stmt: &CpsStmt,
1855) -> CpsReprCraneliftResult<()> {
1856    lower_handler_stmt(
1857        cx.module_backend,
1858        cx.builder,
1859        cx.function,
1860        stmt,
1861        cx.functions,
1862        cx.handlers,
1863        cx.values,
1864    )
1865}
1866
1867fn lower_call_stmt<M: Module>(
1868    module_backend: &mut M,
1869    builder: &mut FunctionBuilder<'_>,
1870    function: &CpsReprAbiFunction,
1871    stmt: &CpsStmt,
1872    functions: &DeclaredFunctions,
1873    handlers: &HandlerRegistry,
1874    values: &mut LocalValueCache,
1875) -> CpsReprCraneliftResult<()> {
1876    match stmt {
1877        CpsStmt::DirectCall { dest, target, args } => {
1878            lower_direct_call_stmt(
1879                module_backend,
1880                builder,
1881                function,
1882                functions,
1883                values,
1884                *dest,
1885                target,
1886                args,
1887            )?;
1888        }
1889        CpsStmt::ApplyClosure { dest, closure, arg } => {
1890            let closure = read_value(builder, function, *closure)?;
1891            let arg = read_value(builder, function, *arg)?;
1892            // write27-d d5: fresh eval context for the sync apply.
1893            let (saved_eval, saved_initial) = enter_callee_eval_context(module_backend, builder)?;
1894            let value = call_i64_helper(
1895                module_backend,
1896                builder,
1897                "yulang_cps_apply_closure_i64",
1898                &[closure, arg],
1899            )?;
1900            restore_caller_eval_context(module_backend, builder, saved_eval, saved_initial)?;
1901            let value = abort_result_or_return(module_backend, builder, value)?;
1902            return_if_scope_return_active(module_backend, builder, value)?;
1903            builder.def_var(variable(*dest), value);
1904        }
1905        CpsStmt::CloneContinuation { dest, source } => {
1906            let source = read_value(builder, function, *source)?;
1907            builder.def_var(variable(*dest), source);
1908        }
1909        CpsStmt::Resume {
1910            dest,
1911            resumption,
1912            arg,
1913        } => {
1914            let helper = declare_import(
1915                module_backend,
1916                builder,
1917                "yulang_cps_resume_i64",
1918                &[types::I64, types::I64],
1919                types::I64,
1920            )?;
1921            let resumption = read_value(builder, function, *resumption)?;
1922            let arg = read_value(builder, function, *arg)?;
1923            // write27-d d5: fresh eval context for the sync resume.
1924            let (saved_eval, saved_initial) = enter_callee_eval_context(module_backend, builder)?;
1925            let call = builder.ins().call(helper, &[resumption, arg]);
1926            let results = builder.inst_results(call);
1927            let result = results[0];
1928            restore_caller_eval_context(module_backend, builder, saved_eval, saved_initial)?;
1929            let result = abort_result_or_return(module_backend, builder, result)?;
1930            let scope_fallback = scope_return_fallback_value(builder, function, *dest, result);
1931            return_if_scope_return_active(module_backend, builder, scope_fallback)?;
1932            builder.def_var(variable(*dest), result);
1933        }
1934        CpsStmt::ResumeWithHandler {
1935            dest,
1936            resumption,
1937            arg,
1938            handler,
1939            envs,
1940        } => {
1941            let updates_existing_handler_env = envs.iter().any(|env| !env.targets.is_empty());
1942            capture_handler_envs(module_backend, builder, function, *handler, envs)?;
1943            let helper = declare_import(
1944                module_backend,
1945                builder,
1946                "yulang_cps_resume_with_handler_i64",
1947                &[
1948                    types::I64,
1949                    types::I64,
1950                    types::I64,
1951                    types::I64,
1952                    types::I64,
1953                    types::I64,
1954                ],
1955                types::I64,
1956            )?;
1957            let resumption = read_value(builder, function, *resumption)?;
1958            let arg = read_value(builder, function, *arg)?;
1959            let handler_id = *handler;
1960            let handler = builder.ins().iconst(types::I64, handler_id.0 as i64);
1961            let consumes_mask = builder.ins().iconst(
1962                types::I64,
1963                handlers.handler_consumes_mask(function, handler_id)?,
1964            );
1965            let owner_function = builder
1966                .ins()
1967                .iconst(types::I64, function_runtime_id(function, functions)? as i64);
1968            let updates_existing_handler_env = builder
1969                .ins()
1970                .iconst(types::I64, i64::from(updates_existing_handler_env));
1971            // write27-d d5: fresh eval context for the sync resume-with-handler.
1972            let (saved_eval, saved_initial) = enter_callee_eval_context(module_backend, builder)?;
1973            let call = builder.ins().call(
1974                helper,
1975                &[
1976                    resumption,
1977                    arg,
1978                    handler,
1979                    consumes_mask,
1980                    owner_function,
1981                    updates_existing_handler_env,
1982                ],
1983            );
1984            let results = builder.inst_results(call);
1985            let result = results[0];
1986            restore_caller_eval_context(module_backend, builder, saved_eval, saved_initial)?;
1987            let result = abort_result_or_return(module_backend, builder, result)?;
1988            let scope_fallback = scope_return_fallback_value(builder, function, *dest, result);
1989            return_if_scope_return_active_without_routing(module_backend, builder, scope_fallback)?;
1990            builder.def_var(variable(*dest), result);
1991        }
1992        _ => unreachable!("lower_call_stmt called with non-call statement"),
1993    }
1994    Ok(())
1995}
1996
1997fn lower_handler_stmt<M: Module>(
1998    module_backend: &mut M,
1999    builder: &mut FunctionBuilder<'_>,
2000    function: &CpsReprAbiFunction,
2001    stmt: &CpsStmt,
2002    functions: &DeclaredFunctions,
2003    handlers: &HandlerRegistry,
2004    _values: &mut LocalValueCache,
2005) -> CpsReprCraneliftResult<()> {
2006    match stmt {
2007        CpsStmt::InstallHandler {
2008            handler,
2009            envs,
2010            value,
2011            escape,
2012        } => {
2013            capture_handler_envs(module_backend, builder, function, *handler, envs)?;
2014            // write27-c c3: prefer the full install helper when the
2015            // escape continuation has an environment shape we support
2016            // (<= 4 slots). Otherwise fall back to the legacy install
2017            // — those handlers still work through the abort_i64
2018            // path in `perform_finish_i64`.
2019            let escape_cont = lookup_continuation(function, *escape)?;
2020            if escape_cont.environment.len() <= 4 {
2021                let func_ref =
2022                    continuation_func_ref(module_backend, builder, function, *escape, functions)?;
2023                let escape_ptr = builder.ins().func_addr(types::I64, func_ref);
2024                let threshold = call_i64_helper(
2025                    module_backend,
2026                    builder,
2027                    "yulang_cps_handler_return_frame_threshold_i64",
2028                    &[],
2029                )?;
2030                let current_initial = call_i64_helper(
2031                    module_backend,
2032                    builder,
2033                    "yulang_cps_current_initial_frame_count_i64",
2034                    &[],
2035                )?;
2036                let prompt =
2037                    call_i64_helper(module_backend, builder, "yulang_cps_fresh_prompt_i64", &[])?;
2038                let install_eval = call_i64_helper(
2039                    module_backend,
2040                    builder,
2041                    "yulang_cps_current_eval_id_i64",
2042                    &[],
2043                )?;
2044                let owner_function = builder
2045                    .ins()
2046                    .iconst(types::I64, function_runtime_id(function, functions)? as i64);
2047                let inherited = builder.ins().iconst(types::I64, 0);
2048                let handler_id = builder.ins().iconst(types::I64, handler.0 as i64);
2049                let consumes_mask = builder.ins().iconst(
2050                    types::I64,
2051                    handlers.handler_consumes_mask(function, *handler)?,
2052                );
2053                let mut args = vec![
2054                    handler_id,
2055                    consumes_mask,
2056                    escape_ptr,
2057                    threshold,
2058                    prompt,
2059                    install_eval,
2060                    owner_function,
2061                    inherited,
2062                ];
2063                args.extend(read_continuation_environment_values(
2064                    builder,
2065                    function,
2066                    escape_cont,
2067                )?);
2068                let escape_targets = continuation_environment_targets(builder, escape_cont);
2069                let (target_ptr, target_len) = stack_i64_slice(builder, &escape_targets)?;
2070                let _ = call_i64_helper(
2071                    module_backend,
2072                    builder,
2073                    "yulang_cps_set_pending_escape_env_targets_i64",
2074                    &[target_ptr, target_len],
2075                )?;
2076                let helper_name = INSTALL_HANDLER_FULL_HELPERS.fixed(escape_cont.environment.len());
2077                let _ = call_i64_helper(module_backend, builder, helper_name, &args)?;
2078                let value_cont = lookup_continuation(function, *value)?;
2079                let value_ref =
2080                    continuation_func_ref(module_backend, builder, function, *value, functions)?;
2081                let value_ptr = builder.ins().func_addr(types::I64, value_ref);
2082                let value_continuation_id = builder.ins().iconst(types::I64, value.0 as i64);
2083                let immediately_forces = builder.ins().iconst(
2084                    types::I64,
2085                    i64::from(resume_continuation_immediately_forces_param(value_cont)),
2086                );
2087                let value_env =
2088                    read_continuation_environment_values(builder, function, value_cont)?;
2089                if value_env.is_empty() {
2090                    let _ = call_i64_helper(
2091                        module_backend,
2092                        builder,
2093                        "yulang_cps_push_prompt_exit_frame_i64_0",
2094                        &[
2095                            prompt,
2096                            value_ptr,
2097                            value_continuation_id,
2098                            current_initial,
2099                            install_eval,
2100                            owner_function,
2101                            immediately_forces,
2102                        ],
2103                    )?;
2104                } else {
2105                    let (env_ptr, env_len) = stack_i64_slice(builder, &value_env)?;
2106                    let _ = call_i64_helper(
2107                        module_backend,
2108                        builder,
2109                        "yulang_cps_push_prompt_exit_frame_i64_many",
2110                        &[
2111                            prompt,
2112                            value_ptr,
2113                            value_continuation_id,
2114                            current_initial,
2115                            install_eval,
2116                            owner_function,
2117                            immediately_forces,
2118                            env_ptr,
2119                            env_len,
2120                        ],
2121                    )?;
2122                }
2123            } else {
2124                let handler_id = builder.ins().iconst(types::I64, handler.0 as i64);
2125                let consumes_mask = builder.ins().iconst(
2126                    types::I64,
2127                    handlers.handler_consumes_mask(function, *handler)?,
2128                );
2129                let _ = call_i64_helper(
2130                    module_backend,
2131                    builder,
2132                    "yulang_cps_install_handler_i64",
2133                    &[handler_id, consumes_mask],
2134                )?;
2135            }
2136        }
2137        CpsStmt::UninstallHandler { handler } => {
2138            let handler = builder.ins().iconst(types::I64, handler.0 as i64);
2139            let _ = call_i64_helper(
2140                module_backend,
2141                builder,
2142                "yulang_cps_uninstall_handler_i64",
2143                &[handler],
2144            )?;
2145        }
2146        _ => unreachable!("lower_handler_stmt called with non-handler statement"),
2147    }
2148    Ok(())
2149}
2150
2151fn stmt_dest(stmt: &CpsStmt) -> Option<CpsValueId> {
2152    match stmt {
2153        CpsStmt::Literal { dest, .. }
2154        | CpsStmt::Primitive { dest, .. }
2155        | CpsStmt::Tuple { dest, .. }
2156        | CpsStmt::Record { dest, .. }
2157        | CpsStmt::RecordWithoutFields { dest, .. }
2158        | CpsStmt::Variant { dest, .. }
2159        | CpsStmt::Select { dest, .. }
2160        | CpsStmt::SelectWithDefault { dest, .. }
2161        | CpsStmt::RecordHasField { dest, .. }
2162        | CpsStmt::TupleGet { dest, .. }
2163        | CpsStmt::VariantTagEq { dest, .. }
2164        | CpsStmt::VariantPayload { dest, .. }
2165        | CpsStmt::DirectCall { dest, .. }
2166        | CpsStmt::ApplyClosure { dest, .. }
2167        | CpsStmt::CloneContinuation { dest, .. }
2168        | CpsStmt::MakeThunk { dest, .. }
2169        | CpsStmt::AddThunkBoundary { dest, .. }
2170        | CpsStmt::MakeClosure { dest, .. }
2171        | CpsStmt::MakeRecursiveClosure { dest, .. }
2172        | CpsStmt::ForceThunk { dest, .. }
2173        | CpsStmt::Resume { dest, .. }
2174        | CpsStmt::ResumeWithHandler { dest, .. }
2175        | CpsStmt::FreshGuard { dest, .. }
2176        | CpsStmt::PeekGuard { dest }
2177        | CpsStmt::FindGuard { dest, .. } => Some(*dest),
2178        CpsStmt::InstallHandler { .. } | CpsStmt::UninstallHandler { .. } => None,
2179    }
2180}
2181
2182fn lower_effect_terminator<M: Module, L: CpsLiteralStore>(
2183    cx: &mut CpsCraneliftLowerCx<'_, '_, M, L>,
2184    continuation: &CpsReprAbiContinuation,
2185) -> CpsReprCraneliftResult<()> {
2186    match &continuation.terminator {
2187        CpsTerminator::Return(value) => {
2188            lower_return_terminator(cx, *value)?;
2189        }
2190        CpsTerminator::Continue { target, args } => {
2191            lower_continue_terminator(cx, *target, args)?;
2192        }
2193        CpsTerminator::Branch {
2194            cond,
2195            then_cont,
2196            else_cont,
2197        } => {
2198            lower_branch_terminator(cx, *cond, *then_cont, *else_cont)?;
2199        }
2200        CpsTerminator::Perform {
2201            effect,
2202            payload,
2203            resume,
2204            handler,
2205            blocked,
2206        } => {
2207            lower_perform_terminator_case(
2208                cx,
2209                PerformTerminatorCase {
2210                    effect,
2211                    payload: *payload,
2212                    resume: *resume,
2213                    handler: *handler,
2214                    blocked: *blocked,
2215                },
2216            )?;
2217        }
2218        CpsTerminator::EffectfulCall {
2219            target,
2220            args,
2221            resume,
2222        } => {
2223            lower_effectful_call_terminator(
2224                cx,
2225                EffectfulCallTerminatorCase {
2226                    target,
2227                    args,
2228                    resume: *resume,
2229                },
2230            )?;
2231        }
2232        CpsTerminator::EffectfulForce { thunk, resume } => {
2233            lower_effectful_force_terminator(
2234                cx,
2235                EffectfulForceTerminatorCase {
2236                    thunk: *thunk,
2237                    resume: *resume,
2238                },
2239            )?;
2240        }
2241        CpsTerminator::EffectfulApply {
2242            closure,
2243            arg,
2244            resume,
2245        } => {
2246            lower_effectful_apply_terminator(
2247                cx,
2248                EffectfulApplyTerminatorCase {
2249                    closure: *closure,
2250                    arg: *arg,
2251                    resume: *resume,
2252                },
2253            )?;
2254        }
2255    }
2256    Ok(())
2257}
2258
2259fn lower_return_terminator<M: Module, L: CpsLiteralStore>(
2260    cx: &mut CpsCraneliftLowerCx<'_, '_, M, L>,
2261    value: CpsValueId,
2262) -> CpsReprCraneliftResult<()> {
2263    // write27-b: route the return value through the JIT-side
2264    // `yulang_cps_return_i64` helper so the eval-level Return semantics
2265    // (pre-force v2, continue_return_frame on remaining own-frames) match
2266    // cps_eval/cps_repr.
2267    //
2268    // The helper is a no-op when there are no own return frames
2269    // (frame_len <= initial_frame_count), so this is safe even for tests
2270    // that don't use effectful terminators; the path simply returns
2271    // `value` unchanged.
2272    let value = read_value(cx.builder, cx.function, value)?;
2273    let routed = call_i64_helper(
2274        cx.module_backend,
2275        cx.builder,
2276        "yulang_cps_return_i64",
2277        &[value],
2278    )?;
2279    cx.builder.ins().return_(&[routed]);
2280    Ok(())
2281}
2282
2283fn lower_continue_terminator<M: Module, L: CpsLiteralStore>(
2284    cx: &mut CpsCraneliftLowerCx<'_, '_, M, L>,
2285    target: CpsContinuationId,
2286    args: &[CpsValueId],
2287) -> CpsReprCraneliftResult<()> {
2288    let value = call_continuation(
2289        cx.module_backend,
2290        cx.builder,
2291        cx.function,
2292        target,
2293        args,
2294        cx.functions,
2295    )?;
2296    cx.builder.ins().return_(&[value]);
2297    Ok(())
2298}
2299
2300fn lower_branch_terminator<M: Module, L: CpsLiteralStore>(
2301    cx: &mut CpsCraneliftLowerCx<'_, '_, M, L>,
2302    cond: CpsValueId,
2303    then_cont: CpsContinuationId,
2304    else_cont: CpsContinuationId,
2305) -> CpsReprCraneliftResult<()> {
2306    lower_effect_branch(
2307        cx.module_backend,
2308        cx.builder,
2309        cx.function,
2310        cond,
2311        then_cont,
2312        else_cont,
2313        cx.functions,
2314    )
2315}
2316
2317fn lower_perform_terminator_case<M: Module, L: CpsLiteralStore>(
2318    cx: &mut CpsCraneliftLowerCx<'_, '_, M, L>,
2319    case: PerformTerminatorCase<'_>,
2320) -> CpsReprCraneliftResult<()> {
2321    lower_perform_terminator(
2322        cx.module_backend,
2323        cx.builder,
2324        cx.function,
2325        case.effect,
2326        case.payload,
2327        case.resume,
2328        case.handler,
2329        cx.functions,
2330        cx.handlers,
2331        case.blocked,
2332    )
2333}
2334
2335fn lower_effectful_call_terminator<M: Module, L: CpsLiteralStore>(
2336    cx: &mut CpsCraneliftLowerCx<'_, '_, M, L>,
2337    case: EffectfulCallTerminatorCase<'_>,
2338) -> CpsReprCraneliftResult<()> {
2339    lower_effectful_call_tail(
2340        cx.module_backend,
2341        cx.builder,
2342        cx.function,
2343        case.target,
2344        case.args,
2345        case.resume,
2346        cx.functions,
2347    )
2348}
2349
2350fn lower_effectful_force_terminator<M: Module, L: CpsLiteralStore>(
2351    cx: &mut CpsCraneliftLowerCx<'_, '_, M, L>,
2352    case: EffectfulForceTerminatorCase,
2353) -> CpsReprCraneliftResult<()> {
2354    lower_effectful_force_tail(
2355        cx.module_backend,
2356        cx.builder,
2357        cx.function,
2358        case.thunk,
2359        case.resume,
2360        cx.functions,
2361    )
2362}
2363
2364fn lower_effectful_apply_terminator<M: Module, L: CpsLiteralStore>(
2365    cx: &mut CpsCraneliftLowerCx<'_, '_, M, L>,
2366    case: EffectfulApplyTerminatorCase,
2367) -> CpsReprCraneliftResult<()> {
2368    lower_effectful_apply_tail(
2369        cx.module_backend,
2370        cx.builder,
2371        cx.function,
2372        case.closure,
2373        case.arg,
2374        case.resume,
2375        cx.functions,
2376    )
2377}
2378
2379fn lower_perform_terminator<M: Module>(
2380    module_backend: &mut M,
2381    builder: &mut FunctionBuilder<'_>,
2382    function: &CpsReprAbiFunction,
2383    effect: &typed_ir::Path,
2384    payload: CpsValueId,
2385    resume: CpsContinuationId,
2386    handler: CpsHandlerId,
2387    functions: &DeclaredFunctions,
2388    handlers: &HandlerRegistry,
2389    blocked: Option<CpsValueId>,
2390) -> CpsReprCraneliftResult<()> {
2391    let host_fallback = host_console_effect_kind(effect);
2392    let candidates = handlers.candidates_for_effect(effect);
2393    if candidates.is_empty() {
2394        let Some(kind) = host_fallback else {
2395            return Err(CpsReprCraneliftError::UnsupportedTerminator {
2396                function: function.name.clone(),
2397                kind: "perform without handler entry",
2398            });
2399        };
2400        let payload = read_value(builder, function, payload)?;
2401        lower_host_console_perform(
2402            module_backend,
2403            builder,
2404            function,
2405            kind,
2406            payload,
2407            resume,
2408            functions,
2409        )?;
2410        return Ok(());
2411    }
2412    let resumption = make_resumption(
2413        module_backend,
2414        builder,
2415        function,
2416        resume,
2417        handler,
2418        functions,
2419    )?;
2420    let payload = read_value(builder, function, payload)?;
2421    let fallback_handler = if handler.0 == usize::MAX {
2422        -1
2423    } else {
2424        handler.0 as i64
2425    };
2426    let fallback = builder.ins().iconst(types::I64, fallback_handler);
2427    let static_blocked = blocked
2428        .map(|blocked| read_value(builder, function, blocked))
2429        .transpose()?
2430        .unwrap_or_else(|| builder.ins().iconst(types::I64, -1));
2431    let effect_mask = handlers.effect_mask(function, effect)?;
2432    let effect_mask = builder.ins().iconst(types::I64, effect_mask);
2433    let active_blocked = call_i64_helper(
2434        module_backend,
2435        builder,
2436        "yulang_cps_active_blocked_guard_i64",
2437        &[effect_mask],
2438    )?;
2439    let no_static_block = builder
2440        .ins()
2441        .icmp_imm(ir::condcodes::IntCC::Equal, static_blocked, -1);
2442    let blocked = builder
2443        .ins()
2444        .select(no_static_block, active_blocked, static_blocked);
2445    let selected = call_i64_helper(
2446        module_backend,
2447        builder,
2448        "yulang_cps_select_handler_i64",
2449        &[fallback, effect_mask, blocked],
2450    )?;
2451    // write27-d d2: now that select_handler has recorded the
2452    // matched real handler's meta, write it back into the
2453    // resumption as `handled_anchor`. apply_resumption uses
2454    // this to drop redundant inherited frames during the
2455    // anchor merge.
2456    let _ = call_i64_helper(
2457        module_backend,
2458        builder,
2459        "yulang_cps_set_resumption_anchor_from_selected_i64",
2460        &[resumption],
2461    )?;
2462    lower_selected_handler_return(
2463        module_backend,
2464        builder,
2465        function,
2466        &candidates,
2467        selected,
2468        payload,
2469        resumption,
2470        host_fallback.map(|kind| (kind, resume)),
2471        functions,
2472    )
2473}
2474
2475fn lower_effectful_call_tail<M: Module>(
2476    module_backend: &mut M,
2477    builder: &mut FunctionBuilder<'_>,
2478    function: &CpsReprAbiFunction,
2479    target: &str,
2480    args: &[CpsValueId],
2481    resume: CpsContinuationId,
2482    functions: &DeclaredFunctions,
2483) -> CpsReprCraneliftResult<()> {
2484    // write27-b: EffectfulCall codegen. Push a return frame for the
2485    // resume continuation, set a fresh eval context, and tail-call the
2486    // target. The target's Return helper (write27-b/yulang_cps_return_i64)
2487    // consumes the frame and invokes the resume continuation when it
2488    // finally bottoms out.
2489    let resume_cont = lookup_continuation(function, resume)?;
2490    check_resume_continuation_shape(function, resume_cont)?;
2491    let immediate_force = resume_continuation_immediately_forces_param(resume_cont);
2492    // c0: read pre_push_count BEFORE pushing F_post so the callee's
2493    // initial_frame_count points at F_post itself (matches Layer 1/2
2494    // semantics).
2495    let pre_push_count = call_i64_helper(
2496        module_backend,
2497        builder,
2498        "yulang_cps_return_frame_len_i64",
2499        &[],
2500    )?;
2501    // Push F_post(resume_cont, env, current_initial, current_eval, immediate_force).
2502    push_return_frame_for_resume(
2503        module_backend,
2504        builder,
2505        function,
2506        resume_cont,
2507        immediate_force,
2508        functions,
2509    )?;
2510    // Read target args BEFORE switching eval context (so we see the caller's value table state).
2511    let arg_values = read_values(builder, function, args)?;
2512    // Set callee eval context: fresh eval id + initial = pre_push_count
2513    // (F_post is consumable, frames below are not).
2514    switch_eval_context_for_callee(module_backend, builder, pre_push_count)?;
2515    let id = functions.functions.get(target).copied().ok_or_else(|| {
2516        CpsReprCraneliftError::MissingFunction {
2517            name: target.to_string(),
2518        }
2519    })?;
2520    let callee = module_backend.declare_func_in_func(id, builder.func);
2521    let call = builder.ins().call(callee, &arg_values);
2522    let results = builder.inst_results(call);
2523    if results.len() != 1 {
2524        return Err(CpsReprCraneliftError::InvalidReturnArity {
2525            function: target.to_string(),
2526            arity: results.len(),
2527        });
2528    }
2529    let result = results[0];
2530    builder.ins().return_(&[result]);
2531    Ok(())
2532}
2533
2534fn lower_effectful_apply_tail<M: Module>(
2535    module_backend: &mut M,
2536    builder: &mut FunctionBuilder<'_>,
2537    function: &CpsReprAbiFunction,
2538    closure: CpsValueId,
2539    arg: CpsValueId,
2540    resume: CpsContinuationId,
2541    functions: &DeclaredFunctions,
2542) -> CpsReprCraneliftResult<()> {
2543    // write27-d d4: EffectfulApply dispatches at runtime between Closure
2544    // and Resumption based on `yulang_cps_is_resumption_i64`. The Closure
2545    // path pushes F_post and calls apply_closure_i64. The Resumption path
2546    // delegates anchor-merge + combined-frames logic to a Rust helper.
2547    let resume_cont = lookup_continuation(function, resume)?;
2548    check_resume_continuation_shape(function, resume_cont)?;
2549    let immediate_force = resume_continuation_immediately_forces_param(resume_cont);
2550    let closure_value = read_value(builder, function, closure)?;
2551    let arg_value = read_value(builder, function, arg)?;
2552
2553    let func_ref =
2554        continuation_func_ref(module_backend, builder, function, resume_cont.id, functions)?;
2555    let post_cont_ptr = builder.ins().func_addr(types::I64, func_ref);
2556    let current_eval = call_i64_helper(
2557        module_backend,
2558        builder,
2559        "yulang_cps_current_eval_id_i64",
2560        &[],
2561    )?;
2562    let current_initial = call_i64_helper(
2563        module_backend,
2564        builder,
2565        "yulang_cps_current_initial_frame_count_i64",
2566        &[],
2567    )?;
2568    let owner_function = builder
2569        .ins()
2570        .iconst(types::I64, function_runtime_id(function, functions)? as i64);
2571    let immediate_force_value = builder.ins().iconst(types::I64, i64::from(immediate_force));
2572    let env_args = read_continuation_environment_values(builder, function, resume_cont)?;
2573
2574    let is_resumption = call_i64_helper(
2575        module_backend,
2576        builder,
2577        "yulang_cps_is_resumption_i64",
2578        &[closure_value],
2579    )?;
2580    let resumption_block = builder.create_block();
2581    let closure_block = builder.create_block();
2582    builder
2583        .ins()
2584        .brif(is_resumption, resumption_block, &[], closure_block, &[]);
2585
2586    builder.switch_to_block(closure_block);
2587    builder.seal_block(closure_block);
2588    let pre_push_count = call_i64_helper(
2589        module_backend,
2590        builder,
2591        "yulang_cps_return_frame_len_i64",
2592        &[],
2593    )?;
2594    push_return_frame_for_resume(
2595        module_backend,
2596        builder,
2597        function,
2598        resume_cont,
2599        immediate_force,
2600        functions,
2601    )?;
2602    switch_eval_context_for_callee(module_backend, builder, pre_push_count)?;
2603    let closure_result = call_i64_helper(
2604        module_backend,
2605        builder,
2606        "yulang_cps_apply_closure_i64",
2607        &[closure_value, arg_value],
2608    )?;
2609    builder.ins().return_(&[closure_result]);
2610
2611    builder.switch_to_block(resumption_block);
2612    builder.seal_block(resumption_block);
2613    let mut resumption_args = vec![
2614        closure_value,
2615        arg_value,
2616        post_cont_ptr,
2617        current_initial,
2618        current_eval,
2619        owner_function,
2620        immediate_force_value,
2621    ];
2622    let resumption_helper = if env_args.len() > 4 {
2623        let (env_ptr, env_len) = stack_i64_slice(builder, &env_args)?;
2624        resumption_args.push(env_ptr);
2625        resumption_args.push(env_len);
2626        EFFECTFUL_APPLY_RESUMPTION_HELPERS.many
2627    } else {
2628        resumption_args.extend_from_slice(&env_args);
2629        EFFECTFUL_APPLY_RESUMPTION_HELPERS.fixed(resume_cont.environment.len())
2630    };
2631    let resumption_result =
2632        call_i64_helper(module_backend, builder, resumption_helper, &resumption_args)?;
2633    builder.ins().return_(&[resumption_result]);
2634    Ok(())
2635}
2636
2637fn lower_effectful_force_tail<M: Module>(
2638    module_backend: &mut M,
2639    builder: &mut FunctionBuilder<'_>,
2640    function: &CpsReprAbiFunction,
2641    thunk: CpsValueId,
2642    resume: CpsContinuationId,
2643    functions: &DeclaredFunctions,
2644) -> CpsReprCraneliftResult<()> {
2645    let resume_cont = lookup_continuation(function, resume)?;
2646    check_resume_continuation_shape(function, resume_cont)?;
2647    let thunk_value = read_value(builder, function, thunk)?;
2648    let is_thunk = call_i64_helper(
2649        module_backend,
2650        builder,
2651        "yulang_cps_is_thunk_i64",
2652        &[thunk_value],
2653    )?;
2654    let is_thunk = builder
2655        .ins()
2656        .icmp_imm(ir::condcodes::IntCC::NotEqual, is_thunk, 0);
2657    let force_block = builder.create_block();
2658    let value_block = builder.create_block();
2659    builder
2660        .ins()
2661        .brif(is_thunk, force_block, &[], value_block, &[]);
2662
2663    builder.switch_to_block(value_block);
2664    builder.seal_block(value_block);
2665    let value = call_continuation_with_values(
2666        module_backend,
2667        builder,
2668        function,
2669        resume,
2670        &[thunk_value],
2671        functions,
2672    )?;
2673    builder.ins().return_(&[value]);
2674
2675    builder.switch_to_block(force_block);
2676    builder.seal_block(force_block);
2677    let immediate_force = resume_continuation_immediately_forces_param(resume_cont);
2678    let pre_push_count = call_i64_helper(
2679        module_backend,
2680        builder,
2681        "yulang_cps_return_frame_len_i64",
2682        &[],
2683    )?;
2684    push_return_frame_for_resume(
2685        module_backend,
2686        builder,
2687        function,
2688        resume_cont,
2689        immediate_force,
2690        functions,
2691    )?;
2692    // Force the thunk body with the just-pushed post frame inherited, not
2693    // consumable. Effects inside the body can still capture it, and the
2694    // EffectfulForce terminator consumes it only after forcing reaches a value.
2695    let force_initial = call_i64_helper(
2696        module_backend,
2697        builder,
2698        "yulang_cps_return_frame_len_i64",
2699        &[],
2700    )?;
2701    let force_eval = call_i64_helper(module_backend, builder, "yulang_cps_fresh_eval_id_i64", &[])?;
2702    let _ = call_i64_helper(
2703        module_backend,
2704        builder,
2705        "yulang_cps_set_eval_context_i64",
2706        &[force_eval, force_initial],
2707    )?;
2708    let result = call_i64_helper(
2709        module_backend,
2710        builder,
2711        "yulang_cps_force_thunk_i64",
2712        &[thunk_value],
2713    )?;
2714    switch_eval_context_for_callee(module_backend, builder, pre_push_count)?;
2715    let routed = call_i64_helper(module_backend, builder, "yulang_cps_return_i64", &[result])?;
2716    builder.ins().return_(&[routed]);
2717    Ok(())
2718}
2719
2720/// write27-b: validate that a resume continuation has the shape the
2721/// return-frame stack supports: exactly 1 param (the call's result) and
2722/// at most 4 env slots.
2723fn check_resume_continuation_shape(
2724    function: &CpsReprAbiFunction,
2725    resume_cont: &CpsReprAbiContinuation,
2726) -> CpsReprCraneliftResult<()> {
2727    if resume_cont.params.len() != 1 {
2728        return Err(CpsReprCraneliftError::UnsupportedTerminator {
2729            function: function.name.clone(),
2730            kind: "resume continuation arity",
2731        });
2732    }
2733    Ok(())
2734}
2735
2736fn read_continuation_environment_values(
2737    builder: &mut FunctionBuilder<'_>,
2738    function: &CpsReprAbiFunction,
2739    continuation: &CpsReprAbiContinuation,
2740) -> CpsReprCraneliftResult<Vec<ir::Value>> {
2741    let mut values = Vec::with_capacity(continuation.environment.len());
2742    for slot in &continuation.environment {
2743        validate_environment_lane(function, slot.value, slot.lane)?;
2744        values.push(read_value(builder, function, slot.value)?);
2745    }
2746    Ok(values)
2747}
2748
2749fn continuation_environment_targets(
2750    builder: &mut FunctionBuilder<'_>,
2751    continuation: &CpsReprAbiContinuation,
2752) -> Vec<ir::Value> {
2753    continuation
2754        .environment
2755        .iter()
2756        .map(|slot| builder.ins().iconst(types::I64, slot.value.0 as i64))
2757        .collect()
2758}
2759
2760fn handler_arm_continues_to_installed_escape(
2761    function: &CpsReprAbiFunction,
2762    handler: CpsHandlerId,
2763    entry: CpsContinuationId,
2764) -> bool {
2765    let Some(escape) = handler_arm_continue_chain_escape(function, handler, entry) else {
2766        return false;
2767    };
2768    function.continuations.iter().any(|continuation| {
2769        continuation.stmts.iter().any(|stmt| {
2770            matches!(
2771                stmt,
2772                CpsStmt::InstallHandler {
2773                    handler: id,
2774                    escape: installed_escape,
2775                    ..
2776                } if *id == handler && *installed_escape == escape
2777            )
2778        })
2779    })
2780}
2781
2782fn handler_arm_continue_chain_escape(
2783    function: &CpsReprAbiFunction,
2784    handler: CpsHandlerId,
2785    entry: CpsContinuationId,
2786) -> Option<CpsContinuationId> {
2787    let mut current = entry;
2788    let mut saw_uninstall = false;
2789    let mut visited = HashSet::new();
2790    while visited.insert(current) {
2791        let continuation = function
2792            .continuations
2793            .iter()
2794            .find(|continuation| continuation.id == current)?;
2795        saw_uninstall |= continuation.stmts.iter().any(
2796            |stmt| matches!(stmt, CpsStmt::UninstallHandler { handler: id } if *id == handler),
2797        );
2798        let CpsTerminator::Continue { target, .. } = &continuation.terminator else {
2799            return saw_uninstall.then_some(current);
2800        };
2801        if saw_uninstall
2802            && function.continuations.iter().any(|candidate| {
2803                candidate.stmts.iter().any(|stmt| {
2804                    matches!(
2805                        stmt,
2806                        CpsStmt::InstallHandler {
2807                            handler: id,
2808                            escape,
2809                            ..
2810                        } if *id == handler && escape == target
2811                    )
2812                })
2813            })
2814        {
2815            return Some(*target);
2816        }
2817        current = *target;
2818    }
2819    None
2820}
2821
2822fn handler_arm_uses_resume_with_handler(
2823    function: &CpsReprAbiFunction,
2824    entry: CpsContinuationId,
2825) -> bool {
2826    let mut current = entry;
2827    let mut visited = HashSet::new();
2828    while visited.insert(current) {
2829        let Some(continuation) = function
2830            .continuations
2831            .iter()
2832            .find(|continuation| continuation.id == current)
2833        else {
2834            return false;
2835        };
2836        if continuation
2837            .stmts
2838            .iter()
2839            .any(|stmt| matches!(stmt, CpsStmt::ResumeWithHandler { .. }))
2840        {
2841            return true;
2842        }
2843        let CpsTerminator::Continue { target, .. } = &continuation.terminator else {
2844            return false;
2845        };
2846        current = *target;
2847    }
2848    false
2849}
2850
2851/// write27-b: mirror of `return_frame_immediately_forces_param` in
2852/// cps_eval/cps_repr. Returns true when the continuation's first stmt
2853/// is `ForceThunk` on its first param. Used to fire pre-force v2 in
2854/// the JIT Return path.
2855fn resume_continuation_immediately_forces_param(resume_cont: &CpsReprAbiContinuation) -> bool {
2856    let Some(first_param) = resume_cont.params.first() else {
2857        return false;
2858    };
2859    matches!(
2860        resume_cont.stmts.first(),
2861        Some(CpsStmt::ForceThunk { thunk, .. }) if *thunk == first_param.value
2862    )
2863}
2864
2865/// write27-b: emit the codegen for "push a return frame for this
2866/// resume continuation". Reads the resume continuation's env slots from
2867/// the current function's value table, gets a function pointer to the
2868/// continuation, and calls `yulang_cps_push_return_frame_i64_N` or the
2869/// many-slot variant with current_initial, current_eval, the immediate_force
2870/// flag, and the env slots.
2871fn push_return_frame_for_resume<M: Module>(
2872    module_backend: &mut M,
2873    builder: &mut FunctionBuilder<'_>,
2874    function: &CpsReprAbiFunction,
2875    resume_cont: &CpsReprAbiContinuation,
2876    immediate_force: bool,
2877    functions: &DeclaredFunctions,
2878) -> CpsReprCraneliftResult<()> {
2879    let func_ref =
2880        continuation_func_ref(module_backend, builder, function, resume_cont.id, functions)?;
2881    let cont_ptr = builder.ins().func_addr(types::I64, func_ref);
2882    let current_eval = call_i64_helper(
2883        module_backend,
2884        builder,
2885        "yulang_cps_current_eval_id_i64",
2886        &[],
2887    )?;
2888    let current_initial = call_i64_helper(
2889        module_backend,
2890        builder,
2891        "yulang_cps_current_initial_frame_count_i64",
2892        &[],
2893    )?;
2894    let owner_function = builder
2895        .ins()
2896        .iconst(types::I64, function_runtime_id(function, functions)? as i64);
2897    let immediate_force_value = builder.ins().iconst(types::I64, i64::from(immediate_force));
2898    let continuation_id = builder.ins().iconst(types::I64, resume_cont.id.0 as i64);
2899    let mut env_values = Vec::with_capacity(resume_cont.environment.len());
2900    for slot in &resume_cont.environment {
2901        validate_environment_lane(function, slot.value, slot.lane)?;
2902        env_values.push(read_value(builder, function, slot.value)?);
2903    }
2904    if env_values.len() > 4 {
2905        let (env_ptr, env_len) = stack_i64_slice(builder, &env_values)?;
2906        let args = vec![
2907            cont_ptr,
2908            continuation_id,
2909            current_initial,
2910            current_eval,
2911            owner_function,
2912            immediate_force_value,
2913            env_ptr,
2914            env_len,
2915        ];
2916        let _ = call_i64_helper(
2917            module_backend,
2918            builder,
2919            PUSH_RETURN_FRAME_HELPERS.many,
2920            &args,
2921        )?;
2922        return Ok(());
2923    }
2924    let mut args = vec![
2925        cont_ptr,
2926        continuation_id,
2927        current_initial,
2928        current_eval,
2929        owner_function,
2930        immediate_force_value,
2931    ];
2932    args.extend(env_values);
2933    let helper_name = PUSH_RETURN_FRAME_HELPERS.fixed(resume_cont.environment.len());
2934    let _ = call_i64_helper(module_backend, builder, helper_name, &args)?;
2935    Ok(())
2936}
2937
2938/// write27-c c0 HOTFIX: switch to the callee's eval context using
2939/// `pre_push_count` (return_frame_len observed BEFORE F_post push), not
2940/// the post-push length. Layer 1/2 semantics:
2941///   pre_push_count = return_frames.len();
2942///   push(F_post);
2943///   callee.initial_frame_count = pre_push_count;
2944/// If we used the post-push length, the callee's `frame_len <= initial`
2945/// invariant would hold immediately and F_post would never be consumed,
2946/// killing the EffectfulCall return-frame chain.
2947fn switch_eval_context_for_callee<M: Module>(
2948    module_backend: &mut M,
2949    builder: &mut FunctionBuilder<'_>,
2950    pre_push_count: ir::Value,
2951) -> CpsReprCraneliftResult<()> {
2952    let fresh_eval = call_i64_helper(module_backend, builder, "yulang_cps_fresh_eval_id_i64", &[])?;
2953    let _ = call_i64_helper(
2954        module_backend,
2955        builder,
2956        "yulang_cps_set_eval_context_i64",
2957        &[fresh_eval, pre_push_count],
2958    )?;
2959    Ok(())
2960}
2961
2962fn lower_effect_branch<M: Module>(
2963    module_backend: &mut M,
2964    builder: &mut FunctionBuilder<'_>,
2965    function: &CpsReprAbiFunction,
2966    cond: CpsValueId,
2967    then_cont: CpsContinuationId,
2968    else_cont: CpsContinuationId,
2969    functions: &DeclaredFunctions,
2970) -> CpsReprCraneliftResult<()> {
2971    let then_block = builder.create_block();
2972    let else_block = builder.create_block();
2973    let merge_block = builder.create_block();
2974    builder.append_block_param(merge_block, types::I64);
2975
2976    let cond_id = cond;
2977    let cond = read_value(builder, function, cond_id)?;
2978    let cond = force_branch_condition_if_thunk(module_backend, builder, function, cond_id, cond)?;
2979    let cond = builder
2980        .ins()
2981        .icmp_imm(ir::condcodes::IntCC::NotEqual, cond, 0);
2982    builder.ins().brif(cond, then_block, &[], else_block, &[]);
2983
2984    builder.switch_to_block(then_block);
2985    let then_value =
2986        call_continuation(module_backend, builder, function, then_cont, &[], functions)?;
2987    builder
2988        .ins()
2989        .jump(merge_block, &[ir::BlockArg::Value(then_value)]);
2990
2991    builder.switch_to_block(else_block);
2992    let else_value =
2993        call_continuation(module_backend, builder, function, else_cont, &[], functions)?;
2994    builder
2995        .ins()
2996        .jump(merge_block, &[ir::BlockArg::Value(else_value)]);
2997
2998    builder.switch_to_block(merge_block);
2999    let result = builder.block_params(merge_block)[0];
3000    builder.ins().return_(&[result]);
3001    Ok(())
3002}
3003
3004fn lower_selected_handler_return<M: Module>(
3005    module_backend: &mut M,
3006    builder: &mut FunctionBuilder<'_>,
3007    function: &CpsReprAbiFunction,
3008    candidates: &[HandlerCandidate],
3009    selected: ir::Value,
3010    payload: ir::Value,
3011    resumption: ir::Value,
3012    host_fallback: Option<(HostConsoleEffect, CpsContinuationId)>,
3013    functions: &DeclaredFunctions,
3014) -> CpsReprCraneliftResult<()> {
3015    let missing_block = builder.create_block();
3016    let selected_owner = call_i64_helper(
3017        module_backend,
3018        builder,
3019        "yulang_cps_selected_handler_owner_function_i64",
3020        &[],
3021    )?;
3022
3023    for (index, candidate) in candidates.iter().enumerate() {
3024        let call_block = builder.create_block();
3025        let owner_check_block = builder.create_block();
3026        let owner_compare_block = builder.create_block();
3027        let next_block = if index + 1 == candidates.len() {
3028            missing_block
3029        } else {
3030            builder.create_block()
3031        };
3032        let compare = builder.ins().icmp_imm(
3033            ir::condcodes::IntCC::Equal,
3034            selected,
3035            candidate.handler.0 as i64,
3036        );
3037        builder
3038            .ins()
3039            .brif(compare, owner_check_block, &[], next_block, &[]);
3040
3041        builder.switch_to_block(owner_check_block);
3042        let owner_unknown = builder
3043            .ins()
3044            .icmp_imm(ir::condcodes::IntCC::Equal, selected_owner, 0);
3045        builder
3046            .ins()
3047            .brif(owner_unknown, call_block, &[], owner_compare_block, &[]);
3048
3049        builder.switch_to_block(owner_compare_block);
3050        let candidate_owner = functions
3051            .function_ids
3052            .get(&candidate.function)
3053            .copied()
3054            .ok_or_else(|| CpsReprCraneliftError::MissingFunction {
3055                name: candidate.function.clone(),
3056            })?;
3057        let candidate_owner = builder.ins().iconst(types::I64, candidate_owner as i64);
3058        let owner_matches =
3059            builder
3060                .ins()
3061                .icmp(ir::condcodes::IntCC::Equal, selected_owner, candidate_owner);
3062        builder
3063            .ins()
3064            .brif(owner_matches, call_block, &[], next_block, &[]);
3065
3066        builder.switch_to_block(call_block);
3067        let callee = continuation_func_ref_by_name(
3068            module_backend,
3069            builder,
3070            &candidate.function,
3071            candidate.entry,
3072            functions,
3073        )?;
3074        let fallback_env = if candidate.function == function.name {
3075            continuation_environment_argument(module_backend, builder, function, candidate.entry)?
3076        } else {
3077            builder.ins().iconst(types::I64, 0)
3078        };
3079        let entry = builder.ins().iconst(types::I64, candidate.entry.0 as i64);
3080        let handler_env = call_i64_helper(
3081            module_backend,
3082            builder,
3083            "yulang_cps_selected_handler_env_or_i64",
3084            &[entry, fallback_env],
3085        )?;
3086        // Handler arm bodies are evaluated with an empty return-frame stack in
3087        // cps_eval/cps_repr. Keeping the perform-site frames here would let
3088        // the arm's natural return continue the caller rest before
3089        // `perform_finish_i64` wraps the arm result.
3090        let _ = call_i64_helper(
3091            module_backend,
3092            builder,
3093            "yulang_cps_enter_handler_arm_i64",
3094            &[],
3095        )?;
3096        // write27-d d5: arm body runs in a fresh eval context. The return
3097        // frame helper above makes `initial_frame_count` observe zero, matching
3098        // Layer 1/2's local `eval_continuations(..., return_frames = [])`.
3099        let (saved_eval, saved_initial) = enter_callee_eval_context(module_backend, builder)?;
3100        let call = builder
3101            .ins()
3102            .call(callee, &[handler_env, payload, resumption]);
3103        let results = builder.inst_results(call);
3104        if results.len() != 1 {
3105            return Err(CpsReprCraneliftError::InvalidReturnArity {
3106                function: function.name.clone(),
3107                arity: results.len(),
3108            });
3109        }
3110        let result = results[0];
3111        restore_caller_eval_context(module_backend, builder, saved_eval, saved_initial)?;
3112        let _ = call_i64_helper(
3113            module_backend,
3114            builder,
3115            "yulang_cps_exit_handler_arm_i64",
3116            &[],
3117        )?;
3118        if candidate.continues_to_installed_escape {
3119            let value = call_i64_helper(
3120                module_backend,
3121                builder,
3122                "yulang_cps_perform_finish_escaped_i64",
3123                &[result],
3124            )?;
3125            builder.ins().return_(&[value]);
3126            builder.switch_to_block(next_block);
3127            continue;
3128        }
3129        // write27-c c3/c4: Perform-arm post-call routing via the
3130        // combined `perform_finish_i64` helper. It restores the outer
3131        // handler stack, wraps the arm result as a ScopeReturn when
3132        // the selected handler is real, routes the SR (current stack
3133        // walk, frame walk, or propagate), and falls back to the
3134        // legacy `abort_i64` slot for synthetic handlers.
3135        let routed = call_i64_helper(
3136            module_backend,
3137            builder,
3138            "yulang_cps_perform_finish_i64",
3139            &[result],
3140        )?;
3141        builder.ins().return_(&[routed]);
3142
3143        builder.switch_to_block(next_block);
3144    }
3145
3146    builder.switch_to_block(missing_block);
3147    if let Some((kind, resume)) = host_fallback {
3148        lower_host_console_perform(
3149            module_backend,
3150            builder,
3151            function,
3152            kind,
3153            payload,
3154            resume,
3155            functions,
3156        )?;
3157    } else {
3158        let value = builder.ins().iconst(types::I64, 0);
3159        builder.ins().return_(&[value]);
3160    }
3161    builder.seal_block(missing_block);
3162    Ok(())
3163}
3164
3165fn lower_host_console_perform<M: Module>(
3166    module_backend: &mut M,
3167    builder: &mut FunctionBuilder<'_>,
3168    function: &CpsReprAbiFunction,
3169    kind: HostConsoleEffect,
3170    payload: ir::Value,
3171    resume: CpsContinuationId,
3172    functions: &DeclaredFunctions,
3173) -> CpsReprCraneliftResult<()> {
3174    let helper = match kind {
3175        HostConsoleEffect::Debug => "yulang_cps_debug_i64",
3176        HostConsoleEffect::OutWrite => "yulang_cps_out_write_i64",
3177        HostConsoleEffect::ErrWrite => "yulang_cps_err_write_i64",
3178        HostConsoleEffect::WarnWrite => "yulang_cps_warn_write_i64",
3179        HostConsoleEffect::DieDie => "yulang_cps_die_i64",
3180    };
3181    let result = call_i64_helper(module_backend, builder, helper, &[payload])?;
3182    let resume_value = match kind {
3183        HostConsoleEffect::OutWrite
3184        | HostConsoleEffect::ErrWrite
3185        | HostConsoleEffect::WarnWrite
3186        | HostConsoleEffect::DieDie => builder.ins().iconst(types::I64, 0),
3187        HostConsoleEffect::Debug => result,
3188    };
3189    let value = call_continuation_with_values(
3190        module_backend,
3191        builder,
3192        function,
3193        resume,
3194        &[resume_value],
3195        functions,
3196    )?;
3197    builder.ins().return_(&[value]);
3198    Ok(())
3199}
3200
3201fn force_branch_condition_if_thunk<M: Module>(
3202    module_backend: &mut M,
3203    builder: &mut FunctionBuilder<'_>,
3204    function: &CpsReprAbiFunction,
3205    cond_id: CpsValueId,
3206    cond: ir::Value,
3207) -> CpsReprCraneliftResult<ir::Value> {
3208    if value_lane(function, cond_id) != Some(CpsReprAbiLane::ThunkPtr)
3209        && !value_is_make_thunk(function, cond_id)
3210    {
3211        return Ok(cond);
3212    }
3213    let helper = declare_import(
3214        module_backend,
3215        builder,
3216        "yulang_cps_force_thunk_i64",
3217        &[types::I64],
3218        types::I64,
3219    )?;
3220    let call = builder.ins().call(helper, &[cond]);
3221    Ok(builder.inst_results(call)[0])
3222}
3223
3224fn call_continuation<M: Module>(
3225    module_backend: &mut M,
3226    builder: &mut FunctionBuilder<'_>,
3227    function: &CpsReprAbiFunction,
3228    target: CpsContinuationId,
3229    args: &[CpsValueId],
3230    functions: &DeclaredFunctions,
3231) -> CpsReprCraneliftResult<ir::Value> {
3232    let args = read_values(builder, function, args)?;
3233    call_continuation_with_values(module_backend, builder, function, target, &args, functions)
3234}
3235
3236fn call_continuation_with_values<M: Module>(
3237    module_backend: &mut M,
3238    builder: &mut FunctionBuilder<'_>,
3239    function: &CpsReprAbiFunction,
3240    target: CpsContinuationId,
3241    args: &[ir::Value],
3242    functions: &DeclaredFunctions,
3243) -> CpsReprCraneliftResult<ir::Value> {
3244    let callee = continuation_func_ref(module_backend, builder, function, target, functions)?;
3245    let env = continuation_environment_argument(module_backend, builder, function, target)?;
3246    let mut call_args = vec![env];
3247    call_args.extend_from_slice(args);
3248    let call = builder.ins().call(callee, &call_args);
3249    let results = builder.inst_results(call);
3250    if results.len() != 1 {
3251        return Err(CpsReprCraneliftError::InvalidReturnArity {
3252            function: function.name.clone(),
3253            arity: results.len(),
3254        });
3255    }
3256    let result = results[0];
3257    let result = abort_result_or_return(module_backend, builder, result)?;
3258    return_if_scope_return_active(module_backend, builder, result)?;
3259    Ok(result)
3260}
3261
3262fn continuation_environment_argument<M: Module>(
3263    module_backend: &mut M,
3264    builder: &mut FunctionBuilder<'_>,
3265    function: &CpsReprAbiFunction,
3266    target: CpsContinuationId,
3267) -> CpsReprCraneliftResult<ir::Value> {
3268    let target = continuation(function, target)?;
3269    if target.environment.is_empty() {
3270        return Ok(builder.ins().iconst(types::I64, 0));
3271    }
3272    let mut args = Vec::with_capacity(target.environment.len());
3273    for slot in &target.environment {
3274        validate_environment_lane(function, slot.value, slot.lane)?;
3275        args.push(read_value(builder, function, slot.value)?);
3276    }
3277    make_env(module_backend, builder, function, &args)
3278}
3279
3280fn make_resumption<M: Module>(
3281    module_backend: &mut M,
3282    builder: &mut FunctionBuilder<'_>,
3283    function: &CpsReprAbiFunction,
3284    resume: CpsContinuationId,
3285    handler: CpsHandlerId,
3286    functions: &DeclaredFunctions,
3287) -> CpsReprCraneliftResult<ir::Value> {
3288    let resume_continuation = continuation(function, resume)?;
3289    if resume_continuation.params.len() != 1 {
3290        return Err(CpsReprCraneliftError::UnsupportedTerminator {
3291            function: function.name.clone(),
3292            kind: "resume continuation arity",
3293        });
3294    }
3295    let func_ref = continuation_func_ref(module_backend, builder, function, resume, functions)?;
3296    let code = builder.ins().func_addr(types::I64, func_ref);
3297    let handler = builder.ins().iconst(types::I64, handler.0 as i64);
3298    let mut env_values = Vec::with_capacity(resume_continuation.environment.len());
3299    for slot in &resume_continuation.environment {
3300        validate_environment_lane(function, slot.value, slot.lane)?;
3301        env_values.push(read_value(builder, function, slot.value)?);
3302    }
3303    if env_values.len() > 4 {
3304        let (env_ptr, env_len) = stack_i64_slice(builder, &env_values)?;
3305        return call_i64_helper(
3306            module_backend,
3307            builder,
3308            MAKE_RESUMPTION_HELPERS.many,
3309            &[code, handler, env_ptr, env_len],
3310        );
3311    }
3312    let mut args = vec![code, handler];
3313    args.extend(env_values);
3314    let helper_name = MAKE_RESUMPTION_HELPERS.fixed(resume_continuation.environment.len());
3315    let params = vec![types::I64; args.len()];
3316    let helper = declare_import(module_backend, builder, helper_name, &params, types::I64)?;
3317    let call = builder.ins().call(helper, &args);
3318    let results = builder.inst_results(call);
3319    Ok(results[0])
3320}
3321
3322fn make_thunk<M: Module>(
3323    module_backend: &mut M,
3324    builder: &mut FunctionBuilder<'_>,
3325    function: &CpsReprAbiFunction,
3326    entry: CpsContinuationId,
3327    functions: &DeclaredFunctions,
3328) -> CpsReprCraneliftResult<ir::Value> {
3329    let thunk_continuation = continuation(function, entry)?;
3330    if !thunk_continuation.params.is_empty() {
3331        return Err(CpsReprCraneliftError::UnsupportedStmt {
3332            function: function.name.clone(),
3333            kind: "thunk entry arity",
3334        });
3335    }
3336
3337    let func_ref = continuation_func_ref(module_backend, builder, function, entry, functions)?;
3338    let code = builder.ins().func_addr(types::I64, func_ref);
3339    let mut args = vec![code];
3340    for slot in &thunk_continuation.environment {
3341        validate_environment_lane(function, slot.value, slot.lane)?;
3342        args.push(read_value(builder, function, slot.value)?);
3343    }
3344    if thunk_continuation.environment.len() > 4 {
3345        let (env_ptr, env_len) = stack_i64_slice(builder, &args[1..])?;
3346        return call_i64_helper(
3347            module_backend,
3348            builder,
3349            MAKE_THUNK_HELPERS.many,
3350            &[code, env_ptr, env_len],
3351        );
3352    }
3353    let helper_name = MAKE_THUNK_HELPERS.fixed(thunk_continuation.environment.len());
3354    let params = vec![types::I64; args.len()];
3355    let helper = declare_import(module_backend, builder, helper_name, &params, types::I64)?;
3356    let call = builder.ins().call(helper, &args);
3357    let results = builder.inst_results(call);
3358    Ok(results[0])
3359}
3360
3361fn make_closure<M: Module>(
3362    module_backend: &mut M,
3363    builder: &mut FunctionBuilder<'_>,
3364    function: &CpsReprAbiFunction,
3365    entry: CpsContinuationId,
3366    functions: &DeclaredFunctions,
3367) -> CpsReprCraneliftResult<ir::Value> {
3368    let closure_continuation = continuation(function, entry)?;
3369    if closure_continuation.params.len() != 1 {
3370        return Err(CpsReprCraneliftError::UnsupportedStmt {
3371            function: function.name.clone(),
3372            kind: "closure entry arity",
3373        });
3374    }
3375    let func_ref = continuation_func_ref(module_backend, builder, function, entry, functions)?;
3376    let code = builder.ins().func_addr(types::I64, func_ref);
3377    let mut args = vec![code];
3378    for slot in &closure_continuation.environment {
3379        validate_environment_lane(function, slot.value, slot.lane)?;
3380        args.push(read_value(builder, function, slot.value)?);
3381    }
3382    if closure_continuation.environment.len() > 4 {
3383        let (env_ptr, env_len) = stack_i64_slice(builder, &args[1..])?;
3384        return call_i64_helper(
3385            module_backend,
3386            builder,
3387            MAKE_CLOSURE_HELPERS.many,
3388            &[code, env_ptr, env_len],
3389        );
3390    }
3391    let helper_name = MAKE_CLOSURE_HELPERS.fixed(closure_continuation.environment.len());
3392    let params = vec![types::I64; args.len()];
3393    let helper = declare_import(module_backend, builder, helper_name, &params, types::I64)?;
3394    let call = builder.ins().call(helper, &args);
3395    let results = builder.inst_results(call);
3396    Ok(results[0])
3397}
3398
3399fn make_recursive_closure<M: Module>(
3400    module_backend: &mut M,
3401    builder: &mut FunctionBuilder<'_>,
3402    function: &CpsReprAbiFunction,
3403    dest: CpsValueId,
3404    entry: CpsContinuationId,
3405    functions: &DeclaredFunctions,
3406) -> CpsReprCraneliftResult<ir::Value> {
3407    let closure_continuation = continuation(function, entry)?;
3408    if closure_continuation.params.len() != 1 {
3409        return Err(CpsReprCraneliftError::UnsupportedStmt {
3410            function: function.name.clone(),
3411            kind: "recursive closure entry arity",
3412        });
3413    }
3414    let func_ref = continuation_func_ref(module_backend, builder, function, entry, functions)?;
3415    let code = builder.ins().func_addr(types::I64, func_ref);
3416    let mut args = vec![code];
3417    let mut self_slot = None;
3418    for (index, slot) in closure_continuation.environment.iter().enumerate() {
3419        validate_environment_lane(function, slot.value, slot.lane)?;
3420        if slot.value == dest {
3421            self_slot = Some(index);
3422            args.push(builder.ins().iconst(types::I64, 0));
3423        } else {
3424            args.push(read_value(builder, function, slot.value)?);
3425        }
3426    }
3427    let Some(self_slot) = self_slot else {
3428        return make_closure(module_backend, builder, function, entry, functions);
3429    };
3430    if closure_continuation.environment.len() > 4 {
3431        let self_slot_value = builder.ins().iconst(types::I64, self_slot as i64);
3432        let (env_ptr, env_len) = stack_i64_slice(builder, &args[1..])?;
3433        return call_i64_helper(
3434            module_backend,
3435            builder,
3436            MAKE_RECURSIVE_CLOSURE_HELPERS.many,
3437            &[code, self_slot_value, env_ptr, env_len],
3438        );
3439    }
3440    let self_slot = builder.ins().iconst(types::I64, self_slot as i64);
3441    args.insert(1, self_slot);
3442    let helper_name = MAKE_RECURSIVE_CLOSURE_HELPERS.fixed(closure_continuation.environment.len());
3443    let params = vec![types::I64; args.len()];
3444    let helper = declare_import(module_backend, builder, helper_name, &params, types::I64)?;
3445    let call = builder.ins().call(helper, &args);
3446    let results = builder.inst_results(call);
3447    Ok(results[0])
3448}
3449
3450fn call_i64_helper<M: Module>(
3451    module_backend: &mut M,
3452    builder: &mut FunctionBuilder<'_>,
3453    name: &str,
3454    args: &[ir::Value],
3455) -> CpsReprCraneliftResult<ir::Value> {
3456    let params = vec![types::I64; args.len()];
3457    let helper = declare_import(module_backend, builder, name, &params, types::I64)?;
3458    let call = builder.ins().call(helper, args);
3459    Ok(builder.inst_results(call)[0])
3460}
3461
3462fn call_helper<M: Module>(
3463    module_backend: &mut M,
3464    builder: &mut FunctionBuilder<'_>,
3465    name: &str,
3466    params: &[ir::Type],
3467    ret: ir::Type,
3468    args: &[ir::Value],
3469) -> CpsReprCraneliftResult<ir::Value> {
3470    let helper = declare_import(module_backend, builder, name, params, ret)?;
3471    let call = builder.ins().call(helper, args);
3472    Ok(builder.inst_results(call)[0])
3473}
3474
3475fn stack_i64_slice(
3476    builder: &mut FunctionBuilder<'_>,
3477    args: &[ir::Value],
3478) -> CpsReprCraneliftResult<(ir::Value, ir::Value)> {
3479    let byte_size = u32::try_from(args.len().saturating_mul(8)).map_err(|_| {
3480        CpsReprCraneliftError::Cranelift("CPS repr stack slice is too large".to_string())
3481    })?;
3482    let slot = builder.create_sized_stack_slot(ir::StackSlotData::new(
3483        ir::StackSlotKind::ExplicitSlot,
3484        byte_size,
3485        3,
3486    ));
3487    for (index, arg) in args.iter().copied().enumerate() {
3488        builder.ins().stack_store(arg, slot, (index * 8) as i32);
3489    }
3490    let ptr = builder.ins().stack_addr(types::I64, slot, 0);
3491    let len = builder.ins().iconst(types::I64, args.len() as i64);
3492    Ok((ptr, len))
3493}
3494
3495fn abort_result_or_return<M: Module>(
3496    module_backend: &mut M,
3497    builder: &mut FunctionBuilder<'_>,
3498    value: ir::Value,
3499) -> CpsReprCraneliftResult<ir::Value> {
3500    let mode = call_i64_helper(module_backend, builder, "yulang_cps_abort_mode_i64", &[])?;
3501    let no_abort = builder.create_block();
3502    let abort = builder.create_block();
3503    builder.append_block_param(no_abort, types::I64);
3504    builder
3505        .ins()
3506        .brif(mode, abort, &[], no_abort, &[ir::BlockArg::Value(value)]);
3507
3508    builder.switch_to_block(abort);
3509    builder.seal_block(abort);
3510    let consume = builder.ins().icmp_imm(ir::condcodes::IntCC::Equal, mode, 2);
3511    let consume_block = builder.create_block();
3512    let return_block = builder.create_block();
3513    builder
3514        .ins()
3515        .brif(consume, consume_block, &[], return_block, &[]);
3516
3517    builder.switch_to_block(return_block);
3518    builder.seal_block(return_block);
3519    let abort_value = call_i64_helper(module_backend, builder, "yulang_cps_abort_value_i64", &[])?;
3520    builder.ins().return_(&[abort_value]);
3521
3522    builder.switch_to_block(consume_block);
3523    builder.seal_block(consume_block);
3524    let should_return_routed_jump = call_i64_helper(
3525        module_backend,
3526        builder,
3527        "yulang_cps_routed_jump_should_return_i64",
3528        &[],
3529    )?;
3530    let routed_jump_block = builder.create_block();
3531    let scoped_abort_block = builder.create_block();
3532    builder.ins().brif(
3533        should_return_routed_jump,
3534        routed_jump_block,
3535        &[],
3536        scoped_abort_block,
3537        &[],
3538    );
3539
3540    builder.switch_to_block(routed_jump_block);
3541    builder.seal_block(routed_jump_block);
3542    let abort_value =
3543        call_i64_helper(module_backend, builder, "yulang_cps_consume_abort_i64", &[])?;
3544    builder.ins().return_(&[abort_value]);
3545
3546    builder.switch_to_block(scoped_abort_block);
3547    builder.seal_block(scoped_abort_block);
3548    let abort_value =
3549        call_i64_helper(module_backend, builder, "yulang_cps_consume_abort_i64", &[])?;
3550    builder
3551        .ins()
3552        .jump(no_abort, &[ir::BlockArg::Value(abort_value)]);
3553
3554    builder.switch_to_block(no_abort);
3555    builder.seal_block(no_abort);
3556    Ok(builder.block_params(no_abort)[0])
3557}
3558
3559/// write27-d d5: enter a fresh eval context for a synchronous internal
3560/// call. Mirrors Layer 1/2 where each `eval_continuations` invocation
3561/// gets a fresh `CpsEvalId` plus `initial_frame_count = current frame
3562/// count`. Returns the saved caller `(eval_id, initial)` so the
3563/// caller can restore them post-call.
3564fn enter_callee_eval_context<M: Module>(
3565    module_backend: &mut M,
3566    builder: &mut FunctionBuilder<'_>,
3567) -> CpsReprCraneliftResult<(ir::Value, ir::Value)> {
3568    let saved_eval = call_i64_helper(
3569        module_backend,
3570        builder,
3571        "yulang_cps_current_eval_id_i64",
3572        &[],
3573    )?;
3574    let saved_initial = call_i64_helper(
3575        module_backend,
3576        builder,
3577        "yulang_cps_current_initial_frame_count_i64",
3578        &[],
3579    )?;
3580    let callee_initial = call_i64_helper(
3581        module_backend,
3582        builder,
3583        "yulang_cps_return_frame_len_i64",
3584        &[],
3585    )?;
3586    let callee_eval =
3587        call_i64_helper(module_backend, builder, "yulang_cps_fresh_eval_id_i64", &[])?;
3588    let _ = call_i64_helper(
3589        module_backend,
3590        builder,
3591        "yulang_cps_set_eval_context_i64",
3592        &[callee_eval, callee_initial],
3593    )?;
3594    Ok((saved_eval, saved_initial))
3595}
3596
3597/// write27-d d5: pair with `enter_callee_eval_context` — restores the
3598/// caller's saved `(eval_id, initial)` after the sync call returns.
3599fn restore_caller_eval_context<M: Module>(
3600    module_backend: &mut M,
3601    builder: &mut FunctionBuilder<'_>,
3602    saved_eval: ir::Value,
3603    saved_initial: ir::Value,
3604) -> CpsReprCraneliftResult<()> {
3605    let _ = call_i64_helper(
3606        module_backend,
3607        builder,
3608        "yulang_cps_set_eval_context_i64",
3609        &[saved_eval, saved_initial],
3610    )?;
3611    Ok(())
3612}
3613
3614/// write27-c c5: after each synchronous internal call, check the
3615/// ScopeReturn slot. If active, route it; the route either jumps to
3616/// a matched handler's escape (return its result) or propagates with
3617/// the slot still active (return the fallback). Either way, the
3618/// current function short-circuits — sync callers up the chain run
3619/// their own post-call route to keep propagating.
3620fn return_if_scope_return_active<M: Module>(
3621    module_backend: &mut M,
3622    builder: &mut FunctionBuilder<'_>,
3623    fallback: ir::Value,
3624) -> CpsReprCraneliftResult<()> {
3625    let active = call_i64_helper(
3626        module_backend,
3627        builder,
3628        "yulang_cps_scope_return_active_i64",
3629        &[],
3630    )?;
3631    let route_block = builder.create_block();
3632    let cont_block = builder.create_block();
3633    builder
3634        .ins()
3635        .brif(active, route_block, &[], cont_block, &[]);
3636
3637    builder.switch_to_block(route_block);
3638    builder.seal_block(route_block);
3639    let routed = call_i64_helper(
3640        module_backend,
3641        builder,
3642        "yulang_cps_route_scope_return_i64",
3643        &[fallback],
3644    )?;
3645    builder.ins().return_(&[routed]);
3646
3647    builder.switch_to_block(cont_block);
3648    builder.seal_block(cont_block);
3649    Ok(())
3650}
3651
3652fn return_if_scope_return_active_without_routing<M: Module>(
3653    module_backend: &mut M,
3654    builder: &mut FunctionBuilder<'_>,
3655    fallback: ir::Value,
3656) -> CpsReprCraneliftResult<()> {
3657    let active = call_i64_helper(
3658        module_backend,
3659        builder,
3660        "yulang_cps_scope_return_active_i64",
3661        &[],
3662    )?;
3663    let return_block = builder.create_block();
3664    let cont_block = builder.create_block();
3665    builder
3666        .ins()
3667        .brif(active, return_block, &[], cont_block, &[]);
3668
3669    builder.switch_to_block(return_block);
3670    builder.seal_block(return_block);
3671    builder.ins().return_(&[fallback]);
3672
3673    builder.switch_to_block(cont_block);
3674    builder.seal_block(cont_block);
3675    Ok(())
3676}
3677
3678fn scope_return_fallback_value(
3679    builder: &mut FunctionBuilder<'_>,
3680    function: &CpsReprAbiFunction,
3681    value: CpsValueId,
3682    fallback: ir::Value,
3683) -> ir::Value {
3684    let lane = value_lane(function, value).unwrap_or(CpsReprAbiLane::Unknown);
3685    scope_return_fallback_for_lane(builder, lane, fallback)
3686}
3687
3688fn scope_return_fallback_for_lane(
3689    builder: &mut FunctionBuilder<'_>,
3690    lane: CpsReprAbiLane,
3691    fallback: ir::Value,
3692) -> ir::Value {
3693    match lane {
3694        CpsReprAbiLane::NativeFloat => builder.ins().iconst(types::I64, 0),
3695        _ => fallback,
3696    }
3697}
3698
3699fn make_env<M: Module>(
3700    module_backend: &mut M,
3701    builder: &mut FunctionBuilder<'_>,
3702    _function: &CpsReprAbiFunction,
3703    args: &[ir::Value],
3704) -> CpsReprCraneliftResult<ir::Value> {
3705    if args.len() > 4 {
3706        let (env_ptr, env_len) = stack_i64_slice(builder, args)?;
3707        return call_i64_helper(
3708            module_backend,
3709            builder,
3710            MAKE_ENV_HELPERS.many,
3711            &[env_ptr, env_len],
3712        );
3713    }
3714    let helper_name = MAKE_ENV_HELPERS.fixed(args.len());
3715    let params = vec![types::I64; args.len()];
3716    let helper = declare_import(module_backend, builder, helper_name, &params, types::I64)?;
3717    let call = builder.ins().call(helper, args);
3718    let results = builder.inst_results(call);
3719    Ok(results[0])
3720}
3721
3722fn make_tuple_value<M: Module>(
3723    module_backend: &mut M,
3724    builder: &mut FunctionBuilder<'_>,
3725    args: &[ir::Value],
3726) -> CpsReprCraneliftResult<ir::Value> {
3727    let helper_name = TUPLE_HELPERS.select(args.len());
3728    if args.len() > 4 {
3729        return Err(CpsReprCraneliftError::UnsupportedStmt {
3730            function: "<tuple>".to_string(),
3731            kind: "tuple larger than four slots",
3732        });
3733    }
3734    call_i64_helper(module_backend, builder, helper_name, args)
3735}
3736
3737fn make_record_value<M: Module, L: CpsLiteralStore>(
3738    module_backend: &mut M,
3739    builder: &mut FunctionBuilder<'_>,
3740    function: &CpsReprAbiFunction,
3741    base: Option<CpsValueId>,
3742    fields: &[CpsRecordField],
3743    literals: &mut L,
3744    values: &LocalValueCache,
3745) -> CpsReprCraneliftResult<ir::Value> {
3746    let mut record = match base {
3747        Some(base) => read_value(builder, function, base)?,
3748        None => call_i64_helper(module_backend, builder, "yulang_cps_record_empty_i64", &[])?,
3749    };
3750    for field in fields {
3751        let value = read_runtime_value_i64(module_backend, builder, function, values, field.value)?;
3752        let (field_ptr, field_len) =
3753            literals.literal_bytes(module_backend, builder, field.name.0.as_bytes())?;
3754        record = call_i64_helper(
3755            module_backend,
3756            builder,
3757            "yulang_cps_record_insert_i64",
3758            &[record, field_ptr, field_len, value],
3759        )?;
3760    }
3761    Ok(record)
3762}
3763
3764fn make_record_without_fields_value<M: Module, L: CpsLiteralStore>(
3765    module_backend: &mut M,
3766    builder: &mut FunctionBuilder<'_>,
3767    function: &CpsReprAbiFunction,
3768    base: CpsValueId,
3769    fields: &[typed_ir::Name],
3770    literals: &mut L,
3771) -> CpsReprCraneliftResult<ir::Value> {
3772    let mut record = read_value(builder, function, base)?;
3773    for field in fields {
3774        let (field_ptr, field_len) =
3775            literals.literal_bytes(module_backend, builder, field.0.as_bytes())?;
3776        record = call_i64_helper(
3777            module_backend,
3778            builder,
3779            "yulang_cps_record_without_field_i64",
3780            &[record, field_ptr, field_len],
3781        )?;
3782    }
3783    Ok(record)
3784}
3785
3786pub(super) fn tag_hash(tag: &typed_ir::Name) -> i64 {
3787    let mut hash = 0xcbf29ce484222325_u64;
3788    for byte in tag.0.as_bytes() {
3789        hash ^= u64::from(*byte);
3790        hash = hash.wrapping_mul(0x100000001b3);
3791    }
3792    hash as i64
3793}
3794
3795fn register_variant_tag<M: Module, L: CpsLiteralStore>(
3796    module_backend: &mut M,
3797    builder: &mut FunctionBuilder<'_>,
3798    tag: &typed_ir::Name,
3799    literals: &mut L,
3800) -> CpsReprCraneliftResult<ir::Value> {
3801    let tag_hash = builder.ins().iconst(types::I64, tag_hash(tag));
3802    let (name_ptr, name_len) = literals.literal_bytes(module_backend, builder, tag.0.as_bytes())?;
3803    let _ = call_i64_helper(
3804        module_backend,
3805        builder,
3806        "yulang_cps_register_tag_i64",
3807        &[tag_hash, name_ptr, name_len],
3808    )?;
3809    Ok(tag_hash)
3810}
3811
3812trait CpsLiteralStore {
3813    fn literal_bytes<M: Module>(
3814        &mut self,
3815        module_backend: &mut M,
3816        builder: &mut FunctionBuilder<'_>,
3817        bytes: &[u8],
3818    ) -> CpsReprCraneliftResult<(ir::Value, ir::Value)>;
3819}
3820
3821struct HostLiteralStore<'a> {
3822    strings: &'a mut Vec<Box<str>>,
3823}
3824
3825impl CpsLiteralStore for HostLiteralStore<'_> {
3826    fn literal_bytes<M: Module>(
3827        &mut self,
3828        _module_backend: &mut M,
3829        builder: &mut FunctionBuilder<'_>,
3830        bytes: &[u8],
3831    ) -> CpsReprCraneliftResult<(ir::Value, ir::Value)> {
3832        let text = unsafe { std::str::from_utf8_unchecked(bytes) }
3833            .to_string()
3834            .into_boxed_str();
3835        let ptr = text.as_ptr() as i64;
3836        let len = text.len() as i64;
3837        self.strings.push(text);
3838        Ok((
3839            builder.ins().iconst(types::I64, ptr),
3840            builder.ins().iconst(types::I64, len),
3841        ))
3842    }
3843}
3844
3845#[derive(Default)]
3846struct ObjectLiteralStore {
3847    next_id: usize,
3848}
3849
3850impl CpsLiteralStore for ObjectLiteralStore {
3851    fn literal_bytes<M: Module>(
3852        &mut self,
3853        module_backend: &mut M,
3854        builder: &mut FunctionBuilder<'_>,
3855        bytes: &[u8],
3856    ) -> CpsReprCraneliftResult<(ir::Value, ir::Value)> {
3857        let name = format!("__yulang_cps_lit_{}", self.next_id);
3858        self.next_id += 1;
3859        let data_id = module_backend
3860            .declare_data(&name, Linkage::Local, false, false)
3861            .map_err(cranelift_error)?;
3862        let mut data = DataDescription::new();
3863        data.define(bytes.to_vec().into_boxed_slice());
3864        module_backend
3865            .define_data(data_id, &data)
3866            .map_err(cranelift_error)?;
3867        let global = module_backend.declare_data_in_func(data_id, builder.func);
3868        Ok((
3869            builder.ins().symbol_value(types::I64, global),
3870            builder.ins().iconst(types::I64, bytes.len() as i64),
3871        ))
3872    }
3873}
3874
3875/// write27-b: alias for the `continuation()` lookup, used in scopes
3876/// where a parameter named `continuation` shadows the function name.
3877fn lookup_continuation<'a>(
3878    function: &'a CpsReprAbiFunction,
3879    id: CpsContinuationId,
3880) -> CpsReprCraneliftResult<&'a CpsReprAbiContinuation> {
3881    continuation(function, id)
3882}
3883
3884fn continuation(
3885    function: &CpsReprAbiFunction,
3886    id: CpsContinuationId,
3887) -> CpsReprCraneliftResult<&CpsReprAbiContinuation> {
3888    function
3889        .continuations
3890        .iter()
3891        .find(|continuation| continuation.id == id)
3892        .ok_or_else(|| CpsReprCraneliftError::MissingContinuation {
3893            function: function.name.clone(),
3894            continuation: id,
3895        })
3896}
3897
3898fn declare_import<M: Module>(
3899    module_backend: &mut M,
3900    builder: &mut FunctionBuilder<'_>,
3901    name: &str,
3902    params: &[ir::Type],
3903    ret: ir::Type,
3904) -> CpsReprCraneliftResult<ir::FuncRef> {
3905    let mut sig = module_backend.make_signature();
3906    sig.params.extend(params.iter().copied().map(AbiParam::new));
3907    sig.returns.push(AbiParam::new(ret));
3908    let id = module_backend
3909        .declare_function(name, Linkage::Import, &sig)
3910        .map_err(cranelift_error)?;
3911    Ok(module_backend.declare_func_in_func(id, builder.func))
3912}
3913
3914fn function_signature<M: Module>(
3915    module_backend: &M,
3916    function: &CpsReprAbiFunction,
3917) -> ir::Signature {
3918    let mut sig = module_backend.make_signature();
3919    sig.params.extend(
3920        effective_function_param_lanes(function)
3921            .into_iter()
3922            .map(|lane| AbiParam::new(lane_type(lane))),
3923    );
3924    let return_lane = continuation(function, function.entry)
3925        .map(|entry| effective_continuation_return_lane(function, entry))
3926        .unwrap_or(CpsReprAbiLane::Unknown);
3927    sig.returns.push(AbiParam::new(lane_type(return_lane)));
3928    sig
3929}
3930
3931fn lower_function<M: Module, L: CpsLiteralStore>(
3932    module_backend: &mut M,
3933    ctx: &mut cranelift_codegen::Context,
3934    function: &CpsReprAbiFunction,
3935    functions: &DeclaredFunctions,
3936    literals: &mut L,
3937) -> CpsReprCraneliftResult<()> {
3938    let mut builder_context = FunctionBuilderContext::new();
3939    let mut builder = FunctionBuilder::new(&mut ctx.func, &mut builder_context);
3940    let blocks = create_blocks(&mut builder, function);
3941    declare_variables(&mut builder, function);
3942    let mut values = LocalValueCache::default();
3943    bind_function_params(&mut builder, function, &blocks, &mut values)?;
3944
3945    for continuation in &function.continuations {
3946        let block = continuation_block(function, &blocks, continuation.id)?;
3947        builder.switch_to_block(block);
3948        bind_continuation_params(&mut builder, function, continuation, block, &mut values)?;
3949        for stmt in &continuation.stmts {
3950            lower_stmt(
3951                module_backend,
3952                &mut builder,
3953                function,
3954                stmt,
3955                functions,
3956                literals,
3957                &mut values,
3958            )?;
3959        }
3960        lower_terminator(
3961            &mut builder,
3962            function,
3963            &blocks,
3964            continuation,
3965            &continuation.terminator,
3966            &mut values,
3967        )?;
3968    }
3969    builder.seal_all_blocks();
3970    builder.finalize();
3971    Ok(())
3972}
3973
3974fn create_blocks(
3975    builder: &mut FunctionBuilder<'_>,
3976    function: &CpsReprAbiFunction,
3977) -> HashMap<CpsContinuationId, ir::Block> {
3978    function
3979        .continuations
3980        .iter()
3981        .map(|continuation| {
3982            let block = builder.create_block();
3983            if continuation.id != function.entry {
3984                for param in &continuation.params {
3985                    builder.append_block_param(
3986                        block,
3987                        lane_type(effective_value_lane(function, param.value)),
3988                    );
3989                }
3990            }
3991            (continuation.id, block)
3992        })
3993        .collect()
3994}
3995
3996fn declare_variables(builder: &mut FunctionBuilder<'_>, function: &CpsReprAbiFunction) {
3997    for value in function_value_ids(function) {
3998        builder.declare_var(variable(value), types::I64);
3999        builder.declare_var(
4000            variable_for_lane(value, CpsReprAbiLane::NativeFloat),
4001            types::F64,
4002        );
4003    }
4004}
4005
4006fn bind_function_params(
4007    builder: &mut FunctionBuilder<'_>,
4008    function: &CpsReprAbiFunction,
4009    blocks: &HashMap<CpsContinuationId, ir::Block>,
4010    values: &mut LocalValueCache,
4011) -> CpsReprCraneliftResult<()> {
4012    let entry = continuation_block(function, blocks, function.entry)?;
4013    builder.append_block_params_for_function_params(entry);
4014    builder.switch_to_block(entry);
4015    let params = builder.block_params(entry).to_vec();
4016    let entry_continuation = function
4017        .continuations
4018        .iter()
4019        .find(|continuation| continuation.id == function.entry)
4020        .ok_or(CpsReprCraneliftError::MissingContinuation {
4021            function: function.name.clone(),
4022            continuation: function.entry,
4023        })?;
4024    if entry_continuation.params.len() != function.params.len() {
4025        return Err(CpsReprCraneliftError::UnsupportedFunction {
4026            function: function.name.clone(),
4027            reason: "entry continuation parameter arity",
4028        });
4029    }
4030    for ((function_param, continuation_param), value) in function
4031        .params
4032        .iter()
4033        .zip(&entry_continuation.params)
4034        .zip(params)
4035    {
4036        let lane = effective_value_lane(function, continuation_param.value);
4037        define_value_as_lane(builder, values, function_param.value, lane, value);
4038        if continuation_param.value != function_param.value {
4039            define_value_as_lane(builder, values, continuation_param.value, lane, value);
4040        }
4041    }
4042    Ok(())
4043}
4044
4045fn bind_continuation_params(
4046    builder: &mut FunctionBuilder<'_>,
4047    function: &CpsReprAbiFunction,
4048    continuation: &CpsReprAbiContinuation,
4049    block: ir::Block,
4050    values: &mut LocalValueCache,
4051) -> CpsReprCraneliftResult<()> {
4052    if continuation.id == function.entry {
4053        return Ok(());
4054    }
4055    let params = builder.block_params(block).to_vec();
4056    for (param, value) in continuation.params.iter().zip(params) {
4057        define_value_as_lane(
4058            builder,
4059            values,
4060            param.value,
4061            effective_value_lane(function, param.value),
4062            value,
4063        );
4064    }
4065    Ok(())
4066}
4067
4068fn lower_stmt<M: Module, L: CpsLiteralStore>(
4069    module_backend: &mut M,
4070    builder: &mut FunctionBuilder<'_>,
4071    function: &CpsReprAbiFunction,
4072    stmt: &CpsStmt,
4073    functions: &DeclaredFunctions,
4074    literals: &mut L,
4075    values: &mut LocalValueCache,
4076) -> CpsReprCraneliftResult<()> {
4077    match stmt {
4078        CpsStmt::Literal { dest, literal } => {
4079            let value = lower_literal(module_backend, builder, function, literal, literals)?;
4080            if matches!(literal, CpsLiteral::Float(_)) {
4081                define_value_as_lane(builder, values, *dest, CpsReprAbiLane::NativeFloat, value);
4082                let boxed = call_helper(
4083                    module_backend,
4084                    builder,
4085                    "yulang_cps_box_float_f64",
4086                    &[types::F64],
4087                    types::I64,
4088                    &[value],
4089                )?;
4090                builder.def_var(variable(*dest), boxed);
4091                return Ok(());
4092            }
4093            define_value_as_lane(builder, values, *dest, literal_lane(literal), value);
4094        }
4095        CpsStmt::FreshGuard { dest, .. } => {
4096            let value =
4097                call_i64_helper(module_backend, builder, "yulang_cps_fresh_guard_i64", &[])?;
4098            builder.def_var(variable(*dest), value);
4099        }
4100        CpsStmt::PeekGuard { dest } => {
4101            let value = call_i64_helper(module_backend, builder, "yulang_cps_peek_guard_i64", &[])?;
4102            builder.def_var(variable(*dest), value);
4103        }
4104        CpsStmt::FindGuard { dest, guard } => {
4105            let guard = read_value(builder, function, *guard)?;
4106            let value = call_i64_helper(
4107                module_backend,
4108                builder,
4109                "yulang_cps_find_guard_i64",
4110                &[guard],
4111            )?;
4112            builder.def_var(variable(*dest), value);
4113        }
4114        CpsStmt::MakeThunk { dest, entry } => {
4115            let value = make_thunk(module_backend, builder, function, *entry, functions)?;
4116            builder.def_var(variable(*dest), value);
4117        }
4118        CpsStmt::AddThunkBoundary { dest, thunk, .. } => {
4119            let value = read_value(builder, function, *thunk)?;
4120            builder.def_var(variable(*dest), value);
4121        }
4122        CpsStmt::MakeClosure { dest, entry } => {
4123            let value = make_closure(module_backend, builder, function, *entry, functions)?;
4124            builder.def_var(variable(*dest), value);
4125        }
4126        CpsStmt::MakeRecursiveClosure { dest, entry } => {
4127            let value = make_recursive_closure(
4128                module_backend,
4129                builder,
4130                function,
4131                *dest,
4132                *entry,
4133                functions,
4134            )?;
4135            builder.def_var(variable(*dest), value);
4136        }
4137        CpsStmt::ForceThunk { dest, thunk } => {
4138            if force_thunk_passes_native_float(function, values, *thunk) {
4139                let value = read_value_as_lane(
4140                    builder,
4141                    function,
4142                    values,
4143                    *thunk,
4144                    CpsReprAbiLane::NativeFloat,
4145                )?;
4146                define_value_as_lane(builder, values, *dest, CpsReprAbiLane::NativeFloat, value);
4147                return Ok(());
4148            }
4149            let helper = declare_import(
4150                module_backend,
4151                builder,
4152                "yulang_cps_force_thunk_i64",
4153                &[types::I64],
4154                types::I64,
4155            )?;
4156            let thunk = read_value(builder, function, *thunk)?;
4157            // write27-d d5: fresh eval context for the sync force.
4158            let (saved_eval, saved_initial) = enter_callee_eval_context(module_backend, builder)?;
4159            let call = builder.ins().call(helper, &[thunk]);
4160            let results = builder.inst_results(call);
4161            let result = results[0];
4162            restore_caller_eval_context(module_backend, builder, saved_eval, saved_initial)?;
4163            let result = abort_result_or_return(module_backend, builder, result)?;
4164            let scope_fallback = scope_return_fallback_value(builder, function, *dest, result);
4165            return_if_scope_return_active(module_backend, builder, scope_fallback)?;
4166            builder.def_var(variable(*dest), result);
4167        }
4168        CpsStmt::Tuple { dest, items } => {
4169            let items = read_runtime_values_i64(module_backend, builder, function, values, items)?;
4170            let value = make_tuple_value(module_backend, builder, &items)?;
4171            builder.def_var(variable(*dest), value);
4172        }
4173        CpsStmt::Record { dest, base, fields } => {
4174            let value = make_record_value(
4175                module_backend,
4176                builder,
4177                function,
4178                *base,
4179                fields,
4180                literals,
4181                values,
4182            )?;
4183            builder.def_var(variable(*dest), value);
4184        }
4185        CpsStmt::RecordWithoutFields { dest, base, fields } => {
4186            let value = make_record_without_fields_value(
4187                module_backend,
4188                builder,
4189                function,
4190                *base,
4191                fields,
4192                literals,
4193            )?;
4194            builder.def_var(variable(*dest), value);
4195        }
4196        CpsStmt::Select { dest, base, field } => {
4197            let base = read_value(builder, function, *base)?;
4198            let (field_ptr, field_len) =
4199                literals.literal_bytes(module_backend, builder, field.0.as_bytes())?;
4200            let value = call_i64_helper(
4201                module_backend,
4202                builder,
4203                "yulang_cps_record_select_i64",
4204                &[base, field_ptr, field_len],
4205            )?;
4206            builder.def_var(variable(*dest), value);
4207        }
4208        CpsStmt::SelectWithDefault {
4209            dest,
4210            base,
4211            field,
4212            default,
4213        } => {
4214            let base = read_value(builder, function, *base)?;
4215            let default = read_value(builder, function, *default)?;
4216            let (field_ptr, field_len) =
4217                literals.literal_bytes(module_backend, builder, field.0.as_bytes())?;
4218            let value = call_i64_helper(
4219                module_backend,
4220                builder,
4221                "yulang_cps_record_select_or_default_i64",
4222                &[base, field_ptr, field_len, default],
4223            )?;
4224            builder.def_var(variable(*dest), value);
4225        }
4226        CpsStmt::RecordHasField { dest, base, field } => {
4227            let base = read_value(builder, function, *base)?;
4228            let (field_ptr, field_len) =
4229                literals.literal_bytes(module_backend, builder, field.0.as_bytes())?;
4230            let value = call_i64_helper(
4231                module_backend,
4232                builder,
4233                "yulang_cps_record_has_field_i64",
4234                &[base, field_ptr, field_len],
4235            )?;
4236            builder.def_var(variable(*dest), value);
4237        }
4238        CpsStmt::Variant { dest, tag, value } => {
4239            let value = value
4240                .map(|value| {
4241                    read_runtime_value_i64(module_backend, builder, function, values, value)
4242                })
4243                .transpose()?;
4244            let tag = register_variant_tag(module_backend, builder, tag, literals)?;
4245            let result = if let Some(value) = value {
4246                call_i64_helper(
4247                    module_backend,
4248                    builder,
4249                    "yulang_cps_variant_i64_1",
4250                    &[tag, value],
4251                )?
4252            } else {
4253                call_i64_helper(module_backend, builder, "yulang_cps_variant_i64_0", &[tag])?
4254            };
4255            builder.def_var(variable(*dest), result);
4256        }
4257        CpsStmt::TupleGet { dest, tuple, index } => {
4258            let tuple = read_value(builder, function, *tuple)?;
4259            let index = builder.ins().iconst(types::I64, *index as i64);
4260            let value = call_i64_helper(
4261                module_backend,
4262                builder,
4263                "yulang_cps_tuple_get_i64",
4264                &[tuple, index],
4265            )?;
4266            builder.def_var(variable(*dest), value);
4267        }
4268        CpsStmt::VariantTagEq { dest, variant, tag } => {
4269            let variant = read_value(builder, function, *variant)?;
4270            let tag = builder.ins().iconst(types::I64, tag_hash(tag));
4271            let value = call_i64_helper(
4272                module_backend,
4273                builder,
4274                "yulang_cps_variant_tag_eq_i64",
4275                &[variant, tag],
4276            )?;
4277            builder.def_var(variable(*dest), value);
4278        }
4279        CpsStmt::VariantPayload { dest, variant } => {
4280            let variant = read_value(builder, function, *variant)?;
4281            let value = call_i64_helper(
4282                module_backend,
4283                builder,
4284                "yulang_cps_variant_payload_i64",
4285                &[variant],
4286            )?;
4287            builder.def_var(variable(*dest), value);
4288        }
4289        CpsStmt::Primitive { dest, op, args } => {
4290            let args = read_primitive_args(module_backend, builder, function, values, *op, args)?;
4291            let value = lower_primitive(module_backend, builder, function, *op, &args)?;
4292            define_value_as_lane(builder, values, *dest, primitive_result_lane(*op), value);
4293        }
4294        CpsStmt::DirectCall { dest, target, args } => {
4295            lower_direct_call_stmt(
4296                module_backend,
4297                builder,
4298                function,
4299                functions,
4300                values,
4301                *dest,
4302                target,
4303                args,
4304            )?;
4305        }
4306        CpsStmt::ApplyClosure { dest, closure, arg } => {
4307            let closure = read_value(builder, function, *closure)?;
4308            let arg = read_value(builder, function, *arg)?;
4309            // write27-d d5: fresh eval context for the sync apply.
4310            let (saved_eval, saved_initial) = enter_callee_eval_context(module_backend, builder)?;
4311            let value = call_i64_helper(
4312                module_backend,
4313                builder,
4314                "yulang_cps_apply_closure_i64",
4315                &[closure, arg],
4316            )?;
4317            restore_caller_eval_context(module_backend, builder, saved_eval, saved_initial)?;
4318            let value = abort_result_or_return(module_backend, builder, value)?;
4319            return_if_scope_return_active(module_backend, builder, value)?;
4320            builder.def_var(variable(*dest), value);
4321        }
4322        CpsStmt::CloneContinuation { dest, source } => {
4323            let source = read_value(builder, function, *source)?;
4324            builder.def_var(variable(*dest), source);
4325        }
4326        CpsStmt::Resume { .. } | CpsStmt::ResumeWithHandler { .. } => {
4327            return Err(CpsReprCraneliftError::UnsupportedStmt {
4328                function: function.name.clone(),
4329                kind: "resume",
4330            });
4331        }
4332        CpsStmt::InstallHandler { handler, envs, .. } => {
4333            capture_handler_envs(module_backend, builder, function, *handler, envs)?;
4334            let handler = builder.ins().iconst(types::I64, handler.0 as i64);
4335            let consumes_mask = builder.ins().iconst(types::I64, -1);
4336            let _ = call_i64_helper(
4337                module_backend,
4338                builder,
4339                "yulang_cps_install_handler_i64",
4340                &[handler, consumes_mask],
4341            )?;
4342        }
4343        CpsStmt::UninstallHandler { handler } => {
4344            let handler = builder.ins().iconst(types::I64, handler.0 as i64);
4345            let _ = call_i64_helper(
4346                module_backend,
4347                builder,
4348                "yulang_cps_uninstall_handler_i64",
4349                &[handler],
4350            )?;
4351        }
4352    }
4353    Ok(())
4354}
4355
4356fn lower_terminator(
4357    builder: &mut FunctionBuilder<'_>,
4358    function: &CpsReprAbiFunction,
4359    blocks: &HashMap<CpsContinuationId, ir::Block>,
4360    continuation: &CpsReprAbiContinuation,
4361    terminator: &CpsTerminator,
4362    values: &LocalValueCache,
4363) -> CpsReprCraneliftResult<()> {
4364    match terminator {
4365        CpsTerminator::Return(value) => {
4366            let value = read_value_as_lane(
4367                builder,
4368                function,
4369                values,
4370                *value,
4371                effective_continuation_return_lane(function, continuation),
4372            )?;
4373            builder.ins().return_(&[value]);
4374        }
4375        CpsTerminator::Continue { target, args } => {
4376            let target_continuation = lookup_continuation(function, *target)?;
4377            let target = continuation_block(function, blocks, *target)?;
4378            let args = read_block_args(builder, function, values, target_continuation, args)?;
4379            builder.ins().jump(target, &args);
4380        }
4381        CpsTerminator::Branch {
4382            cond,
4383            then_cont,
4384            else_cont,
4385        } => {
4386            let cond = read_value(builder, function, *cond)?;
4387            let cond = builder
4388                .ins()
4389                .icmp_imm(ir::condcodes::IntCC::NotEqual, cond, 0);
4390            let then_cont = continuation_block(function, blocks, *then_cont)?;
4391            let else_cont = continuation_block(function, blocks, *else_cont)?;
4392            builder.ins().brif(cond, then_cont, &[], else_cont, &[]);
4393        }
4394        CpsTerminator::Perform { .. } => {
4395            return Err(CpsReprCraneliftError::UnsupportedTerminator {
4396                function: function.name.clone(),
4397                kind: "perform",
4398            });
4399        }
4400        CpsTerminator::EffectfulCall { .. } => {
4401            return Err(CpsReprCraneliftError::UnsupportedTerminator {
4402                function: function.name.clone(),
4403                kind: "effectful-call",
4404            });
4405        }
4406        CpsTerminator::EffectfulApply { .. } => {
4407            return Err(CpsReprCraneliftError::UnsupportedTerminator {
4408                function: function.name.clone(),
4409                kind: "effectful-apply",
4410            });
4411        }
4412        CpsTerminator::EffectfulForce { .. } => {
4413            return Err(CpsReprCraneliftError::UnsupportedTerminator {
4414                function: function.name.clone(),
4415                kind: "effectful-force",
4416            });
4417        }
4418    }
4419    Ok(())
4420}
4421
4422fn lower_direct_call_stmt<M: Module>(
4423    module_backend: &mut M,
4424    builder: &mut FunctionBuilder<'_>,
4425    function: &CpsReprAbiFunction,
4426    functions: &DeclaredFunctions,
4427    values: &mut LocalValueCache,
4428    dest: CpsValueId,
4429    target: &str,
4430    args: &[CpsValueId],
4431) -> CpsReprCraneliftResult<()> {
4432    let id = functions.functions.get(target).copied().ok_or_else(|| {
4433        CpsReprCraneliftError::MissingFunction {
4434            name: target.to_string(),
4435        }
4436    })?;
4437    let callee = module_backend.declare_func_in_func(id, builder.func);
4438    let args = read_call_args(builder, function, values, args, target, functions)?;
4439    let result_lane = functions
4440        .function_returns
4441        .get(target)
4442        .copied()
4443        .unwrap_or(CpsReprAbiLane::Unknown);
4444
4445    if !functions
4446        .function_effect_flow
4447        .get(target)
4448        .copied()
4449        .unwrap_or(true)
4450    {
4451        let call = builder.ins().call(callee, &args);
4452        let results = builder.inst_results(call);
4453        if results.len() != 1 {
4454            return Err(CpsReprCraneliftError::InvalidReturnArity {
4455                function: target.to_string(),
4456                arity: results.len(),
4457            });
4458        }
4459        define_value_as_lane(builder, values, dest, result_lane, results[0]);
4460        return Ok(());
4461    }
4462
4463    // Effectful callees may route local returns through the eval context, so
4464    // they keep the heavier call protocol. Pure callees use the plain call
4465    // path above and can be optimized like ordinary SSA calls.
4466    let (saved_eval, saved_initial) = enter_callee_eval_context(module_backend, builder)?;
4467    let call = builder.ins().call(callee, &args);
4468    let results = builder.inst_results(call);
4469    if results.len() != 1 {
4470        return Err(CpsReprCraneliftError::InvalidReturnArity {
4471            function: target.to_string(),
4472            arity: results.len(),
4473        });
4474    }
4475    let result = results[0];
4476    restore_caller_eval_context(module_backend, builder, saved_eval, saved_initial)?;
4477    let result = abort_result_or_return(module_backend, builder, result)?;
4478    let scope_fallback = scope_return_fallback_for_lane(builder, result_lane, result);
4479    return_if_scope_return_active(module_backend, builder, scope_fallback)?;
4480    define_value_as_lane(builder, values, dest, result_lane, result);
4481    Ok(())
4482}
4483
4484fn effect_matches(expected: &typed_ir::Path, actual: &typed_ir::Path) -> bool {
4485    effect_path_matches_allowed(expected, actual)
4486}
4487
4488fn effect_allowed_by_type(allowed: &typed_ir::Type, effect: &typed_ir::Path) -> bool {
4489    match allowed {
4490        typed_ir::Type::Any => true,
4491        typed_ir::Type::Never => false,
4492        typed_ir::Type::Named { path, .. } => effect_path_matches_allowed(path, effect),
4493        typed_ir::Type::Row { items, tail } => {
4494            items
4495                .iter()
4496                .any(|item| effect_allowed_by_type(item, effect))
4497                || matches!(tail.as_ref(), typed_ir::Type::Any)
4498        }
4499        _ => false,
4500    }
4501}
4502
4503fn effect_allowed_by_type_for_registered(
4504    allowed: &typed_ir::Type,
4505    function: &str,
4506    effect: &typed_ir::Path,
4507) -> bool {
4508    if effect_allowed_by_type(allowed, effect) {
4509        return true;
4510    }
4511    registered_effect_absolute_path(function, effect)
4512        .as_ref()
4513        .is_some_and(|effect| effect_allowed_by_type(allowed, effect))
4514}
4515
4516fn registered_effect_absolute_path(
4517    function: &str,
4518    effect: &typed_ir::Path,
4519) -> Option<typed_ir::Path> {
4520    // Handler candidates may carry paths relative to their defining
4521    // function, while effect masks are checked against caller-visible
4522    // absolute effect paths.
4523    if effect
4524        .segments
4525        .first()
4526        .is_some_and(|segment| segment.0 == "std")
4527    {
4528        return Some(effect.clone());
4529    }
4530    let base = function
4531        .split_once("__mono")
4532        .map_or(function, |(base, _)| base);
4533    let mut segments = base
4534        .split("::")
4535        .map(|segment| typed_ir::Name(segment.to_string()))
4536        .collect::<Vec<_>>();
4537    segments.pop()?;
4538    segments.extend(effect.segments.iter().cloned());
4539    Some(typed_ir::Path { segments })
4540}
4541
4542fn effect_path_matches_allowed(allowed: &typed_ir::Path, effect: &typed_ir::Path) -> bool {
4543    if effect.segments.starts_with(&allowed.segments) {
4544        return true;
4545    }
4546    if allowed.segments.len() > 1
4547        && effect.segments.len() == allowed.segments.len()
4548        && effect.segments[..effect.segments.len() - 1]
4549            == allowed.segments[..allowed.segments.len() - 1]
4550        && effect_segment_matches_allowed(
4551            &allowed.segments[allowed.segments.len() - 1],
4552            &effect.segments[effect.segments.len() - 1],
4553        )
4554    {
4555        return true;
4556    }
4557    effect
4558        .segments
4559        .iter()
4560        .enumerate()
4561        .skip(1)
4562        .any(|(index, _)| effect.segments[index..].starts_with(&allowed.segments))
4563}
4564
4565fn effect_segment_matches_allowed(allowed: &typed_ir::Name, effect: &typed_ir::Name) -> bool {
4566    allowed == effect
4567        || effect
4568            .0
4569            .strip_suffix("#effect")
4570            .is_some_and(|base| base == allowed.0)
4571}
4572
4573#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4574enum HostConsoleEffect {
4575    Debug,
4576    OutWrite,
4577    ErrWrite,
4578    WarnWrite,
4579    DieDie,
4580}
4581
4582fn host_console_effect_kind(effect: &typed_ir::Path) -> Option<HostConsoleEffect> {
4583    match effect.segments.as_slice() {
4584        [std, prelude, role, method]
4585            if std.0 == "std"
4586                && prelude.0 == "prelude"
4587                && role.0 == "Debug"
4588                && method.0 == "debug" =>
4589        {
4590            Some(HostConsoleEffect::Debug)
4591        }
4592        [std, module_seg, act_seg, operation] if std.0 == "std" && module_seg.0 == "out" => {
4593            match (act_seg.0.as_str(), operation.0.as_str()) {
4594                ("out", "write") => Some(HostConsoleEffect::OutWrite),
4595                ("err", "write") => Some(HostConsoleEffect::ErrWrite),
4596                ("warn", "warn") => Some(HostConsoleEffect::WarnWrite),
4597                ("die", "die") => Some(HostConsoleEffect::DieDie),
4598                _ => None,
4599            }
4600        }
4601        _ => None,
4602    }
4603}
4604
4605fn lower_literal<M: Module, L: CpsLiteralStore>(
4606    module_backend: &mut M,
4607    builder: &mut FunctionBuilder<'_>,
4608    function: &CpsReprAbiFunction,
4609    literal: &CpsLiteral,
4610    literals: &mut L,
4611) -> CpsReprCraneliftResult<ir::Value> {
4612    match literal {
4613        CpsLiteral::Int(value) => {
4614            let value =
4615                value
4616                    .parse::<i64>()
4617                    .map_err(|_| CpsReprCraneliftError::UnsupportedLiteral {
4618                        function: function.name.clone(),
4619                        literal: literal.clone(),
4620                    })?;
4621            Ok(builder.ins().iconst(types::I64, value))
4622        }
4623        CpsLiteral::Bool(value) => Ok(builder.ins().iconst(types::I64, i64::from(*value))),
4624        CpsLiteral::Unit => call_i64_helper(module_backend, builder, "yulang_cps_unit_i64", &[]),
4625        CpsLiteral::Float(value) => {
4626            let value =
4627                value
4628                    .parse::<f64>()
4629                    .map_err(|_| CpsReprCraneliftError::UnsupportedLiteral {
4630                        function: function.name.clone(),
4631                        literal: literal.clone(),
4632                    })?;
4633            Ok(builder.ins().f64const(value))
4634        }
4635        CpsLiteral::String(value) => {
4636            let (ptr, len) = literals.literal_bytes(module_backend, builder, value.as_bytes())?;
4637            call_i64_helper(
4638                module_backend,
4639                builder,
4640                "yulang_cps_string_literal_i64",
4641                &[ptr, len],
4642            )
4643        }
4644    }
4645}
4646
4647fn lower_primitive<M: Module>(
4648    module_backend: &mut M,
4649    builder: &mut FunctionBuilder<'_>,
4650    _function: &CpsReprAbiFunction,
4651    op: typed_ir::PrimitiveOp,
4652    args: &[ir::Value],
4653) -> CpsReprCraneliftResult<ir::Value> {
4654    let value = match op {
4655        typed_ir::PrimitiveOp::BoolNot => {
4656            let zero = builder.ins().iconst(types::I64, 0);
4657            let is_zero = builder
4658                .ins()
4659                .icmp(ir::condcodes::IntCC::Equal, args[0], zero);
4660            builder.ins().uextend(types::I64, is_zero)
4661        }
4662        typed_ir::PrimitiveOp::BoolEq | typed_ir::PrimitiveOp::IntEq => {
4663            let eq = builder
4664                .ins()
4665                .icmp(ir::condcodes::IntCC::Equal, args[0], args[1]);
4666            builder.ins().uextend(types::I64, eq)
4667        }
4668        typed_ir::PrimitiveOp::IntAdd => builder.ins().iadd(args[0], args[1]),
4669        typed_ir::PrimitiveOp::IntSub => builder.ins().isub(args[0], args[1]),
4670        typed_ir::PrimitiveOp::IntMul => builder.ins().imul(args[0], args[1]),
4671        typed_ir::PrimitiveOp::IntDiv => builder.ins().sdiv(args[0], args[1]),
4672        typed_ir::PrimitiveOp::IntLt => {
4673            int_cmp(builder, ir::condcodes::IntCC::SignedLessThan, args)
4674        }
4675        typed_ir::PrimitiveOp::IntLe => {
4676            int_cmp(builder, ir::condcodes::IntCC::SignedLessThanOrEqual, args)
4677        }
4678        typed_ir::PrimitiveOp::IntGt => {
4679            int_cmp(builder, ir::condcodes::IntCC::SignedGreaterThan, args)
4680        }
4681        typed_ir::PrimitiveOp::IntGe => int_cmp(
4682            builder,
4683            ir::condcodes::IntCC::SignedGreaterThanOrEqual,
4684            args,
4685        ),
4686        typed_ir::PrimitiveOp::FloatAdd => builder.ins().fadd(args[0], args[1]),
4687        typed_ir::PrimitiveOp::FloatSub => builder.ins().fsub(args[0], args[1]),
4688        typed_ir::PrimitiveOp::FloatMul => builder.ins().fmul(args[0], args[1]),
4689        typed_ir::PrimitiveOp::FloatDiv => builder.ins().fdiv(args[0], args[1]),
4690        typed_ir::PrimitiveOp::FloatEq => float_cmp(builder, ir::condcodes::FloatCC::Equal, args),
4691        typed_ir::PrimitiveOp::FloatLt => {
4692            float_cmp(builder, ir::condcodes::FloatCC::LessThan, args)
4693        }
4694        typed_ir::PrimitiveOp::FloatLe => {
4695            float_cmp(builder, ir::condcodes::FloatCC::LessThanOrEqual, args)
4696        }
4697        typed_ir::PrimitiveOp::FloatGt => {
4698            float_cmp(builder, ir::condcodes::FloatCC::GreaterThan, args)
4699        }
4700        typed_ir::PrimitiveOp::FloatGe => {
4701            float_cmp(builder, ir::condcodes::FloatCC::GreaterThanOrEqual, args)
4702        }
4703        typed_ir::PrimitiveOp::ListEmpty => {
4704            call_i64_helper(module_backend, builder, "yulang_cps_list_empty_i64", &[])?
4705        }
4706        typed_ir::PrimitiveOp::ListSingleton => call_i64_helper(
4707            module_backend,
4708            builder,
4709            "yulang_cps_list_singleton_i64",
4710            &[args[0]],
4711        )?,
4712        typed_ir::PrimitiveOp::ListMerge => call_i64_helper(
4713            module_backend,
4714            builder,
4715            "yulang_cps_list_merge_i64",
4716            &[args[0], args[1]],
4717        )?,
4718        typed_ir::PrimitiveOp::ListLen => call_i64_helper(
4719            module_backend,
4720            builder,
4721            "yulang_cps_list_len_i64",
4722            &[args[0]],
4723        )?,
4724        typed_ir::PrimitiveOp::ListIndex => call_i64_helper(
4725            module_backend,
4726            builder,
4727            "yulang_cps_list_index_i64",
4728            &[args[0], args[1]],
4729        )?,
4730        typed_ir::PrimitiveOp::ListIndexRangeRaw => call_i64_helper(
4731            module_backend,
4732            builder,
4733            "yulang_cps_list_index_range_raw_i64",
4734            &[args[0], args[1], args[2]],
4735        )?,
4736        typed_ir::PrimitiveOp::ListIndexRange => call_i64_helper(
4737            module_backend,
4738            builder,
4739            "yulang_cps_list_index_range_i64",
4740            &[args[0], args[1]],
4741        )?,
4742        typed_ir::PrimitiveOp::ListSpliceRaw => call_i64_helper(
4743            module_backend,
4744            builder,
4745            "yulang_cps_list_splice_raw_i64",
4746            &[args[0], args[1], args[2], args[3]],
4747        )?,
4748        typed_ir::PrimitiveOp::ListSplice => call_i64_helper(
4749            module_backend,
4750            builder,
4751            "yulang_cps_list_splice_i64",
4752            &[args[0], args[1], args[2]],
4753        )?,
4754        typed_ir::PrimitiveOp::ListViewRaw => call_i64_helper(
4755            module_backend,
4756            builder,
4757            "yulang_cps_list_view_raw_i64",
4758            &[args[0]],
4759        )?,
4760        typed_ir::PrimitiveOp::IntToString => call_i64_helper(
4761            module_backend,
4762            builder,
4763            "yulang_cps_int_to_string_i64",
4764            &[args[0]],
4765        )?,
4766        typed_ir::PrimitiveOp::IntToHex => call_i64_helper(
4767            module_backend,
4768            builder,
4769            "yulang_cps_int_to_hex_i64",
4770            &[args[0]],
4771        )?,
4772        typed_ir::PrimitiveOp::IntToUpperHex => call_i64_helper(
4773            module_backend,
4774            builder,
4775            "yulang_cps_int_to_upper_hex_i64",
4776            &[args[0]],
4777        )?,
4778        typed_ir::PrimitiveOp::BoolToString => call_i64_helper(
4779            module_backend,
4780            builder,
4781            "yulang_cps_bool_to_string_i64",
4782            &[args[0]],
4783        )?,
4784        typed_ir::PrimitiveOp::FloatToString => call_helper(
4785            module_backend,
4786            builder,
4787            "yulang_cps_float_to_string_f64",
4788            &[types::F64],
4789            types::I64,
4790            &[args[0]],
4791        )?,
4792        typed_ir::PrimitiveOp::StringConcat => call_i64_helper(
4793            module_backend,
4794            builder,
4795            "yulang_cps_string_concat_i64",
4796            &[args[0], args[1]],
4797        )?,
4798        typed_ir::PrimitiveOp::StringEq => call_i64_helper(
4799            module_backend,
4800            builder,
4801            "yulang_cps_string_eq_i64",
4802            &[args[0], args[1]],
4803        )?,
4804        typed_ir::PrimitiveOp::StringLen => call_i64_helper(
4805            module_backend,
4806            builder,
4807            "yulang_cps_string_len_i64",
4808            &[args[0]],
4809        )?,
4810        typed_ir::PrimitiveOp::StringIndex => call_i64_helper(
4811            module_backend,
4812            builder,
4813            "yulang_cps_string_index_i64",
4814            &[args[0], args[1]],
4815        )?,
4816        typed_ir::PrimitiveOp::StringIndexRangeRaw => call_i64_helper(
4817            module_backend,
4818            builder,
4819            "yulang_cps_string_index_range_raw_i64",
4820            &[args[0], args[1], args[2]],
4821        )?,
4822        typed_ir::PrimitiveOp::StringIndexRange => call_i64_helper(
4823            module_backend,
4824            builder,
4825            "yulang_cps_string_index_range_i64",
4826            &[args[0], args[1]],
4827        )?,
4828        typed_ir::PrimitiveOp::StringSpliceRaw => call_i64_helper(
4829            module_backend,
4830            builder,
4831            "yulang_cps_string_splice_raw_i64",
4832            &[args[0], args[1], args[2], args[3]],
4833        )?,
4834        typed_ir::PrimitiveOp::StringSplice => call_i64_helper(
4835            module_backend,
4836            builder,
4837            "yulang_cps_string_splice_i64",
4838            &[args[0], args[1], args[2]],
4839        )?,
4840        typed_ir::PrimitiveOp::StringToBytes => call_i64_helper(
4841            module_backend,
4842            builder,
4843            "yulang_cps_string_to_bytes_i64",
4844            &[args[0]],
4845        )?,
4846        typed_ir::PrimitiveOp::BytesLen => call_i64_helper(
4847            module_backend,
4848            builder,
4849            "yulang_cps_bytes_len_i64",
4850            &[args[0]],
4851        )?,
4852        typed_ir::PrimitiveOp::BytesEq => call_i64_helper(
4853            module_backend,
4854            builder,
4855            "yulang_cps_bytes_eq_i64",
4856            &[args[0], args[1]],
4857        )?,
4858        typed_ir::PrimitiveOp::BytesConcat => call_i64_helper(
4859            module_backend,
4860            builder,
4861            "yulang_cps_bytes_concat_i64",
4862            &[args[0], args[1]],
4863        )?,
4864        typed_ir::PrimitiveOp::BytesIndex => call_i64_helper(
4865            module_backend,
4866            builder,
4867            "yulang_cps_bytes_index_i64",
4868            &[args[0], args[1]],
4869        )?,
4870        typed_ir::PrimitiveOp::BytesIndexRange => call_i64_helper(
4871            module_backend,
4872            builder,
4873            "yulang_cps_bytes_index_range_i64",
4874            &[args[0], args[1]],
4875        )?,
4876        typed_ir::PrimitiveOp::BytesToUtf8Raw => call_i64_helper(
4877            module_backend,
4878            builder,
4879            "yulang_cps_bytes_to_utf8_raw_i64",
4880            &[args[0]],
4881        )?,
4882        typed_ir::PrimitiveOp::BytesToPath => call_i64_helper(
4883            module_backend,
4884            builder,
4885            "yulang_cps_bytes_to_path_i64",
4886            &[args[0]],
4887        )?,
4888        typed_ir::PrimitiveOp::PathToBytes => call_i64_helper(
4889            module_backend,
4890            builder,
4891            "yulang_cps_path_to_bytes_i64",
4892            &[args[0]],
4893        )?,
4894    };
4895    Ok(value)
4896}
4897
4898fn int_cmp(
4899    builder: &mut FunctionBuilder<'_>,
4900    code: ir::condcodes::IntCC,
4901    args: &[ir::Value],
4902) -> ir::Value {
4903    let cmp = builder.ins().icmp(code, args[0], args[1]);
4904    builder.ins().uextend(types::I64, cmp)
4905}
4906
4907fn float_cmp(
4908    builder: &mut FunctionBuilder<'_>,
4909    code: ir::condcodes::FloatCC,
4910    args: &[ir::Value],
4911) -> ir::Value {
4912    let cmp = builder.ins().fcmp(code, args[0], args[1]);
4913    builder.ins().uextend(types::I64, cmp)
4914}
4915
4916fn validate_scalar_subset(module: &CpsReprAbiModule) -> CpsReprCraneliftResult<()> {
4917    for function in &module.functions {
4918        validate_scalar_function(function, false)?;
4919    }
4920    for function in &module.roots {
4921        validate_scalar_function(function, true)?;
4922    }
4923    Ok(())
4924}
4925
4926fn validate_scalar_function(
4927    function: &CpsReprAbiFunction,
4928    require_scalar_entry_return: bool,
4929) -> CpsReprCraneliftResult<()> {
4930    let has_effect_flow = function_has_effect_flow(function);
4931    for param in &function.params {
4932        validate_value_lane(function, param)?;
4933    }
4934    for continuation in &function.continuations {
4935        let return_lane = effective_continuation_return_lane(function, continuation);
4936        if !has_effect_flow && !continuation.environment.is_empty() {
4937            return Err(CpsReprCraneliftError::UnsupportedFunction {
4938                function: function.name.clone(),
4939                reason: "continuation environment",
4940            });
4941        }
4942        for slot in &continuation.environment {
4943            validate_environment_lane(function, slot.value, slot.lane)?;
4944        }
4945        for param in &continuation.params {
4946            validate_value_lane(function, param)?;
4947        }
4948        for stmt in &continuation.stmts {
4949            match stmt {
4950                CpsStmt::Literal { literal, .. } => match literal {
4951                    CpsLiteral::Int(_)
4952                    | CpsLiteral::Float(_)
4953                    | CpsLiteral::Bool(_)
4954                    | CpsLiteral::Unit
4955                    | CpsLiteral::String(_) => {}
4956                },
4957                CpsStmt::FreshGuard { .. }
4958                | CpsStmt::PeekGuard { .. }
4959                | CpsStmt::FindGuard { .. } => {}
4960                CpsStmt::MakeThunk { .. }
4961                | CpsStmt::AddThunkBoundary { .. }
4962                | CpsStmt::MakeClosure { .. }
4963                | CpsStmt::MakeRecursiveClosure { .. }
4964                | CpsStmt::ForceThunk { .. } => {}
4965                CpsStmt::Primitive { op, .. } => validate_primitive(function, *op)?,
4966                CpsStmt::Tuple { .. }
4967                | CpsStmt::Record { .. }
4968                | CpsStmt::RecordWithoutFields { .. }
4969                | CpsStmt::Variant { .. }
4970                | CpsStmt::Select { .. }
4971                | CpsStmt::SelectWithDefault { .. }
4972                | CpsStmt::RecordHasField { .. }
4973                | CpsStmt::TupleGet { .. }
4974                | CpsStmt::VariantTagEq { .. }
4975                | CpsStmt::VariantPayload { .. } => {}
4976                CpsStmt::DirectCall { .. }
4977                | CpsStmt::ApplyClosure { .. }
4978                | CpsStmt::CloneContinuation { .. } => {}
4979                CpsStmt::InstallHandler { .. } | CpsStmt::UninstallHandler { .. } => {}
4980                CpsStmt::Resume { .. } if has_effect_flow => {}
4981                CpsStmt::ResumeWithHandler { .. } if has_effect_flow => {}
4982                CpsStmt::ResumeWithHandler { .. } => {
4983                    return Err(CpsReprCraneliftError::UnsupportedStmt {
4984                        function: function.name.clone(),
4985                        kind: "resume with dynamic handler",
4986                    });
4987                }
4988                CpsStmt::Resume { .. } => {
4989                    return Err(CpsReprCraneliftError::UnsupportedStmt {
4990                        function: function.name.clone(),
4991                        kind: "resume",
4992                    });
4993                }
4994            }
4995        }
4996        if require_scalar_entry_return
4997            && continuation.id == function.entry
4998            && return_lane != CpsReprAbiLane::ScalarI64
4999            && return_lane != CpsReprAbiLane::RuntimeValuePtr
5000            && return_lane != CpsReprAbiLane::ClosurePtr
5001            && return_lane != CpsReprAbiLane::ThunkPtr
5002            && return_lane != CpsReprAbiLane::OpaqueI64
5003            && return_lane != CpsReprAbiLane::Unknown
5004        {
5005            return Err(CpsReprCraneliftError::UnsupportedReturnLane {
5006                function: function.name.clone(),
5007                continuation: continuation.id,
5008                lane: return_lane,
5009            });
5010        }
5011        if return_lane != CpsReprAbiLane::ScalarI64 {
5012            match return_lane {
5013                CpsReprAbiLane::NativeFloat
5014                    if !has_effect_flow && !continuation_has_internal_call(continuation) => {}
5015                CpsReprAbiLane::RuntimeValuePtr
5016                | CpsReprAbiLane::ClosurePtr
5017                | CpsReprAbiLane::ThunkPtr
5018                | CpsReprAbiLane::ResumptionPtr
5019                | CpsReprAbiLane::OpaqueI64
5020                | CpsReprAbiLane::Unknown => {}
5021                lane => {
5022                    return Err(CpsReprCraneliftError::UnsupportedReturnLane {
5023                        function: function.name.clone(),
5024                        continuation: continuation.id,
5025                        lane,
5026                    });
5027                }
5028            }
5029        }
5030    }
5031    Ok(())
5032}
5033
5034fn continuation_has_internal_call(continuation: &CpsReprAbiContinuation) -> bool {
5035    continuation.stmts.iter().any(|stmt| {
5036        matches!(
5037            stmt,
5038            CpsStmt::DirectCall { .. }
5039                | CpsStmt::ApplyClosure { .. }
5040                | CpsStmt::ForceThunk { .. }
5041                | CpsStmt::Resume { .. }
5042                | CpsStmt::ResumeWithHandler { .. }
5043        )
5044    })
5045}
5046
5047fn validate_value_lane(
5048    function: &CpsReprAbiFunction,
5049    value: &CpsReprAbiValue,
5050) -> CpsReprCraneliftResult<()> {
5051    match value.lane {
5052        CpsReprAbiLane::ScalarI64
5053        | CpsReprAbiLane::NativeFloat
5054        | CpsReprAbiLane::RuntimeValuePtr
5055        | CpsReprAbiLane::ClosurePtr
5056        | CpsReprAbiLane::ThunkPtr
5057        | CpsReprAbiLane::ResumptionPtr
5058        | CpsReprAbiLane::OpaqueI64
5059        | CpsReprAbiLane::Unknown => Ok(()),
5060        lane => Err(CpsReprCraneliftError::UnsupportedLane {
5061            function: function.name.clone(),
5062            value: value.value,
5063            lane,
5064        }),
5065    }
5066}
5067
5068fn validate_environment_lane(
5069    function: &CpsReprAbiFunction,
5070    value: CpsValueId,
5071    lane: CpsReprAbiLane,
5072) -> CpsReprCraneliftResult<()> {
5073    match lane {
5074        CpsReprAbiLane::ScalarI64
5075        | CpsReprAbiLane::RuntimeValuePtr
5076        | CpsReprAbiLane::ClosurePtr
5077        | CpsReprAbiLane::ThunkPtr
5078        | CpsReprAbiLane::ResumptionPtr
5079        | CpsReprAbiLane::OpaqueI64
5080        | CpsReprAbiLane::Unknown => Ok(()),
5081        lane => Err(CpsReprCraneliftError::UnsupportedLane {
5082            function: function.name.clone(),
5083            value,
5084            lane,
5085        }),
5086    }
5087}
5088
5089fn validate_primitive(
5090    _function: &CpsReprAbiFunction,
5091    op: typed_ir::PrimitiveOp,
5092) -> CpsReprCraneliftResult<()> {
5093    match op {
5094        typed_ir::PrimitiveOp::BoolNot
5095        | typed_ir::PrimitiveOp::BoolEq
5096        | typed_ir::PrimitiveOp::IntAdd
5097        | typed_ir::PrimitiveOp::IntSub
5098        | typed_ir::PrimitiveOp::IntMul
5099        | typed_ir::PrimitiveOp::IntDiv
5100        | typed_ir::PrimitiveOp::IntEq
5101        | typed_ir::PrimitiveOp::IntLt
5102        | typed_ir::PrimitiveOp::IntLe
5103        | typed_ir::PrimitiveOp::IntGt
5104        | typed_ir::PrimitiveOp::IntGe
5105        | typed_ir::PrimitiveOp::FloatAdd
5106        | typed_ir::PrimitiveOp::FloatSub
5107        | typed_ir::PrimitiveOp::FloatMul
5108        | typed_ir::PrimitiveOp::FloatDiv
5109        | typed_ir::PrimitiveOp::FloatEq
5110        | typed_ir::PrimitiveOp::FloatLt
5111        | typed_ir::PrimitiveOp::FloatLe
5112        | typed_ir::PrimitiveOp::FloatGt
5113        | typed_ir::PrimitiveOp::FloatGe
5114        | typed_ir::PrimitiveOp::ListEmpty
5115        | typed_ir::PrimitiveOp::ListSingleton
5116        | typed_ir::PrimitiveOp::ListMerge
5117        | typed_ir::PrimitiveOp::ListLen
5118        | typed_ir::PrimitiveOp::ListIndex
5119        | typed_ir::PrimitiveOp::ListIndexRangeRaw
5120        | typed_ir::PrimitiveOp::ListIndexRange
5121        | typed_ir::PrimitiveOp::ListSpliceRaw
5122        | typed_ir::PrimitiveOp::ListSplice
5123        | typed_ir::PrimitiveOp::ListViewRaw
5124        | typed_ir::PrimitiveOp::IntToString
5125        | typed_ir::PrimitiveOp::IntToHex
5126        | typed_ir::PrimitiveOp::IntToUpperHex
5127        | typed_ir::PrimitiveOp::BoolToString
5128        | typed_ir::PrimitiveOp::FloatToString
5129        | typed_ir::PrimitiveOp::StringConcat
5130        | typed_ir::PrimitiveOp::StringEq
5131        | typed_ir::PrimitiveOp::StringLen
5132        | typed_ir::PrimitiveOp::StringIndex
5133        | typed_ir::PrimitiveOp::StringIndexRangeRaw
5134        | typed_ir::PrimitiveOp::StringIndexRange
5135        | typed_ir::PrimitiveOp::StringSpliceRaw
5136        | typed_ir::PrimitiveOp::StringSplice
5137        | typed_ir::PrimitiveOp::StringToBytes
5138        | typed_ir::PrimitiveOp::BytesLen
5139        | typed_ir::PrimitiveOp::BytesEq
5140        | typed_ir::PrimitiveOp::BytesConcat
5141        | typed_ir::PrimitiveOp::BytesIndex
5142        | typed_ir::PrimitiveOp::BytesIndexRange
5143        | typed_ir::PrimitiveOp::BytesToUtf8Raw
5144        | typed_ir::PrimitiveOp::BytesToPath
5145        | typed_ir::PrimitiveOp::PathToBytes => Ok(()),
5146    }
5147}
5148
5149fn read_values(
5150    builder: &mut FunctionBuilder<'_>,
5151    function: &CpsReprAbiFunction,
5152    values: &[CpsValueId],
5153) -> CpsReprCraneliftResult<Vec<ir::Value>> {
5154    values
5155        .iter()
5156        .map(|value| read_value(builder, function, *value))
5157        .collect()
5158}
5159
5160fn read_values_as_lanes(
5161    builder: &mut FunctionBuilder<'_>,
5162    function: &CpsReprAbiFunction,
5163    local_values: &LocalValueCache,
5164    values: &[CpsValueId],
5165    lanes: impl IntoIterator<Item = CpsReprAbiLane>,
5166) -> CpsReprCraneliftResult<Vec<ir::Value>> {
5167    values
5168        .iter()
5169        .zip(lanes)
5170        .map(|(value, lane)| read_value_as_lane(builder, function, local_values, *value, lane))
5171        .collect()
5172}
5173
5174fn read_primitive_args<M: Module>(
5175    module_backend: &mut M,
5176    builder: &mut FunctionBuilder<'_>,
5177    function: &CpsReprAbiFunction,
5178    local_values: &LocalValueCache,
5179    op: typed_ir::PrimitiveOp,
5180    values: &[CpsValueId],
5181) -> CpsReprCraneliftResult<Vec<ir::Value>> {
5182    if op == typed_ir::PrimitiveOp::ListSingleton {
5183        return read_runtime_values_i64(module_backend, builder, function, local_values, values);
5184    }
5185
5186    let lanes = primitive_arg_lanes(op);
5187    values
5188        .iter()
5189        .enumerate()
5190        .map(|(index, value)| {
5191            let lane = lanes
5192                .and_then(|lanes| lanes.get(index).copied())
5193                .unwrap_or(CpsReprAbiLane::ScalarI64);
5194            if lane == CpsReprAbiLane::NativeFloat {
5195                return read_native_float_value(
5196                    module_backend,
5197                    builder,
5198                    function,
5199                    local_values,
5200                    *value,
5201                );
5202            }
5203            read_value_as_lane(builder, function, local_values, *value, lane)
5204        })
5205        .collect()
5206}
5207
5208fn read_call_args(
5209    builder: &mut FunctionBuilder<'_>,
5210    function: &CpsReprAbiFunction,
5211    local_values: &LocalValueCache,
5212    values: &[CpsValueId],
5213    target: &str,
5214    functions: &DeclaredFunctions,
5215) -> CpsReprCraneliftResult<Vec<ir::Value>> {
5216    if let Some(lanes) = functions.function_params.get(target) {
5217        return read_values_as_lanes(
5218            builder,
5219            function,
5220            local_values,
5221            values,
5222            lanes.iter().copied(),
5223        );
5224    }
5225    read_values(builder, function, values)
5226}
5227
5228fn read_value(
5229    builder: &mut FunctionBuilder<'_>,
5230    function: &CpsReprAbiFunction,
5231    value: CpsValueId,
5232) -> CpsReprCraneliftResult<ir::Value> {
5233    if !function_value_ids(function).contains(&value) {
5234        return Err(CpsReprCraneliftError::MissingValue {
5235            function: function.name.clone(),
5236            value,
5237        });
5238    }
5239    Ok(builder.use_var(variable(value)))
5240}
5241
5242fn read_value_as_lane(
5243    builder: &mut FunctionBuilder<'_>,
5244    function: &CpsReprAbiFunction,
5245    local_values: &LocalValueCache,
5246    value: CpsValueId,
5247    lane: CpsReprAbiLane,
5248) -> CpsReprCraneliftResult<ir::Value> {
5249    if !function_value_ids(function).contains(&value) {
5250        return Err(CpsReprCraneliftError::MissingValue {
5251            function: function.name.clone(),
5252            value,
5253        });
5254    }
5255    if lane == CpsReprAbiLane::NativeFloat
5256        && let Some(value) = local_values.native_float.get(&value).copied()
5257    {
5258        return Ok(value);
5259    }
5260    Ok(builder.use_var(variable_for_lane(value, lane)))
5261}
5262
5263fn read_runtime_values_i64<M: Module>(
5264    module_backend: &mut M,
5265    builder: &mut FunctionBuilder<'_>,
5266    function: &CpsReprAbiFunction,
5267    local_values: &LocalValueCache,
5268    values: &[CpsValueId],
5269) -> CpsReprCraneliftResult<Vec<ir::Value>> {
5270    values
5271        .iter()
5272        .map(|value| {
5273            read_runtime_value_i64(module_backend, builder, function, local_values, *value)
5274        })
5275        .collect()
5276}
5277
5278fn read_runtime_value_i64<M: Module>(
5279    module_backend: &mut M,
5280    builder: &mut FunctionBuilder<'_>,
5281    function: &CpsReprAbiFunction,
5282    local_values: &LocalValueCache,
5283    value: CpsValueId,
5284) -> CpsReprCraneliftResult<ir::Value> {
5285    if let Some(literal) = value_literal(function, value) {
5286        match literal {
5287            CpsLiteral::Bool(_) => {
5288                let value = read_value(builder, function, value)?;
5289                return call_i64_helper(
5290                    module_backend,
5291                    builder,
5292                    "yulang_cps_box_bool_i64",
5293                    &[value],
5294                );
5295            }
5296            CpsLiteral::Unit => {
5297                return call_i64_helper(module_backend, builder, "yulang_cps_unit_i64", &[]);
5298            }
5299            _ => {}
5300        }
5301    }
5302    if effective_value_lane(function, value) == CpsReprAbiLane::NativeFloat
5303        || local_values.native_float.contains_key(&value)
5304    {
5305        let value =
5306            read_native_float_value(module_backend, builder, function, local_values, value)?;
5307        return call_helper(
5308            module_backend,
5309            builder,
5310            "yulang_cps_box_float_f64",
5311            &[types::F64],
5312            types::I64,
5313            &[value],
5314        );
5315    }
5316    read_value(builder, function, value)
5317}
5318
5319fn value_literal(function: &CpsReprAbiFunction, value: CpsValueId) -> Option<&CpsLiteral> {
5320    function
5321        .continuations
5322        .iter()
5323        .flat_map(|continuation| continuation.stmts.iter())
5324        .find_map(|stmt| match stmt {
5325            CpsStmt::Literal { dest, literal } if *dest == value => Some(literal),
5326            _ => None,
5327        })
5328}
5329
5330fn read_native_float_value<M: Module>(
5331    module_backend: &mut M,
5332    builder: &mut FunctionBuilder<'_>,
5333    function: &CpsReprAbiFunction,
5334    local_values: &LocalValueCache,
5335    value: CpsValueId,
5336) -> CpsReprCraneliftResult<ir::Value> {
5337    if let Some(value) = local_values.native_float.get(&value).copied() {
5338        return Ok(value);
5339    }
5340    let value = read_value(builder, function, value)?;
5341    call_helper(
5342        module_backend,
5343        builder,
5344        "yulang_cps_unbox_float_i64",
5345        &[types::I64],
5346        types::F64,
5347        &[value],
5348    )
5349}
5350
5351fn define_value_as_lane(
5352    builder: &mut FunctionBuilder<'_>,
5353    local_values: &mut LocalValueCache,
5354    value: CpsValueId,
5355    lane: CpsReprAbiLane,
5356    ir_value: ir::Value,
5357) {
5358    if lane == CpsReprAbiLane::NativeFloat {
5359        local_values.native_float.insert(value, ir_value);
5360    }
5361    builder.def_var(variable_for_lane(value, lane), ir_value);
5362}
5363
5364fn force_thunk_passes_native_float(
5365    function: &CpsReprAbiFunction,
5366    local_values: &LocalValueCache,
5367    value: CpsValueId,
5368) -> bool {
5369    effective_value_lane(function, value) == CpsReprAbiLane::NativeFloat
5370        || local_values.native_float.contains_key(&value)
5371}
5372
5373fn lane_type(lane: CpsReprAbiLane) -> ir::Type {
5374    match lane {
5375        CpsReprAbiLane::NativeFloat => types::F64,
5376        CpsReprAbiLane::ScalarI64
5377        | CpsReprAbiLane::RuntimeValuePtr
5378        | CpsReprAbiLane::ClosurePtr
5379        | CpsReprAbiLane::ThunkPtr
5380        | CpsReprAbiLane::ResumptionPtr
5381        | CpsReprAbiLane::OpaqueI64
5382        | CpsReprAbiLane::Conflict
5383        | CpsReprAbiLane::Unknown => types::I64,
5384    }
5385}
5386
5387fn read_block_args(
5388    builder: &mut FunctionBuilder<'_>,
5389    function: &CpsReprAbiFunction,
5390    local_values: &LocalValueCache,
5391    target: &CpsReprAbiContinuation,
5392    values: &[CpsValueId],
5393) -> CpsReprCraneliftResult<Vec<ir::BlockArg>> {
5394    Ok(read_values_as_lanes(
5395        builder,
5396        function,
5397        local_values,
5398        values,
5399        target
5400            .params
5401            .iter()
5402            .map(|param| effective_value_lane(function, param.value)),
5403    )?
5404    .into_iter()
5405    .map(ir::BlockArg::Value)
5406    .collect())
5407}
5408
5409fn continuation_block(
5410    function: &CpsReprAbiFunction,
5411    blocks: &HashMap<CpsContinuationId, ir::Block>,
5412    id: CpsContinuationId,
5413) -> CpsReprCraneliftResult<ir::Block> {
5414    blocks
5415        .get(&id)
5416        .copied()
5417        .ok_or_else(|| CpsReprCraneliftError::MissingContinuation {
5418            function: function.name.clone(),
5419            continuation: id,
5420        })
5421}
5422
5423fn function_value_ids(function: &CpsReprAbiFunction) -> Vec<CpsValueId> {
5424    let mut values = function
5425        .params
5426        .iter()
5427        .map(|value| value.value)
5428        .collect::<Vec<_>>();
5429    for continuation in &function.continuations {
5430        values.extend(continuation.params.iter().map(|value| value.value));
5431        values.extend(continuation.environment.iter().map(|slot| slot.value));
5432        for stmt in &continuation.stmts {
5433            match stmt {
5434                CpsStmt::Literal { dest, .. }
5435                | CpsStmt::FreshGuard { dest, .. }
5436                | CpsStmt::PeekGuard { dest }
5437                | CpsStmt::FindGuard { dest, .. }
5438                | CpsStmt::MakeThunk { dest, .. }
5439                | CpsStmt::AddThunkBoundary { dest, .. }
5440                | CpsStmt::MakeClosure { dest, .. }
5441                | CpsStmt::MakeRecursiveClosure { dest, .. }
5442                | CpsStmt::ForceThunk { dest, .. }
5443                | CpsStmt::Tuple { dest, .. }
5444                | CpsStmt::Record { dest, .. }
5445                | CpsStmt::RecordWithoutFields { dest, .. }
5446                | CpsStmt::Variant { dest, .. }
5447                | CpsStmt::Select { dest, .. }
5448                | CpsStmt::SelectWithDefault { dest, .. }
5449                | CpsStmt::RecordHasField { dest, .. }
5450                | CpsStmt::TupleGet { dest, .. }
5451                | CpsStmt::VariantTagEq { dest, .. }
5452                | CpsStmt::VariantPayload { dest, .. }
5453                | CpsStmt::Primitive { dest, .. }
5454                | CpsStmt::DirectCall { dest, .. }
5455                | CpsStmt::ApplyClosure { dest, .. }
5456                | CpsStmt::CloneContinuation { dest, .. }
5457                | CpsStmt::Resume { dest, .. }
5458                | CpsStmt::ResumeWithHandler { dest, .. } => values.push(*dest),
5459                CpsStmt::InstallHandler { .. } | CpsStmt::UninstallHandler { .. } => {}
5460            }
5461        }
5462    }
5463    values.sort();
5464    values.dedup();
5465    values
5466}
5467
5468fn value_is_make_thunk(function: &CpsReprAbiFunction, value: CpsValueId) -> bool {
5469    function.continuations.iter().any(|continuation| {
5470        continuation
5471            .stmts
5472            .iter()
5473            .any(|stmt| matches!(stmt, CpsStmt::MakeThunk { dest, .. } if *dest == value))
5474    })
5475}
5476
5477fn value_lane(function: &CpsReprAbiFunction, value: CpsValueId) -> Option<CpsReprAbiLane> {
5478    for param in &function.params {
5479        if param.value == value {
5480            return Some(param.lane);
5481        }
5482    }
5483    for continuation in &function.continuations {
5484        for param in &continuation.params {
5485            if param.value == value {
5486                return Some(param.lane);
5487            }
5488        }
5489        for slot in &continuation.environment {
5490            if slot.value == value {
5491                return Some(slot.lane);
5492            }
5493        }
5494        for stmt in &continuation.stmts {
5495            if stmt_dest(stmt) == Some(value) {
5496                return Some(stmt_result_lane(stmt));
5497            }
5498        }
5499    }
5500    None
5501}
5502
5503fn effective_value_lane(function: &CpsReprAbiFunction, value: CpsValueId) -> CpsReprAbiLane {
5504    match value_lane(function, value) {
5505        Some(CpsReprAbiLane::Unknown) | None => {
5506            inferred_value_lane(function, value).unwrap_or(CpsReprAbiLane::Unknown)
5507        }
5508        Some(CpsReprAbiLane::OpaqueI64) => {
5509            inferred_value_lane(function, value).unwrap_or(CpsReprAbiLane::OpaqueI64)
5510        }
5511        Some(lane) => lane,
5512    }
5513}
5514
5515fn inferred_value_lane(function: &CpsReprAbiFunction, value: CpsValueId) -> Option<CpsReprAbiLane> {
5516    let mut lane = None;
5517    for continuation in &function.continuations {
5518        if matches!(&continuation.terminator, CpsTerminator::Return(returned) if *returned == value)
5519            && continuation.return_lane != CpsReprAbiLane::Unknown
5520        {
5521            merge_inferred_lane(&mut lane, continuation.return_lane);
5522        }
5523        for stmt in &continuation.stmts {
5524            if stmt_dest(stmt) == Some(value) {
5525                merge_inferred_lane(&mut lane, stmt_result_lane(stmt));
5526            }
5527            if let CpsStmt::Primitive { op, args, .. } = stmt
5528                && let Some(arg_lanes) = primitive_arg_lanes(*op)
5529            {
5530                for (arg, arg_lane) in args.iter().zip(arg_lanes.iter().copied()) {
5531                    if *arg == value {
5532                        merge_inferred_lane(&mut lane, arg_lane);
5533                    }
5534                }
5535            }
5536        }
5537    }
5538    lane
5539}
5540
5541fn effective_continuation_return_lane(
5542    function: &CpsReprAbiFunction,
5543    continuation: &CpsReprAbiContinuation,
5544) -> CpsReprAbiLane {
5545    if continuation.return_lane != CpsReprAbiLane::Unknown {
5546        return continuation.return_lane;
5547    }
5548    match &continuation.terminator {
5549        CpsTerminator::Return(value) => effective_value_lane(function, *value),
5550        _ => CpsReprAbiLane::Unknown,
5551    }
5552}
5553
5554fn effective_function_param_lanes(function: &CpsReprAbiFunction) -> Vec<CpsReprAbiLane> {
5555    let entry = continuation(function, function.entry).ok();
5556    function
5557        .params
5558        .iter()
5559        .enumerate()
5560        .map(|(index, param)| {
5561            entry
5562                .and_then(|entry| entry.params.get(index))
5563                .map(|entry_param| effective_value_lane(function, entry_param.value))
5564                .filter(|lane| *lane != CpsReprAbiLane::Unknown)
5565                .unwrap_or_else(|| effective_value_lane(function, param.value))
5566        })
5567        .collect()
5568}
5569
5570fn merge_inferred_lane(slot: &mut Option<CpsReprAbiLane>, lane: CpsReprAbiLane) {
5571    if lane == CpsReprAbiLane::Unknown {
5572        return;
5573    }
5574    *slot = Some(match *slot {
5575        None | Some(CpsReprAbiLane::Unknown) => lane,
5576        Some(existing) if existing == lane => existing,
5577        Some(CpsReprAbiLane::NativeFloat) => CpsReprAbiLane::NativeFloat,
5578        Some(_) if lane == CpsReprAbiLane::NativeFloat => CpsReprAbiLane::NativeFloat,
5579        Some(existing) => existing,
5580    });
5581}
5582
5583fn stmt_result_lane(stmt: &CpsStmt) -> CpsReprAbiLane {
5584    match stmt {
5585        CpsStmt::Literal { literal, .. } => literal_lane(literal),
5586        CpsStmt::FreshGuard { .. }
5587        | CpsStmt::PeekGuard { .. }
5588        | CpsStmt::FindGuard { .. }
5589        | CpsStmt::VariantTagEq { .. }
5590        | CpsStmt::RecordHasField { .. } => CpsReprAbiLane::ScalarI64,
5591        CpsStmt::MakeThunk { .. } | CpsStmt::AddThunkBoundary { .. } => CpsReprAbiLane::ThunkPtr,
5592        CpsStmt::MakeClosure { .. } | CpsStmt::MakeRecursiveClosure { .. } => {
5593            CpsReprAbiLane::ClosurePtr
5594        }
5595        CpsStmt::Tuple { .. }
5596        | CpsStmt::Record { .. }
5597        | CpsStmt::RecordWithoutFields { .. }
5598        | CpsStmt::Variant { .. }
5599        | CpsStmt::Select { .. }
5600        | CpsStmt::SelectWithDefault { .. }
5601        | CpsStmt::TupleGet { .. }
5602        | CpsStmt::VariantPayload { .. }
5603        | CpsStmt::ForceThunk { .. }
5604        | CpsStmt::DirectCall { .. }
5605        | CpsStmt::ApplyClosure { .. }
5606        | CpsStmt::CloneContinuation { .. }
5607        | CpsStmt::Resume { .. }
5608        | CpsStmt::ResumeWithHandler { .. } => CpsReprAbiLane::Unknown,
5609        CpsStmt::Primitive { op, .. } => primitive_result_lane(*op),
5610        CpsStmt::InstallHandler { .. } | CpsStmt::UninstallHandler { .. } => {
5611            CpsReprAbiLane::Unknown
5612        }
5613    }
5614}
5615
5616fn literal_lane(literal: &CpsLiteral) -> CpsReprAbiLane {
5617    match literal {
5618        CpsLiteral::Int(_) | CpsLiteral::Bool(_) | CpsLiteral::Unit => CpsReprAbiLane::ScalarI64,
5619        CpsLiteral::Float(_) => CpsReprAbiLane::NativeFloat,
5620        CpsLiteral::String(_) => CpsReprAbiLane::RuntimeValuePtr,
5621    }
5622}
5623
5624fn primitive_arg_lanes(op: typed_ir::PrimitiveOp) -> Option<&'static [CpsReprAbiLane]> {
5625    const FLOAT_UNARY: &[CpsReprAbiLane] = &[CpsReprAbiLane::NativeFloat];
5626    const FLOAT_BINARY: &[CpsReprAbiLane] =
5627        &[CpsReprAbiLane::NativeFloat, CpsReprAbiLane::NativeFloat];
5628    match op {
5629        typed_ir::PrimitiveOp::FloatToString => Some(FLOAT_UNARY),
5630        typed_ir::PrimitiveOp::FloatAdd
5631        | typed_ir::PrimitiveOp::FloatSub
5632        | typed_ir::PrimitiveOp::FloatMul
5633        | typed_ir::PrimitiveOp::FloatDiv
5634        | typed_ir::PrimitiveOp::FloatEq
5635        | typed_ir::PrimitiveOp::FloatLt
5636        | typed_ir::PrimitiveOp::FloatLe
5637        | typed_ir::PrimitiveOp::FloatGt
5638        | typed_ir::PrimitiveOp::FloatGe => Some(FLOAT_BINARY),
5639        _ => None,
5640    }
5641}
5642
5643fn primitive_result_lane(op: typed_ir::PrimitiveOp) -> CpsReprAbiLane {
5644    match op {
5645        typed_ir::PrimitiveOp::BoolNot
5646        | typed_ir::PrimitiveOp::BoolEq
5647        | typed_ir::PrimitiveOp::IntEq
5648        | typed_ir::PrimitiveOp::IntLt
5649        | typed_ir::PrimitiveOp::IntLe
5650        | typed_ir::PrimitiveOp::IntGt
5651        | typed_ir::PrimitiveOp::IntGe
5652        | typed_ir::PrimitiveOp::IntAdd
5653        | typed_ir::PrimitiveOp::IntSub
5654        | typed_ir::PrimitiveOp::IntMul
5655        | typed_ir::PrimitiveOp::IntDiv
5656        | typed_ir::PrimitiveOp::ListLen
5657        | typed_ir::PrimitiveOp::StringLen
5658        | typed_ir::PrimitiveOp::BytesLen
5659        | typed_ir::PrimitiveOp::BytesIndex
5660        | typed_ir::PrimitiveOp::FloatEq
5661        | typed_ir::PrimitiveOp::FloatLt
5662        | typed_ir::PrimitiveOp::FloatLe
5663        | typed_ir::PrimitiveOp::FloatGt
5664        | typed_ir::PrimitiveOp::FloatGe
5665        | typed_ir::PrimitiveOp::StringEq
5666        | typed_ir::PrimitiveOp::BytesEq => CpsReprAbiLane::ScalarI64,
5667        typed_ir::PrimitiveOp::FloatAdd
5668        | typed_ir::PrimitiveOp::FloatSub
5669        | typed_ir::PrimitiveOp::FloatMul
5670        | typed_ir::PrimitiveOp::FloatDiv => CpsReprAbiLane::NativeFloat,
5671        typed_ir::PrimitiveOp::ListEmpty
5672        | typed_ir::PrimitiveOp::ListSingleton
5673        | typed_ir::PrimitiveOp::ListMerge
5674        | typed_ir::PrimitiveOp::ListIndexRange
5675        | typed_ir::PrimitiveOp::ListSplice
5676        | typed_ir::PrimitiveOp::ListIndexRangeRaw
5677        | typed_ir::PrimitiveOp::ListSpliceRaw
5678        | typed_ir::PrimitiveOp::ListViewRaw
5679        | typed_ir::PrimitiveOp::StringIndex
5680        | typed_ir::PrimitiveOp::StringIndexRange
5681        | typed_ir::PrimitiveOp::StringSplice
5682        | typed_ir::PrimitiveOp::StringIndexRangeRaw
5683        | typed_ir::PrimitiveOp::StringSpliceRaw
5684        | typed_ir::PrimitiveOp::StringConcat
5685        | typed_ir::PrimitiveOp::StringToBytes
5686        | typed_ir::PrimitiveOp::BytesConcat
5687        | typed_ir::PrimitiveOp::BytesIndexRange
5688        | typed_ir::PrimitiveOp::BytesToUtf8Raw
5689        | typed_ir::PrimitiveOp::BytesToPath
5690        | typed_ir::PrimitiveOp::PathToBytes
5691        | typed_ir::PrimitiveOp::IntToString
5692        | typed_ir::PrimitiveOp::IntToHex
5693        | typed_ir::PrimitiveOp::IntToUpperHex
5694        | typed_ir::PrimitiveOp::FloatToString
5695        | typed_ir::PrimitiveOp::BoolToString => CpsReprAbiLane::RuntimeValuePtr,
5696        typed_ir::PrimitiveOp::ListIndex => CpsReprAbiLane::Unknown,
5697    }
5698}
5699
5700fn variable(value: CpsValueId) -> Variable {
5701    variable_for_lane(value, CpsReprAbiLane::ScalarI64)
5702}
5703
5704fn variable_for_lane(value: CpsValueId, lane: CpsReprAbiLane) -> Variable {
5705    let slot = match lane {
5706        CpsReprAbiLane::NativeFloat => 1,
5707        _ => 0,
5708    };
5709    Variable::from_u32((value.0 as u32) * 2 + slot)
5710}
5711
5712fn cranelift_error(error: impl fmt::Display) -> CpsReprCraneliftError {
5713    CpsReprCraneliftError::Cranelift(error.to_string())
5714}
5715
5716#[cfg(test)]
5717mod tests {
5718    use crate::cps_ir::{
5719        CpsContinuation, CpsFunction, CpsLiteral, CpsModule, CpsShotKind, CpsStmt, CpsTerminator,
5720        CpsValueId,
5721    };
5722    use crate::cps_repr::lower_cps_repr_module;
5723    use crate::cps_repr_abi::lower_cps_repr_abi_module;
5724
5725    use super::*;
5726
5727    #[test]
5728    fn jit_runs_pure_continuation_flow() {
5729        let abi = pure_add_abi();
5730        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5731
5732        assert_eq!(jit.run_roots_i64().expect("ran"), vec![42]);
5733    }
5734
5735    #[test]
5736    fn jit_preserves_unit_literal_after_continuation_hop_in_runtime_tuple() {
5737        let abi = lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
5738            functions: Vec::new(),
5739            roots: vec![CpsFunction {
5740                name: "root".to_string(),
5741                params: Vec::new(),
5742                entry: CpsContinuationId(0),
5743                handlers: Vec::new(),
5744                continuations: vec![
5745                    CpsContinuation {
5746                        id: CpsContinuationId(0),
5747                        params: Vec::new(),
5748                        captures: Vec::new(),
5749                        shot_kind: CpsShotKind::MultiShot,
5750                        stmts: vec![CpsStmt::Literal {
5751                            dest: CpsValueId(0),
5752                            literal: CpsLiteral::Unit,
5753                        }],
5754                        terminator: CpsTerminator::Continue {
5755                            target: CpsContinuationId(1),
5756                            args: vec![CpsValueId(0)],
5757                        },
5758                    },
5759                    CpsContinuation {
5760                        id: CpsContinuationId(1),
5761                        params: vec![CpsValueId(1)],
5762                        captures: Vec::new(),
5763                        shot_kind: CpsShotKind::MultiShot,
5764                        stmts: vec![CpsStmt::Tuple {
5765                            dest: CpsValueId(2),
5766                            items: vec![CpsValueId(1)],
5767                        }],
5768                        terminator: CpsTerminator::Return(CpsValueId(2)),
5769                    },
5770                ],
5771            }],
5772        }));
5773        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5774
5775        assert_eq!(jit.run_roots_display().expect("ran"), vec!["((),)"]);
5776    }
5777
5778    #[test]
5779    fn jit_lowers_pure_effectful_continuation_successor_as_block() {
5780        let abi = effectful_function_with_pure_continue_abi();
5781        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5782
5783        assert_eq!(jit.run_roots_i64().expect("ran"), vec![42]);
5784        let entry_ir = jit
5785            .cranelift_ir()
5786            .iter()
5787            .find(|ir| ir.contains(";; cps-repr continuation root::CpsContinuationId(0)"))
5788            .expect("entry continuation ir");
5789        assert!(!entry_ir.contains("root$k1"));
5790    }
5791
5792    #[test]
5793    fn jit_lowers_pure_effectful_branch_successors_as_blocks() {
5794        let abi = effectful_function_with_pure_branch_abi();
5795        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5796
5797        assert_eq!(jit.run_roots_i64().expect("ran"), vec![42]);
5798        let entry_ir = jit
5799            .cranelift_ir()
5800            .iter()
5801            .find(|ir| ir.contains(";; cps-repr continuation root::CpsContinuationId(0)"))
5802            .expect("entry continuation ir");
5803        assert!(!entry_ir.contains("root$k1"));
5804        assert!(!entry_ir.contains("root$k2"));
5805    }
5806
5807    #[test]
5808    fn jit_calls_pure_callee_from_effectful_island_without_eval_context() {
5809        let abi = effectful_function_with_pure_direct_call_abi();
5810        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5811
5812        assert_eq!(jit.run_roots_i64().expect("ran"), vec![42]);
5813        let entry_ir = jit
5814            .cranelift_ir()
5815            .iter()
5816            .find(|ir| ir.contains(";; cps-repr continuation root::CpsContinuationId(0)"))
5817            .expect("entry continuation ir");
5818        assert!(entry_ir.contains("call fn"));
5819        assert!(!entry_ir.contains("yulang_cps_fresh_eval_id_i64"));
5820        assert!(!entry_ir.contains("yulang_cps_set_eval_context_i64"));
5821        assert!(!entry_ir.contains("yulang_cps_scope_return_active_i64"));
5822    }
5823
5824    #[test]
5825    fn jit_rewrites_effectful_call_to_pure_callee_before_codegen() {
5826        let abi = effectful_call_to_pure_callee_abi();
5827        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5828
5829        assert_eq!(jit.run_roots_i64().expect("ran"), vec![42]);
5830        assert_eq!(jit.optimization_profile().rewritten_pure_effectful_calls, 1);
5831        let entry_ir = jit
5832            .cranelift_ir()
5833            .iter()
5834            .find(|ir| ir.contains(";; cps-repr continuation root::CpsContinuationId(0)"))
5835            .expect("entry continuation ir");
5836        assert!(!entry_ir.contains("yulang_cps_return_frame_len_i64"));
5837        assert!(!entry_ir.contains("yulang_cps_fresh_eval_id_i64"));
5838        assert!(!entry_ir.contains("yulang_cps_set_eval_context_i64"));
5839    }
5840
5841    #[test]
5842    fn jit_runs_perform_with_tail_resume_handler() {
5843        let abi = tail_resume_effect_abi();
5844        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5845
5846        assert_eq!(jit.run_roots_i64().expect("ran"), vec![42]);
5847    }
5848
5849    #[test]
5850    fn jit_runs_multishot_resumption_calls() {
5851        let abi = multishot_resume_effect_abi();
5852        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5853
5854        assert_eq!(jit.run_roots_i64().expect("ran"), vec![22]);
5855    }
5856
5857    #[test]
5858    fn jit_runs_guard_stack_helpers_without_effect_flow() {
5859        let abi = guard_stack_abi();
5860        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5861
5862        assert_eq!(jit.run_roots_i64().expect("ran"), vec![1]);
5863    }
5864
5865    #[test]
5866    fn jit_skips_handler_frame_blocked_by_guard_snapshot() {
5867        let abi = blocked_handler_snapshot_abi();
5868        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5869
5870        assert_eq!(jit.run_roots_i64().expect("ran"), vec![100]);
5871    }
5872
5873    #[test]
5874    fn jit_uses_active_thunk_boundary_when_selecting_handler() {
5875        let abi = active_thunk_boundary_abi(typed_ir::Type::Never, ThunkBoundaryStorage::Direct);
5876        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5877
5878        assert_eq!(jit.run_roots_i64().expect("ran"), vec![20]);
5879    }
5880
5881    #[test]
5882    fn jit_uses_active_thunk_boundary_after_list_index() {
5883        let abi = active_thunk_boundary_abi(typed_ir::Type::Never, ThunkBoundaryStorage::ListIndex);
5884        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5885
5886        assert_eq!(jit.run_roots_i64().expect("ran"), vec![20]);
5887    }
5888
5889    #[test]
5890    fn jit_uses_active_thunk_boundary_after_record_select() {
5891        let abi =
5892            active_thunk_boundary_abi(typed_ir::Type::Never, ThunkBoundaryStorage::RecordSelect);
5893        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5894
5895        assert_eq!(jit.run_roots_i64().expect("ran"), vec![20]);
5896    }
5897
5898    #[test]
5899    fn jit_uses_active_thunk_boundary_after_variant_payload() {
5900        let abi =
5901            active_thunk_boundary_abi(typed_ir::Type::Never, ThunkBoundaryStorage::VariantPayload);
5902        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5903
5904        assert_eq!(jit.run_roots_i64().expect("ran"), vec![20]);
5905    }
5906
5907    #[test]
5908    fn jit_keeps_allowed_thunk_boundary_visible_to_inner_handler() {
5909        let choose = typed_ir::Path::from_name(typed_ir::Name("choose".to_string()));
5910        let abi = active_thunk_boundary_abi(
5911            typed_ir::Type::Named {
5912                path: choose,
5913                args: Vec::new(),
5914            },
5915            ThunkBoundaryStorage::Direct,
5916        );
5917        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
5918
5919        assert_eq!(jit.run_roots_i64().expect("ran"), vec![10]);
5920    }
5921
5922    #[test]
5923    fn object_compiles_multishot_resumption_calls() {
5924        let abi = multishot_resume_effect_abi();
5925        let object = compile_cps_repr_abi_module_to_object(&abi).expect("compiled");
5926
5927        assert!(!object.bytes().is_empty());
5928        assert_eq!(object.roots(), &["root".to_string()]);
5929    }
5930
5931    #[test]
5932    fn rejects_perform_until_effect_codegen_exists() {
5933        let effect = typed_ir::Path::from_name(typed_ir::Name("choose".to_string()));
5934        let abi = lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
5935            functions: Vec::new(),
5936            roots: vec![CpsFunction {
5937                name: "root".to_string(),
5938                params: Vec::new(),
5939                entry: CpsContinuationId(0),
5940                handlers: Vec::new(),
5941                continuations: vec![CpsContinuation {
5942                    id: CpsContinuationId(0),
5943                    params: Vec::new(),
5944                    captures: Vec::new(),
5945                    shot_kind: CpsShotKind::OneShot,
5946                    stmts: vec![CpsStmt::Literal {
5947                        dest: CpsValueId(0),
5948                        literal: CpsLiteral::Int("1".to_string()),
5949                    }],
5950                    terminator: CpsTerminator::Perform {
5951                        effect,
5952                        payload: CpsValueId(0),
5953                        resume: CpsContinuationId(0),
5954                        handler: crate::cps_ir::CpsHandlerId(0),
5955                        blocked: None,
5956                    },
5957                }],
5958            }],
5959        }));
5960
5961        let error = match compile_cps_repr_abi_module(&abi) {
5962            Ok(_) => panic!("expected unsupported perform"),
5963            Err(error) => error,
5964        };
5965
5966        assert!(matches!(
5967            error,
5968            CpsReprCraneliftError::UnsupportedTerminator {
5969                kind: "perform without handler entry",
5970                ..
5971            }
5972        ));
5973    }
5974
5975    #[test]
5976    fn jit_runs_unhandled_host_out_write_and_resumes() {
5977        let abi = lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
5978            functions: Vec::new(),
5979            roots: vec![CpsFunction {
5980                name: "root".to_string(),
5981                params: Vec::new(),
5982                entry: CpsContinuationId(0),
5983                handlers: Vec::new(),
5984                continuations: vec![
5985                    CpsContinuation {
5986                        id: CpsContinuationId(0),
5987                        params: Vec::new(),
5988                        captures: Vec::new(),
5989                        shot_kind: CpsShotKind::OneShot,
5990                        stmts: vec![
5991                            CpsStmt::Literal {
5992                                dest: CpsValueId(0),
5993                                literal: CpsLiteral::Int("42".to_string()),
5994                            },
5995                            CpsStmt::Primitive {
5996                                dest: CpsValueId(1),
5997                                op: typed_ir::PrimitiveOp::IntToString,
5998                                args: vec![CpsValueId(0)],
5999                            },
6000                        ],
6001                        terminator: CpsTerminator::Perform {
6002                            effect: out_effect_path("out", "write"),
6003                            payload: CpsValueId(1),
6004                            resume: CpsContinuationId(1),
6005                            handler: crate::cps_ir::CpsHandlerId(0),
6006                            blocked: None,
6007                        },
6008                    },
6009                    CpsContinuation {
6010                        id: CpsContinuationId(1),
6011                        params: vec![CpsValueId(2)],
6012                        captures: Vec::new(),
6013                        shot_kind: CpsShotKind::OneShot,
6014                        stmts: vec![CpsStmt::Literal {
6015                            dest: CpsValueId(3),
6016                            literal: CpsLiteral::Int("7".to_string()),
6017                        }],
6018                        terminator: CpsTerminator::Return(CpsValueId(3)),
6019                    },
6020                ],
6021            }],
6022        }));
6023        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
6024
6025        assert_eq!(jit.run_roots_i64().expect("ran"), vec![7]);
6026    }
6027
6028    #[test]
6029    fn jit_runs_runtime_module_through_cps_pipeline() {
6030        let expr = apply(
6031            apply(
6032                primitive(typed_ir::PrimitiveOp::IntAdd),
6033                unknown_lit(typed_ir::Lit::Int("20".to_string())),
6034            ),
6035            unknown_lit(typed_ir::Lit::Int("22".to_string())),
6036        );
6037        let mut jit = compile_runtime_module_to_cps_repr_jit(&module_with_root(expr))
6038            .expect("compiled runtime module through CPS repr");
6039
6040        assert_eq!(jit.run_roots_i64().expect("ran"), vec![42]);
6041    }
6042
6043    #[test]
6044    fn jit_runs_int_to_string_runtime_value_root() {
6045        let expr = apply(
6046            primitive(typed_ir::PrimitiveOp::IntToString),
6047            unknown_lit(typed_ir::Lit::Int("42".to_string())),
6048        );
6049        let mut jit = compile_runtime_module_to_cps_repr_jit(&module_with_root(expr))
6050            .expect("compiled runtime module through CPS repr");
6051        let roots = jit.run_roots_i64().expect("ran");
6052
6053        assert_eq!(roots.len(), 1);
6054        assert_eq!(describe_native_i64_value(roots[0]), "42");
6055    }
6056
6057    #[test]
6058    fn jit_runs_string_literal_runtime_value_root() {
6059        let mut jit = compile_runtime_module_to_cps_repr_jit(&module_with_root(unknown_lit(
6060            typed_ir::Lit::String("aあ🙂z".to_string()),
6061        )))
6062        .expect("compiled runtime module through CPS repr");
6063        let roots = jit.run_roots_i64().expect("ran");
6064
6065        assert_eq!(roots.len(), 1);
6066        assert_eq!(describe_native_i64_value(roots[0]), "aあ🙂z");
6067    }
6068
6069    #[test]
6070    fn jit_keeps_native_float_through_plain_force_thunk() {
6071        let abi = lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
6072            functions: Vec::new(),
6073            roots: vec![CpsFunction {
6074                name: "root".to_string(),
6075                params: Vec::new(),
6076                entry: CpsContinuationId(0),
6077                handlers: Vec::new(),
6078                continuations: vec![CpsContinuation {
6079                    id: CpsContinuationId(0),
6080                    params: Vec::new(),
6081                    captures: Vec::new(),
6082                    shot_kind: CpsShotKind::OneShot,
6083                    stmts: vec![
6084                        CpsStmt::Literal {
6085                            dest: CpsValueId(0),
6086                            literal: CpsLiteral::Float("1.5".to_string()),
6087                        },
6088                        CpsStmt::ForceThunk {
6089                            dest: CpsValueId(1),
6090                            thunk: CpsValueId(0),
6091                        },
6092                        CpsStmt::Literal {
6093                            dest: CpsValueId(2),
6094                            literal: CpsLiteral::Float("2.0".to_string()),
6095                        },
6096                        CpsStmt::ForceThunk {
6097                            dest: CpsValueId(3),
6098                            thunk: CpsValueId(2),
6099                        },
6100                        CpsStmt::Primitive {
6101                            dest: CpsValueId(4),
6102                            op: typed_ir::PrimitiveOp::FloatAdd,
6103                            args: vec![CpsValueId(1), CpsValueId(3)],
6104                        },
6105                        CpsStmt::ForceThunk {
6106                            dest: CpsValueId(5),
6107                            thunk: CpsValueId(4),
6108                        },
6109                        CpsStmt::Primitive {
6110                            dest: CpsValueId(6),
6111                            op: typed_ir::PrimitiveOp::FloatToString,
6112                            args: vec![CpsValueId(5)],
6113                        },
6114                    ],
6115                    terminator: CpsTerminator::Return(CpsValueId(6)),
6116                }],
6117            }],
6118        }));
6119        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
6120        let roots = jit.run_roots_i64().expect("ran");
6121
6122        assert_eq!(roots.len(), 1);
6123        assert_eq!(describe_native_i64_value(roots[0]), "3.5");
6124    }
6125
6126    #[test]
6127    fn jit_forces_thunk_with_many_environment_slots() {
6128        let abi = lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
6129            functions: Vec::new(),
6130            roots: vec![CpsFunction {
6131                name: "root".to_string(),
6132                params: Vec::new(),
6133                entry: CpsContinuationId(0),
6134                handlers: Vec::new(),
6135                continuations: vec![
6136                    CpsContinuation {
6137                        id: CpsContinuationId(0),
6138                        params: Vec::new(),
6139                        captures: Vec::new(),
6140                        shot_kind: CpsShotKind::OneShot,
6141                        stmts: vec![
6142                            CpsStmt::Literal {
6143                                dest: CpsValueId(0),
6144                                literal: CpsLiteral::Int("1".to_string()),
6145                            },
6146                            CpsStmt::Literal {
6147                                dest: CpsValueId(1),
6148                                literal: CpsLiteral::Int("2".to_string()),
6149                            },
6150                            CpsStmt::Literal {
6151                                dest: CpsValueId(2),
6152                                literal: CpsLiteral::Int("3".to_string()),
6153                            },
6154                            CpsStmt::Literal {
6155                                dest: CpsValueId(3),
6156                                literal: CpsLiteral::Int("4".to_string()),
6157                            },
6158                            CpsStmt::Literal {
6159                                dest: CpsValueId(4),
6160                                literal: CpsLiteral::Int("5".to_string()),
6161                            },
6162                            CpsStmt::MakeThunk {
6163                                dest: CpsValueId(5),
6164                                entry: CpsContinuationId(1),
6165                            },
6166                            CpsStmt::ForceThunk {
6167                                dest: CpsValueId(6),
6168                                thunk: CpsValueId(5),
6169                            },
6170                        ],
6171                        terminator: CpsTerminator::Return(CpsValueId(6)),
6172                    },
6173                    CpsContinuation {
6174                        id: CpsContinuationId(1),
6175                        params: Vec::new(),
6176                        captures: vec![
6177                            CpsValueId(0),
6178                            CpsValueId(1),
6179                            CpsValueId(2),
6180                            CpsValueId(3),
6181                            CpsValueId(4),
6182                        ],
6183                        shot_kind: CpsShotKind::OneShot,
6184                        stmts: vec![
6185                            CpsStmt::Primitive {
6186                                dest: CpsValueId(7),
6187                                op: typed_ir::PrimitiveOp::IntAdd,
6188                                args: vec![CpsValueId(0), CpsValueId(1)],
6189                            },
6190                            CpsStmt::Primitive {
6191                                dest: CpsValueId(8),
6192                                op: typed_ir::PrimitiveOp::IntAdd,
6193                                args: vec![CpsValueId(7), CpsValueId(2)],
6194                            },
6195                            CpsStmt::Primitive {
6196                                dest: CpsValueId(9),
6197                                op: typed_ir::PrimitiveOp::IntAdd,
6198                                args: vec![CpsValueId(8), CpsValueId(3)],
6199                            },
6200                            CpsStmt::Primitive {
6201                                dest: CpsValueId(10),
6202                                op: typed_ir::PrimitiveOp::IntAdd,
6203                                args: vec![CpsValueId(9), CpsValueId(4)],
6204                            },
6205                        ],
6206                        terminator: CpsTerminator::Return(CpsValueId(10)),
6207                    },
6208                ],
6209            }],
6210        }));
6211        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
6212
6213        assert_eq!(jit.run_roots_i64().expect("ran"), vec![15]);
6214    }
6215
6216    #[test]
6217    fn jit_runs_record_construct_and_select() {
6218        let abi = lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
6219            functions: Vec::new(),
6220            roots: vec![CpsFunction {
6221                name: "root".to_string(),
6222                params: Vec::new(),
6223                entry: CpsContinuationId(0),
6224                handlers: Vec::new(),
6225                continuations: vec![CpsContinuation {
6226                    id: CpsContinuationId(0),
6227                    params: Vec::new(),
6228                    captures: Vec::new(),
6229                    shot_kind: CpsShotKind::OneShot,
6230                    stmts: vec![
6231                        CpsStmt::Literal {
6232                            dest: CpsValueId(0),
6233                            literal: CpsLiteral::Int("10".to_string()),
6234                        },
6235                        CpsStmt::Literal {
6236                            dest: CpsValueId(1),
6237                            literal: CpsLiteral::Int("42".to_string()),
6238                        },
6239                        CpsStmt::Record {
6240                            dest: CpsValueId(2),
6241                            base: None,
6242                            fields: vec![
6243                                CpsRecordField {
6244                                    name: typed_ir::Name("a".to_string()),
6245                                    value: CpsValueId(0),
6246                                },
6247                                CpsRecordField {
6248                                    name: typed_ir::Name("answer".to_string()),
6249                                    value: CpsValueId(1),
6250                                },
6251                            ],
6252                        },
6253                        CpsStmt::Select {
6254                            dest: CpsValueId(3),
6255                            base: CpsValueId(2),
6256                            field: typed_ir::Name("answer".to_string()),
6257                        },
6258                    ],
6259                    terminator: CpsTerminator::Return(CpsValueId(3)),
6260                }],
6261            }],
6262        }));
6263        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
6264
6265        assert_eq!(jit.run_roots_i64().expect("ran"), vec![42]);
6266    }
6267
6268    #[test]
6269    fn jit_forces_thunk_selected_from_record() {
6270        let abi = lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
6271            functions: Vec::new(),
6272            roots: vec![CpsFunction {
6273                name: "root".to_string(),
6274                params: Vec::new(),
6275                entry: CpsContinuationId(0),
6276                handlers: Vec::new(),
6277                continuations: vec![
6278                    CpsContinuation {
6279                        id: CpsContinuationId(0),
6280                        params: Vec::new(),
6281                        captures: Vec::new(),
6282                        shot_kind: CpsShotKind::OneShot,
6283                        stmts: vec![
6284                            CpsStmt::MakeThunk {
6285                                dest: CpsValueId(0),
6286                                entry: CpsContinuationId(1),
6287                            },
6288                            CpsStmt::Record {
6289                                dest: CpsValueId(1),
6290                                base: None,
6291                                fields: vec![crate::cps_ir::CpsRecordField {
6292                                    name: typed_ir::Name("run".to_string()),
6293                                    value: CpsValueId(0),
6294                                }],
6295                            },
6296                            CpsStmt::Select {
6297                                dest: CpsValueId(2),
6298                                base: CpsValueId(1),
6299                                field: typed_ir::Name("run".to_string()),
6300                            },
6301                            CpsStmt::ForceThunk {
6302                                dest: CpsValueId(3),
6303                                thunk: CpsValueId(2),
6304                            },
6305                        ],
6306                        terminator: CpsTerminator::Return(CpsValueId(3)),
6307                    },
6308                    CpsContinuation {
6309                        id: CpsContinuationId(1),
6310                        params: Vec::new(),
6311                        captures: Vec::new(),
6312                        shot_kind: CpsShotKind::OneShot,
6313                        stmts: vec![CpsStmt::Literal {
6314                            dest: CpsValueId(4),
6315                            literal: CpsLiteral::Int("42".to_string()),
6316                        }],
6317                        terminator: CpsTerminator::Return(CpsValueId(4)),
6318                    },
6319                ],
6320            }],
6321        }));
6322        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
6323
6324        assert_eq!(jit.run_roots_i64().expect("ran"), vec![42]);
6325    }
6326
6327    #[test]
6328    fn jit_forces_thunk_indexed_from_list_more_than_once() {
6329        let abi = lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
6330            functions: Vec::new(),
6331            roots: vec![CpsFunction {
6332                name: "root".to_string(),
6333                params: Vec::new(),
6334                entry: CpsContinuationId(0),
6335                handlers: Vec::new(),
6336                continuations: vec![
6337                    CpsContinuation {
6338                        id: CpsContinuationId(0),
6339                        params: Vec::new(),
6340                        captures: Vec::new(),
6341                        shot_kind: CpsShotKind::OneShot,
6342                        stmts: vec![
6343                            CpsStmt::MakeThunk {
6344                                dest: CpsValueId(0),
6345                                entry: CpsContinuationId(1),
6346                            },
6347                            CpsStmt::Primitive {
6348                                dest: CpsValueId(1),
6349                                op: typed_ir::PrimitiveOp::ListSingleton,
6350                                args: vec![CpsValueId(0)],
6351                            },
6352                            CpsStmt::Literal {
6353                                dest: CpsValueId(2),
6354                                literal: CpsLiteral::Int("0".to_string()),
6355                            },
6356                            CpsStmt::Primitive {
6357                                dest: CpsValueId(3),
6358                                op: typed_ir::PrimitiveOp::ListIndex,
6359                                args: vec![CpsValueId(1), CpsValueId(2)],
6360                            },
6361                            CpsStmt::ForceThunk {
6362                                dest: CpsValueId(4),
6363                                thunk: CpsValueId(3),
6364                            },
6365                            CpsStmt::ForceThunk {
6366                                dest: CpsValueId(5),
6367                                thunk: CpsValueId(3),
6368                            },
6369                            CpsStmt::Primitive {
6370                                dest: CpsValueId(6),
6371                                op: typed_ir::PrimitiveOp::IntAdd,
6372                                args: vec![CpsValueId(4), CpsValueId(5)],
6373                            },
6374                        ],
6375                        terminator: CpsTerminator::Return(CpsValueId(6)),
6376                    },
6377                    CpsContinuation {
6378                        id: CpsContinuationId(1),
6379                        params: Vec::new(),
6380                        captures: Vec::new(),
6381                        shot_kind: CpsShotKind::OneShot,
6382                        stmts: vec![CpsStmt::Literal {
6383                            dest: CpsValueId(7),
6384                            literal: CpsLiteral::Int("21".to_string()),
6385                        }],
6386                        terminator: CpsTerminator::Return(CpsValueId(7)),
6387                    },
6388                ],
6389            }],
6390        }));
6391        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
6392
6393        assert_eq!(jit.run_roots_i64().expect("ran"), vec![42]);
6394    }
6395
6396    #[test]
6397    fn jit_forces_string_thunk_indexed_from_list() {
6398        let abi = lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
6399            functions: Vec::new(),
6400            roots: vec![CpsFunction {
6401                name: "root".to_string(),
6402                params: Vec::new(),
6403                entry: CpsContinuationId(0),
6404                handlers: Vec::new(),
6405                continuations: vec![
6406                    CpsContinuation {
6407                        id: CpsContinuationId(0),
6408                        params: Vec::new(),
6409                        captures: Vec::new(),
6410                        shot_kind: CpsShotKind::OneShot,
6411                        stmts: vec![
6412                            CpsStmt::MakeThunk {
6413                                dest: CpsValueId(0),
6414                                entry: CpsContinuationId(1),
6415                            },
6416                            CpsStmt::Primitive {
6417                                dest: CpsValueId(1),
6418                                op: typed_ir::PrimitiveOp::ListSingleton,
6419                                args: vec![CpsValueId(0)],
6420                            },
6421                            CpsStmt::Literal {
6422                                dest: CpsValueId(2),
6423                                literal: CpsLiteral::Int("0".to_string()),
6424                            },
6425                            CpsStmt::Primitive {
6426                                dest: CpsValueId(3),
6427                                op: typed_ir::PrimitiveOp::ListIndex,
6428                                args: vec![CpsValueId(1), CpsValueId(2)],
6429                            },
6430                            CpsStmt::ForceThunk {
6431                                dest: CpsValueId(4),
6432                                thunk: CpsValueId(3),
6433                            },
6434                        ],
6435                        terminator: CpsTerminator::Return(CpsValueId(4)),
6436                    },
6437                    CpsContinuation {
6438                        id: CpsContinuationId(1),
6439                        params: Vec::new(),
6440                        captures: Vec::new(),
6441                        shot_kind: CpsShotKind::OneShot,
6442                        stmts: vec![CpsStmt::Literal {
6443                            dest: CpsValueId(5),
6444                            literal: CpsLiteral::String("thunked".to_string()),
6445                        }],
6446                        terminator: CpsTerminator::Return(CpsValueId(5)),
6447                    },
6448                ],
6449            }],
6450        }));
6451        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
6452        let roots = jit.run_roots_i64().expect("ran");
6453
6454        assert_eq!(roots.len(), 1);
6455        assert_eq!(describe_native_i64_value(roots[0]), "thunked");
6456    }
6457
6458    #[test]
6459    fn jit_calls_closure_indexed_from_list() {
6460        let abi = lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
6461            functions: Vec::new(),
6462            roots: vec![CpsFunction {
6463                name: "root".to_string(),
6464                params: Vec::new(),
6465                entry: CpsContinuationId(0),
6466                handlers: Vec::new(),
6467                continuations: vec![
6468                    CpsContinuation {
6469                        id: CpsContinuationId(0),
6470                        params: Vec::new(),
6471                        captures: Vec::new(),
6472                        shot_kind: CpsShotKind::OneShot,
6473                        stmts: vec![
6474                            CpsStmt::MakeClosure {
6475                                dest: CpsValueId(0),
6476                                entry: CpsContinuationId(1),
6477                            },
6478                            CpsStmt::Primitive {
6479                                dest: CpsValueId(1),
6480                                op: typed_ir::PrimitiveOp::ListSingleton,
6481                                args: vec![CpsValueId(0)],
6482                            },
6483                            CpsStmt::Literal {
6484                                dest: CpsValueId(2),
6485                                literal: CpsLiteral::Int("0".to_string()),
6486                            },
6487                            CpsStmt::Primitive {
6488                                dest: CpsValueId(3),
6489                                op: typed_ir::PrimitiveOp::ListIndex,
6490                                args: vec![CpsValueId(1), CpsValueId(2)],
6491                            },
6492                            CpsStmt::Literal {
6493                                dest: CpsValueId(4),
6494                                literal: CpsLiteral::Int("41".to_string()),
6495                            },
6496                            CpsStmt::ApplyClosure {
6497                                dest: CpsValueId(5),
6498                                closure: CpsValueId(3),
6499                                arg: CpsValueId(4),
6500                            },
6501                        ],
6502                        terminator: CpsTerminator::Return(CpsValueId(5)),
6503                    },
6504                    CpsContinuation {
6505                        id: CpsContinuationId(1),
6506                        params: vec![CpsValueId(6)],
6507                        captures: Vec::new(),
6508                        shot_kind: CpsShotKind::OneShot,
6509                        stmts: vec![
6510                            CpsStmt::Literal {
6511                                dest: CpsValueId(7),
6512                                literal: CpsLiteral::Int("1".to_string()),
6513                            },
6514                            CpsStmt::Primitive {
6515                                dest: CpsValueId(8),
6516                                op: typed_ir::PrimitiveOp::IntAdd,
6517                                args: vec![CpsValueId(6), CpsValueId(7)],
6518                            },
6519                        ],
6520                        terminator: CpsTerminator::Return(CpsValueId(8)),
6521                    },
6522                ],
6523            }],
6524        }));
6525        let mut jit = compile_cps_repr_abi_module(&abi).expect("compiled");
6526
6527        assert_eq!(jit.run_roots_i64().expect("ran"), vec![42]);
6528    }
6529
6530    #[test]
6531    fn jit_displays_nested_heap_values_as_yulang_values() {
6532        let expr = tuple(vec![
6533            unknown_lit(typed_ir::Lit::Int("1".to_string())),
6534            list_expr(vec![2, 3]),
6535            record(vec![(
6536                "answer",
6537                unknown_lit(typed_ir::Lit::Int("42".to_string())),
6538            )]),
6539        ]);
6540        let mut jit = compile_runtime_module_to_cps_repr_jit(&module_with_root(expr))
6541            .expect("compiled runtime module");
6542        let roots = jit.run_roots_i64().expect("ran");
6543
6544        assert_eq!(roots.len(), 1);
6545        assert_eq!(
6546            describe_native_i64_value(roots[0]),
6547            "(1, [2, 3], { answer: 42 })"
6548        );
6549    }
6550
6551    #[test]
6552    fn jit_forces_runtime_thunk_indexed_from_list() {
6553        let thunk_list = primitive_call(
6554            typed_ir::PrimitiveOp::ListSingleton,
6555            vec![thunk(unknown_lit(typed_ir::Lit::String(
6556                "runtime-thunk".to_string(),
6557            )))],
6558        );
6559        let indexed = primitive_call(
6560            typed_ir::PrimitiveOp::ListIndex,
6561            vec![thunk_list, unknown_lit(typed_ir::Lit::Int("0".to_string()))],
6562        );
6563        let mut jit = compile_runtime_module_to_cps_repr_jit(&module_with_root(bind_here(indexed)))
6564            .expect("compiled runtime module");
6565        let roots = jit.run_roots_i64().expect("ran");
6566
6567        assert_eq!(roots.len(), 1);
6568        assert_eq!(describe_native_i64_value(roots[0]), "runtime-thunk");
6569    }
6570
6571    #[test]
6572    fn jit_runs_string_primitives_runtime_value_roots() {
6573        let cases = vec![
6574            (
6575                apply(
6576                    apply(
6577                        primitive(typed_ir::PrimitiveOp::StringConcat),
6578                        unknown_lit(typed_ir::Lit::String("yu".to_string())),
6579                    ),
6580                    unknown_lit(typed_ir::Lit::String("lang".to_string())),
6581                ),
6582                "yulang",
6583            ),
6584            (
6585                apply(
6586                    primitive(typed_ir::PrimitiveOp::StringLen),
6587                    unknown_lit(typed_ir::Lit::String("aあ🙂".to_string())),
6588                ),
6589                "3",
6590            ),
6591            (
6592                apply(
6593                    apply(
6594                        primitive(typed_ir::PrimitiveOp::StringIndex),
6595                        unknown_lit(typed_ir::Lit::String("aあ🙂".to_string())),
6596                    ),
6597                    unknown_lit(typed_ir::Lit::Int("1".to_string())),
6598                ),
6599                "あ",
6600            ),
6601            (
6602                apply(
6603                    apply(
6604                        primitive(typed_ir::PrimitiveOp::StringIndexRange),
6605                        unknown_lit(typed_ir::Lit::String("aあ🙂z".to_string())),
6606                    ),
6607                    range_expr(1, 3),
6608                ),
6609                "あ🙂",
6610            ),
6611            (
6612                apply(
6613                    apply(
6614                        apply(
6615                            primitive(typed_ir::PrimitiveOp::StringSplice),
6616                            unknown_lit(typed_ir::Lit::String("abcz".to_string())),
6617                        ),
6618                        range_expr(1, 3),
6619                    ),
6620                    unknown_lit(typed_ir::Lit::String("XY".to_string())),
6621                ),
6622                "aXYz",
6623            ),
6624            (
6625                apply(
6626                    apply(
6627                        apply(
6628                            primitive(typed_ir::PrimitiveOp::StringIndexRangeRaw),
6629                            unknown_lit(typed_ir::Lit::String("aあ🙂z".to_string())),
6630                        ),
6631                        unknown_lit(typed_ir::Lit::Int("1".to_string())),
6632                    ),
6633                    unknown_lit(typed_ir::Lit::Int("3".to_string())),
6634                ),
6635                "あ🙂",
6636            ),
6637            (
6638                apply(
6639                    apply(
6640                        apply(
6641                            apply(
6642                                primitive(typed_ir::PrimitiveOp::StringSpliceRaw),
6643                                unknown_lit(typed_ir::Lit::String("abcz".to_string())),
6644                            ),
6645                            unknown_lit(typed_ir::Lit::Int("1".to_string())),
6646                        ),
6647                        unknown_lit(typed_ir::Lit::Int("3".to_string())),
6648                    ),
6649                    unknown_lit(typed_ir::Lit::String("XY".to_string())),
6650                ),
6651                "aXYz",
6652            ),
6653            (
6654                apply(
6655                    apply(
6656                        primitive(typed_ir::PrimitiveOp::StringEq),
6657                        unknown_lit(typed_ir::Lit::String("ok".to_string())),
6658                    ),
6659                    unknown_lit(typed_ir::Lit::String("ok".to_string())),
6660                ),
6661                "1",
6662            ),
6663            (
6664                apply(
6665                    primitive(typed_ir::PrimitiveOp::IntToHex),
6666                    unknown_lit(typed_ir::Lit::Int("255".to_string())),
6667                ),
6668                "ff",
6669            ),
6670            (
6671                apply(
6672                    primitive(typed_ir::PrimitiveOp::IntToUpperHex),
6673                    unknown_lit(typed_ir::Lit::Int("255".to_string())),
6674                ),
6675                "FF",
6676            ),
6677            (
6678                apply(
6679                    primitive(typed_ir::PrimitiveOp::BoolToString),
6680                    unknown_lit(typed_ir::Lit::Bool(true)),
6681                ),
6682                "true",
6683            ),
6684            (
6685                apply(
6686                    primitive(typed_ir::PrimitiveOp::FloatToString),
6687                    primitive_call(
6688                        typed_ir::PrimitiveOp::FloatAdd,
6689                        vec![
6690                            unknown_lit(typed_ir::Lit::Float("1.5".to_string())),
6691                            unknown_lit(typed_ir::Lit::Float("2.0".to_string())),
6692                        ],
6693                    ),
6694                ),
6695                "3.5",
6696            ),
6697            (
6698                apply(
6699                    primitive(typed_ir::PrimitiveOp::FloatToString),
6700                    primitive_call(
6701                        typed_ir::PrimitiveOp::FloatSub,
6702                        vec![
6703                            unknown_lit(typed_ir::Lit::Float("5.0".to_string())),
6704                            unknown_lit(typed_ir::Lit::Float("2.0".to_string())),
6705                        ],
6706                    ),
6707                ),
6708                "3.0",
6709            ),
6710            (
6711                primitive_call(
6712                    typed_ir::PrimitiveOp::FloatEq,
6713                    vec![
6714                        unknown_lit(typed_ir::Lit::Float("1.5".to_string())),
6715                        unknown_lit(typed_ir::Lit::Float("1.5".to_string())),
6716                    ],
6717                ),
6718                "1",
6719            ),
6720        ];
6721
6722        for (expr, expected) in cases {
6723            let mut jit = compile_runtime_module_to_cps_repr_jit(&module_with_root(expr))
6724                .expect("compiled runtime module");
6725            let roots = jit.run_roots_i64().expect("ran");
6726            assert_eq!(roots.len(), 1);
6727            assert_eq!(describe_native_i64_value(roots[0]), expected);
6728        }
6729    }
6730
6731    #[test]
6732    fn jit_runs_bytes_primitives_runtime_value_roots() {
6733        let hello_bytes = primitive_call(
6734            typed_ir::PrimitiveOp::StringToBytes,
6735            vec![unknown_lit(typed_ir::Lit::String("hello".to_string()))],
6736        );
6737        let cases = vec![
6738            (
6739                primitive_call(typed_ir::PrimitiveOp::BytesLen, vec![hello_bytes.clone()]),
6740                "5",
6741            ),
6742            (
6743                primitive_call(
6744                    typed_ir::PrimitiveOp::BytesIndex,
6745                    vec![
6746                        hello_bytes.clone(),
6747                        unknown_lit(typed_ir::Lit::Int("1".to_string())),
6748                    ],
6749                ),
6750                "101",
6751            ),
6752            (
6753                primitive_call(
6754                    typed_ir::PrimitiveOp::BytesLen,
6755                    vec![primitive_call(
6756                        typed_ir::PrimitiveOp::BytesConcat,
6757                        vec![hello_bytes.clone(), hello_bytes.clone()],
6758                    )],
6759                ),
6760                "10",
6761            ),
6762            (
6763                primitive_call(
6764                    typed_ir::PrimitiveOp::BytesEq,
6765                    vec![
6766                        hello_bytes.clone(),
6767                        primitive_call(
6768                            typed_ir::PrimitiveOp::StringToBytes,
6769                            vec![unknown_lit(typed_ir::Lit::String("hello".to_string()))],
6770                        ),
6771                    ],
6772                ),
6773                "1",
6774            ),
6775            (
6776                primitive_call(
6777                    typed_ir::PrimitiveOp::BytesLen,
6778                    vec![primitive_call(
6779                        typed_ir::PrimitiveOp::BytesIndexRange,
6780                        vec![hello_bytes, range_expr(1, 4)],
6781                    )],
6782                ),
6783                "3",
6784            ),
6785            (
6786                primitive_call(
6787                    typed_ir::PrimitiveOp::BytesLen,
6788                    vec![primitive_call(
6789                        typed_ir::PrimitiveOp::PathToBytes,
6790                        vec![primitive_call(
6791                            typed_ir::PrimitiveOp::BytesToPath,
6792                            vec![primitive_call(
6793                                typed_ir::PrimitiveOp::StringToBytes,
6794                                vec![unknown_lit(typed_ir::Lit::String("/tmp".to_string()))],
6795                            )],
6796                        )],
6797                    )],
6798                ),
6799                "4",
6800            ),
6801            (
6802                primitive_call(
6803                    typed_ir::PrimitiveOp::BytesToUtf8Raw,
6804                    vec![primitive_call(
6805                        typed_ir::PrimitiveOp::StringToBytes,
6806                        vec![unknown_lit(typed_ir::Lit::String("hello".to_string()))],
6807                    )],
6808                ),
6809                "(hello, 5)",
6810            ),
6811        ];
6812
6813        for (expr, expected) in cases {
6814            let mut jit = compile_runtime_module_to_cps_repr_jit(&module_with_root(expr))
6815                .expect("compiled runtime module");
6816            let roots = jit.run_roots_i64().expect("ran");
6817            assert_eq!(roots.len(), 1);
6818            assert_eq!(describe_native_i64_value(roots[0]), expected);
6819        }
6820    }
6821
6822    #[test]
6823    fn jit_runs_list_range_primitives_runtime_value_roots() {
6824        let sliced = apply(
6825            apply(
6826                primitive(typed_ir::PrimitiveOp::ListIndexRange),
6827                list_expr(vec![1, 2, 3, 4]),
6828            ),
6829            range_expr(1, 3),
6830        );
6831        let spliced = apply(
6832            apply(
6833                apply(
6834                    primitive(typed_ir::PrimitiveOp::ListSplice),
6835                    list_expr(vec![1, 2, 3, 4]),
6836                ),
6837                range_expr(1, 3),
6838            ),
6839            list_expr(vec![8, 9]),
6840        );
6841        let cases = vec![
6842            (
6843                apply(primitive(typed_ir::PrimitiveOp::ListLen), sliced.clone()),
6844                "2",
6845            ),
6846            (
6847                apply(
6848                    apply(primitive(typed_ir::PrimitiveOp::ListIndex), sliced),
6849                    unknown_lit(typed_ir::Lit::Int("0".to_string())),
6850                ),
6851                "2",
6852            ),
6853            (
6854                apply(primitive(typed_ir::PrimitiveOp::ListLen), spliced.clone()),
6855                "4",
6856            ),
6857            (
6858                apply(
6859                    apply(primitive(typed_ir::PrimitiveOp::ListIndex), spliced),
6860                    unknown_lit(typed_ir::Lit::Int("1".to_string())),
6861                ),
6862                "8",
6863            ),
6864        ];
6865
6866        for (expr, expected) in cases {
6867            let mut jit = compile_runtime_module_to_cps_repr_jit(&module_with_root(expr))
6868                .expect("compiled runtime module");
6869            let roots = jit.run_roots_i64().expect("ran");
6870            assert_eq!(roots.len(), 1);
6871            assert_eq!(describe_native_i64_value(roots[0]), expected);
6872        }
6873    }
6874
6875    #[test]
6876    fn jit_preserves_float_inside_runtime_value_containers() {
6877        let list = primitive_call(
6878            typed_ir::PrimitiveOp::ListSingleton,
6879            vec![unknown_lit(typed_ir::Lit::Float("2.0".to_string()))],
6880        );
6881        let indexed = primitive_call(
6882            typed_ir::PrimitiveOp::ListIndex,
6883            vec![list, unknown_lit(typed_ir::Lit::Int("0".to_string()))],
6884        );
6885        let rendered = primitive_call(typed_ir::PrimitiveOp::FloatToString, vec![indexed]);
6886
6887        let mut jit = compile_runtime_module_to_cps_repr_jit(&module_with_root(rendered))
6888            .expect("compiled runtime module");
6889        let roots = jit.run_roots_i64().expect("ran");
6890
6891        assert_eq!(roots.len(), 1);
6892        assert_eq!(describe_native_i64_value(roots[0]), "2.0");
6893    }
6894
6895    fn out_effect_path(act: &str, operation: &str) -> typed_ir::Path {
6896        typed_ir::Path {
6897            segments: vec![
6898                typed_ir::Name("std".to_string()),
6899                typed_ir::Name("out".to_string()),
6900                typed_ir::Name(act.to_string()),
6901                typed_ir::Name(operation.to_string()),
6902            ],
6903        }
6904    }
6905
6906    fn pure_add_abi() -> CpsReprAbiModule {
6907        lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
6908            functions: Vec::new(),
6909            roots: vec![CpsFunction {
6910                name: "root".to_string(),
6911                params: Vec::new(),
6912                entry: CpsContinuationId(0),
6913                handlers: Vec::new(),
6914                continuations: vec![
6915                    CpsContinuation {
6916                        id: CpsContinuationId(0),
6917                        params: Vec::new(),
6918                        captures: Vec::new(),
6919                        shot_kind: CpsShotKind::OneShot,
6920                        stmts: vec![
6921                            CpsStmt::Literal {
6922                                dest: CpsValueId(0),
6923                                literal: CpsLiteral::Int("40".to_string()),
6924                            },
6925                            CpsStmt::Literal {
6926                                dest: CpsValueId(1),
6927                                literal: CpsLiteral::Int("2".to_string()),
6928                            },
6929                            CpsStmt::Primitive {
6930                                dest: CpsValueId(2),
6931                                op: typed_ir::PrimitiveOp::IntAdd,
6932                                args: vec![CpsValueId(0), CpsValueId(1)],
6933                            },
6934                        ],
6935                        terminator: CpsTerminator::Continue {
6936                            target: CpsContinuationId(1),
6937                            args: vec![CpsValueId(2)],
6938                        },
6939                    },
6940                    CpsContinuation {
6941                        id: CpsContinuationId(1),
6942                        params: vec![CpsValueId(3)],
6943                        captures: Vec::new(),
6944                        shot_kind: CpsShotKind::OneShot,
6945                        stmts: Vec::new(),
6946                        terminator: CpsTerminator::Return(CpsValueId(3)),
6947                    },
6948                ],
6949            }],
6950        }))
6951    }
6952
6953    fn effectful_function_with_pure_continue_abi() -> CpsReprAbiModule {
6954        let effect = typed_ir::Path::from_name(typed_ir::Name("unused".to_string()));
6955        lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
6956            functions: Vec::new(),
6957            roots: vec![CpsFunction {
6958                name: "root".to_string(),
6959                params: Vec::new(),
6960                entry: CpsContinuationId(0),
6961                handlers: vec![crate::cps_ir::CpsHandler {
6962                    id: crate::cps_ir::CpsHandlerId(0),
6963                    arms: vec![crate::cps_ir::CpsHandlerArm {
6964                        effect,
6965                        entry: CpsContinuationId(2),
6966                    }],
6967                }],
6968                continuations: vec![
6969                    CpsContinuation {
6970                        id: CpsContinuationId(0),
6971                        params: Vec::new(),
6972                        captures: Vec::new(),
6973                        shot_kind: CpsShotKind::OneShot,
6974                        stmts: vec![
6975                            CpsStmt::Literal {
6976                                dest: CpsValueId(0),
6977                                literal: CpsLiteral::Int("40".to_string()),
6978                            },
6979                            CpsStmt::Literal {
6980                                dest: CpsValueId(1),
6981                                literal: CpsLiteral::Int("2".to_string()),
6982                            },
6983                            CpsStmt::Primitive {
6984                                dest: CpsValueId(2),
6985                                op: typed_ir::PrimitiveOp::IntAdd,
6986                                args: vec![CpsValueId(0), CpsValueId(1)],
6987                            },
6988                        ],
6989                        terminator: CpsTerminator::Continue {
6990                            target: CpsContinuationId(1),
6991                            args: vec![CpsValueId(2)],
6992                        },
6993                    },
6994                    CpsContinuation {
6995                        id: CpsContinuationId(1),
6996                        params: vec![CpsValueId(3)],
6997                        captures: Vec::new(),
6998                        shot_kind: CpsShotKind::OneShot,
6999                        stmts: Vec::new(),
7000                        terminator: CpsTerminator::Return(CpsValueId(3)),
7001                    },
7002                    CpsContinuation {
7003                        id: CpsContinuationId(2),
7004                        params: vec![CpsValueId(4), CpsValueId(5)],
7005                        captures: Vec::new(),
7006                        shot_kind: CpsShotKind::OneShot,
7007                        stmts: Vec::new(),
7008                        terminator: CpsTerminator::Return(CpsValueId(4)),
7009                    },
7010                ],
7011            }],
7012        }))
7013    }
7014
7015    fn effectful_function_with_pure_branch_abi() -> CpsReprAbiModule {
7016        let effect = typed_ir::Path::from_name(typed_ir::Name("unused".to_string()));
7017        lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
7018            functions: Vec::new(),
7019            roots: vec![CpsFunction {
7020                name: "root".to_string(),
7021                params: Vec::new(),
7022                entry: CpsContinuationId(0),
7023                handlers: vec![crate::cps_ir::CpsHandler {
7024                    id: crate::cps_ir::CpsHandlerId(0),
7025                    arms: vec![crate::cps_ir::CpsHandlerArm {
7026                        effect,
7027                        entry: CpsContinuationId(3),
7028                    }],
7029                }],
7030                continuations: vec![
7031                    CpsContinuation {
7032                        id: CpsContinuationId(0),
7033                        params: Vec::new(),
7034                        captures: Vec::new(),
7035                        shot_kind: CpsShotKind::OneShot,
7036                        stmts: vec![CpsStmt::Literal {
7037                            dest: CpsValueId(0),
7038                            literal: CpsLiteral::Bool(true),
7039                        }],
7040                        terminator: CpsTerminator::Branch {
7041                            cond: CpsValueId(0),
7042                            then_cont: CpsContinuationId(1),
7043                            else_cont: CpsContinuationId(2),
7044                        },
7045                    },
7046                    CpsContinuation {
7047                        id: CpsContinuationId(1),
7048                        params: Vec::new(),
7049                        captures: Vec::new(),
7050                        shot_kind: CpsShotKind::OneShot,
7051                        stmts: vec![CpsStmt::Literal {
7052                            dest: CpsValueId(1),
7053                            literal: CpsLiteral::Int("42".to_string()),
7054                        }],
7055                        terminator: CpsTerminator::Return(CpsValueId(1)),
7056                    },
7057                    CpsContinuation {
7058                        id: CpsContinuationId(2),
7059                        params: Vec::new(),
7060                        captures: Vec::new(),
7061                        shot_kind: CpsShotKind::OneShot,
7062                        stmts: vec![CpsStmt::Literal {
7063                            dest: CpsValueId(2),
7064                            literal: CpsLiteral::Int("0".to_string()),
7065                        }],
7066                        terminator: CpsTerminator::Return(CpsValueId(2)),
7067                    },
7068                    CpsContinuation {
7069                        id: CpsContinuationId(3),
7070                        params: vec![CpsValueId(3), CpsValueId(4)],
7071                        captures: Vec::new(),
7072                        shot_kind: CpsShotKind::OneShot,
7073                        stmts: Vec::new(),
7074                        terminator: CpsTerminator::Return(CpsValueId(3)),
7075                    },
7076                ],
7077            }],
7078        }))
7079    }
7080
7081    fn effectful_function_with_pure_direct_call_abi() -> CpsReprAbiModule {
7082        let effect = typed_ir::Path::from_name(typed_ir::Name("unused".to_string()));
7083        lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
7084            functions: vec![CpsFunction {
7085                name: "add".to_string(),
7086                params: vec![CpsValueId(0), CpsValueId(1)],
7087                entry: CpsContinuationId(0),
7088                handlers: Vec::new(),
7089                continuations: vec![
7090                    CpsContinuation {
7091                        id: CpsContinuationId(0),
7092                        params: vec![CpsValueId(0), CpsValueId(1)],
7093                        captures: Vec::new(),
7094                        shot_kind: CpsShotKind::OneShot,
7095                        stmts: vec![CpsStmt::Primitive {
7096                            dest: CpsValueId(2),
7097                            op: typed_ir::PrimitiveOp::IntAdd,
7098                            args: vec![CpsValueId(0), CpsValueId(1)],
7099                        }],
7100                        terminator: CpsTerminator::Continue {
7101                            target: CpsContinuationId(1),
7102                            args: vec![CpsValueId(2)],
7103                        },
7104                    },
7105                    CpsContinuation {
7106                        id: CpsContinuationId(1),
7107                        params: vec![CpsValueId(5)],
7108                        captures: Vec::new(),
7109                        shot_kind: CpsShotKind::OneShot,
7110                        stmts: Vec::new(),
7111                        terminator: CpsTerminator::Return(CpsValueId(5)),
7112                    },
7113                ],
7114            }],
7115            roots: vec![CpsFunction {
7116                name: "root".to_string(),
7117                params: Vec::new(),
7118                entry: CpsContinuationId(0),
7119                handlers: vec![crate::cps_ir::CpsHandler {
7120                    id: crate::cps_ir::CpsHandlerId(0),
7121                    arms: vec![crate::cps_ir::CpsHandlerArm {
7122                        effect,
7123                        entry: CpsContinuationId(1),
7124                    }],
7125                }],
7126                continuations: vec![
7127                    CpsContinuation {
7128                        id: CpsContinuationId(0),
7129                        params: Vec::new(),
7130                        captures: Vec::new(),
7131                        shot_kind: CpsShotKind::OneShot,
7132                        stmts: vec![
7133                            CpsStmt::Literal {
7134                                dest: CpsValueId(3),
7135                                literal: CpsLiteral::Int("40".to_string()),
7136                            },
7137                            CpsStmt::Literal {
7138                                dest: CpsValueId(4),
7139                                literal: CpsLiteral::Int("2".to_string()),
7140                            },
7141                            CpsStmt::DirectCall {
7142                                dest: CpsValueId(5),
7143                                target: "add".to_string(),
7144                                args: vec![CpsValueId(3), CpsValueId(4)],
7145                            },
7146                        ],
7147                        terminator: CpsTerminator::Return(CpsValueId(5)),
7148                    },
7149                    CpsContinuation {
7150                        id: CpsContinuationId(1),
7151                        params: vec![CpsValueId(6), CpsValueId(7)],
7152                        captures: Vec::new(),
7153                        shot_kind: CpsShotKind::OneShot,
7154                        stmts: Vec::new(),
7155                        terminator: CpsTerminator::Return(CpsValueId(6)),
7156                    },
7157                ],
7158            }],
7159        }))
7160    }
7161
7162    fn effectful_call_to_pure_callee_abi() -> CpsReprAbiModule {
7163        let effect = typed_ir::Path::from_name(typed_ir::Name("unused".to_string()));
7164        lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
7165            functions: vec![CpsFunction {
7166                name: "add".to_string(),
7167                params: vec![CpsValueId(0), CpsValueId(1)],
7168                entry: CpsContinuationId(0),
7169                handlers: Vec::new(),
7170                continuations: vec![
7171                    CpsContinuation {
7172                        id: CpsContinuationId(0),
7173                        params: vec![CpsValueId(0), CpsValueId(1)],
7174                        captures: Vec::new(),
7175                        shot_kind: CpsShotKind::OneShot,
7176                        stmts: vec![CpsStmt::Primitive {
7177                            dest: CpsValueId(2),
7178                            op: typed_ir::PrimitiveOp::IntAdd,
7179                            args: vec![CpsValueId(0), CpsValueId(1)],
7180                        }],
7181                        terminator: CpsTerminator::Continue {
7182                            target: CpsContinuationId(1),
7183                            args: vec![CpsValueId(2)],
7184                        },
7185                    },
7186                    CpsContinuation {
7187                        id: CpsContinuationId(1),
7188                        params: vec![CpsValueId(8)],
7189                        captures: Vec::new(),
7190                        shot_kind: CpsShotKind::OneShot,
7191                        stmts: Vec::new(),
7192                        terminator: CpsTerminator::Return(CpsValueId(8)),
7193                    },
7194                ],
7195            }],
7196            roots: vec![CpsFunction {
7197                name: "root".to_string(),
7198                params: Vec::new(),
7199                entry: CpsContinuationId(0),
7200                handlers: vec![crate::cps_ir::CpsHandler {
7201                    id: crate::cps_ir::CpsHandlerId(0),
7202                    arms: vec![crate::cps_ir::CpsHandlerArm {
7203                        effect,
7204                        entry: CpsContinuationId(2),
7205                    }],
7206                }],
7207                continuations: vec![
7208                    CpsContinuation {
7209                        id: CpsContinuationId(0),
7210                        params: Vec::new(),
7211                        captures: Vec::new(),
7212                        shot_kind: CpsShotKind::OneShot,
7213                        stmts: vec![
7214                            CpsStmt::Literal {
7215                                dest: CpsValueId(3),
7216                                literal: CpsLiteral::Int("40".to_string()),
7217                            },
7218                            CpsStmt::Literal {
7219                                dest: CpsValueId(4),
7220                                literal: CpsLiteral::Int("2".to_string()),
7221                            },
7222                        ],
7223                        terminator: CpsTerminator::EffectfulCall {
7224                            target: "add".to_string(),
7225                            args: vec![CpsValueId(3), CpsValueId(4)],
7226                            resume: CpsContinuationId(1),
7227                        },
7228                    },
7229                    CpsContinuation {
7230                        id: CpsContinuationId(1),
7231                        params: vec![CpsValueId(5)],
7232                        captures: Vec::new(),
7233                        shot_kind: CpsShotKind::OneShot,
7234                        stmts: Vec::new(),
7235                        terminator: CpsTerminator::Return(CpsValueId(5)),
7236                    },
7237                    CpsContinuation {
7238                        id: CpsContinuationId(2),
7239                        params: vec![CpsValueId(6), CpsValueId(7)],
7240                        captures: Vec::new(),
7241                        shot_kind: CpsShotKind::OneShot,
7242                        stmts: Vec::new(),
7243                        terminator: CpsTerminator::Return(CpsValueId(6)),
7244                    },
7245                ],
7246            }],
7247        }))
7248    }
7249
7250    fn guard_stack_abi() -> CpsReprAbiModule {
7251        lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
7252            functions: Vec::new(),
7253            roots: vec![CpsFunction {
7254                name: "root".to_string(),
7255                params: Vec::new(),
7256                entry: CpsContinuationId(0),
7257                handlers: Vec::new(),
7258                continuations: vec![CpsContinuation {
7259                    id: CpsContinuationId(0),
7260                    params: Vec::new(),
7261                    captures: Vec::new(),
7262                    shot_kind: CpsShotKind::OneShot,
7263                    stmts: vec![
7264                        CpsStmt::FreshGuard {
7265                            dest: CpsValueId(0),
7266                            var: yulang_runtime::EffectIdVar(0),
7267                        },
7268                        CpsStmt::PeekGuard {
7269                            dest: CpsValueId(1),
7270                        },
7271                        CpsStmt::FindGuard {
7272                            dest: CpsValueId(2),
7273                            guard: CpsValueId(1),
7274                        },
7275                    ],
7276                    terminator: CpsTerminator::Return(CpsValueId(2)),
7277                }],
7278            }],
7279        }))
7280    }
7281
7282    fn blocked_handler_snapshot_abi() -> CpsReprAbiModule {
7283        let start = typed_ir::Path::from_name(typed_ir::Name("start".to_string()));
7284        let choose = typed_ir::Path::from_name(typed_ir::Name("choose".to_string()));
7285        lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
7286            functions: Vec::new(),
7287            roots: vec![CpsFunction {
7288                name: "root".to_string(),
7289                params: Vec::new(),
7290                entry: CpsContinuationId(0),
7291                handlers: vec![
7292                    crate::cps_ir::CpsHandler {
7293                        id: crate::cps_ir::CpsHandlerId(0),
7294                        arms: vec![
7295                            crate::cps_ir::CpsHandlerArm {
7296                                effect: start.clone(),
7297                                entry: CpsContinuationId(2),
7298                            },
7299                            crate::cps_ir::CpsHandlerArm {
7300                                effect: choose.clone(),
7301                                entry: CpsContinuationId(5),
7302                            },
7303                        ],
7304                    },
7305                    crate::cps_ir::CpsHandler {
7306                        id: crate::cps_ir::CpsHandlerId(1),
7307                        arms: vec![crate::cps_ir::CpsHandlerArm {
7308                            effect: choose.clone(),
7309                            entry: CpsContinuationId(4),
7310                        }],
7311                    },
7312                ],
7313                continuations: vec![
7314                    CpsContinuation {
7315                        id: CpsContinuationId(0),
7316                        params: Vec::new(),
7317                        captures: Vec::new(),
7318                        shot_kind: CpsShotKind::MultiShot,
7319                        stmts: vec![CpsStmt::Literal {
7320                            dest: CpsValueId(0),
7321                            literal: CpsLiteral::Int("1".to_string()),
7322                        }],
7323                        terminator: CpsTerminator::Perform {
7324                            effect: start,
7325                            payload: CpsValueId(0),
7326                            resume: CpsContinuationId(1),
7327                            handler: crate::cps_ir::CpsHandlerId(0),
7328                            blocked: None,
7329                        },
7330                    },
7331                    CpsContinuation {
7332                        id: CpsContinuationId(1),
7333                        params: vec![CpsValueId(1)],
7334                        captures: Vec::new(),
7335                        shot_kind: CpsShotKind::MultiShot,
7336                        stmts: vec![CpsStmt::Literal {
7337                            dest: CpsValueId(2),
7338                            literal: CpsLiteral::Int("0".to_string()),
7339                        }],
7340                        terminator: CpsTerminator::Perform {
7341                            effect: choose.clone(),
7342                            payload: CpsValueId(2),
7343                            resume: CpsContinuationId(3),
7344                            handler: crate::cps_ir::CpsHandlerId(0),
7345                            blocked: Some(CpsValueId(1)),
7346                        },
7347                    },
7348                    CpsContinuation {
7349                        id: CpsContinuationId(2),
7350                        params: vec![CpsValueId(3), CpsValueId(4)],
7351                        captures: Vec::new(),
7352                        shot_kind: CpsShotKind::MultiShot,
7353                        stmts: vec![
7354                            CpsStmt::FreshGuard {
7355                                dest: CpsValueId(5),
7356                                var: yulang_runtime::EffectIdVar(0),
7357                            },
7358                            CpsStmt::ResumeWithHandler {
7359                                dest: CpsValueId(6),
7360                                resumption: CpsValueId(4),
7361                                arg: CpsValueId(5),
7362                                handler: crate::cps_ir::CpsHandlerId(1),
7363                                envs: Vec::new(),
7364                            },
7365                        ],
7366                        terminator: CpsTerminator::Return(CpsValueId(6)),
7367                    },
7368                    CpsContinuation {
7369                        id: CpsContinuationId(3),
7370                        params: vec![CpsValueId(7)],
7371                        captures: Vec::new(),
7372                        shot_kind: CpsShotKind::MultiShot,
7373                        stmts: Vec::new(),
7374                        terminator: CpsTerminator::Return(CpsValueId(7)),
7375                    },
7376                    CpsContinuation {
7377                        id: CpsContinuationId(4),
7378                        params: vec![CpsValueId(8), CpsValueId(9)],
7379                        captures: Vec::new(),
7380                        shot_kind: CpsShotKind::MultiShot,
7381                        stmts: vec![CpsStmt::Literal {
7382                            dest: CpsValueId(10),
7383                            literal: CpsLiteral::Int("200".to_string()),
7384                        }],
7385                        terminator: CpsTerminator::Return(CpsValueId(10)),
7386                    },
7387                    CpsContinuation {
7388                        id: CpsContinuationId(5),
7389                        params: vec![CpsValueId(11), CpsValueId(12)],
7390                        captures: Vec::new(),
7391                        shot_kind: CpsShotKind::MultiShot,
7392                        stmts: vec![CpsStmt::Literal {
7393                            dest: CpsValueId(13),
7394                            literal: CpsLiteral::Int("100".to_string()),
7395                        }],
7396                        terminator: CpsTerminator::Return(CpsValueId(13)),
7397                    },
7398                ],
7399            }],
7400        }))
7401    }
7402
7403    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
7404    enum ThunkBoundaryStorage {
7405        Direct,
7406        ListIndex,
7407        RecordSelect,
7408        VariantPayload,
7409    }
7410
7411    fn active_thunk_boundary_abi(
7412        allowed: typed_ir::Type,
7413        storage: ThunkBoundaryStorage,
7414    ) -> CpsReprAbiModule {
7415        let choose = typed_ir::Path::from_name(typed_ir::Name("choose".to_string()));
7416        let mut root_stmts = vec![
7417            CpsStmt::InstallHandler {
7418                handler: crate::cps_ir::CpsHandlerId(0),
7419                envs: Vec::new(),
7420                value: CpsContinuationId(5),
7421                escape: CpsContinuationId(5),
7422            },
7423            CpsStmt::FreshGuard {
7424                dest: CpsValueId(0),
7425                var: yulang_runtime::EffectIdVar(0),
7426            },
7427            CpsStmt::MakeThunk {
7428                dest: CpsValueId(1),
7429                entry: CpsContinuationId(1),
7430            },
7431            CpsStmt::AddThunkBoundary {
7432                dest: CpsValueId(2),
7433                thunk: CpsValueId(1),
7434                guard: CpsValueId(0),
7435                allowed,
7436                active: true,
7437            },
7438        ];
7439        let thunk = match storage {
7440            ThunkBoundaryStorage::Direct => CpsValueId(2),
7441            ThunkBoundaryStorage::ListIndex => {
7442                root_stmts.extend([
7443                    CpsStmt::Primitive {
7444                        dest: CpsValueId(14),
7445                        op: typed_ir::PrimitiveOp::ListSingleton,
7446                        args: vec![CpsValueId(2)],
7447                    },
7448                    CpsStmt::Literal {
7449                        dest: CpsValueId(15),
7450                        literal: CpsLiteral::Int("0".to_string()),
7451                    },
7452                    CpsStmt::Primitive {
7453                        dest: CpsValueId(16),
7454                        op: typed_ir::PrimitiveOp::ListIndex,
7455                        args: vec![CpsValueId(14), CpsValueId(15)],
7456                    },
7457                ]);
7458                CpsValueId(16)
7459            }
7460            ThunkBoundaryStorage::RecordSelect => {
7461                root_stmts.extend([
7462                    CpsStmt::Record {
7463                        dest: CpsValueId(14),
7464                        base: None,
7465                        fields: vec![CpsRecordField {
7466                            name: typed_ir::Name("callback".to_string()),
7467                            value: CpsValueId(2),
7468                        }],
7469                    },
7470                    CpsStmt::Select {
7471                        dest: CpsValueId(16),
7472                        base: CpsValueId(14),
7473                        field: typed_ir::Name("callback".to_string()),
7474                    },
7475                ]);
7476                CpsValueId(16)
7477            }
7478            ThunkBoundaryStorage::VariantPayload => {
7479                root_stmts.extend([
7480                    CpsStmt::Variant {
7481                        dest: CpsValueId(14),
7482                        tag: typed_ir::Name("some".to_string()),
7483                        value: Some(CpsValueId(2)),
7484                    },
7485                    CpsStmt::VariantPayload {
7486                        dest: CpsValueId(16),
7487                        variant: CpsValueId(14),
7488                    },
7489                ]);
7490                CpsValueId(16)
7491            }
7492        };
7493        root_stmts.extend([
7494            CpsStmt::InstallHandler {
7495                handler: crate::cps_ir::CpsHandlerId(1),
7496                envs: Vec::new(),
7497                value: CpsContinuationId(6),
7498                escape: CpsContinuationId(6),
7499            },
7500            CpsStmt::ForceThunk {
7501                dest: CpsValueId(3),
7502                thunk,
7503            },
7504        ]);
7505        lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
7506            functions: Vec::new(),
7507            roots: vec![CpsFunction {
7508                name: "root".to_string(),
7509                params: Vec::new(),
7510                entry: CpsContinuationId(0),
7511                handlers: vec![
7512                    crate::cps_ir::CpsHandler {
7513                        id: crate::cps_ir::CpsHandlerId(0),
7514                        arms: vec![crate::cps_ir::CpsHandlerArm {
7515                            effect: choose.clone(),
7516                            entry: CpsContinuationId(3),
7517                        }],
7518                    },
7519                    crate::cps_ir::CpsHandler {
7520                        id: crate::cps_ir::CpsHandlerId(1),
7521                        arms: vec![crate::cps_ir::CpsHandlerArm {
7522                            effect: choose.clone(),
7523                            entry: CpsContinuationId(4),
7524                        }],
7525                    },
7526                ],
7527                continuations: vec![
7528                    CpsContinuation {
7529                        id: CpsContinuationId(0),
7530                        params: Vec::new(),
7531                        captures: Vec::new(),
7532                        shot_kind: CpsShotKind::MultiShot,
7533                        stmts: root_stmts,
7534                        terminator: CpsTerminator::Return(CpsValueId(3)),
7535                    },
7536                    CpsContinuation {
7537                        id: CpsContinuationId(1),
7538                        params: Vec::new(),
7539                        captures: Vec::new(),
7540                        shot_kind: CpsShotKind::MultiShot,
7541                        stmts: vec![CpsStmt::Literal {
7542                            dest: CpsValueId(4),
7543                            literal: CpsLiteral::Int("1".to_string()),
7544                        }],
7545                        terminator: CpsTerminator::Perform {
7546                            effect: choose,
7547                            payload: CpsValueId(4),
7548                            resume: CpsContinuationId(2),
7549                            handler: crate::cps_ir::CpsHandlerId(0),
7550                            blocked: None,
7551                        },
7552                    },
7553                    CpsContinuation {
7554                        id: CpsContinuationId(2),
7555                        params: vec![CpsValueId(5)],
7556                        captures: Vec::new(),
7557                        shot_kind: CpsShotKind::MultiShot,
7558                        stmts: Vec::new(),
7559                        terminator: CpsTerminator::Return(CpsValueId(5)),
7560                    },
7561                    CpsContinuation {
7562                        id: CpsContinuationId(3),
7563                        params: vec![CpsValueId(6), CpsValueId(7)],
7564                        captures: Vec::new(),
7565                        shot_kind: CpsShotKind::MultiShot,
7566                        stmts: vec![CpsStmt::Literal {
7567                            dest: CpsValueId(8),
7568                            literal: CpsLiteral::Int("20".to_string()),
7569                        }],
7570                        terminator: CpsTerminator::Return(CpsValueId(8)),
7571                    },
7572                    CpsContinuation {
7573                        id: CpsContinuationId(4),
7574                        params: vec![CpsValueId(9), CpsValueId(10)],
7575                        captures: Vec::new(),
7576                        shot_kind: CpsShotKind::MultiShot,
7577                        stmts: vec![CpsStmt::Literal {
7578                            dest: CpsValueId(11),
7579                            literal: CpsLiteral::Int("10".to_string()),
7580                        }],
7581                        terminator: CpsTerminator::Return(CpsValueId(11)),
7582                    },
7583                    CpsContinuation {
7584                        id: CpsContinuationId(5),
7585                        params: vec![CpsValueId(12)],
7586                        captures: Vec::new(),
7587                        shot_kind: CpsShotKind::MultiShot,
7588                        stmts: Vec::new(),
7589                        terminator: CpsTerminator::Return(CpsValueId(12)),
7590                    },
7591                    CpsContinuation {
7592                        id: CpsContinuationId(6),
7593                        params: vec![CpsValueId(13)],
7594                        captures: Vec::new(),
7595                        shot_kind: CpsShotKind::MultiShot,
7596                        stmts: Vec::new(),
7597                        terminator: CpsTerminator::Return(CpsValueId(13)),
7598                    },
7599                ],
7600            }],
7601        }))
7602    }
7603
7604    fn tail_resume_effect_abi() -> CpsReprAbiModule {
7605        let effect = typed_ir::Path::from_name(typed_ir::Name("choose".to_string()));
7606        lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
7607            functions: Vec::new(),
7608            roots: vec![CpsFunction {
7609                name: "root".to_string(),
7610                params: Vec::new(),
7611                entry: CpsContinuationId(0),
7612                handlers: vec![crate::cps_ir::CpsHandler {
7613                    id: crate::cps_ir::CpsHandlerId(0),
7614                    arms: vec![crate::cps_ir::CpsHandlerArm {
7615                        effect: effect.clone(),
7616                        entry: CpsContinuationId(2),
7617                    }],
7618                }],
7619                continuations: vec![
7620                    CpsContinuation {
7621                        id: CpsContinuationId(0),
7622                        params: Vec::new(),
7623                        captures: Vec::new(),
7624                        shot_kind: CpsShotKind::MultiShot,
7625                        stmts: vec![
7626                            CpsStmt::Literal {
7627                                dest: CpsValueId(0),
7628                                literal: CpsLiteral::Int("1".to_string()),
7629                            },
7630                            CpsStmt::Literal {
7631                                dest: CpsValueId(2),
7632                                literal: CpsLiteral::Int("10".to_string()),
7633                            },
7634                        ],
7635                        terminator: CpsTerminator::Perform {
7636                            effect,
7637                            payload: CpsValueId(0),
7638                            resume: CpsContinuationId(1),
7639                            handler: crate::cps_ir::CpsHandlerId(0),
7640                            blocked: None,
7641                        },
7642                    },
7643                    CpsContinuation {
7644                        id: CpsContinuationId(1),
7645                        params: vec![CpsValueId(1)],
7646                        captures: Vec::new(),
7647                        shot_kind: CpsShotKind::MultiShot,
7648                        stmts: vec![
7649                            CpsStmt::Literal {
7650                                dest: CpsValueId(2),
7651                                literal: CpsLiteral::Int("41".to_string()),
7652                            },
7653                            CpsStmt::Primitive {
7654                                dest: CpsValueId(3),
7655                                op: typed_ir::PrimitiveOp::IntAdd,
7656                                args: vec![CpsValueId(1), CpsValueId(2)],
7657                            },
7658                        ],
7659                        terminator: CpsTerminator::Return(CpsValueId(3)),
7660                    },
7661                    CpsContinuation {
7662                        id: CpsContinuationId(2),
7663                        params: vec![CpsValueId(4), CpsValueId(5)],
7664                        captures: Vec::new(),
7665                        shot_kind: CpsShotKind::MultiShot,
7666                        stmts: vec![CpsStmt::Resume {
7667                            dest: CpsValueId(6),
7668                            resumption: CpsValueId(5),
7669                            arg: CpsValueId(4),
7670                        }],
7671                        terminator: CpsTerminator::Return(CpsValueId(6)),
7672                    },
7673                ],
7674            }],
7675        }))
7676    }
7677
7678    fn multishot_resume_effect_abi() -> CpsReprAbiModule {
7679        let effect = typed_ir::Path::from_name(typed_ir::Name("choose".to_string()));
7680        lower_cps_repr_abi_module(&lower_cps_repr_module(&CpsModule {
7681            functions: Vec::new(),
7682            roots: vec![CpsFunction {
7683                name: "root".to_string(),
7684                params: Vec::new(),
7685                entry: CpsContinuationId(0),
7686                handlers: vec![crate::cps_ir::CpsHandler {
7687                    id: crate::cps_ir::CpsHandlerId(0),
7688                    arms: vec![crate::cps_ir::CpsHandlerArm {
7689                        effect: effect.clone(),
7690                        entry: CpsContinuationId(2),
7691                    }],
7692                }],
7693                continuations: vec![
7694                    CpsContinuation {
7695                        id: CpsContinuationId(0),
7696                        params: Vec::new(),
7697                        captures: Vec::new(),
7698                        shot_kind: CpsShotKind::MultiShot,
7699                        stmts: vec![
7700                            CpsStmt::Literal {
7701                                dest: CpsValueId(0),
7702                                literal: CpsLiteral::Int("1".to_string()),
7703                            },
7704                            CpsStmt::Literal {
7705                                dest: CpsValueId(2),
7706                                literal: CpsLiteral::Int("10".to_string()),
7707                            },
7708                        ],
7709                        terminator: CpsTerminator::Perform {
7710                            effect,
7711                            payload: CpsValueId(0),
7712                            resume: CpsContinuationId(1),
7713                            handler: crate::cps_ir::CpsHandlerId(0),
7714                            blocked: None,
7715                        },
7716                    },
7717                    CpsContinuation {
7718                        id: CpsContinuationId(1),
7719                        params: vec![CpsValueId(1)],
7720                        captures: vec![CpsValueId(2)],
7721                        shot_kind: CpsShotKind::MultiShot,
7722                        stmts: vec![CpsStmt::Primitive {
7723                            dest: CpsValueId(3),
7724                            op: typed_ir::PrimitiveOp::IntAdd,
7725                            args: vec![CpsValueId(1), CpsValueId(2)],
7726                        }],
7727                        terminator: CpsTerminator::Return(CpsValueId(3)),
7728                    },
7729                    CpsContinuation {
7730                        id: CpsContinuationId(2),
7731                        params: vec![CpsValueId(4), CpsValueId(5)],
7732                        captures: Vec::new(),
7733                        shot_kind: CpsShotKind::MultiShot,
7734                        stmts: vec![
7735                            CpsStmt::CloneContinuation {
7736                                dest: CpsValueId(9),
7737                                source: CpsValueId(5),
7738                            },
7739                            CpsStmt::Resume {
7740                                dest: CpsValueId(6),
7741                                resumption: CpsValueId(5),
7742                                arg: CpsValueId(4),
7743                            },
7744                            CpsStmt::Resume {
7745                                dest: CpsValueId(7),
7746                                resumption: CpsValueId(9),
7747                                arg: CpsValueId(4),
7748                            },
7749                            CpsStmt::Primitive {
7750                                dest: CpsValueId(8),
7751                                op: typed_ir::PrimitiveOp::IntAdd,
7752                                args: vec![CpsValueId(6), CpsValueId(7)],
7753                            },
7754                        ],
7755                        terminator: CpsTerminator::Return(CpsValueId(8)),
7756                    },
7757                ],
7758            }],
7759        }))
7760    }
7761
7762    fn unknown_lit(lit: typed_ir::Lit) -> runtime::Expr {
7763        runtime::Expr::typed(runtime::ExprKind::Lit(lit), runtime::Type::unknown())
7764    }
7765
7766    fn primitive(op: typed_ir::PrimitiveOp) -> runtime::Expr {
7767        runtime::Expr::typed(runtime::ExprKind::PrimitiveOp(op), runtime::Type::unknown())
7768    }
7769
7770    fn apply(callee: runtime::Expr, arg: runtime::Expr) -> runtime::Expr {
7771        runtime::Expr::typed(
7772            runtime::ExprKind::Apply {
7773                callee: Box::new(callee),
7774                arg: Box::new(arg),
7775                evidence: None,
7776                instantiation: None,
7777            },
7778            runtime::Type::unknown(),
7779        )
7780    }
7781
7782    fn thunk(expr: runtime::Expr) -> runtime::Expr {
7783        runtime::Expr::typed(
7784            runtime::ExprKind::Thunk {
7785                effect: typed_ir::Type::Unknown,
7786                value: runtime::Type::unknown(),
7787                expr: Box::new(expr),
7788            },
7789            runtime::Type::unknown(),
7790        )
7791    }
7792
7793    fn bind_here(expr: runtime::Expr) -> runtime::Expr {
7794        runtime::Expr::typed(
7795            runtime::ExprKind::BindHere {
7796                expr: Box::new(expr),
7797            },
7798            runtime::Type::unknown(),
7799        )
7800    }
7801
7802    fn primitive_call(op: typed_ir::PrimitiveOp, args: Vec<runtime::Expr>) -> runtime::Expr {
7803        args.into_iter()
7804            .fold(primitive(op), |callee, arg| apply(callee, arg))
7805    }
7806
7807    fn list_expr(items: Vec<i64>) -> runtime::Expr {
7808        items
7809            .into_iter()
7810            .map(|item| {
7811                primitive_call(
7812                    typed_ir::PrimitiveOp::ListSingleton,
7813                    vec![unknown_lit(typed_ir::Lit::Int(item.to_string()))],
7814                )
7815            })
7816            .fold(
7817                primitive_call(
7818                    typed_ir::PrimitiveOp::ListEmpty,
7819                    vec![unknown_lit(typed_ir::Lit::Unit)],
7820                ),
7821                |acc, item| primitive_call(typed_ir::PrimitiveOp::ListMerge, vec![acc, item]),
7822            )
7823    }
7824
7825    fn range_expr(start: i64, end: i64) -> runtime::Expr {
7826        variant(
7827            "within",
7828            Some(tuple(vec![
7829                variant(
7830                    "included",
7831                    Some(unknown_lit(typed_ir::Lit::Int(start.to_string()))),
7832                ),
7833                variant(
7834                    "excluded",
7835                    Some(unknown_lit(typed_ir::Lit::Int(end.to_string()))),
7836                ),
7837            ])),
7838        )
7839    }
7840
7841    fn tuple(items: Vec<runtime::Expr>) -> runtime::Expr {
7842        runtime::Expr::typed(runtime::ExprKind::Tuple(items), runtime::Type::unknown())
7843    }
7844
7845    fn record(fields: Vec<(&str, runtime::Expr)>) -> runtime::Expr {
7846        runtime::Expr::typed(
7847            runtime::ExprKind::Record {
7848                fields: fields
7849                    .into_iter()
7850                    .map(|(name, value)| runtime::RecordExprField {
7851                        name: typed_ir::Name(name.to_string()),
7852                        value,
7853                    })
7854                    .collect(),
7855                spread: None,
7856            },
7857            runtime::Type::unknown(),
7858        )
7859    }
7860
7861    fn variant(tag: &str, value: Option<runtime::Expr>) -> runtime::Expr {
7862        runtime::Expr::typed(
7863            runtime::ExprKind::Variant {
7864                tag: typed_ir::Name(tag.to_string()),
7865                value: value.map(Box::new),
7866            },
7867            runtime::Type::unknown(),
7868        )
7869    }
7870
7871    fn module_with_root(expr: runtime::Expr) -> runtime::Module {
7872        runtime::Module {
7873            path: typed_ir::Path::default(),
7874            bindings: Vec::new(),
7875            root_exprs: vec![expr],
7876            roots: vec![runtime::Root::Expr(0)],
7877            role_impls: Vec::new(),
7878        }
7879    }
7880}