1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//! Conditional execution
use super::*;
/// Conditional execution
#[derive(Clone)]
pub struct Branch {
branches: Vec<(Expr, Block)>,
default: Option<Block>,
}
impl Branch {
pub fn new() -> Self {
Self {
branches: vec![],
default: None,
}
}
/// Expects a condition that evaluates to boolean `true`
pub fn add_condition(&mut self, condition: Expr) -> &mut Block {
self.branches.push((condition, Block::new()));
let (_, block) = self.branches.last_mut().unwrap();
block
}
/// `Block` to execute if no condition evaluates to `true`
pub fn default_condition(&mut self) -> &mut Block {
self.default = Some(Block::new());
self.default.as_mut().unwrap()
}
}
impl HirLowering for Branch {
fn lower<'hir, 'lir>(&'hir self, runtime: &mut HirLoweringRuntime<'lir>)
where
'hir: 'lir,
{
// branches without conditions but a default block make no sense
if self.branches.is_empty() && self.default.is_some() {
panic!("cannot lower branch: no conditions");
}
// push a new branch section. the stack is not really
// required (?) but it's quite handy to always have access
// to the current branch
let branch = runtime.push_branch();
let branch_start = branch.start();
let branch_end = branch.end();
// the branch section starts now
runtime.emit(LirElement::Label(branch_start));
for (condition, block) in self.branches.iter() {
let cond = runtime.branch_mut().unwrap().add_condition();
// declare the start of a new condition
runtime.emit(LirElement::Label(cond.start()));
// lower condition expression. if this evaluates to false at runtime, jump
// to the condition section's end - which is also the starting offset
// for the next condition section.
condition.lower(runtime);
runtime.emit(LirElement::jump_conditional(false, cond.end()));
// lower the actual code that should be executed if the condition is true
block.lower(runtime);
// if above block was executed, terminate the whole branch by jumping to its
// end label
runtime.emit(LirElement::jump(branch_end.clone()));
runtime.emit(LirElement::Label(cond.end()));
}
if let Some(default_block) = &self.default {
default_block.lower(runtime);
}
// the branche's end label is a jump target for every sucessfully
// executed condition block
runtime.emit(LirElement::Label(branch_end));
// branch has been lowered -> cleanup
runtime.pop_branch().unwrap();
}
}