@@ -16,6 +16,7 @@ pub fn compile_to_ir(pt: &LuaParseTree) -> LuaIR {
16
16
}
17
17
18
18
/// Represents a compiler which translates a given Lua parse tree to an SSA IR.
19
+ /// The compiler assumes that the `_ENV` variable is always stored in register 0!
19
20
struct LuaToIR<'a> {
20
21
pt: &'a LuaParseTree,
21
22
reg_map: RegisterMap<'a>,
@@ -43,22 +44,7 @@ impl<'a> LuaToIR<'a> {
43
44
ridx: RIdx(ridx),
44
45
ref nodes,
45
46
} if ridx == lua5_3_y::R_STAT => {
46
- debug_assert!(nodes.len() == 3);
47
- match nodes[1] {
48
- Term { lexeme } if lexeme.tok_id() == lua5_3_l::T_EQ => {
49
- let value = self.compile_expr(&nodes[2]);
50
- let name = self.compile_variable(&nodes[0]);
51
- // because we are creating an IR which is in SSA form, it
52
- // means that each assignment creates a new register
53
- let reg = self.reg_map.get_new_reg();
54
- // if a variable is assigned a value multiple times, we have
55
- // to make sure that the map knows the new register which
56
- // holds the new value
57
- self.reg_map.set_reg(name, reg);
58
- self.instrs.push(HLInstr(Opcode::MOV, reg, value, 0));
59
- }
60
- _ => {}
61
- }
47
+ self.compile_stat(nodes);
62
48
}
63
49
Nonterm { ridx: _, ref nodes } => {
64
50
for i in (0..nodes.len()).rev() {
@@ -73,6 +59,32 @@ impl<'a> LuaToIR<'a> {
73
59
LuaIR::new(self.instrs, self.const_map, self.reg_map.get_lifetimes())
74
60
}
75
61
62
+ fn compile_stat(&mut self, nodes: &Vec<Node<u8>>) {
63
+ debug_assert!(nodes.len() == 3);
64
+ match nodes[1] {
65
+ Term { lexeme } if lexeme.tok_id() == lua5_3_l::T_EQ => {
66
+ // x = 3 => _ENV["x"] = 3
67
+ // compile the expression on the right
68
+ let value = self.compile_expr(&nodes[2]);
69
+ // load a reference to _ENV
70
+ let env_reg = self.reg_map.get_reg("_ENV").unwrap();
71
+ // prepare the attribute for _ENV which is the name of the variable
72
+ let name = self.compile_variable(&nodes[0]);
73
+ let name_index = self.const_map.get_str(name.to_string());
74
+ let attr_reg = self.reg_map.get_new_reg();
75
+ self.instrs
76
+ .push(HLInstr(Opcode::LDS, attr_reg, name_index, 0));
77
+ // if a variable is assigned a value multiple times, we have
78
+ // to make sure that the map knows the new register which
79
+ // holds the new value
80
+ self.reg_map.set_reg(name, value);
81
+ self.instrs
82
+ .push(HLInstr(Opcode::SetAttr, env_reg, attr_reg, value));
83
+ }
84
+ _ => {}
85
+ }
86
+ }
87
+
76
88
/// Jumps to the first child of <node> which denotes a variable name.
77
89
fn compile_variable(&self, node: &Node<u8>) -> &'a str {
78
90
let name = LuaToIR::find_term(node, lua5_3_l::T_NAME);
@@ -130,7 +142,24 @@ impl<'a> LuaToIR<'a> {
130
142
self.instrs.push(HLInstr(Opcode::LDS, reg, short_str, 0));
131
143
reg
132
144
}
133
- _ => self.reg_map.get_reg(value),
145
+ lua5_3_l::T_NAME => {
146
+ // if the variable is in a register, then we can return reg number
147
+ // otherwise we have to generate code for `_ENV[<name>]`
148
+ self.reg_map.get_reg(value).unwrap_or_else(|| {
149
+ let env_reg = self.reg_map.get_reg("_ENV").unwrap();
150
+ let name_index = self.const_map.get_str(value.to_string());
151
+ let attr_reg = self.reg_map.get_new_reg();
152
+ self.instrs
153
+ .push(HLInstr(Opcode::LDS, attr_reg, name_index, 0));
154
+ let reg = self.reg_map.get_new_reg();
155
+ self.instrs
156
+ .push(HLInstr(Opcode::GetAttr, reg, env_reg, attr_reg));
157
+ reg
158
+ })
159
+ }
160
+ _ => panic!(
161
+ "Cannot compile terminals that are not variable names, numbers or strings."
162
+ ),
134
163
}
135
164
}
136
165
}
@@ -187,20 +216,21 @@ mod tests {
187
216
let pt = LuaParseTree::from_str(String::from("x = 1 + 2 * 3 / 2 ^ 2.0 // 1 - 2"));
188
217
let ir = compile_to_ir(&pt.unwrap());
189
218
let expected_instrs = vec![
190
- HLInstr(Opcode::LDI, 0, 0, 0),
191
- HLInstr(Opcode::LDI, 1, 1, 0),
192
- HLInstr(Opcode::LDI, 2, 2, 0),
193
- HLInstr(Opcode::MUL, 3, 1, 2),
194
- HLInstr(Opcode::LDI, 4, 1, 0),
195
- HLInstr(Opcode::LDF, 5, 0, 0),
196
- HLInstr(Opcode::EXP, 6, 4, 5),
197
- HLInstr(Opcode::DIV, 7, 3, 6),
198
- HLInstr(Opcode::LDI, 8, 0, 0),
199
- HLInstr(Opcode::FDIV, 9, 7, 8),
200
- HLInstr(Opcode::ADD, 10, 0, 9),
201
- HLInstr(Opcode::LDI, 11, 1, 0),
202
- HLInstr(Opcode::SUB, 12, 10, 11),
203
- HLInstr(Opcode::MOV, 13, 12, 0),
219
+ HLInstr(Opcode::LDI, 1, 0, 0),
220
+ HLInstr(Opcode::LDI, 2, 1, 0),
221
+ HLInstr(Opcode::LDI, 3, 2, 0),
222
+ HLInstr(Opcode::MUL, 4, 2, 3),
223
+ HLInstr(Opcode::LDI, 5, 1, 0),
224
+ HLInstr(Opcode::LDF, 6, 0, 0),
225
+ HLInstr(Opcode::EXP, 7, 5, 6),
226
+ HLInstr(Opcode::DIV, 8, 4, 7),
227
+ HLInstr(Opcode::LDI, 9, 0, 0),
228
+ HLInstr(Opcode::FDIV, 10, 8, 9),
229
+ HLInstr(Opcode::ADD, 11, 1, 10),
230
+ HLInstr(Opcode::LDI, 12, 1, 0),
231
+ HLInstr(Opcode::SUB, 13, 11, 12),
232
+ HLInstr(Opcode::LDS, 14, 0, 0),
233
+ HLInstr(Opcode::SetAttr, 0, 14, 13),
204
234
];
205
235
assert_eq!(ir.instrs.len(), expected_instrs.len());
206
236
for (lhs, rhs) in ir.instrs.iter().zip(expected_instrs.iter()) {
@@ -213,11 +243,15 @@ mod tests {
213
243
regs[i.1] = !regs[i.1];
214
244
// if at any point this assertion fails, it means that a register has been
215
245
// assigned a value multiple times
216
- assert!(regs[i.1]);
246
+ // SetAttr only updates the state of a register, so it doesn't mess up the
247
+ // correctness of the SSA
248
+ if i.0 != Opcode::SetAttr {
249
+ assert!(regs[i.1]);
250
+ }
217
251
}
218
252
// check lifetimes
219
253
let expected_lifetimes = vec![
220
- Lifetime::with_end_point(0, 1 ),
254
+ Lifetime::with_end_point(0, 15 ),
221
255
Lifetime::with_end_point(1, 2),
222
256
Lifetime::with_end_point(2, 3),
223
257
Lifetime::with_end_point(3, 4),
@@ -231,6 +265,7 @@ mod tests {
231
265
Lifetime::with_end_point(11, 12),
232
266
Lifetime::with_end_point(12, 13),
233
267
Lifetime::with_end_point(13, 14),
268
+ Lifetime::with_end_point(14, 15),
234
269
];
235
270
assert_eq!(ir.lifetimes.len(), expected_lifetimes.len());
236
271
for (lhs, rhs) in ir.lifetimes.iter().zip(expected_lifetimes.iter()) {
@@ -249,6 +284,81 @@ mod tests {
249
284
for (lhs, rhs) in floats.iter().zip(expected_floats.iter()) {
250
285
assert_eq!(lhs, rhs);
251
286
}
252
- assert_eq!(ir.const_map.get_strings().len(), 0);
287
+ let expected_strings = vec!["x"];
288
+ let strings = ir.const_map.get_strings();
289
+ assert_eq!(strings.len(), expected_strings.len());
290
+ for (lhs, rhs) in strings.iter().zip(expected_strings.iter()) {
291
+ assert_eq!(lhs, rhs);
292
+ }
293
+ }
294
+
295
+ #[test]
296
+ fn correctness_of_ssa_ir2() {
297
+ let pt = LuaParseTree::from_str(String::from("x = 1\ny = x"));
298
+ let ir = compile_to_ir(&pt.unwrap());
299
+ let expected_instrs = vec![
300
+ HLInstr(Opcode::LDI, 1, 0, 0),
301
+ HLInstr(Opcode::LDS, 2, 0, 0),
302
+ HLInstr(Opcode::SetAttr, 0, 2, 1),
303
+ HLInstr(Opcode::LDS, 3, 1, 0),
304
+ HLInstr(Opcode::SetAttr, 0, 3, 1),
305
+ ];
306
+ assert_eq!(ir.instrs.len(), expected_instrs.len());
307
+ for (lhs, rhs) in ir.instrs.iter().zip(expected_instrs.iter()) {
308
+ assert_eq!(lhs, rhs);
309
+ }
310
+ // check that the IR is in SSA form
311
+ let mut regs = Vec::with_capacity(ir.instrs.len());
312
+ regs.resize(ir.instrs.len(), false);
313
+ for i in &ir.instrs {
314
+ regs[i.1] = !regs[i.1];
315
+ // if at any point this assertion fails, it means that a register has been
316
+ // assigned a value multiple times
317
+ if i.0 != Opcode::SetAttr {
318
+ assert!(regs[i.1]);
319
+ }
320
+ }
321
+ // check lifetimes
322
+ let expected_lifetimes = vec![
323
+ Lifetime::with_end_point(0, 4),
324
+ Lifetime::with_end_point(1, 4),
325
+ Lifetime::with_end_point(2, 3),
326
+ Lifetime::with_end_point(3, 4),
327
+ ];
328
+ assert_eq!(ir.lifetimes.len(), expected_lifetimes.len());
329
+ for (lhs, rhs) in ir.lifetimes.iter().zip(expected_lifetimes.iter()) {
330
+ assert_eq!(lhs, rhs);
331
+ }
332
+ // check constats map
333
+ let expected_ints = vec![1];
334
+ let ints = ir.const_map.get_ints();
335
+ assert_eq!(ints.len(), expected_ints.len());
336
+ for (lhs, rhs) in ints.iter().zip(expected_ints.iter()) {
337
+ assert_eq!(lhs, rhs);
338
+ }
339
+ assert!(ir.const_map.get_floats().is_empty());
340
+ let expected_strings = vec!["x", "y"];
341
+ let strings = ir.const_map.get_strings();
342
+ assert_eq!(strings.len(), expected_strings.len());
343
+ for (lhs, rhs) in strings.iter().zip(expected_strings.iter()) {
344
+ assert_eq!(lhs, rhs);
345
+ }
253
346
}
347
+
348
+ #[test]
349
+ fn generates_get_attr_instr() {
350
+ let pt = LuaParseTree::from_str(String::from("x = y"));
351
+ let ir = compile_to_ir(&pt.unwrap());
352
+ let expected_instrs = vec![
353
+ HLInstr(Opcode::LDS, 1, 0, 0),
354
+ HLInstr(Opcode::GetAttr, 2, 0, 1),
355
+ HLInstr(Opcode::LDS, 3, 1, 0),
356
+ HLInstr(Opcode::SetAttr, 0, 3, 2),
357
+ ];
358
+ assert_eq!(ir.instrs.len(), expected_instrs.len());
359
+ for (lhs, rhs) in ir.instrs.iter().zip(expected_instrs.iter()) {
360
+ assert_eq!(lhs, rhs);
361
+ }
362
+ }
363
+
254
364
}
0 commit comments