1pub mod case;
2pub mod expr;
3pub mod join;
4pub mod primop;
5
6use cranelift_codegen::ir::{FuncRef, SigRef, Value};
7use std::collections::HashMap;
8use tidepool_repr::{CoreExpr, JoinId, PrimOpKind, VarId};
9
10pub use crate::layout::*;
11
12pub struct EmitSession<'a> {
14 pub pipeline: &'a mut crate::pipeline::CodegenPipeline,
15 pub vmctx: Value,
16 pub gc_sig: SigRef,
17 pub oom_func: FuncRef,
18 pub tree: &'a CoreExpr,
19}
20
21#[derive(Debug, Clone, Copy)]
23pub enum SsaVal {
24 Raw(Value, i64),
26 HeapPtr(Value),
28}
29
30impl SsaVal {
31 pub fn value(self) -> Value {
32 match self {
33 SsaVal::Raw(v, _) | SsaVal::HeapPtr(v) => v,
34 }
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum TailCtx {
40 Tail,
41 NonTail,
42}
43
44impl TailCtx {
45 pub fn is_tail(self) -> bool {
46 matches!(self, TailCtx::Tail)
47 }
48}
49
50pub struct ScopedEnv {
52 inner: HashMap<VarId, SsaVal>,
53}
54
55#[allow(clippy::new_without_default)]
56impl ScopedEnv {
57 pub fn new() -> Self {
58 Self {
59 inner: HashMap::new(),
60 }
61 }
62
63 pub fn get(&self, var: &VarId) -> Option<&SsaVal> {
64 self.inner.get(var)
65 }
66
67 pub fn contains_key(&self, var: &VarId) -> bool {
68 self.inner.contains_key(var)
69 }
70
71 pub fn insert(&mut self, var: VarId, val: SsaVal) -> Option<SsaVal> {
72 self.inner.insert(var, val)
73 }
74
75 pub fn restore(&mut self, var: VarId, old: Option<SsaVal>) {
77 if let Some(val) = old {
78 self.inner.insert(var, val);
79 } else {
80 self.inner.remove(&var);
81 }
82 }
83
84 pub fn iter(&self) -> std::collections::hash_map::Iter<'_, VarId, SsaVal> {
85 self.inner.iter()
86 }
87
88 pub fn keys(&self) -> std::collections::hash_map::Keys<'_, VarId, SsaVal> {
89 self.inner.keys()
90 }
91
92 pub fn len(&self) -> usize {
93 self.inner.len()
94 }
95
96 pub fn is_empty(&self) -> bool {
97 self.inner.is_empty()
98 }
99
100 pub fn insert_scoped(&mut self, scope: &mut EnvScope, var: VarId, val: SsaVal) {
102 let old = self.insert(var, val);
103 scope.saved.push((var, old));
104 }
105
106 pub fn restore_scope(&mut self, scope: EnvScope) {
108 for (var, old) in scope.saved.into_iter().rev() {
109 self.restore(var, old);
110 }
111 }
112}
113
114pub struct EnvScope {
116 pub(crate) saved: Vec<(VarId, Option<SsaVal>)>,
117}
118
119impl EnvScope {
120 pub fn new() -> Self {
121 Self { saved: Vec::new() }
122 }
123}
124
125impl Default for EnvScope {
126 fn default() -> Self {
127 Self::new()
128 }
129}
130
131#[allow(dead_code)]
138pub(crate) struct EnvGuard<'a> {
139 env: &'a mut ScopedEnv,
140 scope: EnvScope,
141}
142
143#[allow(dead_code)]
144impl<'a> EnvGuard<'a> {
145 pub fn new(env: &'a mut ScopedEnv) -> Self {
146 Self {
147 env,
148 scope: EnvScope::new(),
149 }
150 }
151
152 pub fn insert(&mut self, var: VarId, val: SsaVal) {
153 self.env.insert_scoped(&mut self.scope, var, val);
154 }
155}
156
157impl<'a> Drop for EnvGuard<'a> {
158 fn drop(&mut self) {
159 let scope = std::mem::take(&mut self.scope);
160 self.env.restore_scope(scope);
161 }
162}
163
164pub struct EmitContext {
166 pub env: ScopedEnv,
167 pub(crate) join_blocks: JoinPointRegistry,
168 pub lambda_counter: u32,
169 pub prefix: String,
170 pub(crate) letrec_states: Vec<crate::emit::expr::LetRecDeferredState>,
172}
173
174pub(crate) struct JoinPointRegistry {
175 blocks: HashMap<JoinId, JoinInfo>,
176}
177
178impl JoinPointRegistry {
179 pub(crate) fn new() -> Self {
180 Self {
181 blocks: HashMap::new(),
182 }
183 }
184
185 pub(crate) fn register(&mut self, label: JoinId, info: JoinInfo) {
186 self.blocks.insert(label, info);
187 }
188
189 pub(crate) fn get(&self, label: &JoinId) -> Result<&JoinInfo, EmitError> {
190 self.blocks.get(label).ok_or_else(|| {
191 EmitError::NotYetImplemented(format!("Jump to unregistered join {:?}", label))
192 })
193 }
194
195 pub(crate) fn remove(&mut self, label: &JoinId) -> Option<JoinInfo> {
196 self.blocks.remove(label)
197 }
198}
199
200pub struct JoinInfo {
202 pub block: cranelift_codegen::ir::Block,
203 pub param_types: Vec<SsaVal>,
204}
205
206#[derive(Debug)]
208pub enum EmitError {
209 UnboundVariable(VarId),
210 NotYetImplemented(String),
211 CraneliftError(String),
212 Pipeline(crate::pipeline::PipelineError),
213 InvalidArity(PrimOpKind, usize, usize),
214 MissingCaptureVar(VarId, String),
216 InternalError(String),
218}
219
220impl std::fmt::Display for EmitError {
221 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222 match self {
223 EmitError::UnboundVariable(v) => write!(f, "unbound variable: {:?}", v),
224 EmitError::NotYetImplemented(s) => write!(f, "not yet implemented: {}", s),
225 EmitError::CraneliftError(s) => write!(f, "cranelift error: {}", s),
226 EmitError::Pipeline(e) => write!(f, "pipeline error: {}", e),
227 EmitError::InvalidArity(op, expected, got) => {
228 write!(
229 f,
230 "invalid arity for {:?}: expected {}, got {}",
231 op, expected, got
232 )
233 }
234 EmitError::MissingCaptureVar(v, ctx) => {
235 write!(f, "missing capture variable VarId({:#x}): {}", v.0, ctx)
236 }
237 EmitError::InternalError(msg) => write!(f, "internal error: {}", msg),
238 }
239 }
240}
241
242impl std::error::Error for EmitError {}
243
244impl From<crate::pipeline::PipelineError> for EmitError {
245 fn from(e: crate::pipeline::PipelineError) -> Self {
246 EmitError::Pipeline(e)
247 }
248}
249
250impl EmitContext {
251 pub fn new(prefix: String) -> Self {
252 Self {
253 env: ScopedEnv::new(),
254 join_blocks: JoinPointRegistry::new(),
255 lambda_counter: 0,
256 prefix,
257 letrec_states: Vec::new(),
258 }
259 }
260
261 pub fn declare_env(&self, builder: &mut cranelift_frontend::FunctionBuilder) {
266 let mut keys: Vec<_> = self.env.keys().collect();
268 keys.sort_by_key(|v| v.0);
269 for &k in keys {
270 if let Some(SsaVal::HeapPtr(v)) = self.env.get(&k) {
271 builder.declare_value_needs_stack_map(*v);
272 }
273 }
274 }
275
276 pub fn trace_scope(&self, msg: &str) {
277 if crate::debug::trace_level() >= crate::debug::TraceLevel::Scope {
278 eprintln!("[scope:{}] {}", self.prefix, msg);
279 }
280 }
281
282 pub fn next_lambda_name(&mut self) -> String {
283 let n = self.lambda_counter;
284 self.lambda_counter += 1;
285 format!("{}_lambda_{}", self.prefix, n)
286 }
287
288 pub fn next_thunk_name(&mut self) -> String {
289 let n = self.lambda_counter;
290 self.lambda_counter += 1;
291 format!("{}_thunk_{}", self.prefix, n)
292 }
293}