1use std::cell::RefCell;
2use std::collections::{BTreeMap, HashMap, HashSet};
3use std::fmt;
4use std::rc::Rc;
5
6use yulang_runtime as runtime;
7use yulang_typed_ir as typed_ir;
8
9use crate::cps_frame_trace::{
10 CpsFrameTraceEvent, CpsFrameTraceLayer, CpsFrameTraceSlot, push_cps_frame_trace_event,
11};
12use crate::cps_ir::{
13 CpsContinuation, CpsContinuationId, CpsFunction, CpsHandlerEnv, CpsHandlerId, CpsLiteral,
14 CpsModule, CpsShotKind, CpsStmt, CpsTerminator, CpsValueId,
15};
16
17pub type CpsReprEvalResult<T> = Result<T, CpsReprEvalError>;
18
19thread_local! {
20 static LATEST_REPR_HANDLER_ENVS: RefCell<Vec<CpsReprLatestHandlerEnv>> = const { RefCell::new(Vec::new()) };
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum CpsReprValueKind {
25 Plain,
26 Resumption,
27 Unknown,
28}
29
30impl CpsReprValueKind {
31 fn merge(self, other: Self) -> Self {
32 match (self, other) {
33 (left, right) if left == right => left,
34 (Self::Unknown, known) | (known, Self::Unknown) => known,
35 _ => Self::Unknown,
36 }
37 }
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct CpsReprValueAnalysis {
42 pub functions: HashMap<String, CpsReprFunctionValueAnalysis>,
43}
44
45impl CpsReprValueAnalysis {
46 pub fn value_kind(&self, function: &str, value: CpsValueId) -> Option<CpsReprValueKind> {
47 self.functions.get(function)?.values.get(&value).copied()
48 }
49
50 pub fn continuation_return_kind(
51 &self,
52 function: &str,
53 continuation: CpsContinuationId,
54 ) -> Option<CpsReprValueKind> {
55 self.functions
56 .get(function)?
57 .continuation_returns
58 .get(&continuation)
59 .copied()
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct CpsReprFunctionValueAnalysis {
65 pub values: HashMap<CpsValueId, CpsReprValueKind>,
66 pub continuation_returns: HashMap<CpsContinuationId, CpsReprValueKind>,
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum CpsReprAbiLane {
71 ScalarI64,
72 NativeFloat,
73 RuntimeValuePtr,
74 ClosurePtr,
75 ThunkPtr,
76 ResumptionPtr,
77 OpaqueI64,
78 Conflict,
79 Unknown,
80}
81
82impl CpsReprAbiLane {
83 fn merge(self, other: Self) -> Self {
84 match (self, other) {
85 (left, right) if left == right => left,
86 (Self::Unknown, known) | (known, Self::Unknown) => known,
87 (Self::Conflict, _) | (_, Self::Conflict) => Self::Conflict,
88 (Self::NativeFloat, Self::OpaqueI64) | (Self::OpaqueI64, Self::NativeFloat) => {
89 Self::NativeFloat
90 }
91 (Self::NativeFloat, _) | (_, Self::NativeFloat) => Self::Conflict,
92 (Self::OpaqueI64, _) | (_, Self::OpaqueI64) => Self::OpaqueI64,
93 _ => Self::OpaqueI64,
94 }
95 }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
99pub struct CpsReprAbiAnalysis {
100 pub functions: HashMap<String, CpsReprFunctionAbiAnalysis>,
101}
102
103impl CpsReprAbiAnalysis {
104 pub fn function_return_lane(&self, function: &str) -> Option<CpsReprAbiLane> {
105 let function = self.functions.get(function)?;
106 function.continuation_returns.get(&function.entry).copied()
107 }
108
109 pub fn value_lane(&self, function: &str, value: CpsValueId) -> Option<CpsReprAbiLane> {
110 self.functions.get(function)?.values.get(&value).copied()
111 }
112
113 pub fn continuation_return_lane(
114 &self,
115 function: &str,
116 continuation: CpsContinuationId,
117 ) -> Option<CpsReprAbiLane> {
118 self.functions
119 .get(function)?
120 .continuation_returns
121 .get(&continuation)
122 .copied()
123 }
124}
125
126#[derive(Debug, Clone, PartialEq, Eq)]
127pub struct CpsReprFunctionAbiAnalysis {
128 pub entry: CpsContinuationId,
129 pub values: HashMap<CpsValueId, CpsReprAbiLane>,
130 pub continuation_returns: HashMap<CpsContinuationId, CpsReprAbiLane>,
131}
132
133#[derive(Debug, Clone, PartialEq, Eq)]
134pub struct CpsReprModule {
135 pub functions: Vec<CpsReprFunction>,
136 pub roots: Vec<CpsReprFunction>,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq)]
140pub struct CpsReprFunction {
141 pub name: String,
142 pub params: Vec<CpsValueId>,
143 pub entry: CpsContinuationId,
144 pub continuations: Vec<CpsReprContinuation>,
145 pub handlers: Vec<CpsReprHandler>,
146}
147
148#[derive(Debug, Clone, PartialEq, Eq)]
149pub struct CpsReprContinuation {
150 pub id: CpsContinuationId,
151 pub params: Vec<CpsValueId>,
152 pub environment: Vec<CpsReprEnvironmentSlot>,
153 pub shot_kind: CpsShotKind,
154 pub stmts: Vec<CpsStmt>,
155 pub terminator: CpsTerminator,
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub struct CpsReprEnvironmentSlot {
160 pub index: usize,
161 pub value: CpsValueId,
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
165pub struct CpsReprHandler {
166 pub id: CpsHandlerId,
167 pub arms: Vec<CpsReprHandlerArm>,
168}
169
170#[derive(Debug, Clone, PartialEq, Eq)]
171pub struct CpsReprHandlerArm {
172 pub effect: typed_ir::Path,
173 pub entry: CpsContinuationId,
174}
175
176#[derive(Debug, Clone, PartialEq)]
177pub enum CpsReprEvalError {
178 MissingFunction {
179 name: String,
180 },
181 MissingContinuation {
182 function: String,
183 id: CpsContinuationId,
184 },
185 MissingHandler {
186 function: String,
187 id: CpsHandlerId,
188 },
189 ContinuationArgumentMismatch {
190 function: String,
191 id: CpsContinuationId,
192 expected: usize,
193 actual: usize,
194 },
195 FunctionArgumentMismatch {
196 function: String,
197 expected: usize,
198 actual: usize,
199 },
200 MissingValue {
201 function: String,
202 id: CpsValueId,
203 },
204 ExpectedPlainValue {
205 function: String,
206 id: CpsValueId,
207 },
208 ExpectedResumption {
209 function: String,
210 id: CpsValueId,
211 },
212 ExpectedGuard {
213 function: String,
214 id: CpsValueId,
215 value: runtime::VmValue,
216 },
217 MissingGuard {
218 function: String,
219 },
220 UnsupportedStmt {
221 function: String,
222 kind: &'static str,
223 },
224 UnsupportedPrimitive {
225 op: typed_ir::PrimitiveOp,
226 },
227 PrimitiveTypeMismatch {
228 op: typed_ir::PrimitiveOp,
229 value: runtime::VmValue,
230 },
231 InvalidPrimitiveArity {
232 op: typed_ir::PrimitiveOp,
233 expected: usize,
234 actual: usize,
235 },
236 ExpectedRecord {
237 function: String,
238 value: runtime::VmValue,
239 },
240 MissingRecordField {
241 function: String,
242 field: typed_ir::Name,
243 },
244}
245
246impl fmt::Display for CpsReprEvalError {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 match self {
249 CpsReprEvalError::MissingFunction { name } => {
250 write!(f, "CPS repr function {name} is missing")
251 }
252 CpsReprEvalError::MissingContinuation { function, id } => {
253 write!(
254 f,
255 "CPS repr function {function} is missing continuation {id:?}"
256 )
257 }
258 CpsReprEvalError::MissingHandler { function, id } => {
259 write!(f, "CPS repr function {function} is missing handler {id:?}")
260 }
261 CpsReprEvalError::ContinuationArgumentMismatch {
262 function,
263 id,
264 expected,
265 actual,
266 } => write!(
267 f,
268 "CPS repr continuation {function}::{id:?} expected {expected} arguments, got {actual}"
269 ),
270 CpsReprEvalError::FunctionArgumentMismatch {
271 function,
272 expected,
273 actual,
274 } => write!(
275 f,
276 "CPS repr function {function} expected {expected} arguments, got {actual}"
277 ),
278 CpsReprEvalError::MissingValue { function, id } => {
279 write!(
280 f,
281 "CPS repr function {function} references missing value {id:?}"
282 )
283 }
284 CpsReprEvalError::ExpectedPlainValue { function, id } => write!(
285 f,
286 "CPS repr function {function} expected plain value {id:?}"
287 ),
288 CpsReprEvalError::ExpectedResumption { function, id } => write!(
289 f,
290 "CPS repr function {function} expected resumption value {id:?}"
291 ),
292 CpsReprEvalError::ExpectedGuard {
293 function,
294 id,
295 value,
296 } => write!(
297 f,
298 "CPS repr function {function} expected guard value {id:?}, got {value:?}"
299 ),
300 CpsReprEvalError::MissingGuard { function } => {
301 write!(f, "CPS repr function {function} has no active guard id")
302 }
303 CpsReprEvalError::UnsupportedStmt { function, kind } => write!(
304 f,
305 "CPS repr evaluator does not support {kind} statements in `{function}` yet"
306 ),
307 CpsReprEvalError::UnsupportedPrimitive { op } => {
308 write!(
309 f,
310 "CPS repr evaluator does not support primitive {op:?} yet"
311 )
312 }
313 CpsReprEvalError::PrimitiveTypeMismatch { op, value } => {
314 write!(f, "CPS repr primitive {op:?} cannot accept value {value:?}")
315 }
316 CpsReprEvalError::InvalidPrimitiveArity {
317 op,
318 expected,
319 actual,
320 } => write!(
321 f,
322 "CPS repr primitive {op:?} expected {expected} args, got {actual}"
323 ),
324 CpsReprEvalError::ExpectedRecord { function, value } => write!(
325 f,
326 "CPS repr function {function} expected record value, got {value:?}"
327 ),
328 CpsReprEvalError::MissingRecordField { function, field } => write!(
329 f,
330 "CPS repr function {function} selected missing record field {field:?}"
331 ),
332 }
333 }
334}
335
336impl std::error::Error for CpsReprEvalError {}
337
338pub fn lower_cps_repr_module(module: &CpsModule) -> CpsReprModule {
339 CpsReprModule {
340 functions: module.functions.iter().map(lower_function).collect(),
341 roots: module.roots.iter().map(lower_function).collect(),
342 }
343}
344
345pub fn eval_cps_repr_module(module: &CpsReprModule) -> CpsReprEvalResult<Vec<runtime::VmValue>> {
346 module
347 .roots
348 .iter()
349 .map(|root| {
350 let value =
351 with_fresh_repr_handler_env_overlay(|| eval_function(module, root, Vec::new()))?;
352 let value = resolve_routed_jump_repr(module, value, &[])?;
353 let value = unwrap_aborted_repr(value);
354 into_plain_value(root, CpsValueId(usize::MAX), value)
355 })
356 .collect()
357}
358
359fn with_fresh_repr_handler_env_overlay<T>(f: impl FnOnce() -> T) -> T {
360 let previous = LATEST_REPR_HANDLER_ENVS.with(|envs| envs.replace(Vec::new()));
361 let result = f();
362 LATEST_REPR_HANDLER_ENVS.with(|envs| {
363 envs.replace(previous);
364 });
365 result
366}
367
368fn unwrap_aborted_repr(value: CpsReprRuntimeValue) -> CpsReprRuntimeValue {
369 match value {
370 CpsReprRuntimeValue::Aborted(inner) => unwrap_aborted_repr(*inner),
371 other => other,
372 }
373}
374
375fn cps_repr_value_from_vm(value: runtime::VmValue) -> CpsReprRuntimeValue {
376 match value {
377 runtime::VmValue::Tuple(items) => {
378 CpsReprRuntimeValue::Tuple(items.into_iter().map(cps_repr_value_from_vm).collect())
379 }
380 runtime::VmValue::Variant { tag, value } => CpsReprRuntimeValue::Variant {
381 tag,
382 value: value.map(|v| Box::new(cps_repr_value_from_vm(*v))),
383 },
384 runtime::VmValue::List(list) => {
385 let items = list
386 .to_vec()
387 .into_iter()
388 .map(|item| cps_repr_value_from_vm((*item).clone()))
389 .collect::<Vec<_>>();
390 CpsReprRuntimeValue::List(Rc::new(items))
391 }
392 other => CpsReprRuntimeValue::Plain(other),
393 }
394}
395
396fn cps_repr_value_to_vm(value: CpsReprRuntimeValue) -> Option<runtime::VmValue> {
397 match value {
398 CpsReprRuntimeValue::Plain(value) => Some(value),
399 CpsReprRuntimeValue::Aborted(inner) => cps_repr_value_to_vm(*inner),
400 CpsReprRuntimeValue::RoutedJump(_) => None,
401 CpsReprRuntimeValue::Tuple(items) => Some(runtime::VmValue::Tuple(
402 items
403 .into_iter()
404 .map(cps_repr_value_to_vm)
405 .collect::<Option<Vec<_>>>()?,
406 )),
407 CpsReprRuntimeValue::Record(fields) => Some(runtime::VmValue::Record(
408 fields
409 .into_iter()
410 .map(|(name, value)| Some((name, cps_repr_value_to_vm(value)?)))
411 .collect::<Option<BTreeMap<_, _>>>()?,
412 )),
413 CpsReprRuntimeValue::Variant { tag, value } => Some(runtime::VmValue::Variant {
414 tag,
415 value: match value {
416 Some(value) => Some(Box::new(cps_repr_value_to_vm(*value)?)),
417 None => None,
418 },
419 }),
420 CpsReprRuntimeValue::List(items) => {
421 let vm_items = items
422 .iter()
423 .cloned()
424 .map(cps_repr_value_to_vm)
425 .collect::<Option<Vec<_>>>()?;
426 let mut tree = runtime::runtime::list_tree::ListTree::empty();
427 for item in vm_items {
428 tree = runtime::runtime::list_tree::ListTree::concat(
429 tree,
430 runtime::runtime::list_tree::ListTree::singleton(Rc::new(item)),
431 );
432 }
433 Some(runtime::VmValue::List(tree))
434 }
435 CpsReprRuntimeValue::Resumption(_)
436 | CpsReprRuntimeValue::Thunk(_)
437 | CpsReprRuntimeValue::Closure(_)
438 | CpsReprRuntimeValue::ScopeReturn { .. } => None,
442 }
443}
444
445fn eval_cps_repr_primitive(
446 op: typed_ir::PrimitiveOp,
447 args: Vec<CpsReprRuntimeValue>,
448) -> CpsReprEvalResult<CpsReprRuntimeValue> {
449 use typed_ir::PrimitiveOp;
450 match op {
451 PrimitiveOp::ListEmpty => {
452 if args.len() > 1 {
453 return Err(CpsReprEvalError::InvalidPrimitiveArity {
454 op,
455 expected: 1,
456 actual: args.len(),
457 });
458 }
459 Ok(CpsReprRuntimeValue::List(Rc::new(Vec::new())))
460 }
461 PrimitiveOp::ListSingleton => {
462 if args.len() != 1 {
463 return Err(CpsReprEvalError::InvalidPrimitiveArity {
464 op,
465 expected: 1,
466 actual: args.len(),
467 });
468 }
469 Ok(CpsReprRuntimeValue::List(Rc::new(vec![
470 args.into_iter().next().unwrap(),
471 ])))
472 }
473 PrimitiveOp::ListMerge => {
474 if args.len() != 2 {
475 return Err(CpsReprEvalError::InvalidPrimitiveArity {
476 op,
477 expected: 2,
478 actual: args.len(),
479 });
480 }
481 let mut iter = args.into_iter();
482 let left = iter.next().unwrap();
483 let right = iter.next().unwrap();
484 let mut merged = control_repr_list_items(op, left)?;
485 merged.extend(control_repr_list_items(op, right)?);
486 Ok(CpsReprRuntimeValue::List(Rc::new(merged)))
487 }
488 PrimitiveOp::ListLen => {
489 if args.len() != 1 {
490 return Err(CpsReprEvalError::InvalidPrimitiveArity {
491 op,
492 expected: 1,
493 actual: args.len(),
494 });
495 }
496 let items = control_repr_list_items(op, args.into_iter().next().unwrap())?;
497 Ok(CpsReprRuntimeValue::Plain(runtime::VmValue::Int(
498 items.len().to_string(),
499 )))
500 }
501 PrimitiveOp::ListIndex => {
502 if args.len() != 2 {
503 return Err(CpsReprEvalError::InvalidPrimitiveArity {
504 op,
505 expected: 2,
506 actual: args.len(),
507 });
508 }
509 let mut iter = args.into_iter();
510 let list = iter.next().unwrap();
511 let idx_val = iter.next().unwrap();
512 let items = control_repr_list_items(op, list)?;
513 let idx = cps_repr_value_to_usize(op, idx_val)?;
514 items
515 .into_iter()
516 .nth(idx)
517 .ok_or(CpsReprEvalError::UnsupportedPrimitive { op })
518 }
519 PrimitiveOp::ListIndexRangeRaw => {
520 if args.len() != 3 {
521 return Err(CpsReprEvalError::InvalidPrimitiveArity {
522 op,
523 expected: 3,
524 actual: args.len(),
525 });
526 }
527 let mut iter = args.into_iter();
528 let list = iter.next().unwrap();
529 let start_val = iter.next().unwrap();
530 let end_val = iter.next().unwrap();
531 let items = control_repr_list_items(op, list)?;
532 let start = cps_repr_value_to_usize(op, start_val)?;
533 let end = cps_repr_value_to_usize(op, end_val)?;
534 Ok(CpsReprRuntimeValue::List(Rc::new(
535 items.into_iter().skip(start).take(end - start).collect(),
536 )))
537 }
538 _ => {
539 let plain_args = args
540 .into_iter()
541 .map(cps_repr_value_to_vm)
542 .collect::<Option<Vec<_>>>()
543 .ok_or(CpsReprEvalError::UnsupportedPrimitive { op })?;
544 eval_primitive(op, &plain_args).map(cps_repr_value_from_vm)
545 }
546 }
547}
548
549fn cps_repr_value_to_usize(
550 op: typed_ir::PrimitiveOp,
551 value: CpsReprRuntimeValue,
552) -> CpsReprEvalResult<usize> {
553 match value {
554 CpsReprRuntimeValue::Plain(runtime::VmValue::Int(s)) => s
555 .parse::<usize>()
556 .map_err(|_| CpsReprEvalError::UnsupportedPrimitive { op }),
557 _ => Err(CpsReprEvalError::UnsupportedPrimitive { op }),
558 }
559}
560
561fn control_repr_list_items(
562 op: typed_ir::PrimitiveOp,
563 value: CpsReprRuntimeValue,
564) -> CpsReprEvalResult<Vec<CpsReprRuntimeValue>> {
565 match value {
566 CpsReprRuntimeValue::List(items) => Ok(items.as_ref().clone()),
567 CpsReprRuntimeValue::Plain(plain) => match plain {
568 runtime::VmValue::List(list) => Ok(list
569 .to_vec()
570 .into_iter()
571 .map(|item| cps_repr_value_from_vm((*item).clone()))
572 .collect()),
573 other => Err(CpsReprEvalError::PrimitiveTypeMismatch { op, value: other }),
574 },
575 _ => Err(CpsReprEvalError::UnsupportedPrimitive { op }),
576 }
577}
578
579fn function_by_name_repr<'a>(
580 module: &'a CpsReprModule,
581 name: &str,
582) -> CpsReprEvalResult<&'a CpsReprFunction> {
583 module
584 .functions
585 .iter()
586 .chain(module.roots.iter())
587 .find(|function| function.name == name)
588 .ok_or_else(|| CpsReprEvalError::MissingFunction {
589 name: name.to_string(),
590 })
591}
592
593pub fn analyze_cps_repr_values(module: &CpsReprModule) -> CpsReprValueAnalysis {
594 CpsReprValueAnalysis {
595 functions: module
596 .functions
597 .iter()
598 .chain(&module.roots)
599 .map(|function| (function.name.clone(), analyze_function_values(function)))
600 .collect(),
601 }
602}
603
604fn propagate_direct_call_argument_lanes(
605 module: &CpsReprModule,
606 analysis: &mut CpsReprAbiAnalysis,
607) -> bool {
608 let function_params = module
609 .functions
610 .iter()
611 .chain(&module.roots)
612 .map(|function| (function.name.as_str(), function.params.as_slice()))
613 .collect::<HashMap<_, _>>();
614 let mut changed = false;
615 for function in module.functions.iter().chain(&module.roots) {
616 for continuation in &function.continuations {
617 for stmt in &continuation.stmts {
618 let CpsStmt::DirectCall { target, args, .. } = stmt else {
619 continue;
620 };
621 let Some(params) = function_params.get(target.as_str()).copied() else {
622 continue;
623 };
624 for (param, arg) in params.iter().zip(args) {
625 let lane = analysis
626 .value_lane(&function.name, *arg)
627 .unwrap_or(CpsReprAbiLane::Unknown);
628 if let Some(target_analysis) = analysis.functions.get_mut(target) {
629 changed |= merge_abi_lane(&mut target_analysis.values, *param, lane);
630 }
631 }
632 }
633 }
634 }
635 changed
636}
637
638pub fn analyze_cps_repr_abi_lanes(module: &CpsReprModule) -> CpsReprAbiAnalysis {
639 let mut analysis = CpsReprAbiAnalysis {
640 functions: module
641 .functions
642 .iter()
643 .chain(&module.roots)
644 .map(|function| {
645 (
646 function.name.clone(),
647 CpsReprFunctionAbiAnalysis {
648 entry: function.entry,
649 values: HashMap::new(),
650 continuation_returns: HashMap::new(),
651 },
652 )
653 })
654 .collect(),
655 };
656 loop {
657 let mut changed = false;
658 for function in module.functions.iter().chain(&module.roots) {
659 let function_analysis = analyze_function_abi_lanes(function, &analysis);
660 if analysis.functions.get(&function.name) != Some(&function_analysis) {
661 analysis
662 .functions
663 .insert(function.name.clone(), function_analysis);
664 changed = true;
665 }
666 }
667 changed |= propagate_direct_call_argument_lanes(module, &mut analysis);
668 if !changed {
669 return analysis;
670 }
671 }
672}
673
674fn lower_function(function: &CpsFunction) -> CpsReprFunction {
675 CpsReprFunction {
676 name: function.name.clone(),
677 params: function.params.clone(),
678 entry: function.entry,
679 continuations: function
680 .continuations
681 .iter()
682 .map(lower_continuation)
683 .collect(),
684 handlers: function
685 .handlers
686 .iter()
687 .map(|handler| CpsReprHandler {
688 id: handler.id,
689 arms: handler
690 .arms
691 .iter()
692 .map(|arm| CpsReprHandlerArm {
693 effect: arm.effect.clone(),
694 entry: arm.entry,
695 })
696 .collect(),
697 })
698 .collect(),
699 }
700}
701
702fn lower_continuation(continuation: &CpsContinuation) -> CpsReprContinuation {
703 CpsReprContinuation {
704 id: continuation.id,
705 params: continuation.params.clone(),
706 environment: continuation
707 .captures
708 .iter()
709 .copied()
710 .enumerate()
711 .map(|(index, value)| CpsReprEnvironmentSlot { index, value })
712 .collect(),
713 shot_kind: continuation.shot_kind,
714 stmts: continuation.stmts.clone(),
715 terminator: continuation.terminator.clone(),
716 }
717}
718
719fn make_thunk_entries(function: &CpsReprFunction) -> HashMap<CpsValueId, CpsContinuationId> {
720 let mut entries = HashMap::new();
721 for continuation in &function.continuations {
722 for stmt in &continuation.stmts {
723 if let CpsStmt::MakeThunk { dest, entry } = stmt {
724 entries.insert(*dest, *entry);
725 }
726 }
727 }
728 entries
729}
730
731fn analyze_function_abi_lanes(
732 function: &CpsReprFunction,
733 module_analysis: &CpsReprAbiAnalysis,
734) -> CpsReprFunctionAbiAnalysis {
735 let mut values = HashMap::new();
736 let mut continuation_returns = HashMap::new();
737 let thunk_entries = make_thunk_entries(function);
738 for param in &function.params {
739 values.insert(
740 *param,
741 module_analysis
742 .value_lane(&function.name, *param)
743 .unwrap_or(CpsReprAbiLane::Unknown),
744 );
745 }
746 for handler in &function.handlers {
747 for arm in &handler.arms {
748 if let Some(entry) = continuation_by_id_opt(function, arm.entry) {
749 if let Some(payload) = entry.params.first() {
750 values.insert(*payload, CpsReprAbiLane::Unknown);
751 }
752 if let Some(resumption) = entry.params.get(1) {
753 values.insert(*resumption, CpsReprAbiLane::ResumptionPtr);
754 }
755 }
756 }
757 }
758
759 loop {
760 let mut changed = false;
761 for continuation in &function.continuations {
762 for param in &continuation.params {
763 values.entry(*param).or_insert(CpsReprAbiLane::Unknown);
764 }
765 for slot in &continuation.environment {
766 values.entry(slot.value).or_insert(CpsReprAbiLane::Unknown);
767 }
768 for stmt in &continuation.stmts {
769 let lane = match stmt {
770 CpsStmt::Literal { literal, .. } => literal_lane(literal),
771 CpsStmt::FreshGuard { .. }
772 | CpsStmt::PeekGuard { .. }
773 | CpsStmt::FindGuard { .. } => CpsReprAbiLane::ScalarI64,
774 CpsStmt::MakeThunk { .. } | CpsStmt::AddThunkBoundary { .. } => {
775 CpsReprAbiLane::ThunkPtr
776 }
777 CpsStmt::MakeClosure { .. } | CpsStmt::MakeRecursiveClosure { .. } => {
778 CpsReprAbiLane::ClosurePtr
779 }
780 CpsStmt::Tuple { .. }
781 | CpsStmt::Record { .. }
782 | CpsStmt::RecordWithoutFields { .. }
783 | CpsStmt::Variant { .. }
784 | CpsStmt::Select { .. }
785 | CpsStmt::SelectWithDefault { .. } => CpsReprAbiLane::RuntimeValuePtr,
786 CpsStmt::RecordHasField { .. } => CpsReprAbiLane::ScalarI64,
787 CpsStmt::TupleGet { .. } | CpsStmt::VariantPayload { .. } => {
788 CpsReprAbiLane::Unknown
789 }
790 CpsStmt::VariantTagEq { .. } => CpsReprAbiLane::ScalarI64,
791 CpsStmt::Primitive { op, .. } => primitive_result_lane(*op),
792 CpsStmt::DirectCall { target, .. } => module_analysis
793 .function_return_lane(target)
794 .unwrap_or(CpsReprAbiLane::Unknown),
795 CpsStmt::ApplyClosure { .. } => CpsReprAbiLane::Unknown,
796 CpsStmt::CloneContinuation { source, .. } => abi_lane(&values, *source),
797 CpsStmt::ForceThunk { thunk, .. } => thunk_entries
798 .get(thunk)
799 .and_then(|entry| continuation_returns.get(entry).copied())
800 .unwrap_or(CpsReprAbiLane::OpaqueI64),
801 CpsStmt::Resume { resumption, .. }
802 | CpsStmt::ResumeWithHandler { resumption, .. } => {
803 resumption_target_return_lane(
804 function,
805 &values,
806 &continuation_returns,
807 *resumption,
808 )
809 }
810 CpsStmt::InstallHandler { .. } | CpsStmt::UninstallHandler { .. } => continue,
811 };
812 if let Some(dest) = stmt_dest(stmt) {
813 if merge_abi_lane(&mut values, dest, lane) {
814 changed = true;
815 }
816 }
817 }
818 if propagate_terminator_argument_lanes(function, &mut values, &continuation.terminator)
819 {
820 changed = true;
821 }
822 let return_lane =
823 terminator_return_lane(function, &values, &continuation_returns, continuation);
824 if continuation_returns.get(&continuation.id) != Some(&return_lane) {
825 continuation_returns.insert(continuation.id, return_lane);
826 changed = true;
827 }
828 }
829 if !changed {
830 return CpsReprFunctionAbiAnalysis {
831 entry: function.entry,
832 values,
833 continuation_returns,
834 };
835 }
836 }
837}
838
839fn propagate_terminator_argument_lanes(
840 function: &CpsReprFunction,
841 values: &mut HashMap<CpsValueId, CpsReprAbiLane>,
842 terminator: &CpsTerminator,
843) -> bool {
844 match terminator {
845 CpsTerminator::Continue { target, args } => {
846 let Some(target) = continuation_by_id_opt(function, *target) else {
847 return false;
848 };
849 merge_param_lanes(values, &target.params, args)
850 }
851 CpsTerminator::Perform {
852 effect,
853 payload,
854 resume,
855 handler,
856 blocked,
857 } => {
858 let mut changed = false;
859 if let Some(blocked) = blocked {
860 changed |= merge_abi_lane(values, *blocked, CpsReprAbiLane::ScalarI64);
861 }
862 if let Some(arm) = handler_arm_for_effect(function, *handler, effect)
863 && let Some(entry) = continuation_by_id_opt(function, arm.entry)
864 && let Some(param) = entry.params.first()
865 {
866 let lane = abi_lane(values, *payload);
867 changed |= merge_abi_lane(values, *param, lane);
868 }
869 if let Some(resume) = continuation_by_id_opt(function, *resume)
870 && let Some(param) = resume.params.first()
871 {
872 changed |= merge_abi_lane(values, *param, abi_lane(values, *payload));
873 }
874 changed
875 }
876 CpsTerminator::EffectfulCall { args, resume, .. } => {
877 if let Some(resume) = continuation_by_id_opt(function, *resume)
879 && let Some(_param) = resume.params.first()
880 {
881 let _ = args;
884 }
885 false
886 }
887 CpsTerminator::EffectfulApply { resume, .. }
888 | CpsTerminator::EffectfulForce { resume, .. } => {
889 let _ = continuation_by_id_opt(function, *resume);
890 false
891 }
892 CpsTerminator::Return(_) | CpsTerminator::Branch { .. } => false,
893 }
894}
895
896fn merge_param_lanes(
897 values: &mut HashMap<CpsValueId, CpsReprAbiLane>,
898 params: &[CpsValueId],
899 args: &[CpsValueId],
900) -> bool {
901 let mut changed = false;
902 for (param, arg) in params.iter().zip(args) {
903 changed |= merge_abi_lane(values, *param, abi_lane(values, *arg));
904 }
905 changed
906}
907
908fn analyze_function_values(function: &CpsReprFunction) -> CpsReprFunctionValueAnalysis {
909 let mut values = HashMap::new();
910 let mut continuation_returns = HashMap::new();
911 let thunk_entries = make_thunk_entries(function);
912 for param in &function.params {
913 values.insert(*param, CpsReprValueKind::Plain);
914 }
915 for handler in &function.handlers {
916 for arm in &handler.arms {
917 if let Some(entry) = function
918 .continuations
919 .iter()
920 .find(|continuation| continuation.id == arm.entry)
921 {
922 if let Some(payload) = entry.params.first() {
923 values.insert(*payload, CpsReprValueKind::Plain);
924 }
925 if let Some(resumption) = entry.params.get(1) {
926 values.insert(*resumption, CpsReprValueKind::Resumption);
927 }
928 }
929 }
930 }
931
932 loop {
933 let mut changed = false;
934 for continuation in &function.continuations {
935 for param in &continuation.params {
936 values.entry(*param).or_insert(CpsReprValueKind::Unknown);
937 }
938 for slot in &continuation.environment {
939 values
940 .entry(slot.value)
941 .or_insert(CpsReprValueKind::Unknown);
942 }
943 for stmt in &continuation.stmts {
944 let kind = match stmt {
945 CpsStmt::Literal { .. }
946 | CpsStmt::FreshGuard { .. }
947 | CpsStmt::PeekGuard { .. }
948 | CpsStmt::FindGuard { .. }
949 | CpsStmt::MakeThunk { .. }
950 | CpsStmt::AddThunkBoundary { .. }
951 | CpsStmt::MakeClosure { .. }
952 | CpsStmt::MakeRecursiveClosure { .. }
953 | CpsStmt::Tuple { .. }
954 | CpsStmt::Record { .. }
955 | CpsStmt::RecordWithoutFields { .. }
956 | CpsStmt::Variant { .. }
957 | CpsStmt::Select { .. }
958 | CpsStmt::SelectWithDefault { .. }
959 | CpsStmt::RecordHasField { .. }
960 | CpsStmt::TupleGet { .. }
961 | CpsStmt::VariantTagEq { .. }
962 | CpsStmt::VariantPayload { .. }
963 | CpsStmt::Primitive { .. }
964 | CpsStmt::DirectCall { .. }
965 | CpsStmt::ApplyClosure { .. } => CpsReprValueKind::Plain,
966 CpsStmt::ForceThunk { thunk, .. } => thunk_entries
967 .get(thunk)
968 .and_then(|entry| continuation_returns.get(entry).copied())
969 .unwrap_or(CpsReprValueKind::Plain),
970 CpsStmt::CloneContinuation { source, .. } => value_kind(&values, *source),
971 CpsStmt::Resume { .. } | CpsStmt::ResumeWithHandler { .. } => {
972 CpsReprValueKind::Plain
973 }
974 CpsStmt::InstallHandler { .. } | CpsStmt::UninstallHandler { .. } => continue,
975 };
976 if let Some(dest) = stmt_dest(stmt) {
977 if merge_value_kind(&mut values, dest, kind) {
978 changed = true;
979 }
980 }
981 }
982 let return_kind = match &continuation.terminator {
983 CpsTerminator::Return(value) => value_kind(&values, *value),
984 CpsTerminator::Continue { target, .. } => continuation_returns
985 .get(target)
986 .copied()
987 .unwrap_or(CpsReprValueKind::Unknown),
988 CpsTerminator::Branch {
989 then_cont,
990 else_cont,
991 ..
992 } => continuation_returns
993 .get(then_cont)
994 .copied()
995 .unwrap_or(CpsReprValueKind::Unknown)
996 .merge(
997 continuation_returns
998 .get(else_cont)
999 .copied()
1000 .unwrap_or(CpsReprValueKind::Unknown),
1001 ),
1002 CpsTerminator::Perform {
1003 effect, handler, ..
1004 } => handler_arm_for_effect(function, *handler, effect)
1005 .and_then(|arm| continuation_returns.get(&arm.entry))
1006 .copied()
1007 .unwrap_or(CpsReprValueKind::Unknown),
1008 CpsTerminator::EffectfulCall { resume, .. }
1009 | CpsTerminator::EffectfulApply { resume, .. }
1010 | CpsTerminator::EffectfulForce { resume, .. } => continuation_returns
1011 .get(resume)
1012 .copied()
1013 .unwrap_or(CpsReprValueKind::Unknown),
1014 };
1015 if continuation_returns.get(&continuation.id) != Some(&return_kind) {
1016 continuation_returns.insert(continuation.id, return_kind);
1017 changed = true;
1018 }
1019 }
1020 if !changed {
1021 return CpsReprFunctionValueAnalysis {
1022 values,
1023 continuation_returns,
1024 };
1025 }
1026 }
1027}
1028
1029fn literal_lane(literal: &CpsLiteral) -> CpsReprAbiLane {
1030 match literal {
1031 CpsLiteral::Int(_) | CpsLiteral::Bool(_) | CpsLiteral::Unit => CpsReprAbiLane::ScalarI64,
1032 CpsLiteral::Float(_) => CpsReprAbiLane::NativeFloat,
1033 CpsLiteral::String(_) => CpsReprAbiLane::RuntimeValuePtr,
1034 }
1035}
1036
1037fn primitive_result_lane(op: typed_ir::PrimitiveOp) -> CpsReprAbiLane {
1038 use typed_ir::PrimitiveOp;
1039 match op {
1040 PrimitiveOp::BoolNot
1041 | PrimitiveOp::BoolEq
1042 | PrimitiveOp::IntEq
1043 | PrimitiveOp::IntLt
1044 | PrimitiveOp::IntLe
1045 | PrimitiveOp::IntGt
1046 | PrimitiveOp::IntGe
1047 | PrimitiveOp::IntAdd
1048 | PrimitiveOp::IntSub
1049 | PrimitiveOp::IntMul
1050 | PrimitiveOp::IntDiv
1051 | PrimitiveOp::ListLen
1052 | PrimitiveOp::StringLen
1053 | PrimitiveOp::BytesLen
1054 | PrimitiveOp::BytesIndex => CpsReprAbiLane::ScalarI64,
1055 PrimitiveOp::FloatEq
1056 | PrimitiveOp::FloatLt
1057 | PrimitiveOp::FloatLe
1058 | PrimitiveOp::FloatGt
1059 | PrimitiveOp::FloatGe
1060 | PrimitiveOp::StringEq
1061 | PrimitiveOp::BytesEq => CpsReprAbiLane::ScalarI64,
1062 PrimitiveOp::FloatAdd
1063 | PrimitiveOp::FloatSub
1064 | PrimitiveOp::FloatMul
1065 | PrimitiveOp::FloatDiv => CpsReprAbiLane::NativeFloat,
1066 PrimitiveOp::ListEmpty
1067 | PrimitiveOp::ListSingleton
1068 | PrimitiveOp::ListMerge
1069 | PrimitiveOp::ListIndexRange
1070 | PrimitiveOp::ListSplice
1071 | PrimitiveOp::ListIndexRangeRaw
1072 | PrimitiveOp::ListSpliceRaw
1073 | PrimitiveOp::ListViewRaw
1074 | PrimitiveOp::StringIndex
1075 | PrimitiveOp::StringIndexRange
1076 | PrimitiveOp::StringSplice
1077 | PrimitiveOp::StringIndexRangeRaw
1078 | PrimitiveOp::StringSpliceRaw
1079 | PrimitiveOp::StringConcat
1080 | PrimitiveOp::StringToBytes
1081 | PrimitiveOp::BytesConcat
1082 | PrimitiveOp::BytesIndexRange
1083 | PrimitiveOp::BytesToUtf8Raw
1084 | PrimitiveOp::BytesToPath
1085 | PrimitiveOp::PathToBytes
1086 | PrimitiveOp::IntToString
1087 | PrimitiveOp::IntToHex
1088 | PrimitiveOp::IntToUpperHex
1089 | PrimitiveOp::FloatToString
1090 | PrimitiveOp::BoolToString => CpsReprAbiLane::RuntimeValuePtr,
1091 PrimitiveOp::ListIndex => CpsReprAbiLane::Unknown,
1092 }
1093}
1094
1095fn terminator_return_lane(
1096 function: &CpsReprFunction,
1097 values: &HashMap<CpsValueId, CpsReprAbiLane>,
1098 continuation_returns: &HashMap<CpsContinuationId, CpsReprAbiLane>,
1099 continuation: &CpsReprContinuation,
1100) -> CpsReprAbiLane {
1101 match &continuation.terminator {
1102 CpsTerminator::Return(value) => abi_lane(values, *value),
1103 CpsTerminator::Continue { target, .. } => continuation_returns
1104 .get(target)
1105 .copied()
1106 .unwrap_or(CpsReprAbiLane::Unknown),
1107 CpsTerminator::Branch {
1108 then_cont,
1109 else_cont,
1110 ..
1111 } => continuation_returns
1112 .get(then_cont)
1113 .copied()
1114 .unwrap_or(CpsReprAbiLane::Unknown)
1115 .merge(
1116 continuation_returns
1117 .get(else_cont)
1118 .copied()
1119 .unwrap_or(CpsReprAbiLane::Unknown),
1120 ),
1121 CpsTerminator::Perform {
1122 effect,
1123 handler,
1124 resume,
1125 ..
1126 } => {
1127 let handler_return = handler_arm_for_effect(function, *handler, effect)
1128 .and_then(|arm| continuation_returns.get(&arm.entry))
1129 .copied();
1130 handler_return
1131 .or_else(|| {
1132 host_console_effect_kind(effect)
1133 .and_then(|_| continuation_returns.get(resume).copied())
1134 })
1135 .unwrap_or(CpsReprAbiLane::Unknown)
1136 }
1137 CpsTerminator::EffectfulCall { resume, .. }
1138 | CpsTerminator::EffectfulApply { resume, .. }
1139 | CpsTerminator::EffectfulForce { resume, .. } => continuation_returns
1140 .get(resume)
1141 .copied()
1142 .unwrap_or(CpsReprAbiLane::Unknown),
1143 }
1144}
1145
1146fn resumption_target_return_lane(
1147 function: &CpsReprFunction,
1148 values: &HashMap<CpsValueId, CpsReprAbiLane>,
1149 continuation_returns: &HashMap<CpsContinuationId, CpsReprAbiLane>,
1150 resumption: CpsValueId,
1151) -> CpsReprAbiLane {
1152 if abi_lane(values, resumption) != CpsReprAbiLane::ResumptionPtr {
1153 return CpsReprAbiLane::Unknown;
1154 }
1155 function
1156 .handlers
1157 .iter()
1158 .flat_map(|handler| handler.arms.iter().map(move |arm| (handler.id, arm)))
1159 .filter(|(_, arm)| {
1160 continuation_by_id_opt(function, arm.entry).and_then(|entry| entry.params.get(1))
1161 == Some(&resumption)
1162 })
1163 .filter_map(|(handler_id, arm)| {
1164 function
1165 .continuations
1166 .iter()
1167 .filter_map(|continuation| match continuation.terminator {
1168 CpsTerminator::Perform {
1169 handler: used,
1170 ref effect,
1171 resume,
1172 ..
1173 } if used == handler_id && effect_matches(&arm.effect, effect) => {
1174 continuation_returns.get(&resume).copied()
1175 }
1176 _ => None,
1177 })
1178 .fold(None, |current, lane| {
1179 Some(current.map_or(lane, |current: CpsReprAbiLane| current.merge(lane)))
1180 })
1181 })
1182 .fold(None, |current, lane| {
1183 Some(current.map_or(lane, |current: CpsReprAbiLane| current.merge(lane)))
1184 })
1185 .unwrap_or(CpsReprAbiLane::Unknown)
1186}
1187
1188fn handler_arm_for_effect<'a>(
1189 function: &'a CpsReprFunction,
1190 id: CpsHandlerId,
1191 effect: &typed_ir::Path,
1192) -> Option<&'a CpsReprHandlerArm> {
1193 function
1194 .handlers
1195 .iter()
1196 .find(|handler| handler.id == id)?
1197 .arms
1198 .iter()
1199 .find(|arm| effect_matches(&arm.effect, effect))
1200}
1201
1202#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1203enum HostConsoleEffect {
1204 OutWrite,
1205 ErrWrite,
1206 WarnWrite,
1207 DieDie,
1208}
1209
1210fn host_console_effect_kind(effect: &typed_ir::Path) -> Option<HostConsoleEffect> {
1211 let [std, module_seg, act_seg, operation] = effect.segments.as_slice() else {
1212 return None;
1213 };
1214 if std.0 != "std" || module_seg.0 != "out" {
1215 return None;
1216 }
1217 match (act_seg.0.as_str(), operation.0.as_str()) {
1218 ("out", "write") => Some(HostConsoleEffect::OutWrite),
1219 ("err", "write") => Some(HostConsoleEffect::ErrWrite),
1220 ("warn", "warn") => Some(HostConsoleEffect::WarnWrite),
1221 ("die", "die") => Some(HostConsoleEffect::DieDie),
1222 _ => None,
1223 }
1224}
1225
1226fn handler_arm_for_effect_in_module<'a>(
1227 module: &'a CpsReprModule,
1228 id: CpsHandlerId,
1229 effect: &typed_ir::Path,
1230) -> Option<(&'a CpsReprHandlerArm, &'a CpsReprFunction)> {
1231 for owner in module.functions.iter().chain(module.roots.iter()) {
1232 if let Some(arm) = handler_arm_for_effect(owner, id, effect) {
1233 return Some((arm, owner));
1234 }
1235 }
1236 None
1237}
1238
1239fn handler_arm_for_stack<'a>(
1240 module: &'a CpsReprModule,
1241 current_function: &'a CpsReprFunction,
1242 stack: &'a [CpsReprHandlerFrame],
1243 effect: &typed_ir::Path,
1244 blocked: Option<u64>,
1245) -> CpsReprEvalResult<(
1246 &'a CpsReprHandlerArm,
1247 &'a CpsReprHandlerFrame,
1248 Vec<CpsReprHandlerFrame>,
1249 &'a CpsReprFunction,
1250)> {
1251 for (index, frame) in stack.iter().enumerate().rev() {
1252 if blocked.is_some_and(|blocked| frame.guard_stack.iter().any(|entry| entry.id == blocked))
1253 {
1254 continue;
1255 }
1256 if let Some((arm, owner)) = handler_arm_for_effect_in_module(module, frame.handler, effect)
1257 {
1258 return Ok((arm, frame, stack[..index].to_vec(), owner));
1259 }
1260 }
1261 Err(CpsReprEvalError::MissingHandler {
1262 function: current_function.name.clone(),
1263 id: stack.last().expect("handler stack is non-empty").handler,
1264 })
1265}
1266
1267fn handler_stack_with_static(
1268 active_handlers: &[CpsReprHandlerFrame],
1269 fallback: CpsHandlerId,
1270 guard_stack: &[CpsReprGuardEntry],
1271) -> Vec<CpsReprHandlerFrame> {
1272 if active_handlers.is_empty() {
1273 vec![CpsReprHandlerFrame {
1274 prompt: fresh_repr_prompt(),
1275 handler: fallback,
1276 guard_stack: guard_stack.to_vec(),
1277 envs: Vec::new(),
1278 escape: REPR_EXIT_RWH_TARGET,
1279 escape_owner_function: String::new(),
1280 return_frame_threshold: 0,
1281 inherited: false,
1282 install_eval_id: REPR_SYNTHETIC_EVAL_ID,
1283 }]
1284 } else {
1285 active_handlers.to_vec()
1286 }
1287}
1288
1289#[allow(dead_code)]
1290fn handler_stack_with_pushed(
1291 active_handlers: &[CpsReprHandlerFrame],
1292 handler: CpsHandlerId,
1293 guard_stack: &[CpsReprGuardEntry],
1294 envs: Vec<CpsReprEvaluatedHandlerEnv>,
1295) -> Vec<CpsReprHandlerFrame> {
1296 let mut stack = active_handlers.to_vec();
1297 stack.push(CpsReprHandlerFrame {
1298 prompt: fresh_repr_prompt(),
1299 handler,
1300 guard_stack: guard_stack.to_vec(),
1301 envs,
1302 escape: REPR_EXIT_RWH_TARGET,
1303 escape_owner_function: String::new(),
1304 return_frame_threshold: 0,
1305 inherited: false,
1306 install_eval_id: REPR_SYNTHETIC_EVAL_ID,
1307 });
1308 stack
1309}
1310
1311fn capture_handler_envs(
1312 function: &CpsReprFunction,
1313 values: &HashMap<CpsValueId, CpsReprRuntimeValue>,
1314 envs: &[CpsHandlerEnv],
1315) -> CpsReprEvalResult<Vec<CpsReprEvaluatedHandlerEnv>> {
1316 envs.iter()
1317 .map(|env| {
1318 let mut values_by_id = Vec::new();
1319 for (index, value) in env.values.iter().enumerate() {
1320 let target = env.targets.get(index).copied().unwrap_or(*value);
1321 values_by_id.push((target, read_value(function, values, *value)?));
1322 }
1323 Ok(CpsReprEvaluatedHandlerEnv {
1324 entry: env.entry,
1325 values: values_by_id,
1326 })
1327 })
1328 .collect()
1329}
1330
1331fn overlay_handler_envs_in_frames_repr(
1332 function: &str,
1333 frames: &mut [CpsReprReturnFrame],
1334 handler: CpsHandlerId,
1335 updates: &[CpsReprEvaluatedHandlerEnv],
1336 remember_latest: bool,
1337) {
1338 for frame in frames {
1339 overlay_handler_envs_in_stack_repr(
1340 function,
1341 &mut frame.active_handlers,
1342 handler,
1343 updates,
1344 remember_latest,
1345 );
1346 }
1347}
1348
1349fn overlay_handler_envs_in_stack_repr(
1350 function: &str,
1351 stack: &mut [CpsReprHandlerFrame],
1352 handler: CpsHandlerId,
1353 updates: &[CpsReprEvaluatedHandlerEnv],
1354 remember_latest: bool,
1355) {
1356 if updates.is_empty() {
1357 return;
1358 }
1359 for frame in stack.iter_mut().filter(|frame| frame.handler == handler) {
1360 overlay_handler_envs_repr(&mut frame.envs, updates);
1361 }
1362 if remember_latest {
1363 remember_latest_repr_handler_envs(handler, updates);
1364 }
1365 push_cps_frame_trace_event(CpsFrameTraceEvent::HandlerEnvOverlay {
1366 layer: CpsFrameTraceLayer::CpsRepr,
1367 function: function.to_string(),
1368 handler: handler.0,
1369 entries: updates.iter().map(|env| env.entry.0).collect(),
1370 values: trace_cps_repr_handler_env_slots(updates),
1371 });
1372}
1373
1374fn overlay_handler_envs_repr(
1375 envs: &mut Vec<CpsReprEvaluatedHandlerEnv>,
1376 updates: &[CpsReprEvaluatedHandlerEnv],
1377) {
1378 for update in updates {
1379 let Some(existing) = envs.iter_mut().find(|env| env.entry == update.entry) else {
1380 envs.push(update.clone());
1381 continue;
1382 };
1383 for (target, value) in &update.values {
1384 if let Some((_, existing_value)) = existing
1385 .values
1386 .iter_mut()
1387 .find(|(existing_target, _)| existing_target == target)
1388 {
1389 *existing_value = value.clone();
1390 } else {
1391 existing.values.push((*target, value.clone()));
1392 }
1393 }
1394 }
1395}
1396
1397fn remember_latest_repr_handler_envs(
1398 handler: CpsHandlerId,
1399 updates: &[CpsReprEvaluatedHandlerEnv],
1400) {
1401 LATEST_REPR_HANDLER_ENVS.with(|latest| {
1402 let mut latest = latest.borrow_mut();
1403 for update in updates {
1404 for (target, value) in &update.values {
1405 if let Some(existing) = latest.iter_mut().find(|existing| {
1406 existing.handler == handler
1407 && existing.entry == update.entry
1408 && existing.target == *target
1409 }) {
1410 existing.value = value.clone();
1411 } else {
1412 latest.push(CpsReprLatestHandlerEnv {
1413 handler,
1414 entry: update.entry,
1415 target: *target,
1416 value: value.clone(),
1417 });
1418 }
1419 }
1420 }
1421 });
1422}
1423
1424fn latest_repr_handler_env_value(
1425 handler: CpsHandlerId,
1426 entry: CpsContinuationId,
1427 target: CpsValueId,
1428) -> Option<CpsReprRuntimeValue> {
1429 LATEST_REPR_HANDLER_ENVS.with(|latest| {
1430 latest
1431 .borrow()
1432 .iter()
1433 .rev()
1434 .find(|latest| {
1435 latest.handler == handler && latest.entry == entry && latest.target == target
1436 })
1437 .map(|latest| latest.value.clone())
1438 })
1439}
1440
1441fn values_with_handler_env(
1442 function: &str,
1443 mut values: HashMap<CpsValueId, CpsReprRuntimeValue>,
1444 frame: &CpsReprHandlerFrame,
1445 entry: CpsContinuationId,
1446) -> HashMap<CpsValueId, CpsReprRuntimeValue> {
1447 let Some(env) = frame.envs.iter().find(|env| env.entry == entry) else {
1448 return values;
1449 };
1450 let effective_values = env
1451 .values
1452 .iter()
1453 .map(|(id, value)| {
1454 (
1455 *id,
1456 latest_repr_handler_env_value(frame.handler, entry, *id)
1457 .unwrap_or_else(|| value.clone()),
1458 )
1459 })
1460 .collect::<Vec<_>>();
1461 push_cps_frame_trace_event(CpsFrameTraceEvent::HandlerEnvRead {
1462 layer: CpsFrameTraceLayer::CpsRepr,
1463 function: function.to_string(),
1464 handler: frame.handler.0,
1465 entry: entry.0,
1466 values: trace_cps_repr_handler_env_value_slots(&effective_values),
1467 });
1468 for (id, value) in effective_values {
1469 values.insert(id, value);
1470 }
1471 values
1472}
1473
1474fn trace_cps_repr_handler_env_slots(envs: &[CpsReprEvaluatedHandlerEnv]) -> Vec<CpsFrameTraceSlot> {
1475 envs.iter()
1476 .flat_map(|env| {
1477 env.values.iter().map(|(target, value)| CpsFrameTraceSlot {
1478 target: target.0,
1479 value: summarize_cps_repr_value(value),
1480 })
1481 })
1482 .collect()
1483}
1484
1485fn trace_cps_repr_handler_env_value_slots(
1486 values: &[(CpsValueId, CpsReprRuntimeValue)],
1487) -> Vec<CpsFrameTraceSlot> {
1488 values
1489 .iter()
1490 .map(|(target, value)| CpsFrameTraceSlot {
1491 target: target.0,
1492 value: summarize_cps_repr_value(value),
1493 })
1494 .collect()
1495}
1496
1497fn add_thunk_boundary_repr(
1498 value: CpsReprRuntimeValue,
1499 guard_id: u64,
1500 allowed: typed_ir::Type,
1501 active: bool,
1502) -> CpsReprRuntimeValue {
1503 let CpsReprRuntimeValue::Thunk(mut thunk) = value else {
1504 return value;
1505 };
1506 thunk.blocked.push(CpsReprBlockedEffect {
1507 guard_id,
1508 allowed,
1509 active,
1510 });
1511 CpsReprRuntimeValue::Thunk(thunk)
1512}
1513
1514fn active_blocked_for_thunk_repr(
1515 current: &[CpsReprBlockedEffect],
1516 thunk: &CpsReprThunk,
1517) -> Vec<CpsReprBlockedEffect> {
1518 let mut active = current.to_vec();
1519 active.extend(
1520 thunk
1521 .blocked
1522 .iter()
1523 .filter(|blocked| blocked.active)
1524 .cloned(),
1525 );
1526 active
1527}
1528
1529fn active_blocked_id_repr(effect: &typed_ir::Path, active: &[CpsReprBlockedEffect]) -> Option<u64> {
1530 active
1531 .iter()
1532 .rev()
1533 .find(|blocked| !effect_allowed_by_type_repr(&blocked.allowed, effect))
1534 .map(|blocked| blocked.guard_id)
1535}
1536
1537fn effect_allowed_by_type_repr(allowed: &typed_ir::Type, effect: &typed_ir::Path) -> bool {
1538 match allowed {
1539 typed_ir::Type::Any => true,
1540 typed_ir::Type::Never => false,
1541 typed_ir::Type::Named { path, .. } => effect_path_matches_allowed_repr(path, effect),
1542 typed_ir::Type::Row { items, tail } => {
1543 items
1544 .iter()
1545 .any(|item| effect_allowed_by_type_repr(item, effect))
1546 || matches!(tail.as_ref(), typed_ir::Type::Any)
1547 }
1548 _ => false,
1549 }
1550}
1551
1552fn effect_path_matches_allowed_repr(allowed: &typed_ir::Path, effect: &typed_ir::Path) -> bool {
1553 if effect.segments.starts_with(&allowed.segments) {
1554 return true;
1555 }
1556 if allowed.segments.len() > 1
1557 && effect.segments.len() == allowed.segments.len()
1558 && effect.segments[..effect.segments.len() - 1]
1559 == allowed.segments[..allowed.segments.len() - 1]
1560 && effect_segment_matches_allowed_repr(
1561 &allowed.segments[allowed.segments.len() - 1],
1562 &effect.segments[effect.segments.len() - 1],
1563 )
1564 {
1565 return true;
1566 }
1567 effect
1568 .segments
1569 .iter()
1570 .enumerate()
1571 .skip(1)
1572 .any(|(index, _)| effect.segments[index..].starts_with(&allowed.segments))
1573}
1574
1575fn effect_segment_matches_allowed_repr(allowed: &typed_ir::Name, effect: &typed_ir::Name) -> bool {
1576 allowed == effect
1577 || effect
1578 .0
1579 .strip_suffix("#effect")
1580 .is_some_and(|base| base == allowed.0)
1581}
1582
1583fn effect_matches(expected: &typed_ir::Path, actual: &typed_ir::Path) -> bool {
1584 effect_path_matches_allowed_repr(expected, actual)
1585}
1586
1587fn abi_lane(values: &HashMap<CpsValueId, CpsReprAbiLane>, value: CpsValueId) -> CpsReprAbiLane {
1588 values
1589 .get(&value)
1590 .copied()
1591 .unwrap_or(CpsReprAbiLane::Unknown)
1592}
1593
1594fn merge_abi_lane(
1595 values: &mut HashMap<CpsValueId, CpsReprAbiLane>,
1596 value: CpsValueId,
1597 lane: CpsReprAbiLane,
1598) -> bool {
1599 let merged = values
1600 .get(&value)
1601 .copied()
1602 .map(|current| current.merge(lane))
1603 .unwrap_or(lane);
1604 if values.get(&value) == Some(&merged) {
1605 false
1606 } else {
1607 values.insert(value, merged);
1608 true
1609 }
1610}
1611
1612fn stmt_dest(stmt: &CpsStmt) -> Option<CpsValueId> {
1613 match stmt {
1614 CpsStmt::Literal { dest, .. }
1615 | CpsStmt::FreshGuard { dest, .. }
1616 | CpsStmt::PeekGuard { dest }
1617 | CpsStmt::FindGuard { dest, .. }
1618 | CpsStmt::MakeThunk { dest, .. }
1619 | CpsStmt::AddThunkBoundary { dest, .. }
1620 | CpsStmt::MakeClosure { dest, .. }
1621 | CpsStmt::MakeRecursiveClosure { dest, .. }
1622 | CpsStmt::ForceThunk { dest, .. }
1623 | CpsStmt::Tuple { dest, .. }
1624 | CpsStmt::Record { dest, .. }
1625 | CpsStmt::RecordWithoutFields { dest, .. }
1626 | CpsStmt::Variant { dest, .. }
1627 | CpsStmt::Select { dest, .. }
1628 | CpsStmt::SelectWithDefault { dest, .. }
1629 | CpsStmt::RecordHasField { dest, .. }
1630 | CpsStmt::TupleGet { dest, .. }
1631 | CpsStmt::VariantTagEq { dest, .. }
1632 | CpsStmt::VariantPayload { dest, .. }
1633 | CpsStmt::Primitive { dest, .. }
1634 | CpsStmt::DirectCall { dest, .. }
1635 | CpsStmt::ApplyClosure { dest, .. }
1636 | CpsStmt::CloneContinuation { dest, .. }
1637 | CpsStmt::Resume { dest, .. }
1638 | CpsStmt::ResumeWithHandler { dest, .. } => Some(*dest),
1639 CpsStmt::InstallHandler { .. } | CpsStmt::UninstallHandler { .. } => None,
1640 }
1641}
1642
1643fn value_kind(
1644 values: &HashMap<CpsValueId, CpsReprValueKind>,
1645 value: CpsValueId,
1646) -> CpsReprValueKind {
1647 values
1648 .get(&value)
1649 .copied()
1650 .unwrap_or(CpsReprValueKind::Unknown)
1651}
1652
1653fn merge_value_kind(
1654 values: &mut HashMap<CpsValueId, CpsReprValueKind>,
1655 value: CpsValueId,
1656 kind: CpsReprValueKind,
1657) -> bool {
1658 let merged = values
1659 .get(&value)
1660 .copied()
1661 .map(|current| current.merge(kind))
1662 .unwrap_or(kind);
1663 if values.get(&value) == Some(&merged) {
1664 false
1665 } else {
1666 values.insert(value, merged);
1667 true
1668 }
1669}
1670
1671fn eval_function(
1672 module: &CpsReprModule,
1673 function: &CpsReprFunction,
1674 args: Vec<runtime::VmValue>,
1675) -> CpsReprEvalResult<CpsReprRuntimeValue> {
1676 eval_function_with_context(
1677 module,
1678 function,
1679 args.into_iter().map(CpsReprRuntimeValue::Plain).collect(),
1680 Vec::new(),
1681 Vec::new(),
1682 Vec::new(),
1683 Vec::new(),
1684 0,
1685 )
1686}
1687
1688fn eval_function_with_context(
1689 module: &CpsReprModule,
1690 function: &CpsReprFunction,
1691 args: Vec<CpsReprRuntimeValue>,
1692 active_handlers: Vec<CpsReprHandlerFrame>,
1693 guard_stack: Vec<CpsReprGuardEntry>,
1694 return_frames: Vec<CpsReprReturnFrame>,
1695 active_blocked: Vec<CpsReprBlockedEffect>,
1696 initial_frame_count: usize,
1697) -> CpsReprEvalResult<CpsReprRuntimeValue> {
1698 if function.params.len() != args.len() {
1699 return Err(CpsReprEvalError::FunctionArgumentMismatch {
1700 function: function.name.clone(),
1701 expected: function.params.len(),
1702 actual: args.len(),
1703 });
1704 }
1705 eval_continuations(
1706 module,
1707 function,
1708 function.entry,
1709 args,
1710 HashMap::new(),
1711 active_handlers,
1712 guard_stack,
1713 return_frames,
1714 active_blocked,
1715 initial_frame_count,
1716 )
1717}
1718
1719fn trace_repr_enabled() -> bool {
1720 std::env::var_os("YULANG_CPS_REPR_TRACE_FRAMES").is_some()
1721}
1722
1723fn trace_repr(label: &str, message: impl std::fmt::Display) {
1724 if trace_repr_enabled() {
1725 eprintln!("[cps-repr-trace] {label}: {message}");
1726 }
1727}
1728
1729fn summarize_repr_handler_stack(stack: &[CpsReprHandlerFrame]) -> String {
1730 let items = stack
1731 .iter()
1732 .map(|frame| {
1733 format!(
1734 "(p={},inh={},eval={},owner={},thr={})",
1735 frame.prompt.0,
1736 if frame.inherited { "T" } else { "F" },
1737 frame.install_eval_id.0,
1738 frame.escape_owner_function,
1739 frame.return_frame_threshold
1740 )
1741 })
1742 .collect::<Vec<_>>();
1743 format!("[{}]", items.join(","))
1744}
1745
1746fn summarize_cps_repr_value(value: &CpsReprRuntimeValue) -> String {
1747 match value {
1748 CpsReprRuntimeValue::Plain(value) => format!("Plain({value:?})"),
1749 CpsReprRuntimeValue::Resumption(resumption) => format!(
1750 "Resumption(owner={}, target={:?})",
1751 resumption.owner_function, resumption.target,
1752 ),
1753 CpsReprRuntimeValue::Thunk(thunk) => {
1754 format!(
1755 "Thunk(owner={}, entry={:?})",
1756 thunk.owner_function, thunk.entry
1757 )
1758 }
1759 CpsReprRuntimeValue::Closure(closure) => format!(
1760 "Closure(owner={}, entry={:?}, recursive_self={:?})",
1761 closure.owner_function, closure.entry, closure.recursive_self,
1762 ),
1763 CpsReprRuntimeValue::List(items) => format!("List(len={})", items.len()),
1764 CpsReprRuntimeValue::Tuple(items) => format!("Tuple(len={})", items.len()),
1765 CpsReprRuntimeValue::Record(fields) => format!("Record(len={})", fields.len()),
1766 CpsReprRuntimeValue::Variant { tag, value } => {
1767 format!("Variant(tag={tag:?}, has_value={})", value.is_some())
1768 }
1769 CpsReprRuntimeValue::Aborted(value) => {
1770 format!("Aborted({})", summarize_cps_repr_value(value))
1771 }
1772 CpsReprRuntimeValue::RoutedJump(jump) => format!(
1773 "RoutedJump(owner={}, target={:?}, value={}, threshold={})",
1774 jump.owner_function,
1775 jump.target,
1776 summarize_cps_repr_value(&jump.value),
1777 jump.return_frame_threshold,
1778 ),
1779 CpsReprRuntimeValue::ScopeReturn {
1780 prompt,
1781 target,
1782 value,
1783 } => format!(
1784 "ScopeReturn(prompt={}, target={:?}, value={})",
1785 prompt.0,
1786 target,
1787 summarize_cps_repr_value(value)
1788 ),
1789 }
1790}
1791
1792fn eval_continuations(
1796 module: &CpsReprModule,
1797 function: &CpsReprFunction,
1798 entry: CpsContinuationId,
1799 args: Vec<CpsReprRuntimeValue>,
1800 values: HashMap<CpsValueId, CpsReprRuntimeValue>,
1801 active_handlers: Vec<CpsReprHandlerFrame>,
1802 guard_stack: Vec<CpsReprGuardEntry>,
1803 return_frames: Vec<CpsReprReturnFrame>,
1804 active_blocked: Vec<CpsReprBlockedEffect>,
1805 initial_frame_count: usize,
1806) -> CpsReprEvalResult<CpsReprRuntimeValue> {
1807 let current_eval_id = fresh_repr_eval_id();
1808 resume_continuation(
1809 module,
1810 function,
1811 entry,
1812 args,
1813 values,
1814 into_inherited_repr(active_handlers),
1815 guard_stack,
1816 return_frames,
1817 active_blocked,
1818 initial_frame_count,
1819 current_eval_id,
1820 )
1821}
1822
1823fn into_inherited_repr(mut handlers: Vec<CpsReprHandlerFrame>) -> Vec<CpsReprHandlerFrame> {
1827 for frame in &mut handlers {
1828 frame.inherited = true;
1829 }
1830 handlers
1831}
1832
1833enum ScopeReturnActionRepr {
1835 Value(CpsReprRuntimeValue),
1836 JumpOrExit {
1837 target: CpsContinuationId,
1838 value: CpsReprRuntimeValue,
1839 return_frame_threshold: usize,
1840 },
1841 Propagate(CpsReprRuntimeValue),
1842}
1843
1844fn handle_scope_return_repr(
1846 result: CpsReprRuntimeValue,
1847 active_handlers: &mut Vec<CpsReprHandlerFrame>,
1848 _return_frames: &[CpsReprReturnFrame],
1849 current_function: &str,
1850 current_eval_id: CpsReprEvalId,
1851) -> ScopeReturnActionRepr {
1852 match result {
1853 CpsReprRuntimeValue::ScopeReturn {
1854 prompt,
1855 target,
1856 value,
1857 } => {
1858 if let Some(index) = active_handlers.iter().rposition(|frame| {
1859 frame.prompt == prompt && frame.install_eval_id == current_eval_id
1860 }) {
1861 let frame = &active_handlers[index];
1862 let frame_owner_match = target == REPR_EXIT_RWH_TARGET
1863 || frame.escape_owner_function == current_function;
1864 let truncate_at = frame.return_frame_threshold;
1865 if !frame_owner_match {
1866 return ScopeReturnActionRepr::Propagate(CpsReprRuntimeValue::ScopeReturn {
1867 prompt,
1868 target,
1869 value,
1870 });
1871 }
1872 active_handlers.truncate(index);
1873 ScopeReturnActionRepr::JumpOrExit {
1874 target,
1875 value: *value,
1876 return_frame_threshold: truncate_at,
1877 }
1878 } else {
1879 ScopeReturnActionRepr::Propagate(CpsReprRuntimeValue::ScopeReturn {
1880 prompt,
1881 target,
1882 value,
1883 })
1884 }
1885 }
1886 other => ScopeReturnActionRepr::Value(other),
1887 }
1888}
1889
1890fn resume_continuation(
1894 module: &CpsReprModule,
1895 function: &CpsReprFunction,
1896 entry: CpsContinuationId,
1897 mut args: Vec<CpsReprRuntimeValue>,
1898 mut values: HashMap<CpsValueId, CpsReprRuntimeValue>,
1899 active_handlers: Vec<CpsReprHandlerFrame>,
1900 guard_stack: Vec<CpsReprGuardEntry>,
1901 return_frames: Vec<CpsReprReturnFrame>,
1902 active_blocked: Vec<CpsReprBlockedEffect>,
1903 initial_frame_count: usize,
1904 current_eval_id: CpsReprEvalId,
1905) -> CpsReprEvalResult<CpsReprRuntimeValue> {
1906 let mut current = entry;
1907 let mut guard_stack = guard_stack;
1908 let mut active_handlers = active_handlers;
1909 let mut return_frames = return_frames;
1910 let active_blocked = active_blocked;
1911 let mut next_guard_id = guard_stack
1912 .iter()
1913 .map(|entry| entry.id)
1914 .max()
1915 .map_or(0, |id| id + 1);
1916 macro_rules! dispatch_scope_return_repr {
1918 ($cont:lifetime, $result:expr, $dest:expr) => {{
1919 let result = resolve_routed_jump_repr(module, $result, &return_frames)?;
1920 if matches!(
1921 result,
1922 CpsReprRuntimeValue::Aborted(_) | CpsReprRuntimeValue::RoutedJump(_)
1923 ) {
1924 return Ok(result);
1925 }
1926 match handle_scope_return_repr(
1927 result,
1928 &mut active_handlers,
1929 &return_frames,
1930 &function.name,
1931 current_eval_id,
1932 ) {
1933 ScopeReturnActionRepr::Value(v) => {
1934 values.insert(*$dest, v);
1935 }
1936 ScopeReturnActionRepr::JumpOrExit { target, value, return_frame_threshold }
1937 if target == REPR_EXIT_RWH_TARGET =>
1938 {
1939 if return_frames.len() > return_frame_threshold {
1940 return_frames.truncate(return_frame_threshold);
1941 }
1942 values.insert(*$dest, value);
1943 }
1944 ScopeReturnActionRepr::JumpOrExit { target, value, return_frame_threshold } => {
1945 if return_frames.len() > return_frame_threshold {
1946 return_frames.truncate(return_frame_threshold);
1947 }
1948 current = target;
1949 args = vec![value];
1950 continue $cont;
1951 }
1952 ScopeReturnActionRepr::Propagate(v) => {
1953 if let Some(routed) =
1954 try_route_scope_return_through_return_frames_repr(
1955 module,
1956 &v,
1957 &return_frames,
1958 initial_frame_count,
1959 )?
1960 {
1961 return Ok(routed);
1962 }
1963 return Ok(v);
1964 }
1965 }
1966 }};
1967 }
1968 'cont: loop {
1969 let continuation = continuation_by_id(function, current)?;
1970 if continuation.params.len() != args.len() {
1971 return Err(CpsReprEvalError::ContinuationArgumentMismatch {
1972 function: function.name.clone(),
1973 id: continuation.id,
1974 expected: continuation.params.len(),
1975 actual: args.len(),
1976 });
1977 }
1978 for (param, value) in continuation.params.iter().copied().zip(args) {
1979 values.insert(param, value);
1980 }
1981 args = Vec::new();
1982
1983 for stmt in &continuation.stmts {
1984 match stmt {
1985 CpsStmt::Literal { dest, literal } => {
1986 values.insert(*dest, CpsReprRuntimeValue::Plain(eval_literal(literal)));
1987 }
1988 CpsStmt::FreshGuard { dest, var } => {
1989 let id = next_guard_id;
1990 next_guard_id += 1;
1991 guard_stack.push(CpsReprGuardEntry { var: *var, id });
1992 values.insert(
1993 *dest,
1994 CpsReprRuntimeValue::Plain(runtime::VmValue::EffectId(id)),
1995 );
1996 }
1997 CpsStmt::PeekGuard { dest } => {
1998 let id = guard_stack.last().map(|entry| entry.id).ok_or_else(|| {
1999 CpsReprEvalError::MissingGuard {
2000 function: function.name.clone(),
2001 }
2002 })?;
2003 values.insert(
2004 *dest,
2005 CpsReprRuntimeValue::Plain(runtime::VmValue::EffectId(id)),
2006 );
2007 }
2008 CpsStmt::FindGuard { dest, guard } => {
2009 let guard = read_effect_id(function, &values, *guard)?;
2010 values.insert(
2011 *dest,
2012 CpsReprRuntimeValue::Plain(runtime::VmValue::Bool(
2013 guard_stack.iter().any(|entry| entry.id == guard),
2014 )),
2015 );
2016 }
2017 CpsStmt::MakeThunk { dest, entry } => {
2018 values.insert(
2019 *dest,
2020 CpsReprRuntimeValue::Thunk(CpsReprThunk {
2021 owner_function: function.name.clone(),
2022 entry: *entry,
2023 values: values.clone(),
2024 handlers: active_handlers.clone(),
2025 guard_stack: guard_stack.clone(),
2026 blocked: Vec::new(),
2027 }),
2028 );
2029 }
2030 CpsStmt::AddThunkBoundary {
2031 dest,
2032 thunk,
2033 guard,
2034 allowed,
2035 active,
2036 } => {
2037 let guard = read_effect_id(function, &values, *guard)?;
2038 let value = add_thunk_boundary_repr(
2039 read_value(function, &values, *thunk)?,
2040 guard,
2041 allowed.clone(),
2042 *active,
2043 );
2044 values.insert(*dest, value);
2045 }
2046 CpsStmt::MakeClosure { dest, entry } => {
2047 values.insert(
2048 *dest,
2049 CpsReprRuntimeValue::Closure(CpsReprClosure {
2050 owner_function: function.name.clone(),
2051 entry: *entry,
2052 values: values.clone(),
2053 recursive_self: None,
2054 }),
2055 );
2056 }
2057 CpsStmt::MakeRecursiveClosure { dest, entry } => {
2058 let closure = CpsReprRuntimeValue::Closure(CpsReprClosure {
2059 owner_function: function.name.clone(),
2060 entry: *entry,
2061 values: values.clone(),
2062 recursive_self: Some(*dest),
2063 });
2064 values.insert(*dest, closure);
2065 }
2066 CpsStmt::ForceThunk { dest, thunk } => {
2067 let mut result = read_value(function, &values, *thunk)?;
2073 loop {
2074 match result {
2075 CpsReprRuntimeValue::Thunk(thunk) => {
2076 let handlers = if !active_handlers.is_empty() {
2077 active_handlers.clone()
2078 } else {
2079 thunk.handlers.clone()
2080 };
2081 let guards = if !guard_stack.is_empty() {
2082 guard_stack.clone()
2083 } else {
2084 thunk.guard_stack.clone()
2085 };
2086 let owner = function_by_name_repr(module, &thunk.owner_function)?;
2087 let inherited = return_frames.len();
2088 result = eval_continuations(
2089 module,
2090 owner,
2091 thunk.entry,
2092 Vec::new(),
2093 thunk.values.clone(),
2094 handlers,
2095 guards,
2096 return_frames.clone(),
2097 active_blocked_for_thunk_repr(&active_blocked, &thunk),
2098 inherited,
2099 )?;
2100 if matches!(result, CpsReprRuntimeValue::ScopeReturn { .. }) {
2101 break;
2102 }
2103 }
2104 _ => break,
2105 }
2106 }
2107 if matches!(result, CpsReprRuntimeValue::Aborted(_)) {
2108 return Ok(result);
2109 }
2110 dispatch_scope_return_repr!('cont, result, dest);
2111 }
2112 CpsStmt::InstallHandler {
2113 handler,
2114 envs,
2115 value,
2116 escape,
2117 } => {
2118 let envs = capture_handler_envs(function, &values, envs)?;
2119 let prompt = fresh_repr_prompt();
2120 let threshold = return_frames.len();
2121 active_handlers.push(CpsReprHandlerFrame {
2122 prompt,
2123 handler: *handler,
2124 guard_stack: guard_stack.clone(),
2125 envs,
2126 escape: *escape,
2127 escape_owner_function: function.name.clone(),
2128 return_frame_threshold: threshold,
2129 inherited: false,
2130 install_eval_id: current_eval_id,
2131 });
2132 return_frames.push(CpsReprReturnFrame {
2133 prompt_exit: Some(CpsReprPromptExitFrame { prompt }),
2134 owner_function: function.name.clone(),
2135 continuation: *value,
2136 values: Rc::new(values.clone()),
2137 active_handlers: active_handlers.clone(),
2138 guard_stack: guard_stack.clone(),
2139 active_blocked: active_blocked.clone(),
2140 owner_initial_frame_count: initial_frame_count,
2141 owner_eval_id: current_eval_id,
2142 });
2143 trace_repr(
2144 "InstallHandler",
2145 format!(
2146 "fn={} eval={} cont={:?} handler={:?} prompt={} value={:?} escape={:?} threshold={} handlers.now={}",
2147 function.name,
2148 current_eval_id.0,
2149 continuation.id,
2150 handler,
2151 prompt.0,
2152 value,
2153 escape,
2154 threshold,
2155 active_handlers.len()
2156 ),
2157 );
2158 }
2159 CpsStmt::UninstallHandler { handler } => {
2160 if let Some(pos) = active_handlers
2161 .iter()
2162 .rposition(|frame| frame.handler == *handler)
2163 {
2164 active_handlers.remove(pos);
2165 }
2166 }
2167 CpsStmt::Tuple { dest, items } => {
2168 let items = items
2169 .iter()
2170 .map(|id| read_value(function, &values, *id))
2171 .collect::<CpsReprEvalResult<Vec<_>>>()?;
2172 values.insert(*dest, CpsReprRuntimeValue::Tuple(items));
2173 }
2174 CpsStmt::Record { dest, base, fields } => {
2175 let mut record = match base {
2176 Some(base) => match read_value(function, &values, *base)? {
2177 CpsReprRuntimeValue::Record(fields) => fields,
2178 CpsReprRuntimeValue::Plain(runtime::VmValue::Record(fields)) => fields
2179 .into_iter()
2180 .map(|(name, value)| (name, CpsReprRuntimeValue::Plain(value)))
2181 .collect(),
2182 value => {
2183 return Err(CpsReprEvalError::ExpectedRecord {
2184 function: function.name.clone(),
2185 value: into_plain_value(function, *base, value)?,
2186 });
2187 }
2188 },
2189 None => BTreeMap::new(),
2190 };
2191 for field in fields {
2192 record.insert(
2193 field.name.clone(),
2194 read_value(function, &values, field.value)?,
2195 );
2196 }
2197 values.insert(*dest, CpsReprRuntimeValue::Record(record));
2198 }
2199 CpsStmt::RecordWithoutFields { dest, base, fields } => {
2200 let mut record = match read_value(function, &values, *base)? {
2201 CpsReprRuntimeValue::Record(fields) => fields,
2202 CpsReprRuntimeValue::Plain(runtime::VmValue::Record(fields)) => fields
2203 .into_iter()
2204 .map(|(name, value)| (name, CpsReprRuntimeValue::Plain(value)))
2205 .collect(),
2206 value => {
2207 return Err(CpsReprEvalError::ExpectedRecord {
2208 function: function.name.clone(),
2209 value: into_plain_value(function, *base, value)?,
2210 });
2211 }
2212 };
2213 for field in fields {
2214 record.remove(field);
2215 }
2216 values.insert(*dest, CpsReprRuntimeValue::Record(record));
2217 }
2218 CpsStmt::Variant { dest, tag, value } => {
2219 let value = value
2220 .map(|id| read_value(function, &values, id))
2221 .transpose()?
2222 .map(Box::new);
2223 values.insert(
2224 *dest,
2225 CpsReprRuntimeValue::Variant {
2226 tag: tag.clone(),
2227 value,
2228 },
2229 );
2230 }
2231 CpsStmt::Select { dest, base, field } => {
2232 let value = match read_value(function, &values, *base)? {
2233 CpsReprRuntimeValue::Record(fields) => fields
2234 .get(field)
2235 .cloned()
2236 .ok_or_else(|| CpsReprEvalError::MissingRecordField {
2237 function: function.name.clone(),
2238 field: field.clone(),
2239 })?,
2240 CpsReprRuntimeValue::Plain(runtime::VmValue::Record(fields)) => fields
2241 .get(field)
2242 .cloned()
2243 .map(CpsReprRuntimeValue::Plain)
2244 .ok_or_else(|| CpsReprEvalError::MissingRecordField {
2245 function: function.name.clone(),
2246 field: field.clone(),
2247 })?,
2248 value => {
2249 return Err(CpsReprEvalError::ExpectedRecord {
2250 function: function.name.clone(),
2251 value: into_plain_value(function, *base, value)?,
2252 });
2253 }
2254 };
2255 values.insert(*dest, value);
2256 }
2257 CpsStmt::TupleGet { dest, tuple, index } => {
2258 let value = match read_value(function, &values, *tuple)? {
2259 CpsReprRuntimeValue::Tuple(items) => items
2260 .get(*index)
2261 .cloned()
2262 .ok_or_else(|| CpsReprEvalError::MissingRecordField {
2263 function: function.name.clone(),
2264 field: typed_ir::Name(index.to_string()),
2265 })?,
2266 CpsReprRuntimeValue::Plain(runtime::VmValue::Tuple(items)) => {
2267 cps_repr_value_from_vm(items.get(*index).cloned().ok_or_else(|| {
2268 CpsReprEvalError::MissingRecordField {
2269 function: function.name.clone(),
2270 field: typed_ir::Name(index.to_string()),
2271 }
2272 })?)
2273 }
2274 other => other,
2275 };
2276 values.insert(*dest, value);
2277 }
2278 CpsStmt::SelectWithDefault {
2279 dest,
2280 base,
2281 field,
2282 default,
2283 } => {
2284 let default = read_value(function, &values, *default)?;
2285 let value = match read_value(function, &values, *base)? {
2286 CpsReprRuntimeValue::Record(fields) => fields.get(field).cloned(),
2287 CpsReprRuntimeValue::Plain(runtime::VmValue::Record(fields)) => {
2288 fields.get(field).cloned().map(CpsReprRuntimeValue::Plain)
2289 }
2290 value => {
2291 return Err(CpsReprEvalError::ExpectedRecord {
2292 function: function.name.clone(),
2293 value: into_plain_value(function, *base, value)?,
2294 });
2295 }
2296 }
2297 .unwrap_or(default);
2298 values.insert(*dest, value);
2299 }
2300 CpsStmt::RecordHasField { dest, base, field } => {
2301 let has_field = match read_value(function, &values, *base)? {
2302 CpsReprRuntimeValue::Record(fields) => fields.contains_key(field),
2303 CpsReprRuntimeValue::Plain(runtime::VmValue::Record(fields)) => {
2304 fields.contains_key(field)
2305 }
2306 value => {
2307 return Err(CpsReprEvalError::ExpectedRecord {
2308 function: function.name.clone(),
2309 value: into_plain_value(function, *base, value)?,
2310 });
2311 }
2312 };
2313 values.insert(
2314 *dest,
2315 CpsReprRuntimeValue::Plain(runtime::VmValue::Bool(has_field)),
2316 );
2317 }
2318 CpsStmt::VariantTagEq { dest, variant, tag } => {
2319 let matches = match read_value(function, &values, *variant)? {
2320 CpsReprRuntimeValue::Variant { tag: actual, .. } => actual == *tag,
2321 CpsReprRuntimeValue::Plain(runtime::VmValue::Variant {
2322 tag: actual,
2323 ..
2324 }) => actual == *tag,
2325 _ => false,
2326 };
2327 values.insert(
2328 *dest,
2329 CpsReprRuntimeValue::Plain(runtime::VmValue::Bool(matches)),
2330 );
2331 }
2332 CpsStmt::VariantPayload { dest, variant } => {
2333 let value = match read_value(function, &values, *variant)? {
2334 CpsReprRuntimeValue::Variant {
2335 value: Some(value), ..
2336 } => *value,
2337 CpsReprRuntimeValue::Variant { value: None, .. } => {
2338 CpsReprRuntimeValue::Plain(runtime::VmValue::Unit)
2339 }
2340 CpsReprRuntimeValue::Plain(runtime::VmValue::Variant {
2341 value: Some(value),
2342 ..
2343 }) => cps_repr_value_from_vm(*value),
2344 CpsReprRuntimeValue::Plain(runtime::VmValue::Variant {
2345 value: None,
2346 ..
2347 }) => CpsReprRuntimeValue::Plain(runtime::VmValue::Unit),
2348 other => other,
2349 };
2350 values.insert(*dest, value);
2351 }
2352 CpsStmt::Primitive { dest, op, args } => {
2353 let args = args
2354 .iter()
2355 .map(|id| read_value(function, &values, *id))
2356 .collect::<CpsReprEvalResult<Vec<_>>>()?;
2357 let result = eval_cps_repr_primitive(*op, args)?;
2358 values.insert(*dest, result);
2359 }
2360 CpsStmt::DirectCall { dest, target, args } => {
2361 let target_function = function_by_name_repr(module, target)?;
2362 let args = args
2363 .iter()
2364 .map(|id| read_value(function, &values, *id))
2365 .collect::<CpsReprEvalResult<Vec<_>>>()?;
2366 let inherited = return_frames.len();
2367 let result = eval_function_with_context(
2368 module,
2369 target_function,
2370 args,
2371 active_handlers.clone(),
2372 guard_stack.clone(),
2373 return_frames.clone(),
2374 active_blocked.clone(),
2375 inherited,
2376 )?;
2377 if matches!(result, CpsReprRuntimeValue::Aborted(_)) {
2378 return Ok(result);
2379 }
2380 dispatch_scope_return_repr!('cont, result, dest);
2381 }
2382 CpsStmt::ApplyClosure { dest, closure, arg } => {
2383 let callable = read_value(function, &values, *closure)?;
2384 let result = match callable {
2385 CpsReprRuntimeValue::Closure(closure) => {
2386 let arg = read_value(function, &values, *arg)?;
2387 let owner = function_by_name_repr(module, &closure.owner_function)?;
2388 let mut closure_values = closure.values.clone();
2389 if let Some(self_id) = closure.recursive_self {
2390 closure_values
2391 .insert(self_id, CpsReprRuntimeValue::Closure(closure.clone()));
2392 }
2393 let inherited = return_frames.len();
2394 eval_continuations(
2395 module,
2396 owner,
2397 closure.entry,
2398 vec![arg],
2399 closure_values,
2400 active_handlers.clone(),
2401 guard_stack.clone(),
2402 return_frames.clone(),
2403 active_blocked.clone(),
2404 inherited,
2405 )?
2406 }
2407 CpsReprRuntimeValue::Resumption(resumption) => {
2408 let arg = read_plain_value(function, &values, *arg)?;
2409 let owner = function_by_name_repr(module, &resumption.owner_function)?;
2410 let anchor = resumption.handled_anchor;
2411 let resumed_handlers = merge_resumption_handlers_repr(
2412 &resumption.handlers,
2413 &active_handlers,
2414 anchor,
2415 );
2416 let adjusted_frames = merge_extras_into_frames_repr(
2417 &resumption.return_frames,
2418 &active_handlers,
2419 anchor,
2420 );
2421 eval_continuations(
2422 module,
2423 owner,
2424 resumption.target,
2425 vec![CpsReprRuntimeValue::Plain(arg)],
2426 resumption.values.clone(),
2427 resumed_handlers,
2428 resumption.guard_stack.clone(),
2429 adjusted_frames,
2430 resumption.active_blocked.clone(),
2431 0,
2432 )?
2433 }
2434 _ => {
2435 return Err(CpsReprEvalError::ExpectedPlainValue {
2436 function: function.name.clone(),
2437 id: *closure,
2438 });
2439 }
2440 };
2441 if matches!(result, CpsReprRuntimeValue::Aborted(_)) {
2442 return Ok(result);
2443 }
2444 dispatch_scope_return_repr!('cont, result, dest);
2445 }
2446 CpsStmt::CloneContinuation { dest, source } => {
2447 let value = read_value(function, &values, *source)?;
2448 values.insert(*dest, value);
2449 }
2450 CpsStmt::Resume {
2451 dest,
2452 resumption,
2453 arg,
2454 } => {
2455 let resumption = read_resumption(function, &values, *resumption)?;
2456 let arg = read_plain_value(function, &values, *arg)?;
2457 let owner = function_by_name_repr(module, &resumption.owner_function)?;
2458 let anchor = resumption.handled_anchor;
2459 let resumed_handlers = merge_resumption_handlers_repr(
2460 &resumption.handlers,
2461 &active_handlers,
2462 anchor,
2463 );
2464 let adjusted_frames = merge_extras_into_frames_repr(
2465 &resumption.return_frames,
2466 &active_handlers,
2467 anchor,
2468 );
2469 let result = eval_continuations(
2470 module,
2471 owner,
2472 resumption.target,
2473 vec![CpsReprRuntimeValue::Plain(arg)],
2474 resumption.values.clone(),
2475 resumed_handlers,
2476 resumption.guard_stack.clone(),
2477 adjusted_frames,
2478 resumption.active_blocked.clone(),
2479 0,
2480 )?;
2481 if matches!(result, CpsReprRuntimeValue::Aborted(_)) {
2482 return Ok(result);
2483 }
2484 dispatch_scope_return_repr!('cont, result, dest);
2485 }
2486 CpsStmt::ResumeWithHandler {
2487 dest,
2488 resumption,
2489 arg,
2490 handler,
2491 envs,
2492 } => {
2493 let resumption = read_resumption(function, &values, *resumption)?;
2494 let arg = read_plain_value(function, &values, *arg)?;
2495 let updates_existing_handler_env =
2496 envs.iter().any(|env| !env.targets.is_empty());
2497 let envs = capture_handler_envs(function, &values, envs)?;
2498 let owner = function_by_name_repr(module, &resumption.owner_function)?;
2499 let rebase_existing_handler_env = updates_existing_handler_env
2500 && resumption
2501 .handlers
2502 .iter()
2503 .any(|frame| frame.handler == *handler);
2504 overlay_handler_envs_in_stack_repr(
2505 &function.name,
2506 &mut active_handlers,
2507 *handler,
2508 &envs,
2509 true,
2510 );
2511 let pushed_prompt = fresh_repr_prompt();
2516 if !rebase_existing_handler_env {
2517 active_handlers.push(CpsReprHandlerFrame {
2518 prompt: pushed_prompt,
2519 handler: *handler,
2520 guard_stack: guard_stack.clone(),
2521 envs: envs.clone(),
2522 escape: REPR_EXIT_RWH_TARGET,
2523 escape_owner_function: function.name.clone(),
2524 return_frame_threshold: return_frames.len(),
2525 inherited: false,
2526 install_eval_id: current_eval_id,
2527 });
2528 }
2529 let inner_handlers = {
2530 let mut stack = resumption.handlers.clone();
2531 overlay_handler_envs_in_stack_repr(
2532 &function.name,
2533 &mut stack,
2534 *handler,
2535 &envs,
2536 false,
2537 );
2538 if !rebase_existing_handler_env {
2539 let mut owned = active_handlers
2540 .last()
2541 .cloned()
2542 .expect("just pushed RWH frame");
2543 owned.inherited = true;
2544 stack.push(owned);
2545 }
2546 stack
2547 };
2548 let pushed_extra = if rebase_existing_handler_env {
2549 Vec::new()
2550 } else {
2551 active_handlers
2552 .iter()
2553 .filter(|frame| frame.prompt == pushed_prompt)
2554 .cloned()
2555 .collect::<Vec<_>>()
2556 };
2557 let mut captured_frames = resumption.return_frames.clone();
2558 overlay_handler_envs_in_frames_repr(
2559 &function.name,
2560 &mut captured_frames,
2561 *handler,
2562 &envs,
2563 false,
2564 );
2565 let adjusted_frames =
2566 append_resume_with_handler_frames_repr(&captured_frames, &pushed_extra);
2567 let adjusted_frames = if rebase_existing_handler_env {
2568 own_captured_return_frames_repr(adjusted_frames)
2569 } else {
2570 adjusted_frames
2571 };
2572 trace_repr(
2573 "ResumeHandlerMerge",
2574 format!(
2575 "site=ResumeWithHandler(rebased) fn={} eval={} pushed_prompt={} captured={} pushed_extra={} inner={}",
2576 function.name,
2577 current_eval_id.0,
2578 pushed_prompt.0,
2579 summarize_repr_handler_stack(&resumption.handlers),
2580 summarize_repr_handler_stack(&pushed_extra),
2581 summarize_repr_handler_stack(&inner_handlers)
2582 ),
2583 );
2584 let result = eval_continuations(
2585 module,
2586 owner,
2587 resumption.target,
2588 vec![CpsReprRuntimeValue::Plain(arg)],
2589 resumption.values.clone(),
2590 inner_handlers,
2591 resumption.guard_stack.clone(),
2592 adjusted_frames,
2593 resumption.active_blocked.clone(),
2594 0,
2595 )?;
2596 if matches!(result, CpsReprRuntimeValue::Aborted(_)) {
2597 return Ok(result);
2598 }
2599 dispatch_scope_return_repr!('cont, result, dest);
2600 if let Some(pos) = active_handlers
2602 .iter()
2603 .rposition(|f| f.prompt == pushed_prompt)
2604 {
2605 active_handlers.truncate(pos);
2606 }
2607 }
2608 }
2609 }
2610
2611 match &continuation.terminator {
2612 CpsTerminator::Return(value) => {
2613 let v = read_value(function, &values, *value)?;
2614 trace_repr(
2615 "Return",
2616 format!(
2617 "fn={} cont={:?} return_frames.len={} initial={}",
2618 function.name,
2619 continuation.id,
2620 return_frames.len(),
2621 initial_frame_count
2622 ),
2623 );
2624 if return_frames.len() <= initial_frame_count {
2625 return Ok(v);
2626 }
2627 if let CpsReprRuntimeValue::Thunk(thunk) = &v {
2629 let top_index = return_frames.len() - 1;
2630 let top_frame = &return_frames[top_index];
2631 if return_frame_immediately_forces_param_repr(module, top_frame)?
2632 && top_index >= initial_frame_count
2633 {
2634 let top_frame = top_frame.clone();
2635 let forced = force_returned_thunk_before_frame_consumption_repr(
2636 module,
2637 thunk.clone(),
2638 &top_frame,
2639 return_frames.clone(),
2640 initial_frame_count,
2641 )?;
2642 if matches!(forced, CpsReprRuntimeValue::ScopeReturn { .. }) {
2643 return Ok(forced);
2644 }
2645 return continue_return_frames_repr(module, forced, &return_frames, &[]);
2646 }
2647 }
2648 return continue_return_frames_repr(module, v, &return_frames, &[]);
2649 }
2650 CpsTerminator::Continue { target, args: next } => {
2651 args = next
2652 .iter()
2653 .map(|id| read_value(function, &values, *id))
2654 .collect::<CpsReprEvalResult<Vec<_>>>()?;
2655 current = *target;
2656 }
2657 CpsTerminator::Branch {
2658 cond,
2659 then_cont,
2660 else_cont,
2661 } => {
2662 let cond = read_plain_value(function, &values, *cond)?;
2663 current = if bool_value(typed_ir::PrimitiveOp::BoolNot, &cond)? {
2664 *then_cont
2665 } else {
2666 *else_cont
2667 };
2668 }
2669 CpsTerminator::Perform {
2670 effect,
2671 payload,
2672 resume,
2673 handler,
2674 blocked,
2675 } => {
2676 let payload = read_plain_value(function, &values, *payload)?;
2677 trace_repr(
2678 "Perform",
2679 format!(
2680 "fn={} eval={} cont={:?} effect={:?} return_frames.len={} initial={} active_handlers={}",
2681 function.name,
2682 current_eval_id.0,
2683 continuation.id,
2684 effect,
2685 return_frames.len(),
2686 initial_frame_count,
2687 summarize_repr_handler_stack(&active_handlers)
2688 ),
2689 );
2690 let blocked = blocked
2691 .map(|blocked| read_effect_id(function, &values, blocked))
2692 .transpose()?
2693 .or_else(|| active_blocked_id_repr(effect, &active_blocked));
2694 let handler_stack =
2695 handler_stack_with_static(&active_handlers, *handler, &guard_stack);
2696 let (handler_arm, frame, handler_body_stack, handler_owner) =
2697 handler_arm_for_stack(module, function, &handler_stack, effect, blocked)?;
2698 trace_repr(
2699 "PerformHandlerSearch",
2700 format!(
2701 "fn={} eval={} effect={:?} stack={} matched_prompt={} matched_install_eval={} matched_owner={}",
2702 function.name,
2703 current_eval_id.0,
2704 effect,
2705 summarize_repr_handler_stack(&handler_stack),
2706 frame.prompt.0,
2707 frame.install_eval_id.0,
2708 frame.escape_owner_function
2709 ),
2710 );
2711 let handler_values = values_with_handler_env(
2712 &handler_owner.name,
2713 HashMap::new(),
2714 frame,
2715 handler_arm.entry,
2716 );
2717 let frame_prompt = frame.prompt;
2718 let frame_escape = frame.escape;
2719 let frame_in_active = active_handlers.iter().any(|f| f.prompt == frame_prompt)
2726 && frame.install_eval_id != REPR_SYNTHETIC_EVAL_ID;
2727 let handled_anchor = if frame_in_active {
2728 Some(CpsReprHandlerAnchor {
2729 prompt: frame.prompt,
2730 install_eval_id: frame.install_eval_id,
2731 })
2732 } else {
2733 None
2734 };
2735 let (resumption_handlers, resumption_return_frames) = if frame_in_active {
2736 let captured = capture_continuation_inside_prompt_repr(
2737 &handler_stack,
2738 &return_frames,
2739 frame,
2740 );
2741 (captured.handlers, captured.return_frames)
2742 } else {
2743 (handler_stack.clone(), return_frames.clone())
2744 };
2745 let resumption = CpsReprRuntimeValue::Resumption(CpsReprResumption {
2746 owner_function: function.name.clone(),
2747 target: *resume,
2748 values: values.clone(),
2749 handlers: resumption_handlers,
2750 guard_stack: guard_stack.clone(),
2751 active_blocked: active_blocked.clone(),
2752 return_frames: resumption_return_frames,
2753 handled_anchor,
2754 });
2755 let result = eval_continuations(
2756 module,
2757 handler_owner,
2758 handler_arm.entry,
2759 vec![CpsReprRuntimeValue::Plain(payload), resumption],
2760 handler_values,
2761 handler_body_stack,
2762 guard_stack.clone(),
2763 Vec::new(),
2764 active_blocked.clone(),
2765 0,
2766 )?;
2767 if !frame_in_active {
2768 return Ok(result);
2771 }
2772 let arm_already_reached_escape = handler_arm_continues_to_installed_escape_repr(
2773 handler_owner,
2774 frame.handler,
2775 handler_arm.entry,
2776 frame_escape,
2777 );
2778 if arm_already_reached_escape
2779 && !matches!(result, CpsReprRuntimeValue::ScopeReturn { .. })
2780 && frame.install_eval_id == current_eval_id
2781 {
2782 let mut frames = return_frames.clone();
2783 if frames.len() > frame.return_frame_threshold {
2784 frames.truncate(frame.return_frame_threshold);
2785 }
2786 return continue_return_frames_repr(module, result, &frames, &[]);
2787 }
2788 if !matches!(result, CpsReprRuntimeValue::ScopeReturn { .. })
2789 && frame.install_eval_id != current_eval_id
2790 && handler_arm_uses_resume_with_handler_repr(
2791 handler_owner,
2792 frame.handler,
2793 handler_arm.entry,
2794 )
2795 {
2796 return Ok(result);
2797 }
2798 let scope_return = match result {
2802 CpsReprRuntimeValue::ScopeReturn { .. } => result,
2803 CpsReprRuntimeValue::Aborted(inner) => {
2804 CpsReprRuntimeValue::ScopeReturn {
2808 prompt: frame_prompt,
2809 target: frame_escape,
2810 value: inner,
2811 }
2812 }
2813 other => CpsReprRuntimeValue::ScopeReturn {
2814 prompt: frame_prompt,
2815 target: frame_escape,
2816 value: Box::new(other),
2817 },
2818 };
2819 match handle_scope_return_repr(
2820 scope_return,
2821 &mut active_handlers,
2822 &return_frames,
2823 &function.name,
2824 current_eval_id,
2825 ) {
2826 ScopeReturnActionRepr::Value(v) => {
2827 return Ok(v);
2828 }
2829 ScopeReturnActionRepr::Propagate(v) => {
2830 if let Some(routed) = try_route_scope_return_through_return_frames_repr(
2831 module,
2832 &v,
2833 &return_frames,
2834 initial_frame_count,
2835 )? {
2836 return Ok(routed);
2837 }
2838 return Ok(v);
2839 }
2840 ScopeReturnActionRepr::JumpOrExit {
2841 target,
2842 value,
2843 return_frame_threshold,
2844 } => {
2845 if return_frames.len() > return_frame_threshold {
2846 return_frames.truncate(return_frame_threshold);
2847 }
2848 if target == REPR_EXIT_RWH_TARGET {
2849 return Ok(value);
2850 }
2851 current = target;
2852 args = vec![value];
2853 continue 'cont;
2854 }
2855 }
2856 }
2857 CpsTerminator::EffectfulCall {
2858 target,
2859 args: arg_ids,
2860 resume,
2861 } => {
2862 let target_function = function_by_name_repr(module, target)?;
2863 let call_args = arg_ids
2864 .iter()
2865 .map(|id| read_value(function, &values, *id))
2866 .collect::<CpsReprEvalResult<Vec<_>>>()?;
2867 let pre_push_count = return_frames.len();
2868 let frame = CpsReprReturnFrame {
2869 prompt_exit: None,
2870 owner_function: function.name.clone(),
2871 continuation: *resume,
2872 values: Rc::new(values.clone()),
2873 active_handlers: active_handlers.clone(),
2874 guard_stack: guard_stack.clone(),
2875 active_blocked: active_blocked.clone(),
2876 owner_initial_frame_count: initial_frame_count,
2877 owner_eval_id: current_eval_id,
2878 };
2879 let mut new_frames = return_frames.clone();
2880 new_frames.push(frame);
2881 return eval_function_with_context(
2882 module,
2883 target_function,
2884 call_args,
2885 active_handlers.clone(),
2886 guard_stack.clone(),
2887 new_frames,
2888 active_blocked.clone(),
2889 pre_push_count,
2890 );
2891 }
2892 CpsTerminator::EffectfulForce { thunk, resume } => {
2893 let value = read_value(function, &values, *thunk)?;
2894 match value {
2895 CpsReprRuntimeValue::Thunk(thunk) => {
2896 let pre_push_count = return_frames.len();
2897 let frame = CpsReprReturnFrame {
2898 prompt_exit: None,
2899 owner_function: function.name.clone(),
2900 continuation: *resume,
2901 values: Rc::new(values.clone()),
2902 active_handlers: active_handlers.clone(),
2903 guard_stack: guard_stack.clone(),
2904 active_blocked: active_blocked.clone(),
2905 owner_initial_frame_count: initial_frame_count,
2906 owner_eval_id: current_eval_id,
2907 };
2908 let mut new_frames = return_frames.clone();
2909 new_frames.push(frame);
2910 let owner = function_by_name_repr(module, &thunk.owner_function)?;
2911 let handlers = if !active_handlers.is_empty() {
2912 active_handlers.clone()
2913 } else {
2914 thunk.handlers.clone()
2915 };
2916 let guards = if !guard_stack.is_empty() {
2917 guard_stack.clone()
2918 } else {
2919 thunk.guard_stack.clone()
2920 };
2921 return eval_continuations(
2922 module,
2923 owner,
2924 thunk.entry,
2925 Vec::new(),
2926 thunk.values.clone(),
2927 handlers,
2928 guards,
2929 new_frames,
2930 active_blocked_for_thunk_repr(&active_blocked, &thunk),
2931 pre_push_count,
2932 );
2933 }
2934 other => {
2935 current = *resume;
2936 args = vec![other];
2937 continue;
2938 }
2939 }
2940 }
2941 CpsTerminator::EffectfulApply {
2942 closure,
2943 arg,
2944 resume,
2945 } => {
2946 let callable = read_value(function, &values, *closure)?;
2947 let pre_push_count = return_frames.len();
2948 let frame = CpsReprReturnFrame {
2949 prompt_exit: None,
2950 owner_function: function.name.clone(),
2951 continuation: *resume,
2952 values: Rc::new(values.clone()),
2953 active_handlers: active_handlers.clone(),
2954 guard_stack: guard_stack.clone(),
2955 active_blocked: active_blocked.clone(),
2956 owner_initial_frame_count: initial_frame_count,
2957 owner_eval_id: current_eval_id,
2958 };
2959 let mut new_frames = return_frames.clone();
2960 new_frames.push(frame);
2961 match callable {
2962 CpsReprRuntimeValue::Closure(closure) => {
2963 let arg = read_value(function, &values, *arg)?;
2964 let owner = function_by_name_repr(module, &closure.owner_function)?;
2965 let mut closure_values = closure.values.clone();
2966 if let Some(self_id) = closure.recursive_self {
2967 closure_values
2968 .insert(self_id, CpsReprRuntimeValue::Closure(closure.clone()));
2969 }
2970 return eval_continuations(
2971 module,
2972 owner,
2973 closure.entry,
2974 vec![arg],
2975 closure_values,
2976 active_handlers.clone(),
2977 guard_stack.clone(),
2978 new_frames,
2979 active_blocked.clone(),
2980 pre_push_count,
2981 );
2982 }
2983 CpsReprRuntimeValue::Resumption(resumption) => {
2984 let arg = read_plain_value(function, &values, *arg)?;
2985 let owner = function_by_name_repr(module, &resumption.owner_function)?;
2986 let anchor = resumption.handled_anchor;
2987 let resumed_handlers = merge_resumption_handlers_repr(
2988 &resumption.handlers,
2989 &active_handlers,
2990 anchor,
2991 );
2992 let adjusted_res = merge_extras_into_frames_repr(
2993 &resumption.return_frames,
2994 &active_handlers,
2995 anchor,
2996 );
2997 let mut combined_frames = new_frames;
2998 combined_frames.extend(adjusted_res);
2999 return eval_continuations(
3000 module,
3001 owner,
3002 resumption.target,
3003 vec![CpsReprRuntimeValue::Plain(arg)],
3004 resumption.values.clone(),
3005 resumed_handlers,
3006 resumption.guard_stack.clone(),
3007 combined_frames,
3008 resumption.active_blocked.clone(),
3009 0,
3010 );
3011 }
3012 _ => {
3013 return Err(CpsReprEvalError::ExpectedPlainValue {
3014 function: function.name.clone(),
3015 id: *closure,
3016 });
3017 }
3018 }
3019 }
3020 }
3021 }
3022}
3023
3024fn same_handler_frame_repr(a: &CpsReprHandlerFrame, b: &CpsReprHandlerFrame) -> bool {
3027 a.prompt == b.prompt && a.install_eval_id == b.install_eval_id
3028}
3029
3030fn capture_continuation_inside_prompt_repr(
3031 handlers: &[CpsReprHandlerFrame],
3032 return_frames: &[CpsReprReturnFrame],
3033 handled: &CpsReprHandlerFrame,
3034) -> CapturedPromptContinuationRepr {
3035 let start = captured_prompt_frame_start_repr(return_frames, handled);
3036 let return_frames = return_frames[start..]
3037 .iter()
3038 .cloned()
3039 .map(|frame| rebase_captured_return_frame_repr(frame, start, handled))
3040 .collect();
3041 CapturedPromptContinuationRepr {
3042 handlers: handlers
3043 .iter()
3044 .cloned()
3045 .map(|handler| rebase_captured_handler_frame_repr(handler, start, handled))
3046 .collect(),
3047 return_frames: own_captured_return_frames_repr(return_frames),
3048 }
3049}
3050
3051fn captured_prompt_frame_start_repr(
3052 return_frames: &[CpsReprReturnFrame],
3053 handled: &CpsReprHandlerFrame,
3054) -> usize {
3055 let start = return_frames
3056 .iter()
3057 .rposition(|frame| {
3058 frame
3059 .prompt_exit
3060 .as_ref()
3061 .is_some_and(|exit| exit.prompt == handled.prompt)
3062 })
3063 .map(|index| index + 1)
3064 .unwrap_or(0)
3069 .min(return_frames.len());
3070 start
3071}
3072
3073fn rebase_captured_return_frame_repr(
3074 mut frame: CpsReprReturnFrame,
3075 dropped_frames: usize,
3076 handled: &CpsReprHandlerFrame,
3077) -> CpsReprReturnFrame {
3078 frame.owner_initial_frame_count = frame
3079 .owner_initial_frame_count
3080 .saturating_sub(dropped_frames);
3081 frame.active_handlers = frame
3082 .active_handlers
3083 .into_iter()
3084 .map(|handler| rebase_captured_handler_frame_repr(handler, dropped_frames, handled))
3085 .collect();
3086 frame
3087}
3088
3089fn rebase_captured_handler_frame_repr(
3090 mut handler: CpsReprHandlerFrame,
3091 dropped_frames: usize,
3092 handled: &CpsReprHandlerFrame,
3093) -> CpsReprHandlerFrame {
3094 if handler.install_eval_id.0 >= handled.install_eval_id.0 {
3095 handler.return_frame_threshold = handler
3096 .return_frame_threshold
3097 .saturating_sub(dropped_frames);
3098 }
3099 handler
3100}
3101
3102fn merge_resumption_handlers_repr(
3106 captured: &[CpsReprHandlerFrame],
3107 current: &[CpsReprHandlerFrame],
3108 anchor: Option<CpsReprHandlerAnchor>,
3109) -> Vec<CpsReprHandlerFrame> {
3110 let is_anchor = |frame: &CpsReprHandlerFrame, anchor: CpsReprHandlerAnchor| {
3111 frame.prompt == anchor.prompt && frame.install_eval_id == anchor.install_eval_id
3112 };
3113 if let Some(anchor) = anchor {
3114 if let Some(anchor_index) = captured.iter().position(|f| is_anchor(f, anchor)) {
3115 let mut merged = Vec::with_capacity(captured.len() + current.len());
3116 merged.extend(captured[..=anchor_index].iter().cloned());
3117 for frame in current {
3118 let in_prefix = merged.iter().any(|m| same_handler_frame_repr(m, frame));
3119 let in_tail = captured[anchor_index + 1..]
3120 .iter()
3121 .any(|c| same_handler_frame_repr(c, frame));
3122 if !in_prefix && !in_tail {
3123 merged.push(frame.clone());
3124 }
3125 }
3126 merged.extend(captured[anchor_index + 1..].iter().cloned());
3127 return merged;
3128 }
3129 }
3130 let mut shared = 0;
3132 while shared < captured.len()
3133 && shared < current.len()
3134 && same_handler_frame_repr(&captured[shared], ¤t[shared])
3135 {
3136 shared += 1;
3137 }
3138 let mut merged = Vec::with_capacity(captured.len() + current.len());
3139 merged.extend(captured[..shared].iter().cloned());
3140 for frame in ¤t[shared..] {
3141 if !captured.iter().any(|c| same_handler_frame_repr(c, frame)) {
3142 merged.push(frame.clone());
3143 }
3144 }
3145 merged.extend(captured[shared..].iter().cloned());
3146 merged
3147}
3148
3149fn append_resume_with_handler_frames_repr(
3150 frames: &[CpsReprReturnFrame],
3151 extra: &[CpsReprHandlerFrame],
3152) -> Vec<CpsReprReturnFrame> {
3153 if extra.is_empty() {
3154 return frames.to_vec();
3155 }
3156 frames
3157 .iter()
3158 .map(|frame| {
3159 let mut adjusted = frame.clone();
3160 for handler in extra {
3161 if !adjusted
3162 .active_handlers
3163 .iter()
3164 .any(|existing| existing.prompt == handler.prompt)
3165 {
3166 adjusted.active_handlers.push(handler.clone());
3167 }
3168 }
3169 adjusted
3170 })
3171 .collect()
3172}
3173
3174fn merge_extras_into_frames_repr(
3176 frames: &[CpsReprReturnFrame],
3177 current: &[CpsReprHandlerFrame],
3178 anchor: Option<CpsReprHandlerAnchor>,
3179) -> Vec<CpsReprReturnFrame> {
3180 frames
3181 .iter()
3182 .map(|frame| {
3183 let merged = merge_resumption_handlers_repr(&frame.active_handlers, current, anchor);
3184 let mut adjusted = frame.clone();
3185 adjusted.active_handlers = merged;
3186 adjusted
3187 })
3188 .collect()
3189}
3190
3191fn own_captured_return_frames_repr(mut frames: Vec<CpsReprReturnFrame>) -> Vec<CpsReprReturnFrame> {
3193 for frame in &mut frames {
3194 frame.owner_initial_frame_count = 0;
3195 }
3196 frames
3197}
3198
3199fn continue_return_frames_repr(
3201 module: &CpsReprModule,
3202 value: CpsReprRuntimeValue,
3203 frames: &[CpsReprReturnFrame],
3204 extra_handlers: &[CpsReprHandlerFrame],
3205) -> CpsReprEvalResult<CpsReprRuntimeValue> {
3206 if frames.is_empty() {
3207 return Ok(value);
3208 }
3209 if matches!(value, CpsReprRuntimeValue::ScopeReturn { .. })
3210 || matches!(value, CpsReprRuntimeValue::Aborted(_))
3211 || matches!(value, CpsReprRuntimeValue::RoutedJump(_))
3212 {
3213 return Ok(value);
3214 }
3215 let (frame, rest) = frames.split_last().expect("non-empty");
3216 let function = function_by_name_repr(module, &frame.owner_function)?;
3217 let mut combined = frame.active_handlers.clone();
3218 for extra in extra_handlers {
3219 if !combined.iter().any(|f| f.prompt == extra.prompt) {
3220 combined.push(extra.clone());
3221 }
3222 }
3223 let owner_initial = frame.owner_initial_frame_count.min(rest.len());
3224 resume_continuation(
3225 module,
3226 function,
3227 frame.continuation,
3228 vec![value],
3229 frame.values.as_ref().clone(),
3230 combined,
3231 frame.guard_stack.clone(),
3232 rest.to_vec(),
3233 frame.active_blocked.clone(),
3234 owner_initial,
3235 frame.owner_eval_id,
3236 )
3237}
3238
3239fn return_frame_immediately_forces_param_repr(
3241 module: &CpsReprModule,
3242 frame: &CpsReprReturnFrame,
3243) -> CpsReprEvalResult<bool> {
3244 let function = function_by_name_repr(module, &frame.owner_function)?;
3245 let Some(continuation) = function
3246 .continuations
3247 .iter()
3248 .find(|c| c.id == frame.continuation)
3249 else {
3250 return Ok(false);
3251 };
3252 let Some(&first_param) = continuation.params.first() else {
3253 return Ok(false);
3254 };
3255 Ok(matches!(
3256 continuation.stmts.first(),
3257 Some(CpsStmt::ForceThunk { thunk, .. }) if *thunk == first_param
3258 ))
3259}
3260
3261fn force_returned_thunk_before_frame_consumption_repr(
3262 module: &CpsReprModule,
3263 mut thunk: CpsReprThunk,
3264 top_frame: &CpsReprReturnFrame,
3265 return_frames: Vec<CpsReprReturnFrame>,
3266 initial_frame_count: usize,
3267) -> CpsReprEvalResult<CpsReprRuntimeValue> {
3268 loop {
3269 let owner = function_by_name_repr(module, &thunk.owner_function)?;
3270 let result = resume_continuation(
3271 module,
3272 owner,
3273 thunk.entry,
3274 Vec::new(),
3275 thunk.values.clone(),
3276 top_frame.active_handlers.clone(),
3277 top_frame.guard_stack.clone(),
3278 return_frames.clone(),
3279 active_blocked_for_thunk_repr(&top_frame.active_blocked, &thunk),
3280 initial_frame_count,
3281 top_frame.owner_eval_id,
3282 )?;
3283 match result {
3284 CpsReprRuntimeValue::Thunk(next) => {
3285 thunk = next;
3286 continue;
3287 }
3288 other => return Ok(other),
3289 }
3290 }
3291}
3292
3293fn try_route_scope_return_through_return_frames_repr(
3295 module: &CpsReprModule,
3296 scope_return: &CpsReprRuntimeValue,
3297 return_frames: &[CpsReprReturnFrame],
3298 initial_frame_count: usize,
3299) -> CpsReprEvalResult<Option<CpsReprRuntimeValue>> {
3300 let CpsReprRuntimeValue::ScopeReturn {
3301 prompt,
3302 target,
3303 value,
3304 } = scope_return
3305 else {
3306 return Ok(None);
3307 };
3308 let prompt = *prompt;
3309 let target = *target;
3310 if target == REPR_EXIT_RWH_TARGET {
3311 return Ok(None);
3312 }
3313 if return_frames.is_empty() {
3314 return Ok(None);
3315 }
3316 for frame_index in (0..return_frames.len()).rev() {
3317 let frame = &return_frames[frame_index];
3318 let frame_eval_id = frame.owner_eval_id;
3319 let frame_owner = &frame.owner_function;
3320 let Some(handler_index) = frame.active_handlers.iter().rposition(|handler| {
3321 handler.prompt == prompt && handler.install_eval_id == frame_eval_id
3322 }) else {
3323 continue;
3324 };
3325 let matched_handler = frame.active_handlers[handler_index].clone();
3326 if matched_handler.escape_owner_function != *frame_owner {
3327 continue;
3328 }
3329 let mut post_handlers = frame.active_handlers.clone();
3330 post_handlers.truncate(handler_index);
3331 let mut rest_frames: Vec<CpsReprReturnFrame> = return_frames[..frame_index].to_vec();
3332 let truncate_at = matched_handler.return_frame_threshold;
3333 if rest_frames.len() > truncate_at {
3334 rest_frames.truncate(truncate_at);
3335 }
3336 let owner_initial = frame.owner_initial_frame_count.min(rest_frames.len());
3337 let routed_jump = CpsReprRoutedJump {
3338 value: value.clone(),
3339 return_frame_threshold: truncate_at,
3340 owner_function: frame.owner_function.clone(),
3341 target: matched_handler.escape,
3342 values: frame.values.clone(),
3343 active_handlers: post_handlers,
3344 guard_stack: frame.guard_stack.clone(),
3345 return_frames: rest_frames,
3346 active_blocked: frame.active_blocked.clone(),
3347 initial_frame_count: owner_initial,
3348 eval_id: frame.owner_eval_id,
3349 };
3350 if frame_index < initial_frame_count {
3355 return Ok(Some(CpsReprRuntimeValue::RoutedJump(Box::new(routed_jump))));
3356 }
3357 let owner = function_by_name_repr(module, &routed_jump.owner_function)?;
3358 let result = resume_continuation(
3359 module,
3360 owner,
3361 routed_jump.target,
3362 vec![*value.clone()],
3363 routed_jump.values.as_ref().clone(),
3364 routed_jump.active_handlers,
3365 routed_jump.guard_stack,
3366 routed_jump.return_frames,
3367 routed_jump.active_blocked,
3368 routed_jump.initial_frame_count,
3369 routed_jump.eval_id,
3370 )?;
3371 return Ok(Some(result));
3372 }
3373 Ok(None)
3374}
3375
3376fn resolve_routed_jump_repr(
3377 module: &CpsReprModule,
3378 value: CpsReprRuntimeValue,
3379 current_return_frames: &[CpsReprReturnFrame],
3380) -> CpsReprEvalResult<CpsReprRuntimeValue> {
3381 let CpsReprRuntimeValue::RoutedJump(jump) = value else {
3382 return Ok(value);
3383 };
3384 if current_return_frames.len() > jump.return_frame_threshold {
3385 return Ok(CpsReprRuntimeValue::RoutedJump(jump));
3386 }
3387 let owner = function_by_name_repr(module, &jump.owner_function)?;
3388 resume_continuation(
3389 module,
3390 owner,
3391 jump.target,
3392 vec![*jump.value],
3393 jump.values.as_ref().clone(),
3394 jump.active_handlers,
3395 jump.guard_stack,
3396 jump.return_frames,
3397 jump.active_blocked,
3398 jump.initial_frame_count,
3399 jump.eval_id,
3400 )
3401}
3402
3403fn continuation_by_id(
3404 function: &CpsReprFunction,
3405 id: CpsContinuationId,
3406) -> CpsReprEvalResult<&CpsReprContinuation> {
3407 function
3408 .continuations
3409 .iter()
3410 .find(|continuation| continuation.id == id)
3411 .ok_or_else(|| CpsReprEvalError::MissingContinuation {
3412 function: function.name.clone(),
3413 id,
3414 })
3415}
3416
3417fn handler_arm_continues_to_installed_escape_repr(
3418 function: &CpsReprFunction,
3419 handler: CpsHandlerId,
3420 entry: CpsContinuationId,
3421 escape: CpsContinuationId,
3422) -> bool {
3423 let escape_is_installed_for_handler = function.continuations.iter().any(|continuation| {
3424 continuation.stmts.iter().any(|stmt| {
3425 matches!(
3426 stmt,
3427 CpsStmt::InstallHandler {
3428 handler: id,
3429 escape: installed_escape,
3430 ..
3431 } if *id == handler && *installed_escape == escape
3432 )
3433 })
3434 });
3435 if !escape_is_installed_for_handler {
3436 return false;
3437 }
3438 handler_arm_continue_chain_reaches_escape_repr(function, handler, entry, escape)
3439}
3440
3441fn handler_arm_continue_chain_reaches_escape_repr(
3442 function: &CpsReprFunction,
3443 handler: CpsHandlerId,
3444 entry: CpsContinuationId,
3445 escape: CpsContinuationId,
3446) -> bool {
3447 let mut current = entry;
3448 let mut saw_uninstall = false;
3449 let mut visited = HashSet::new();
3450 while visited.insert(current) {
3451 let Some(continuation) = function
3452 .continuations
3453 .iter()
3454 .find(|continuation| continuation.id == current)
3455 else {
3456 return false;
3457 };
3458 saw_uninstall |= continuation.stmts.iter().any(
3459 |stmt| matches!(stmt, CpsStmt::UninstallHandler { handler: id } if *id == handler),
3460 );
3461 let CpsTerminator::Continue { target, .. } = &continuation.terminator else {
3462 return saw_uninstall && current == escape;
3463 };
3464 if *target == escape {
3465 return saw_uninstall;
3466 }
3467 current = *target;
3468 }
3469 false
3470}
3471
3472fn handler_arm_uses_resume_with_handler_repr(
3473 function: &CpsReprFunction,
3474 handler: CpsHandlerId,
3475 entry: CpsContinuationId,
3476) -> bool {
3477 let mut current = entry;
3478 let mut visited = HashSet::new();
3479 while visited.insert(current) {
3480 let Some(continuation) = function
3481 .continuations
3482 .iter()
3483 .find(|continuation| continuation.id == current)
3484 else {
3485 return false;
3486 };
3487 if continuation.stmts.iter().any(
3488 |stmt| matches!(stmt, CpsStmt::ResumeWithHandler { handler: id, .. } if *id == handler),
3489 ) {
3490 return true;
3491 }
3492 let CpsTerminator::Continue { target, .. } = &continuation.terminator else {
3493 return false;
3494 };
3495 current = *target;
3496 }
3497 false
3498}
3499
3500fn continuation_by_id_opt(
3501 function: &CpsReprFunction,
3502 id: CpsContinuationId,
3503) -> Option<&CpsReprContinuation> {
3504 function
3505 .continuations
3506 .iter()
3507 .find(|continuation| continuation.id == id)
3508}
3509
3510fn read_value(
3511 function: &CpsReprFunction,
3512 values: &HashMap<CpsValueId, CpsReprRuntimeValue>,
3513 id: CpsValueId,
3514) -> CpsReprEvalResult<CpsReprRuntimeValue> {
3515 values
3516 .get(&id)
3517 .cloned()
3518 .ok_or_else(|| CpsReprEvalError::MissingValue {
3519 function: function.name.clone(),
3520 id,
3521 })
3522}
3523
3524fn read_plain_value(
3525 function: &CpsReprFunction,
3526 values: &HashMap<CpsValueId, CpsReprRuntimeValue>,
3527 id: CpsValueId,
3528) -> CpsReprEvalResult<runtime::VmValue> {
3529 into_plain_value(function, id, read_value(function, values, id)?)
3530}
3531
3532fn read_effect_id(
3533 function: &CpsReprFunction,
3534 values: &HashMap<CpsValueId, CpsReprRuntimeValue>,
3535 id: CpsValueId,
3536) -> CpsReprEvalResult<u64> {
3537 match read_plain_value(function, values, id)? {
3538 runtime::VmValue::EffectId(value_id) => Ok(value_id),
3539 value => Err(CpsReprEvalError::ExpectedGuard {
3540 function: function.name.clone(),
3541 id,
3542 value,
3543 }),
3544 }
3545}
3546
3547fn read_resumption(
3548 function: &CpsReprFunction,
3549 values: &HashMap<CpsValueId, CpsReprRuntimeValue>,
3550 id: CpsValueId,
3551) -> CpsReprEvalResult<CpsReprResumption> {
3552 match read_value(function, values, id)? {
3553 CpsReprRuntimeValue::Resumption(resumption) => Ok(resumption),
3554 _ => Err(CpsReprEvalError::ExpectedResumption {
3555 function: function.name.clone(),
3556 id,
3557 }),
3558 }
3559}
3560
3561fn into_plain_value(
3562 function: &CpsReprFunction,
3563 id: CpsValueId,
3564 value: CpsReprRuntimeValue,
3565) -> CpsReprEvalResult<runtime::VmValue> {
3566 cps_repr_value_to_vm(value).ok_or_else(|| CpsReprEvalError::ExpectedPlainValue {
3567 function: function.name.clone(),
3568 id,
3569 })
3570}
3571
3572#[derive(Debug, Clone, PartialEq)]
3573enum CpsReprRuntimeValue {
3574 Plain(runtime::VmValue),
3575 Resumption(CpsReprResumption),
3576 Thunk(CpsReprThunk),
3577 Closure(CpsReprClosure),
3578 List(Rc<Vec<CpsReprRuntimeValue>>),
3583 Tuple(Vec<CpsReprRuntimeValue>),
3584 Record(BTreeMap<typed_ir::Name, CpsReprRuntimeValue>),
3585 Variant {
3586 tag: typed_ir::Name,
3587 value: Option<Box<CpsReprRuntimeValue>>,
3588 },
3589 #[allow(dead_code)]
3597 Aborted(Box<CpsReprRuntimeValue>),
3598 RoutedJump(Box<CpsReprRoutedJump>),
3601 ScopeReturn {
3606 prompt: CpsReprPromptId,
3607 target: CpsContinuationId,
3608 value: Box<CpsReprRuntimeValue>,
3609 },
3610}
3611
3612#[derive(Debug, Clone, PartialEq)]
3613struct CpsReprRoutedJump {
3614 value: Box<CpsReprRuntimeValue>,
3615 return_frame_threshold: usize,
3616 owner_function: String,
3617 target: CpsContinuationId,
3618 values: Rc<HashMap<CpsValueId, CpsReprRuntimeValue>>,
3619 active_handlers: Vec<CpsReprHandlerFrame>,
3620 guard_stack: Vec<CpsReprGuardEntry>,
3621 return_frames: Vec<CpsReprReturnFrame>,
3622 active_blocked: Vec<CpsReprBlockedEffect>,
3623 initial_frame_count: usize,
3624 eval_id: CpsReprEvalId,
3625}
3626
3627#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3630struct CpsReprPromptId(u64);
3631
3632thread_local! {
3633 static REPR_PROMPT_COUNTER: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
3634}
3635
3636fn fresh_repr_prompt() -> CpsReprPromptId {
3637 REPR_PROMPT_COUNTER.with(|cell| {
3638 let id = cell.get();
3639 cell.set(id + 1);
3640 CpsReprPromptId(id)
3641 })
3642}
3643
3644#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3646struct CpsReprEvalId(u64);
3647
3648thread_local! {
3649 static REPR_EVAL_ID_COUNTER: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
3650}
3651
3652fn fresh_repr_eval_id() -> CpsReprEvalId {
3653 REPR_EVAL_ID_COUNTER.with(|cell| {
3654 let id = cell.get();
3655 cell.set(id + 1);
3656 CpsReprEvalId(id)
3657 })
3658}
3659
3660const REPR_SYNTHETIC_EVAL_ID: CpsReprEvalId = CpsReprEvalId(u64::MAX);
3663
3664const REPR_EXIT_RWH_TARGET: CpsContinuationId = CpsContinuationId(usize::MAX);
3667
3668#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3672struct CpsReprHandlerAnchor {
3673 prompt: CpsReprPromptId,
3674 install_eval_id: CpsReprEvalId,
3675}
3676
3677#[derive(Debug, Clone, PartialEq)]
3678struct CpsReprResumption {
3679 owner_function: String,
3680 target: CpsContinuationId,
3681 values: HashMap<CpsValueId, CpsReprRuntimeValue>,
3682 handlers: Vec<CpsReprHandlerFrame>,
3683 guard_stack: Vec<CpsReprGuardEntry>,
3684 active_blocked: Vec<CpsReprBlockedEffect>,
3685 return_frames: Vec<CpsReprReturnFrame>,
3688 handled_anchor: Option<CpsReprHandlerAnchor>,
3691}
3692
3693struct CapturedPromptContinuationRepr {
3694 handlers: Vec<CpsReprHandlerFrame>,
3695 return_frames: Vec<CpsReprReturnFrame>,
3696}
3697
3698#[derive(Debug, Clone, PartialEq)]
3699struct CpsReprThunk {
3700 owner_function: String,
3701 entry: CpsContinuationId,
3702 values: HashMap<CpsValueId, CpsReprRuntimeValue>,
3703 handlers: Vec<CpsReprHandlerFrame>,
3704 guard_stack: Vec<CpsReprGuardEntry>,
3705 blocked: Vec<CpsReprBlockedEffect>,
3706}
3707
3708#[derive(Debug, Clone, PartialEq)]
3709struct CpsReprBlockedEffect {
3710 guard_id: u64,
3711 allowed: typed_ir::Type,
3712 active: bool,
3713}
3714
3715#[derive(Debug, Clone, PartialEq)]
3716struct CpsReprClosure {
3717 owner_function: String,
3718 entry: CpsContinuationId,
3719 values: HashMap<CpsValueId, CpsReprRuntimeValue>,
3720 recursive_self: Option<CpsValueId>,
3721}
3722
3723#[derive(Debug, Clone, PartialEq)]
3724struct CpsReprHandlerFrame {
3725 prompt: CpsReprPromptId,
3727 handler: CpsHandlerId,
3728 guard_stack: Vec<CpsReprGuardEntry>,
3729 envs: Vec<CpsReprEvaluatedHandlerEnv>,
3730 escape: CpsContinuationId,
3734 escape_owner_function: String,
3736 return_frame_threshold: usize,
3739 inherited: bool,
3741 install_eval_id: CpsReprEvalId,
3745}
3746
3747#[derive(Debug, Clone, PartialEq)]
3748struct CpsReprReturnFrame {
3749 prompt_exit: Option<CpsReprPromptExitFrame>,
3750 owner_function: String,
3751 continuation: CpsContinuationId,
3752 values: Rc<HashMap<CpsValueId, CpsReprRuntimeValue>>,
3753 active_handlers: Vec<CpsReprHandlerFrame>,
3754 guard_stack: Vec<CpsReprGuardEntry>,
3755 active_blocked: Vec<CpsReprBlockedEffect>,
3756 owner_initial_frame_count: usize,
3757 owner_eval_id: CpsReprEvalId,
3758}
3759
3760#[derive(Debug, Clone, PartialEq)]
3761struct CpsReprPromptExitFrame {
3762 prompt: CpsReprPromptId,
3763}
3764
3765#[derive(Debug, Clone, PartialEq)]
3766struct CpsReprEvaluatedHandlerEnv {
3767 entry: CpsContinuationId,
3768 values: Vec<(CpsValueId, CpsReprRuntimeValue)>,
3769}
3770
3771#[derive(Debug, Clone, PartialEq)]
3772struct CpsReprLatestHandlerEnv {
3773 handler: CpsHandlerId,
3774 entry: CpsContinuationId,
3775 target: CpsValueId,
3776 value: CpsReprRuntimeValue,
3777}
3778
3779#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3780struct CpsReprGuardEntry {
3781 var: runtime::EffectIdVar,
3782 id: u64,
3783}
3784
3785fn eval_literal(literal: &CpsLiteral) -> runtime::VmValue {
3786 match literal {
3787 CpsLiteral::Int(value) => runtime::VmValue::Int(value.clone()),
3788 CpsLiteral::Float(value) => runtime::VmValue::Float(value.clone()),
3789 CpsLiteral::String(value) => {
3790 runtime::VmValue::String(runtime::runtime::string_tree::StringTree::from_str(value))
3791 }
3792 CpsLiteral::Bool(value) => runtime::VmValue::Bool(*value),
3793 CpsLiteral::Unit => runtime::VmValue::Unit,
3794 }
3795}
3796
3797fn eval_primitive(
3798 op: typed_ir::PrimitiveOp,
3799 args: &[runtime::VmValue],
3800) -> CpsReprEvalResult<runtime::VmValue> {
3801 crate::eval::eval_primitive_for_abi(op, args).map_err(cps_repr_primitive_error)
3802}
3803
3804fn bool_value(op: typed_ir::PrimitiveOp, value: &runtime::VmValue) -> CpsReprEvalResult<bool> {
3805 match value {
3806 runtime::VmValue::Bool(value) => Ok(*value),
3807 value => Err(CpsReprEvalError::PrimitiveTypeMismatch {
3808 op,
3809 value: value.clone(),
3810 }),
3811 }
3812}
3813
3814fn cps_repr_primitive_error(error: crate::eval::NativeEvalError) -> CpsReprEvalError {
3815 match error {
3816 crate::eval::NativeEvalError::InvalidPrimitiveArity {
3817 op,
3818 expected,
3819 actual,
3820 } => CpsReprEvalError::InvalidPrimitiveArity {
3821 op,
3822 expected,
3823 actual,
3824 },
3825 crate::eval::NativeEvalError::PrimitiveTypeMismatch { op, value } => {
3826 CpsReprEvalError::PrimitiveTypeMismatch { op, value }
3827 }
3828 crate::eval::NativeEvalError::UnsupportedPrimitive { op } => {
3829 CpsReprEvalError::UnsupportedPrimitive { op }
3830 }
3831 other => unreachable!("native primitive evaluator returned non-primitive error: {other}"),
3832 }
3833}
3834
3835#[cfg(test)]
3836mod tests {
3837 use std::rc::Rc;
3838
3839 use super::*;
3840
3841 #[test]
3842 fn lowers_continuations_to_code_with_environment_slots() {
3843 let module = CpsModule {
3844 functions: Vec::new(),
3845 roots: vec![CpsFunction {
3846 name: "root".to_string(),
3847 params: Vec::new(),
3848 entry: CpsContinuationId(0),
3849 handlers: Vec::new(),
3850 continuations: vec![CpsContinuation {
3851 id: CpsContinuationId(0),
3852 params: vec![CpsValueId(0)],
3853 captures: vec![CpsValueId(2), CpsValueId(1)],
3854 shot_kind: CpsShotKind::MultiShot,
3855 stmts: Vec::new(),
3856 terminator: CpsTerminator::Return(CpsValueId(0)),
3857 }],
3858 }],
3859 };
3860
3861 let lowered = lower_cps_repr_module(&module);
3862
3863 assert_eq!(
3864 lowered.roots[0].continuations[0].environment,
3865 vec![
3866 CpsReprEnvironmentSlot {
3867 index: 0,
3868 value: CpsValueId(2),
3869 },
3870 CpsReprEnvironmentSlot {
3871 index: 1,
3872 value: CpsValueId(1),
3873 },
3874 ]
3875 );
3876 }
3877
3878 #[test]
3879 fn evaluates_pure_continuation_flow() {
3880 let module = CpsModule {
3881 functions: Vec::new(),
3882 roots: vec![CpsFunction {
3883 name: "root".to_string(),
3884 params: Vec::new(),
3885 entry: CpsContinuationId(0),
3886 handlers: Vec::new(),
3887 continuations: vec![
3888 CpsContinuation {
3889 id: CpsContinuationId(0),
3890 params: Vec::new(),
3891 captures: Vec::new(),
3892 shot_kind: CpsShotKind::OneShot,
3893 stmts: vec![
3894 CpsStmt::Literal {
3895 dest: CpsValueId(0),
3896 literal: CpsLiteral::Int("40".to_string()),
3897 },
3898 CpsStmt::Literal {
3899 dest: CpsValueId(1),
3900 literal: CpsLiteral::Int("2".to_string()),
3901 },
3902 CpsStmt::Primitive {
3903 dest: CpsValueId(2),
3904 op: typed_ir::PrimitiveOp::IntAdd,
3905 args: vec![CpsValueId(0), CpsValueId(1)],
3906 },
3907 ],
3908 terminator: CpsTerminator::Continue {
3909 target: CpsContinuationId(1),
3910 args: vec![CpsValueId(2)],
3911 },
3912 },
3913 CpsContinuation {
3914 id: CpsContinuationId(1),
3915 params: vec![CpsValueId(3)],
3916 captures: Vec::new(),
3917 shot_kind: CpsShotKind::OneShot,
3918 stmts: Vec::new(),
3919 terminator: CpsTerminator::Return(CpsValueId(3)),
3920 },
3921 ],
3922 }],
3923 };
3924 let lowered = lower_cps_repr_module(&module);
3925
3926 assert_eq!(
3927 eval_cps_repr_module(&lowered).expect("evaluated"),
3928 vec![runtime::VmValue::Int("42".to_string())]
3929 );
3930 }
3931
3932 #[test]
3933 fn evaluates_plain_value_primitives_through_native_evaluator() {
3934 let module = CpsModule {
3935 functions: Vec::new(),
3936 roots: vec![
3937 CpsFunction {
3938 name: "string-root".to_string(),
3939 params: Vec::new(),
3940 entry: CpsContinuationId(0),
3941 handlers: Vec::new(),
3942 continuations: vec![CpsContinuation {
3943 id: CpsContinuationId(0),
3944 params: Vec::new(),
3945 captures: Vec::new(),
3946 shot_kind: CpsShotKind::OneShot,
3947 stmts: vec![
3948 CpsStmt::Literal {
3949 dest: CpsValueId(0),
3950 literal: CpsLiteral::String("aćšz".to_string()),
3951 },
3952 CpsStmt::Literal {
3953 dest: CpsValueId(1),
3954 literal: CpsLiteral::Int("1".to_string()),
3955 },
3956 CpsStmt::Literal {
3957 dest: CpsValueId(2),
3958 literal: CpsLiteral::Int("3".to_string()),
3959 },
3960 CpsStmt::Literal {
3961 dest: CpsValueId(3),
3962 literal: CpsLiteral::String("bc".to_string()),
3963 },
3964 CpsStmt::Primitive {
3965 dest: CpsValueId(4),
3966 op: typed_ir::PrimitiveOp::StringSpliceRaw,
3967 args: vec![
3968 CpsValueId(0),
3969 CpsValueId(1),
3970 CpsValueId(2),
3971 CpsValueId(3),
3972 ],
3973 },
3974 ],
3975 terminator: CpsTerminator::Return(CpsValueId(4)),
3976 }],
3977 },
3978 CpsFunction {
3979 name: "list-root".to_string(),
3980 params: Vec::new(),
3981 entry: CpsContinuationId(0),
3982 handlers: Vec::new(),
3983 continuations: vec![CpsContinuation {
3984 id: CpsContinuationId(0),
3985 params: Vec::new(),
3986 captures: Vec::new(),
3987 shot_kind: CpsShotKind::OneShot,
3988 stmts: vec![
3989 CpsStmt::Literal {
3990 dest: CpsValueId(0),
3991 literal: CpsLiteral::Int("1".to_string()),
3992 },
3993 CpsStmt::Literal {
3994 dest: CpsValueId(1),
3995 literal: CpsLiteral::Int("2".to_string()),
3996 },
3997 CpsStmt::Primitive {
3998 dest: CpsValueId(2),
3999 op: typed_ir::PrimitiveOp::ListSingleton,
4000 args: vec![CpsValueId(0)],
4001 },
4002 CpsStmt::Primitive {
4003 dest: CpsValueId(3),
4004 op: typed_ir::PrimitiveOp::ListSingleton,
4005 args: vec![CpsValueId(1)],
4006 },
4007 CpsStmt::Primitive {
4008 dest: CpsValueId(4),
4009 op: typed_ir::PrimitiveOp::ListMerge,
4010 args: vec![CpsValueId(2), CpsValueId(3)],
4011 },
4012 CpsStmt::Primitive {
4013 dest: CpsValueId(5),
4014 op: typed_ir::PrimitiveOp::ListViewRaw,
4015 args: vec![CpsValueId(4)],
4016 },
4017 ],
4018 terminator: CpsTerminator::Return(CpsValueId(5)),
4019 }],
4020 },
4021 ],
4022 };
4023 let lowered = lower_cps_repr_module(&module);
4024
4025 assert_eq!(
4026 eval_cps_repr_module(&lowered).expect("evaluated"),
4027 vec![
4028 runtime::VmValue::String(runtime::runtime::string_tree::StringTree::from_str(
4029 "abcz",
4030 )),
4031 runtime::VmValue::Variant {
4032 tag: typed_ir::Name("node".to_string()),
4033 value: Some(Box::new(runtime::VmValue::Tuple(vec![
4034 runtime::VmValue::List(runtime::runtime::list_tree::ListTree::from_items(
4035 [Rc::new(runtime::VmValue::Int("1".to_string())),]
4036 )),
4037 runtime::VmValue::List(runtime::runtime::list_tree::ListTree::from_items(
4038 [Rc::new(runtime::VmValue::Int("2".to_string())),]
4039 )),
4040 ]))),
4041 },
4042 ]
4043 );
4044 }
4045
4046 #[test]
4047 fn evaluates_multishot_resumption_flow() {
4048 let module = multishot_resumption_module();
4049 let lowered = lower_cps_repr_module(&module);
4050
4051 assert_eq!(
4052 eval_cps_repr_module(&lowered).expect("evaluated"),
4053 vec![runtime::VmValue::Int("23".to_string())]
4054 );
4055 }
4056
4057 #[test]
4058 fn evaluates_resumption_under_fresh_handler_stack() {
4059 let module = rebased_resumption_module();
4060 let lowered = lower_cps_repr_module(&module);
4061
4062 assert_eq!(
4063 eval_cps_repr_module(&lowered).expect("evaluated"),
4064 vec![runtime::VmValue::Int("13".to_string())]
4065 );
4066 }
4067
4068 #[test]
4069 fn skips_handler_frame_blocked_by_guard_snapshot() {
4070 let module = blocked_handler_snapshot_module();
4071 let lowered = lower_cps_repr_module(&module);
4072
4073 assert_eq!(
4074 eval_cps_repr_module(&lowered).expect("evaluated"),
4075 vec![runtime::VmValue::Int("100".to_string())]
4076 );
4077 }
4078
4079 #[test]
4080 fn analyzes_resumption_value_kind() {
4081 let lowered = lower_cps_repr_module(&multishot_resumption_module());
4082 let analysis = analyze_cps_repr_values(&lowered);
4083
4084 assert_eq!(
4085 analysis.value_kind("root", CpsValueId(4)),
4086 Some(CpsReprValueKind::Plain)
4087 );
4088 assert_eq!(
4089 analysis.value_kind("root", CpsValueId(5)),
4090 Some(CpsReprValueKind::Resumption)
4091 );
4092 assert_eq!(
4093 analysis.value_kind("root", CpsValueId(10)),
4094 Some(CpsReprValueKind::Resumption)
4095 );
4096 assert_eq!(
4097 analysis.value_kind("root", CpsValueId(7)),
4098 Some(CpsReprValueKind::Plain)
4099 );
4100 assert_eq!(
4101 analysis.continuation_return_kind("root", CpsContinuationId(2)),
4102 Some(CpsReprValueKind::Plain)
4103 );
4104 }
4105
4106 #[test]
4107 fn analyzes_resumption_abi_lane() {
4108 let lowered = lower_cps_repr_module(&multishot_resumption_module());
4109 let analysis = analyze_cps_repr_abi_lanes(&lowered);
4110
4111 assert_eq!(
4112 analysis.value_lane("root", CpsValueId(5)),
4113 Some(CpsReprAbiLane::ResumptionPtr)
4114 );
4115 assert_eq!(
4116 analysis.value_lane("root", CpsValueId(7)),
4117 Some(CpsReprAbiLane::ScalarI64)
4118 );
4119 assert_eq!(
4120 analysis.continuation_return_lane("root", CpsContinuationId(2)),
4121 Some(CpsReprAbiLane::ScalarI64)
4122 );
4123 assert_eq!(
4124 analysis.function_return_lane("root"),
4125 Some(CpsReprAbiLane::ScalarI64)
4126 );
4127 }
4128
4129 #[test]
4130 fn propagates_direct_call_abi_lane() {
4131 let module = CpsModule {
4132 functions: vec![CpsFunction {
4133 name: "make_int".to_string(),
4134 params: Vec::new(),
4135 entry: CpsContinuationId(0),
4136 handlers: Vec::new(),
4137 continuations: vec![CpsContinuation {
4138 id: CpsContinuationId(0),
4139 params: Vec::new(),
4140 captures: Vec::new(),
4141 shot_kind: CpsShotKind::OneShot,
4142 stmts: vec![CpsStmt::Literal {
4143 dest: CpsValueId(0),
4144 literal: CpsLiteral::Int("42".to_string()),
4145 }],
4146 terminator: CpsTerminator::Return(CpsValueId(0)),
4147 }],
4148 }],
4149 roots: vec![CpsFunction {
4150 name: "root".to_string(),
4151 params: Vec::new(),
4152 entry: CpsContinuationId(0),
4153 handlers: Vec::new(),
4154 continuations: vec![CpsContinuation {
4155 id: CpsContinuationId(0),
4156 params: Vec::new(),
4157 captures: Vec::new(),
4158 shot_kind: CpsShotKind::OneShot,
4159 stmts: vec![CpsStmt::DirectCall {
4160 dest: CpsValueId(0),
4161 target: "make_int".to_string(),
4162 args: Vec::new(),
4163 }],
4164 terminator: CpsTerminator::Return(CpsValueId(0)),
4165 }],
4166 }],
4167 };
4168 let lowered = lower_cps_repr_module(&module);
4169 let analysis = analyze_cps_repr_abi_lanes(&lowered);
4170
4171 assert_eq!(
4172 analysis.value_lane("root", CpsValueId(0)),
4173 Some(CpsReprAbiLane::ScalarI64)
4174 );
4175 assert_eq!(
4176 analysis.function_return_lane("root"),
4177 Some(CpsReprAbiLane::ScalarI64)
4178 );
4179 }
4180
4181 fn multishot_resumption_module() -> CpsModule {
4182 let effect = typed_ir::Path::from_name(typed_ir::Name("choose".to_string()));
4183 CpsModule {
4184 functions: Vec::new(),
4185 roots: vec![CpsFunction {
4186 name: "root".to_string(),
4187 params: Vec::new(),
4188 entry: CpsContinuationId(0),
4189 handlers: vec![crate::cps_ir::CpsHandler {
4190 id: CpsHandlerId(0),
4191 arms: vec![crate::cps_ir::CpsHandlerArm {
4192 effect: effect.clone(),
4193 entry: CpsContinuationId(2),
4194 }],
4195 }],
4196 continuations: vec![
4197 CpsContinuation {
4198 id: CpsContinuationId(0),
4199 params: Vec::new(),
4200 captures: Vec::new(),
4201 shot_kind: CpsShotKind::MultiShot,
4202 stmts: vec![CpsStmt::Literal {
4203 dest: CpsValueId(0),
4204 literal: CpsLiteral::Int("1".to_string()),
4205 }],
4206 terminator: CpsTerminator::Perform {
4207 effect,
4208 payload: CpsValueId(0),
4209 resume: CpsContinuationId(1),
4210 handler: CpsHandlerId(0),
4211 blocked: None,
4212 },
4213 },
4214 CpsContinuation {
4215 id: CpsContinuationId(1),
4216 params: vec![CpsValueId(1)],
4217 captures: Vec::new(),
4218 shot_kind: CpsShotKind::MultiShot,
4219 stmts: vec![
4220 CpsStmt::Literal {
4221 dest: CpsValueId(2),
4222 literal: CpsLiteral::Int("10".to_string()),
4223 },
4224 CpsStmt::Primitive {
4225 dest: CpsValueId(3),
4226 op: typed_ir::PrimitiveOp::IntAdd,
4227 args: vec![CpsValueId(1), CpsValueId(2)],
4228 },
4229 ],
4230 terminator: CpsTerminator::Return(CpsValueId(3)),
4231 },
4232 CpsContinuation {
4233 id: CpsContinuationId(2),
4234 params: vec![CpsValueId(4), CpsValueId(5)],
4235 captures: Vec::new(),
4236 shot_kind: CpsShotKind::MultiShot,
4237 stmts: vec![
4238 CpsStmt::CloneContinuation {
4239 dest: CpsValueId(10),
4240 source: CpsValueId(5),
4241 },
4242 CpsStmt::Literal {
4243 dest: CpsValueId(6),
4244 literal: CpsLiteral::Int("2".to_string()),
4245 },
4246 CpsStmt::Resume {
4247 dest: CpsValueId(7),
4248 resumption: CpsValueId(5),
4249 arg: CpsValueId(4),
4250 },
4251 CpsStmt::Resume {
4252 dest: CpsValueId(8),
4253 resumption: CpsValueId(10),
4254 arg: CpsValueId(6),
4255 },
4256 CpsStmt::Primitive {
4257 dest: CpsValueId(9),
4258 op: typed_ir::PrimitiveOp::IntAdd,
4259 args: vec![CpsValueId(7), CpsValueId(8)],
4260 },
4261 ],
4262 terminator: CpsTerminator::Return(CpsValueId(9)),
4263 },
4264 ],
4265 }],
4266 }
4267 }
4268
4269 fn rebased_resumption_module() -> CpsModule {
4270 let effect = typed_ir::Path::from_name(typed_ir::Name("choose".to_string()));
4271 CpsModule {
4272 functions: Vec::new(),
4273 roots: vec![CpsFunction {
4274 name: "root".to_string(),
4275 params: Vec::new(),
4276 entry: CpsContinuationId(0),
4277 handlers: vec![
4278 crate::cps_ir::CpsHandler {
4279 id: CpsHandlerId(0),
4280 arms: vec![crate::cps_ir::CpsHandlerArm {
4281 effect: effect.clone(),
4282 entry: CpsContinuationId(2),
4283 }],
4284 },
4285 crate::cps_ir::CpsHandler {
4286 id: CpsHandlerId(1),
4287 arms: vec![crate::cps_ir::CpsHandlerArm {
4288 effect: effect.clone(),
4289 entry: CpsContinuationId(4),
4290 }],
4291 },
4292 ],
4293 continuations: vec![
4294 CpsContinuation {
4295 id: CpsContinuationId(0),
4296 params: Vec::new(),
4297 captures: Vec::new(),
4298 shot_kind: CpsShotKind::MultiShot,
4299 stmts: vec![CpsStmt::Literal {
4300 dest: CpsValueId(0),
4301 literal: CpsLiteral::Int("1".to_string()),
4302 }],
4303 terminator: CpsTerminator::Perform {
4304 effect: effect.clone(),
4305 payload: CpsValueId(0),
4306 resume: CpsContinuationId(1),
4307 handler: CpsHandlerId(0),
4308 blocked: None,
4309 },
4310 },
4311 CpsContinuation {
4312 id: CpsContinuationId(1),
4313 params: vec![CpsValueId(1)],
4314 captures: Vec::new(),
4315 shot_kind: CpsShotKind::MultiShot,
4316 stmts: vec![CpsStmt::Literal {
4317 dest: CpsValueId(2),
4318 literal: CpsLiteral::Int("2".to_string()),
4319 }],
4320 terminator: CpsTerminator::Perform {
4321 effect: effect.clone(),
4322 payload: CpsValueId(2),
4323 resume: CpsContinuationId(3),
4324 handler: CpsHandlerId(0),
4325 blocked: None,
4326 },
4327 },
4328 CpsContinuation {
4329 id: CpsContinuationId(2),
4330 params: vec![CpsValueId(4), CpsValueId(5)],
4331 captures: Vec::new(),
4332 shot_kind: CpsShotKind::MultiShot,
4333 stmts: vec![CpsStmt::ResumeWithHandler {
4334 dest: CpsValueId(6),
4335 resumption: CpsValueId(5),
4336 arg: CpsValueId(4),
4337 handler: CpsHandlerId(1),
4338 envs: Vec::new(),
4339 }],
4340 terminator: CpsTerminator::Return(CpsValueId(6)),
4341 },
4342 CpsContinuation {
4343 id: CpsContinuationId(3),
4344 params: vec![CpsValueId(9)],
4345 captures: vec![CpsValueId(1)],
4346 shot_kind: CpsShotKind::MultiShot,
4347 stmts: vec![CpsStmt::Primitive {
4348 dest: CpsValueId(13),
4349 op: typed_ir::PrimitiveOp::IntAdd,
4350 args: vec![CpsValueId(1), CpsValueId(9)],
4351 }],
4352 terminator: CpsTerminator::Return(CpsValueId(13)),
4353 },
4354 CpsContinuation {
4355 id: CpsContinuationId(4),
4356 params: vec![CpsValueId(7), CpsValueId(8)],
4357 captures: Vec::new(),
4358 shot_kind: CpsShotKind::MultiShot,
4359 stmts: vec![
4360 CpsStmt::Literal {
4361 dest: CpsValueId(10),
4362 literal: CpsLiteral::Int("10".to_string()),
4363 },
4364 CpsStmt::Primitive {
4365 dest: CpsValueId(11),
4366 op: typed_ir::PrimitiveOp::IntAdd,
4367 args: vec![CpsValueId(7), CpsValueId(10)],
4368 },
4369 CpsStmt::Resume {
4370 dest: CpsValueId(12),
4371 resumption: CpsValueId(8),
4372 arg: CpsValueId(11),
4373 },
4374 ],
4375 terminator: CpsTerminator::Return(CpsValueId(12)),
4376 },
4377 ],
4378 }],
4379 }
4380 }
4381
4382 fn blocked_handler_snapshot_module() -> CpsModule {
4383 let start = typed_ir::Path::from_name(typed_ir::Name("start".to_string()));
4384 let choose = typed_ir::Path::from_name(typed_ir::Name("choose".to_string()));
4385 CpsModule {
4386 functions: Vec::new(),
4387 roots: vec![CpsFunction {
4388 name: "root".to_string(),
4389 params: Vec::new(),
4390 entry: CpsContinuationId(0),
4391 handlers: vec![
4392 crate::cps_ir::CpsHandler {
4393 id: CpsHandlerId(0),
4394 arms: vec![
4395 crate::cps_ir::CpsHandlerArm {
4396 effect: start.clone(),
4397 entry: CpsContinuationId(2),
4398 },
4399 crate::cps_ir::CpsHandlerArm {
4400 effect: choose.clone(),
4401 entry: CpsContinuationId(5),
4402 },
4403 ],
4404 },
4405 crate::cps_ir::CpsHandler {
4406 id: CpsHandlerId(1),
4407 arms: vec![crate::cps_ir::CpsHandlerArm {
4408 effect: choose.clone(),
4409 entry: CpsContinuationId(4),
4410 }],
4411 },
4412 ],
4413 continuations: vec![
4414 CpsContinuation {
4415 id: CpsContinuationId(0),
4416 params: Vec::new(),
4417 captures: Vec::new(),
4418 shot_kind: CpsShotKind::MultiShot,
4419 stmts: vec![CpsStmt::Literal {
4420 dest: CpsValueId(0),
4421 literal: CpsLiteral::Int("1".to_string()),
4422 }],
4423 terminator: CpsTerminator::Perform {
4424 effect: start,
4425 payload: CpsValueId(0),
4426 resume: CpsContinuationId(1),
4427 handler: CpsHandlerId(0),
4428 blocked: None,
4429 },
4430 },
4431 CpsContinuation {
4432 id: CpsContinuationId(1),
4433 params: vec![CpsValueId(1)],
4434 captures: Vec::new(),
4435 shot_kind: CpsShotKind::MultiShot,
4436 stmts: vec![CpsStmt::Literal {
4437 dest: CpsValueId(2),
4438 literal: CpsLiteral::Int("0".to_string()),
4439 }],
4440 terminator: CpsTerminator::Perform {
4441 effect: choose.clone(),
4442 payload: CpsValueId(2),
4443 resume: CpsContinuationId(3),
4444 handler: CpsHandlerId(0),
4445 blocked: Some(CpsValueId(1)),
4446 },
4447 },
4448 CpsContinuation {
4449 id: CpsContinuationId(2),
4450 params: vec![CpsValueId(3), CpsValueId(4)],
4451 captures: Vec::new(),
4452 shot_kind: CpsShotKind::MultiShot,
4453 stmts: vec![
4454 CpsStmt::FreshGuard {
4455 dest: CpsValueId(5),
4456 var: yulang_runtime::EffectIdVar(0),
4457 },
4458 CpsStmt::ResumeWithHandler {
4459 dest: CpsValueId(6),
4460 resumption: CpsValueId(4),
4461 arg: CpsValueId(5),
4462 handler: CpsHandlerId(1),
4463 envs: Vec::new(),
4464 },
4465 ],
4466 terminator: CpsTerminator::Return(CpsValueId(6)),
4467 },
4468 CpsContinuation {
4469 id: CpsContinuationId(3),
4470 params: vec![CpsValueId(7)],
4471 captures: Vec::new(),
4472 shot_kind: CpsShotKind::MultiShot,
4473 stmts: Vec::new(),
4474 terminator: CpsTerminator::Return(CpsValueId(7)),
4475 },
4476 CpsContinuation {
4477 id: CpsContinuationId(4),
4478 params: vec![CpsValueId(8), CpsValueId(9)],
4479 captures: Vec::new(),
4480 shot_kind: CpsShotKind::MultiShot,
4481 stmts: vec![CpsStmt::Literal {
4482 dest: CpsValueId(10),
4483 literal: CpsLiteral::Int("200".to_string()),
4484 }],
4485 terminator: CpsTerminator::Return(CpsValueId(10)),
4486 },
4487 CpsContinuation {
4488 id: CpsContinuationId(5),
4489 params: vec![CpsValueId(11), CpsValueId(12)],
4490 captures: Vec::new(),
4491 shot_kind: CpsShotKind::MultiShot,
4492 stmts: vec![CpsStmt::Literal {
4493 dest: CpsValueId(13),
4494 literal: CpsLiteral::Int("100".to_string()),
4495 }],
4496 terminator: CpsTerminator::Return(CpsValueId(13)),
4497 },
4498 ],
4499 }],
4500 }
4501 }
4502}