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(®istered.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 | 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 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 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 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 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 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 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 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 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 let pre_push_count = call_i64_helper(
2496 module_backend,
2497 builder,
2498 "yulang_cps_return_frame_len_i64",
2499 &[],
2500 )?;
2501 push_return_frame_for_resume(
2503 module_backend,
2504 builder,
2505 function,
2506 resume_cont,
2507 immediate_force,
2508 functions,
2509 )?;
2510 let arg_values = read_values(builder, function, args)?;
2512 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 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 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
2720fn 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
2851fn 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
2865fn 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
2938fn 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 let _ = call_i64_helper(
3091 module_backend,
3092 builder,
3093 "yulang_cps_enter_handler_arm_i64",
3094 &[],
3095 )?;
3096 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 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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
3559fn 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
3597fn 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
3614fn 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, ¶ms, 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
3875fn 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 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 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 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 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}