@@ -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\n y = 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