Skip to content

Commit 9dcdb4d

Browse files
committed
feat: ir generator for while loops
1 parent 05a9009 commit 9dcdb4d

File tree

5 files changed

+169
-32
lines changed

5 files changed

+169
-32
lines changed

crates/mun_codegen/src/ir/body.rs

+99-30
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub(crate) struct BodyIrGenerator<'a, 'b, D: IrDatabase> {
3535
function_map: &'a HashMap<mun_hir::Function, FunctionValue>,
3636
dispatch_table: &'b DispatchTable,
3737
active_loop: Option<LoopInfo>,
38+
hir_function: hir::Function,
3839
}
3940

4041
impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
@@ -69,6 +70,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
6970
function_map,
7071
dispatch_table,
7172
active_loop: None,
73+
hir_function,
7274
}
7375
}
7476

@@ -107,11 +109,20 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
107109
// Construct a return statement from the returned value of the body if a return is expected
108110
// in the first place. If the return type of the body is `never` there is no need to
109111
// generate a return statement.
110-
let ret_type = &self.infer[self.body.body_expr()];
111-
if let Some(value) = ret_value {
112-
self.builder.build_return(Some(&value));
113-
} else if !ret_type.is_never() {
114-
self.builder.build_return(None);
112+
let block_ret_type = &self.infer[self.body.body_expr()];
113+
let fn_ret_type = self
114+
.hir_function
115+
.ty(self.db)
116+
.callable_sig(self.db)
117+
.unwrap()
118+
.ret()
119+
.clone();
120+
if !block_ret_type.is_never() {
121+
if fn_ret_type.is_empty() {
122+
self.builder.build_return(None);
123+
} else if let Some(value) = ret_value {
124+
self.builder.build_return(Some(&value));
125+
}
115126
}
116127
}
117128

@@ -143,6 +154,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
143154
} => self.gen_if(expr, *condition, *then_branch, *else_branch),
144155
Expr::Return { expr: ret_expr } => self.gen_return(expr, *ret_expr),
145156
Expr::Loop { body } => self.gen_loop(expr, *body),
157+
Expr::While { condition, body } => self.gen_while(expr, *condition, *body),
146158
Expr::Break { expr: break_expr } => self.gen_break(expr, *break_expr),
147159
_ => unimplemented!("unimplemented expr type {:?}", &body[expr]),
148160
}
@@ -178,6 +190,11 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
178190
}
179191
}
180192

193+
/// Constructs an empty struct value e.g. `{}`
194+
fn gen_empty(&mut self) -> BasicValueEnum {
195+
self.module.get_context().const_struct(&[], false).into()
196+
}
197+
181198
/// Generates IR for the specified block expression.
182199
fn gen_block(
183200
&mut self,
@@ -193,16 +210,13 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
193210
self.gen_let_statement(*pat, *initializer);
194211
}
195212
Statement::Expr(expr) => {
196-
self.gen_expr(*expr);
197-
198213
// No need to generate code after a statement that has a `never` return type.
199-
if self.infer[*expr].is_never() {
200-
return None;
201-
}
214+
self.gen_expr(*expr)?;
202215
}
203216
};
204217
}
205218
tail.and_then(|expr| self.gen_expr(expr))
219+
.or_else(|| Some(self.gen_empty()))
206220
}
207221

208222
/// Constructs a builder that should be used to emit an `alloca` instruction. These instructions
@@ -365,7 +379,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
365379
};
366380
let place = self.gen_place_expr(lhs_expr);
367381
self.builder.build_store(place, rhs);
368-
None
382+
Some(self.gen_empty())
369383
}
370384
_ => unimplemented!("Operator {:?} is not implemented for float", op),
371385
}
@@ -422,7 +436,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
422436
};
423437
let place = self.gen_place_expr(lhs_expr);
424438
self.builder.build_store(place, rhs);
425-
None
439+
Some(self.gen_empty())
426440
}
427441
_ => unreachable!(format!("Operator {:?} is not implemented for integer", op)),
428442
}
@@ -507,10 +521,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
507521
else_branch: Option<ExprId>,
508522
) -> Option<inkwell::values::BasicValueEnum> {
509523
// Generate IR for the condition
510-
let condition_ir = self
511-
.gen_expr(condition)
512-
.expect("condition must have a value")
513-
.into_int_value();
524+
let condition_ir = self.gen_expr(condition)?.into_int_value();
514525

515526
// Generate the code blocks to branch to
516527
let context = self.module.get_context();
@@ -570,7 +581,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
570581
Some(then_block_ir)
571582
}
572583
} else {
573-
None
584+
Some(self.gen_empty())
574585
}
575586
}
576587

@@ -600,34 +611,92 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
600611
None
601612
}
602613

603-
fn gen_loop(&mut self, _expr: ExprId, body_expr: ExprId) -> Option<BasicValueEnum> {
604-
let context = self.module.get_context();
605-
let loop_block = context.append_basic_block(&self.fn_value, "loop");
606-
let exit_block = context.append_basic_block(&self.fn_value, "exit");
607-
614+
fn gen_loop_block_expr(
615+
&mut self,
616+
block: ExprId,
617+
exit_block: BasicBlock,
618+
) -> (
619+
BasicBlock,
620+
Vec<(BasicValueEnum, BasicBlock)>,
621+
Option<BasicValueEnum>,
622+
) {
608623
// Build a new loop info struct
609624
let loop_info = LoopInfo {
610625
exit_block,
611626
break_values: Vec::new(),
612627
};
613628

629+
// Replace previous loop info
614630
let prev_loop = std::mem::replace(&mut self.active_loop, Some(loop_info));
615631

616-
// Insert an explicit fall through from the current block to the loop
617-
self.builder.build_unconditional_branch(&loop_block);
618-
619632
// Start generating code inside the loop
620-
self.builder.position_at_end(&loop_block);
621-
let _ = self.gen_expr(body_expr);
622-
623-
// Jump to the start of the loop
624-
self.builder.build_unconditional_branch(&loop_block);
633+
let value = self.gen_expr(block);
625634

626635
let LoopInfo {
627636
exit_block,
628637
break_values,
629638
} = std::mem::replace(&mut self.active_loop, prev_loop).unwrap();
630639

640+
(exit_block, break_values, value)
641+
}
642+
643+
fn gen_while(
644+
&mut self,
645+
_expr: ExprId,
646+
condition_expr: ExprId,
647+
body_expr: ExprId,
648+
) -> Option<BasicValueEnum> {
649+
let context = self.module.get_context();
650+
let cond_block = context.append_basic_block(&self.fn_value, "whilecond");
651+
let loop_block = context.append_basic_block(&self.fn_value, "while");
652+
let exit_block = context.append_basic_block(&self.fn_value, "afterwhile");
653+
654+
// Insert an explicit fall through from the current block to the condition check
655+
self.builder.build_unconditional_branch(&cond_block);
656+
657+
// Generate condition block
658+
self.builder.position_at_end(&cond_block);
659+
let condition_ir = self.gen_expr(condition_expr);
660+
if let Some(condition_ir) = condition_ir {
661+
self.builder.build_conditional_branch(
662+
condition_ir.into_int_value(),
663+
&loop_block,
664+
&exit_block,
665+
);
666+
} else {
667+
// If the condition doesn't return a value, we also immediately return without a value.
668+
// This can happen if the expression is a `never` expression.
669+
return None;
670+
}
671+
672+
// Generate loop block
673+
self.builder.position_at_end(&loop_block);
674+
let (exit_block, _, value) = self.gen_loop_block_expr(body_expr, exit_block);
675+
if value.is_some() {
676+
self.builder.build_unconditional_branch(&cond_block);
677+
}
678+
679+
// Generate exit block
680+
self.builder.position_at_end(&exit_block);
681+
682+
Some(self.gen_empty())
683+
}
684+
685+
fn gen_loop(&mut self, _expr: ExprId, body_expr: ExprId) -> Option<BasicValueEnum> {
686+
let context = self.module.get_context();
687+
let loop_block = context.append_basic_block(&self.fn_value, "loop");
688+
let exit_block = context.append_basic_block(&self.fn_value, "exit");
689+
690+
// Insert an explicit fall through from the current block to the loop
691+
self.builder.build_unconditional_branch(&loop_block);
692+
693+
// Generate the body of the loop
694+
self.builder.position_at_end(&loop_block);
695+
let (exit_block, break_values, value) = self.gen_loop_block_expr(body_expr, exit_block);
696+
if value.is_some() {
697+
self.builder.build_unconditional_branch(&loop_block);
698+
}
699+
631700
// Move the builder to the exit block
632701
self.builder.position_at_end(&exit_block);
633702

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
source: crates/mun_codegen/src/test.rs
3+
expression: "fn foo(n:int) {\n while n<3 {\n n += 1;\n };\n\n // This will be completely optimized out\n while n<4 {\n break;\n };\n}"
4+
---
5+
; ModuleID = 'main.mun'
6+
source_filename = "main.mun"
7+
8+
define void @foo(i64) {
9+
body:
10+
br label %whilecond
11+
12+
whilecond: ; preds = %while, %body
13+
%n.0 = phi i64 [ %0, %body ], [ %add, %while ]
14+
%less = icmp slt i64 %n.0, 3
15+
br i1 %less, label %while, label %whilecond3
16+
17+
while: ; preds = %whilecond
18+
%add = add i64 %n.0, 1
19+
br label %whilecond
20+
21+
whilecond3: ; preds = %whilecond
22+
ret void
23+
}
24+

crates/mun_codegen/src/test.rs

+18
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,24 @@ fn loop_break_expr() {
346346
)
347347
}
348348

349+
#[test]
350+
fn while_expr() {
351+
test_snapshot(
352+
r#"
353+
fn foo(n:int) {
354+
while n<3 {
355+
n += 1;
356+
};
357+
358+
// This will be completely optimized out
359+
while n<4 {
360+
break;
361+
};
362+
}
363+
"#,
364+
)
365+
}
366+
349367
fn test_snapshot(text: &str) {
350368
let text = text.trim().replace("\n ", "\n");
351369

crates/mun_hir/src/ty/infer.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,16 @@ mod type_variable;
2323

2424
pub use type_variable::TypeVarId;
2525

26+
#[macro_export]
2627
macro_rules! ty_app {
2728
($ctor:pat, $param:pat) => {
28-
$crate::ty::Ty::Apply($crate::ty::ApplicationTy {
29+
$crate::Ty::Apply($crate::ApplicationTy {
2930
ctor: $ctor,
3031
parameters: $param,
3132
})
3233
};
3334
($ctor:pat) => {
34-
$crate::ty::Ty::Apply($crate::ty::ApplicationTy {
35+
$crate::Ty::Apply($crate::ApplicationTy {
3536
ctor: $ctor,
3637
..
3738
})

crates/mun_runtime/src/test.rs

+25
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,31 @@ fn fibonacci_loop_break() {
241241
assert_invoke_eq!(i64, 46368, driver, "fibonacci", 24i64);
242242
}
243243

244+
#[test]
245+
fn fibonacci_while() {
246+
let mut driver = TestDriver::new(
247+
r#"
248+
fn fibonacci(n:int):int {
249+
let a = 0;
250+
let b = 1;
251+
let i = 1;
252+
while i <= n {
253+
let sum = a + b;
254+
a = b;
255+
b = sum;
256+
i += 1;
257+
}
258+
a
259+
}
260+
"#,
261+
);
262+
263+
assert_invoke_eq!(i64, 5, driver, "fibonacci", 5i64);
264+
assert_invoke_eq!(i64, 89, driver, "fibonacci", 11i64);
265+
assert_invoke_eq!(i64, 987, driver, "fibonacci", 16i64);
266+
assert_invoke_eq!(i64, 46368, driver, "fibonacci", 24i64);
267+
}
268+
244269
#[test]
245270
fn true_is_true() {
246271
let mut driver = TestDriver::new(

0 commit comments

Comments
 (0)