1use yulang_typed_ir as typed_ir;
8
9use crate::closure::{
10 NativeClosureBlock, NativeClosureFunction, NativeClosureModule, NativeClosureStmt,
11};
12use crate::control_ir::{
13 BlockId, NativeLiteral, NativeRecordField, NativeStmt, NativeTerminator, ValueId,
14};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct NativeAbiModule {
18 pub functions: Vec<NativeAbiFunction>,
19 pub roots: Vec<NativeAbiFunction>,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct NativeAbiFunction {
24 pub name: String,
25 pub params: Vec<ValueId>,
26 pub environment_slots: usize,
27 pub blocks: Vec<NativeAbiBlock>,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct NativeAbiBlock {
32 pub id: BlockId,
33 pub params: Vec<ValueId>,
34 pub stmts: Vec<NativeAbiStmt>,
35 pub terminator: NativeTerminator,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub enum NativeAbiStmt {
40 Literal {
41 dest: ValueId,
42 literal: NativeLiteral,
43 },
44 Primitive {
45 dest: ValueId,
46 op: typed_ir::PrimitiveOp,
47 args: Vec<ValueId>,
48 },
49 DirectCall {
50 dest: ValueId,
51 target: String,
52 args: Vec<ValueId>,
53 },
54 Tuple {
55 dest: ValueId,
56 items: Vec<ValueId>,
57 },
58 Record {
59 dest: ValueId,
60 base: Option<ValueId>,
61 fields: Vec<NativeRecordField>,
62 },
63 RecordWithoutFields {
64 dest: ValueId,
65 base: ValueId,
66 fields: Vec<typed_ir::Name>,
67 },
68 Variant {
69 dest: ValueId,
70 tag: typed_ir::Name,
71 value: Option<ValueId>,
72 },
73 Select {
74 dest: ValueId,
75 base: ValueId,
76 field: typed_ir::Name,
77 },
78 TupleGet {
79 dest: ValueId,
80 tuple: ValueId,
81 index: usize,
82 },
83 VariantTagEq {
84 dest: ValueId,
85 variant: ValueId,
86 tag: typed_ir::Name,
87 },
88 VariantPayload {
89 dest: ValueId,
90 variant: ValueId,
91 },
92 ValueEq {
93 dest: ValueId,
94 left: ValueId,
95 right: ValueId,
96 },
97 BoolAnd {
98 dest: ValueId,
99 left: ValueId,
100 right: ValueId,
101 },
102 LoadEnv {
103 dest: ValueId,
104 slot: usize,
105 },
106 AllocateClosure {
107 dest: ValueId,
108 target: String,
109 environment: Vec<ValueId>,
110 },
111 IndirectClosureCall {
112 dest: ValueId,
113 callee: ValueId,
114 args: Vec<ValueId>,
115 },
116}
117
118pub fn lower_closure_module_to_abi(module: &NativeClosureModule) -> NativeAbiModule {
119 NativeAbiModule {
120 functions: module.functions.iter().map(lower_function).collect(),
121 roots: module.roots.iter().map(lower_function).collect(),
122 }
123}
124
125fn lower_function(function: &NativeClosureFunction) -> NativeAbiFunction {
126 NativeAbiFunction {
127 name: function.name.clone(),
128 params: function.abi.params.clone(),
129 environment_slots: function.abi.environment.slots,
130 blocks: function.blocks.iter().map(lower_block).collect(),
131 }
132}
133
134fn lower_block(block: &NativeClosureBlock) -> NativeAbiBlock {
135 NativeAbiBlock {
136 id: block.id,
137 params: block.params.clone(),
138 stmts: block.stmts.iter().map(lower_stmt).collect(),
139 terminator: block.terminator.clone(),
140 }
141}
142
143fn lower_stmt(stmt: &NativeClosureStmt) -> NativeAbiStmt {
144 match stmt {
145 NativeClosureStmt::LoadEnv { dest, slot } => NativeAbiStmt::LoadEnv {
146 dest: *dest,
147 slot: *slot,
148 },
149 NativeClosureStmt::MakeClosure {
150 dest,
151 target,
152 environment,
153 } => NativeAbiStmt::AllocateClosure {
154 dest: *dest,
155 target: target.clone(),
156 environment: environment.iter().map(|capture| capture.value).collect(),
157 },
158 NativeClosureStmt::ClosureCall { dest, callee, args } => {
159 NativeAbiStmt::IndirectClosureCall {
160 dest: *dest,
161 callee: *callee,
162 args: args.clone(),
163 }
164 }
165 NativeClosureStmt::Native(stmt) => lower_native_stmt(stmt),
166 }
167}
168
169fn lower_native_stmt(stmt: &NativeStmt) -> NativeAbiStmt {
170 match stmt {
171 NativeStmt::Literal { dest, literal } => NativeAbiStmt::Literal {
172 dest: *dest,
173 literal: literal.clone(),
174 },
175 NativeStmt::Primitive { dest, op, args } => NativeAbiStmt::Primitive {
176 dest: *dest,
177 op: *op,
178 args: args.clone(),
179 },
180 NativeStmt::DirectCall { dest, target, args } => NativeAbiStmt::DirectCall {
181 dest: *dest,
182 target: target.clone(),
183 args: args.clone(),
184 },
185 NativeStmt::Tuple { dest, items } => NativeAbiStmt::Tuple {
186 dest: *dest,
187 items: items.clone(),
188 },
189 NativeStmt::Record { dest, base, fields } => NativeAbiStmt::Record {
190 dest: *dest,
191 base: *base,
192 fields: fields.clone(),
193 },
194 NativeStmt::RecordWithoutFields { dest, base, fields } => {
195 NativeAbiStmt::RecordWithoutFields {
196 dest: *dest,
197 base: *base,
198 fields: fields.clone(),
199 }
200 }
201 NativeStmt::Variant { dest, tag, value } => NativeAbiStmt::Variant {
202 dest: *dest,
203 tag: tag.clone(),
204 value: *value,
205 },
206 NativeStmt::Select { dest, base, field } => NativeAbiStmt::Select {
207 dest: *dest,
208 base: *base,
209 field: field.clone(),
210 },
211 NativeStmt::TupleGet { dest, tuple, index } => NativeAbiStmt::TupleGet {
212 dest: *dest,
213 tuple: *tuple,
214 index: *index,
215 },
216 NativeStmt::VariantTagEq { dest, variant, tag } => NativeAbiStmt::VariantTagEq {
217 dest: *dest,
218 variant: *variant,
219 tag: tag.clone(),
220 },
221 NativeStmt::VariantPayload { dest, variant } => NativeAbiStmt::VariantPayload {
222 dest: *dest,
223 variant: *variant,
224 },
225 NativeStmt::ValueEq { dest, left, right } => NativeAbiStmt::ValueEq {
226 dest: *dest,
227 left: *left,
228 right: *right,
229 },
230 NativeStmt::BoolAnd { dest, left, right } => NativeAbiStmt::BoolAnd {
231 dest: *dest,
232 left: *left,
233 right: *right,
234 },
235 NativeStmt::MakeClosure {
236 dest,
237 target,
238 captures,
239 } => NativeAbiStmt::AllocateClosure {
240 dest: *dest,
241 target: target.clone(),
242 environment: captures.clone(),
243 },
244 NativeStmt::ClosureCall { dest, callee, args } => NativeAbiStmt::IndirectClosureCall {
245 dest: *dest,
246 callee: *callee,
247 args: args.clone(),
248 },
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use crate::closure::{NativeClosureCapture, closure_convert_module};
255 use crate::control_ir::{NativeBlock, NativeFunction, NativeModule, NativeTerminator};
256
257 use super::*;
258
259 #[test]
260 fn lowers_closure_function_abi_shape() {
261 let function = NativeFunction {
262 name: "root#lambda0".to_string(),
263 captures: vec![ValueId(0)],
264 params: vec![ValueId(0), ValueId(1)],
265 blocks: vec![NativeBlock {
266 id: BlockId(0),
267 params: vec![ValueId(0), ValueId(1)],
268 stmts: Vec::new(),
269 terminator: NativeTerminator::Return(ValueId(1)),
270 }],
271 };
272 let module = NativeModule {
273 functions: vec![function],
274 roots: Vec::new(),
275 };
276 let closure_module = closure_convert_module(&module);
277
278 let abi = lower_closure_module_to_abi(&closure_module);
279
280 assert_eq!(
281 abi.functions,
282 vec![NativeAbiFunction {
283 name: "root#lambda0".to_string(),
284 params: vec![ValueId(1)],
285 environment_slots: 1,
286 blocks: vec![NativeAbiBlock {
287 id: BlockId(0),
288 params: vec![ValueId(1)],
289 stmts: vec![NativeAbiStmt::LoadEnv {
290 dest: ValueId(0),
291 slot: 0,
292 }],
293 terminator: NativeTerminator::Return(ValueId(1)),
294 }],
295 }]
296 );
297 }
298
299 #[test]
300 fn lowers_closure_allocation_and_indirect_call() {
301 let closure_module = NativeClosureModule {
302 functions: Vec::new(),
303 roots: vec![crate::closure::NativeClosureFunction {
304 name: "root".to_string(),
305 params: Vec::new(),
306 abi: crate::closure::NativeClosureAbi {
307 code: crate::closure::NativeClosureCodeRef {
308 function: "root".to_string(),
309 },
310 environment: crate::closure::NativeClosureEnvRef { slots: 0 },
311 params: Vec::new(),
312 },
313 environment: crate::closure::NativeClosureEnvironment { slots: Vec::new() },
314 blocks: vec![crate::closure::NativeClosureBlock {
315 id: BlockId(0),
316 params: Vec::new(),
317 stmts: vec![
318 NativeClosureStmt::MakeClosure {
319 dest: ValueId(2),
320 target: "root#lambda0".to_string(),
321 environment: vec![
322 NativeClosureCapture {
323 slot: 0,
324 value: ValueId(0),
325 },
326 NativeClosureCapture {
327 slot: 1,
328 value: ValueId(1),
329 },
330 ],
331 },
332 NativeClosureStmt::ClosureCall {
333 dest: ValueId(3),
334 callee: ValueId(2),
335 args: vec![ValueId(4)],
336 },
337 ],
338 terminator: NativeTerminator::Return(ValueId(3)),
339 }],
340 }],
341 };
342
343 let abi = lower_closure_module_to_abi(&closure_module);
344
345 assert_eq!(
346 abi.roots[0].blocks[0].stmts,
347 vec![
348 NativeAbiStmt::AllocateClosure {
349 dest: ValueId(2),
350 target: "root#lambda0".to_string(),
351 environment: vec![ValueId(0), ValueId(1)],
352 },
353 NativeAbiStmt::IndirectClosureCall {
354 dest: ValueId(3),
355 callee: ValueId(2),
356 args: vec![ValueId(4)],
357 },
358 ]
359 );
360 }
361}