1use super::types::*;
7use shape_ast::ast::{self, Expr, Span, Spanned, Statement};
8
9pub struct MirBuilder {
11 name: String,
13 blocks: Vec<BasicBlock>,
15 current_stmts: Vec<MirStatement>,
17 current_block: BasicBlockId,
19 next_block_id: u32,
21 next_local: u16,
23 next_point: u32,
25 next_loan: u32,
27 locals: Vec<(String, SlotId, LocalTypeInfo)>,
29 param_slots: Vec<SlotId>,
31 span: Span,
33}
34
35impl MirBuilder {
36 pub fn new(name: String, span: Span) -> Self {
37 MirBuilder {
38 name,
39 blocks: Vec::new(),
40 current_stmts: Vec::new(),
41 current_block: BasicBlockId(0),
42 next_block_id: 1,
43 next_local: 0,
44 next_point: 0,
45 next_loan: 0,
46 locals: Vec::new(),
47 param_slots: Vec::new(),
48 span,
49 }
50 }
51
52 pub fn alloc_local(&mut self, name: String, type_info: LocalTypeInfo) -> SlotId {
54 let slot = SlotId(self.next_local);
55 self.next_local += 1;
56 self.locals.push((name, slot, type_info));
57 slot
58 }
59
60 pub fn add_param(&mut self, name: String, type_info: LocalTypeInfo) -> SlotId {
62 let slot = self.alloc_local(name, type_info);
63 self.param_slots.push(slot);
64 slot
65 }
66
67 pub fn next_point(&mut self) -> Point {
69 let p = Point(self.next_point);
70 self.next_point += 1;
71 p
72 }
73
74 pub fn next_loan(&mut self) -> LoanId {
76 let l = LoanId(self.next_loan);
77 self.next_loan += 1;
78 l
79 }
80
81 pub fn new_block(&mut self) -> BasicBlockId {
83 let id = BasicBlockId(self.next_block_id);
84 self.next_block_id += 1;
85 id
86 }
87
88 pub fn push_stmt(&mut self, kind: StatementKind, span: Span) {
90 let point = self.next_point();
91 self.current_stmts.push(MirStatement { kind, span, point });
92 }
93
94 pub fn finish_block(&mut self, terminator_kind: TerminatorKind, span: Span) {
96 let block = BasicBlock {
97 id: self.current_block,
98 statements: std::mem::take(&mut self.current_stmts),
99 terminator: Terminator {
100 kind: terminator_kind,
101 span,
102 },
103 };
104 self.blocks.push(block);
105 }
106
107 pub fn start_block(&mut self, id: BasicBlockId) {
109 self.current_block = id;
110 self.current_stmts.clear();
111 }
112
113 pub fn build(self) -> MirFunction {
115 let local_types = self.locals.iter().map(|(_, _, t)| t.clone()).collect();
116 MirFunction {
117 name: self.name,
118 blocks: self.blocks,
119 num_locals: self.next_local,
120 param_slots: self.param_slots,
121 local_types,
122 span: self.span,
123 }
124 }
125}
126
127pub fn lower_function(
129 name: &str,
130 params: &[ast::FunctionParameter],
131 body: &[Statement],
132 span: Span,
133) -> MirFunction {
134 let mut builder = MirBuilder::new(name.to_string(), span);
135
136 for param in params {
138 let param_name = param.simple_name().unwrap_or("_").to_string();
139 let type_info = if param.is_reference {
140 LocalTypeInfo::NonCopy } else {
142 LocalTypeInfo::Unknown };
144 builder.add_param(param_name, type_info);
145 }
146
147 let exit_block = builder.new_block();
149
150 lower_statements(&mut builder, body, exit_block);
152
153 if builder.current_stmts.len() > 0 || builder.blocks.len() == 0 {
155 builder.finish_block(TerminatorKind::Goto(exit_block), span);
156 }
157
158 builder.start_block(exit_block);
160 builder.finish_block(TerminatorKind::Return, span);
161
162 builder.build()
163}
164
165fn lower_statements(builder: &mut MirBuilder, stmts: &[Statement], exit_block: BasicBlockId) {
167 for stmt in stmts {
168 lower_statement(builder, stmt, exit_block);
169 }
170}
171
172fn lower_statement(builder: &mut MirBuilder, stmt: &Statement, exit_block: BasicBlockId) {
174 match stmt {
175 Statement::VariableDecl(decl, span) => {
176 lower_var_decl(builder, decl, *span);
177 }
178 Statement::Assignment(assign, span) => {
179 lower_assignment(builder, assign, *span);
180 }
181 Statement::Return(value, span) => {
182 if let Some(expr) = value {
183 let result_slot = lower_expr_to_temp(builder, expr);
184 builder.push_stmt(
185 StatementKind::Assign(
186 Place::Local(SlotId(0)), Rvalue::Use(Operand::Move(Place::Local(result_slot))),
188 ),
189 *span,
190 );
191 }
192 builder.finish_block(TerminatorKind::Return, *span);
193 let dead_block = builder.new_block();
195 builder.start_block(dead_block);
196 }
197 Statement::Expression(expr, span) => {
198 let _slot = lower_expr_to_temp(builder, expr);
200 let _ = span; }
202 Statement::If(if_stmt, span) => {
203 lower_if(builder, if_stmt, *span, exit_block);
204 }
205 Statement::While(while_loop, span) => {
206 lower_while(
207 builder,
208 &while_loop.condition,
209 &while_loop.body,
210 *span,
211 exit_block,
212 );
213 }
214 Statement::For(for_loop, span) => {
215 lower_for_loop(builder, for_loop, *span, exit_block);
216 }
217 _ => {
218 let span = stmt.span().unwrap_or(Span::DUMMY);
221 builder.push_stmt(StatementKind::Nop, span);
222 }
223 }
224}
225
226fn lower_var_decl(builder: &mut MirBuilder, decl: &ast::VariableDecl, span: Span) {
228 let name = decl.pattern.as_identifier().unwrap_or("_").to_string();
229 let type_info = LocalTypeInfo::Unknown; let slot = builder.alloc_local(name, type_info);
231
232 if let Some(init_expr) = &decl.value {
233 let init_slot = lower_expr_to_temp(builder, init_expr);
234 let operand = match decl.ownership {
236 ast::OwnershipModifier::Move => Operand::Move(Place::Local(init_slot)),
237 ast::OwnershipModifier::Clone => Operand::Copy(Place::Local(init_slot)),
238 ast::OwnershipModifier::Inferred => {
239 Operand::Move(Place::Local(init_slot))
242 }
243 };
244 let rvalue = match decl.ownership {
245 ast::OwnershipModifier::Clone => Rvalue::Clone(operand),
246 _ => Rvalue::Use(operand),
247 };
248 builder.push_stmt(StatementKind::Assign(Place::Local(slot), rvalue), span);
249 }
250}
251
252fn lower_assignment(builder: &mut MirBuilder, assign: &ast::Assignment, span: Span) {
254 let value_slot = lower_expr_to_temp(builder, &assign.value);
255 builder.push_stmt(
258 StatementKind::Assign(
259 Place::Local(SlotId(0)), Rvalue::Use(Operand::Move(Place::Local(value_slot))),
261 ),
262 span,
263 );
264}
265
266fn lower_expr_to_temp(builder: &mut MirBuilder, expr: &Expr) -> SlotId {
269 let span = expr.span();
270 let temp = builder.alloc_local("_tmp".to_string(), LocalTypeInfo::Unknown);
271
272 match expr {
273 Expr::Literal(_, _) => {
274 builder.push_stmt(
275 StatementKind::Assign(
276 Place::Local(temp),
277 Rvalue::Use(Operand::Constant(MirConstant::Int(0))),
278 ),
279 span,
280 );
281 }
282 Expr::Identifier(_, _) => {
283 builder.push_stmt(
285 StatementKind::Assign(
286 Place::Local(temp),
287 Rvalue::Use(Operand::Copy(Place::Local(SlotId(0)))),
288 ),
289 span,
290 );
291 }
292 Expr::Reference {
293 expr: inner,
294 is_mutable,
295 span: ref_span,
296 } => {
297 let inner_slot = lower_expr_to_temp(builder, inner);
298 let kind = if *is_mutable {
299 BorrowKind::Exclusive
300 } else {
301 BorrowKind::Shared
302 };
303 builder.push_stmt(
304 StatementKind::Assign(
305 Place::Local(temp),
306 Rvalue::Borrow(kind, Place::Local(inner_slot)),
307 ),
308 *ref_span,
309 );
310 }
311 Expr::BinaryOp { left, right, .. } => {
312 let l = lower_expr_to_temp(builder, left);
313 let r = lower_expr_to_temp(builder, right);
314 builder.push_stmt(
315 StatementKind::Assign(
316 Place::Local(temp),
317 Rvalue::BinaryOp(
318 BinOp::Add, Operand::Copy(Place::Local(l)),
320 Operand::Copy(Place::Local(r)),
321 ),
322 ),
323 span,
324 );
325 }
326 Expr::FunctionCall { args, .. } => {
327 let arg_ops: Vec<Operand> = args
329 .iter()
330 .map(|a| {
331 let s = lower_expr_to_temp(builder, a);
332 Operand::Move(Place::Local(s))
333 })
334 .collect();
335 builder.push_stmt(
336 StatementKind::Assign(Place::Local(temp), Rvalue::Aggregate(arg_ops)),
337 span,
338 );
339 }
340 _ => {
341 builder.push_stmt(
343 StatementKind::Assign(
344 Place::Local(temp),
345 Rvalue::Use(Operand::Constant(MirConstant::None)),
346 ),
347 span,
348 );
349 }
350 }
351
352 temp
353}
354
355fn lower_if(
357 builder: &mut MirBuilder,
358 if_stmt: &ast::IfStatement,
359 span: Span,
360 exit_block: BasicBlockId,
361) {
362 let cond_slot = lower_expr_to_temp(builder, &if_stmt.condition);
363
364 let then_block = builder.new_block();
365 let else_block = builder.new_block();
366 let merge_block = builder.new_block();
367
368 builder.finish_block(
369 TerminatorKind::SwitchBool {
370 operand: Operand::Copy(Place::Local(cond_slot)),
371 true_bb: then_block,
372 false_bb: if if_stmt.else_body.is_some() {
373 else_block
374 } else {
375 merge_block
376 },
377 },
378 span,
379 );
380
381 builder.start_block(then_block);
383 lower_statements(builder, &if_stmt.then_body, exit_block);
384 builder.finish_block(TerminatorKind::Goto(merge_block), span);
385
386 if let Some(else_body) = &if_stmt.else_body {
388 builder.start_block(else_block);
389 lower_statements(builder, else_body, exit_block);
390 builder.finish_block(TerminatorKind::Goto(merge_block), span);
391 }
392
393 builder.start_block(merge_block);
395}
396
397fn lower_while(
399 builder: &mut MirBuilder,
400 cond: &Expr,
401 body: &[Statement],
402 span: Span,
403 exit_block: BasicBlockId,
404) {
405 let header = builder.new_block();
406 let body_block = builder.new_block();
407 let after = builder.new_block();
408
409 builder.finish_block(TerminatorKind::Goto(header), span);
410
411 builder.start_block(header);
413 let cond_slot = lower_expr_to_temp(builder, cond);
414 builder.finish_block(
415 TerminatorKind::SwitchBool {
416 operand: Operand::Copy(Place::Local(cond_slot)),
417 true_bb: body_block,
418 false_bb: after,
419 },
420 span,
421 );
422
423 builder.start_block(body_block);
425 lower_statements(builder, body, exit_block);
426 builder.finish_block(TerminatorKind::Goto(header), span);
427
428 builder.start_block(after);
430}
431
432fn lower_for_loop(
434 builder: &mut MirBuilder,
435 for_loop: &ast::ForLoop,
436 span: Span,
437 exit_block: BasicBlockId,
438) {
439 let iter_expr = match &for_loop.init {
441 ast::ForInit::ForIn { iter, .. } => iter,
442 ast::ForInit::ForC { condition, .. } => condition,
443 };
444
445 let _iter_slot = lower_expr_to_temp(builder, iter_expr);
446 let header = builder.new_block();
447 let body_block = builder.new_block();
448 let after = builder.new_block();
449
450 builder.finish_block(TerminatorKind::Goto(header), span);
451
452 builder.start_block(header);
453 builder.finish_block(
454 TerminatorKind::SwitchBool {
455 operand: Operand::Constant(MirConstant::Bool(true)),
456 true_bb: body_block,
457 false_bb: after,
458 },
459 span,
460 );
461
462 builder.start_block(body_block);
463 lower_statements(builder, &for_loop.body, exit_block);
464 builder.finish_block(TerminatorKind::Goto(header), span);
465
466 builder.start_block(after);
467}
468
469trait StatementSpan {
471 fn span(&self) -> Option<Span>;
472}
473
474impl StatementSpan for Statement {
475 fn span(&self) -> Option<Span> {
476 match self {
477 Statement::VariableDecl(_, span) => Some(*span),
478 Statement::Assignment(_, span) => Some(*span),
479 Statement::Expression(_, span) => Some(*span),
480 Statement::Return(_, span) => Some(*span),
481 Statement::If(_, span) => Some(*span),
482 Statement::While(_, span) => Some(*span),
483 Statement::For(_, span) => Some(*span),
484 _ => None,
485 }
486 }
487}
488
489#[cfg(test)]
490mod tests {
491 use super::*;
492 use crate::mir::cfg::ControlFlowGraph;
493 use crate::mir::liveness;
494 use shape_ast::ast::{self, DestructurePattern, OwnershipModifier, VarKind};
495
496 fn span() -> Span {
497 Span { start: 0, end: 1 }
498 }
499
500 #[test]
501 fn test_lower_empty_function() {
502 let mir = lower_function("empty", &[], &[], span());
503 assert_eq!(mir.name, "empty");
504 assert!(mir.blocks.len() >= 2); assert_eq!(mir.num_locals, 0);
506 }
507
508 #[test]
509 fn test_lower_simple_var_decl() {
510 let body = vec![Statement::VariableDecl(
511 ast::VariableDecl {
512 kind: VarKind::Let,
513 is_mut: false,
514 pattern: DestructurePattern::Identifier("x".to_string(), span()),
515 type_annotation: None,
516 value: Some(Expr::Literal(ast::Literal::Int(42), span())),
517 ownership: OwnershipModifier::Inferred,
518 },
519 span(),
520 )];
521 let mir = lower_function("test", &[], &body, span());
522 assert!(mir.num_locals >= 1); assert!(mir.blocks.len() >= 2);
525 }
526
527 #[test]
528 fn test_lower_with_liveness() {
529 let body = vec![
531 Statement::VariableDecl(
532 ast::VariableDecl {
533 kind: VarKind::Let,
534 is_mut: false,
535 pattern: DestructurePattern::Identifier("x".to_string(), span()),
536 type_annotation: None,
537 value: Some(Expr::Literal(ast::Literal::Int(1), span())),
538 ownership: OwnershipModifier::Inferred,
539 },
540 span(),
541 ),
542 Statement::VariableDecl(
543 ast::VariableDecl {
544 kind: VarKind::Let,
545 is_mut: false,
546 pattern: DestructurePattern::Identifier("y".to_string(), span()),
547 type_annotation: None,
548 value: Some(Expr::Identifier("x".to_string(), span())),
549 ownership: OwnershipModifier::Inferred,
550 },
551 span(),
552 ),
553 ];
554 let mir = lower_function("test", &[], &body, span());
555 let cfg = ControlFlowGraph::build(&mir);
556 let _liveness = liveness::compute_liveness(&mir, &cfg);
557 }
559}