Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global variables are now loaded from _ENV. #11

Merged
merged 2 commits into from
Jan 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions luacompiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ name = "luacompiler"
path = "src/lib/mod.rs"

[dependencies]
cfgrammar = { git="https://github.com/softdevteam/grmtools" }
lrlex = { git="https://github.com/softdevteam/grmtools" }
lrpar = { git="https://github.com/softdevteam/grmtools" }
lrtable = { git="https://github.com/softdevteam/grmtools" }
cfgrammar = "0.2"
lrlex = "0.2"
lrpar = "0.2"
bincode = "1.0.1"
serde = "1.0.80"
serde_derive = "1.0"
Expand All @@ -26,5 +25,6 @@ version = "2.32"
default-features = false

[build-dependencies]
lrpar = { git="https://github.com/softdevteam/grmtools" }
lrlex = { git="https://github.com/softdevteam/grmtools" }
cfgrammar = "0.2"
lrlex = "0.2"
lrpar = "0.2"
11 changes: 6 additions & 5 deletions luacompiler/build.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
extern crate cfgrammar;
extern crate lrlex;
extern crate lrpar;

use cfgrammar::yacc::{YaccKind, YaccOriginalActionKind};
use lrlex::LexerBuilder;
use lrpar::ActionKind;
use lrpar::CTParserBuilder;

fn main() -> Result<(), Box<std::error::Error>> {
let mut ct = CTParserBuilder::<u8>::new_with_storaget()
let lex_rule_ids_map = CTParserBuilder::<u8>::new_with_storaget()
.error_on_conflicts(false)
.action_kind(ActionKind::GenericParseTree);
let lex_rule_ids_map = ct.process_file_in_src("lua5_3/lua5_3.y")?;
LexerBuilder::new()
.yacckind(YaccKind::Original(YaccOriginalActionKind::GenericParseTree))
.process_file_in_src("lua5_3/lua5_3.y")?;
LexerBuilder::<u8>::new()
.rule_ids_map(lex_rule_ids_map)
.process_file_in_src("lua5_3/lua5_3.l")?;
Ok(())
Expand Down
24 changes: 13 additions & 11 deletions luacompiler/src/lib/bytecode/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,19 @@ impl HLInstr {
/// refer to at most 256 constants.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Opcode {
MOV = 0, // R(1) = R(2)
LDI = 1, // R(1) = I(1); load integer from the constant table
LDF = 2, // R(1) = F(1); load float from the constant table
LDS = 3, // R(1) = S(1); load string from the constant table
ADD = 4, // R(1) = R(2) + R(3)
SUB = 5, // R(1) = R(2) - R(3)
MUL = 6, // R(1) = R(2) * R(3)
DIV = 7, // R(1) = R(2) / R(3)
MOD = 8, // R(1) = R(2) % R(3)
FDIV = 9, // R(1) = R(2) // R(3)
EXP = 10, // R(1) = R(2) ^ R(3)
MOV = 0, // R(1) = R(2)
LDI = 1, // R(1) = I(1); load integer from the constant table
LDF = 2, // R(1) = F(1); load float from the constant table
LDS = 3, // R(1) = S(1); load string from the constant table
ADD = 4, // R(1) = R(2) + R(3)
SUB = 5, // R(1) = R(2) - R(3)
MUL = 6, // R(1) = R(2) * R(3)
DIV = 7, // R(1) = R(2) / R(3)
MOD = 8, // R(1) = R(2) % R(3)
FDIV = 9, // R(1) = R(2) // R(3)
EXP = 10, // R(1) = R(2) ^ R(3)
GetAttr = 11, // R(1) = R(2)[R(3)]
SetAttr = 12, // R(1)[R(2)] = R(3)
}

#[cfg(test)]
Expand Down
5 changes: 5 additions & 0 deletions luacompiler/src/lib/bytecode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ impl LuaBytecode {
&self.strings[i as usize]
}

/// Gets the size of the string constant table.
pub fn get_strings_len(&self) -> usize {
self.strings.len()
}

/// Serialize the bytecode to a file using bincode.
pub fn serialize_to_file(&self, file: &str) -> io::Result<()> {
let mut f = File::create(file)?;
Expand Down
178 changes: 144 additions & 34 deletions luacompiler/src/lib/irgen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub fn compile_to_ir(pt: &LuaParseTree) -> LuaIR {
}

/// Represents a compiler which translates a given Lua parse tree to an SSA IR.
/// The compiler assumes that the `_ENV` variable is always stored in register 0!
struct LuaToIR<'a> {
pt: &'a LuaParseTree,
reg_map: RegisterMap<'a>,
Expand Down Expand Up @@ -43,22 +44,7 @@ impl<'a> LuaToIR<'a> {
ridx: RIdx(ridx),
ref nodes,
} if ridx == lua5_3_y::R_STAT => {
debug_assert!(nodes.len() == 3);
match nodes[1] {
Term { lexeme } if lexeme.tok_id() == lua5_3_l::T_EQ => {
let value = self.compile_expr(&nodes[2]);
let name = self.compile_variable(&nodes[0]);
// because we are creating an IR which is in SSA form, it
// means that each assignment creates a new register
let reg = self.reg_map.get_new_reg();
// if a variable is assigned a value multiple times, we have
// to make sure that the map knows the new register which
// holds the new value
self.reg_map.set_reg(name, reg);
self.instrs.push(HLInstr(Opcode::MOV, reg, value, 0));
}
_ => {}
}
self.compile_stat(nodes);
}
Nonterm { ridx: _, ref nodes } => {
for i in (0..nodes.len()).rev() {
Expand All @@ -73,6 +59,32 @@ impl<'a> LuaToIR<'a> {
LuaIR::new(self.instrs, self.const_map, self.reg_map.get_lifetimes())
}

fn compile_stat(&mut self, nodes: &Vec<Node<u8>>) {
debug_assert!(nodes.len() == 3);
match nodes[1] {
Term { lexeme } if lexeme.tok_id() == lua5_3_l::T_EQ => {
// x = 3 => _ENV["x"] = 3
// compile the expression on the right
let value = self.compile_expr(&nodes[2]);
// load a reference to _ENV
let env_reg = self.reg_map.get_reg("_ENV").unwrap();
// prepare the attribute for _ENV which is the name of the variable
let name = self.compile_variable(&nodes[0]);
let name_index = self.const_map.get_str(name.to_string());
let attr_reg = self.reg_map.get_new_reg();
self.instrs
.push(HLInstr(Opcode::LDS, attr_reg, name_index, 0));
// if a variable is assigned a value multiple times, we have
// to make sure that the map knows the new register which
// holds the new value
self.reg_map.set_reg(name, value);
self.instrs
.push(HLInstr(Opcode::SetAttr, env_reg, attr_reg, value));
}
_ => {}
}
}

/// Jumps to the first child of <node> which denotes a variable name.
fn compile_variable(&self, node: &Node<u8>) -> &'a str {
let name = LuaToIR::find_term(node, lua5_3_l::T_NAME);
Expand Down Expand Up @@ -130,7 +142,24 @@ impl<'a> LuaToIR<'a> {
self.instrs.push(HLInstr(Opcode::LDS, reg, short_str, 0));
reg
}
_ => self.reg_map.get_reg(value),
lua5_3_l::T_NAME => {
// if the variable is in a register, then we can return reg number
// otherwise we have to generate code for `_ENV[<name>]`
self.reg_map.get_reg(value).unwrap_or_else(|| {
let env_reg = self.reg_map.get_reg("_ENV").unwrap();
let name_index = self.const_map.get_str(value.to_string());
let attr_reg = self.reg_map.get_new_reg();
self.instrs
.push(HLInstr(Opcode::LDS, attr_reg, name_index, 0));
let reg = self.reg_map.get_new_reg();
self.instrs
.push(HLInstr(Opcode::GetAttr, reg, env_reg, attr_reg));
reg
})
}
_ => panic!(
"Cannot compile terminals that are not variable names, numbers or strings."
),
}
}
}
Expand Down Expand Up @@ -187,20 +216,21 @@ mod tests {
let pt = LuaParseTree::from_str(String::from("x = 1 + 2 * 3 / 2 ^ 2.0 // 1 - 2"));
let ir = compile_to_ir(&pt.unwrap());
let expected_instrs = vec![
HLInstr(Opcode::LDI, 0, 0, 0),
HLInstr(Opcode::LDI, 1, 1, 0),
HLInstr(Opcode::LDI, 2, 2, 0),
HLInstr(Opcode::MUL, 3, 1, 2),
HLInstr(Opcode::LDI, 4, 1, 0),
HLInstr(Opcode::LDF, 5, 0, 0),
HLInstr(Opcode::EXP, 6, 4, 5),
HLInstr(Opcode::DIV, 7, 3, 6),
HLInstr(Opcode::LDI, 8, 0, 0),
HLInstr(Opcode::FDIV, 9, 7, 8),
HLInstr(Opcode::ADD, 10, 0, 9),
HLInstr(Opcode::LDI, 11, 1, 0),
HLInstr(Opcode::SUB, 12, 10, 11),
HLInstr(Opcode::MOV, 13, 12, 0),
HLInstr(Opcode::LDI, 1, 0, 0),
HLInstr(Opcode::LDI, 2, 1, 0),
HLInstr(Opcode::LDI, 3, 2, 0),
HLInstr(Opcode::MUL, 4, 2, 3),
HLInstr(Opcode::LDI, 5, 1, 0),
HLInstr(Opcode::LDF, 6, 0, 0),
HLInstr(Opcode::EXP, 7, 5, 6),
HLInstr(Opcode::DIV, 8, 4, 7),
HLInstr(Opcode::LDI, 9, 0, 0),
HLInstr(Opcode::FDIV, 10, 8, 9),
HLInstr(Opcode::ADD, 11, 1, 10),
HLInstr(Opcode::LDI, 12, 1, 0),
HLInstr(Opcode::SUB, 13, 11, 12),
HLInstr(Opcode::LDS, 14, 0, 0),
HLInstr(Opcode::SetAttr, 0, 14, 13),
];
assert_eq!(ir.instrs.len(), expected_instrs.len());
for (lhs, rhs) in ir.instrs.iter().zip(expected_instrs.iter()) {
Expand All @@ -213,11 +243,15 @@ mod tests {
regs[i.1] = !regs[i.1];
// if at any point this assertion fails, it means that a register has been
// assigned a value multiple times
assert!(regs[i.1]);
// SetAttr only updates the state of a register, so it doesn't mess up the
// correctness of the SSA
if i.0 != Opcode::SetAttr {
assert!(regs[i.1]);
}
}
// check lifetimes
let expected_lifetimes = vec![
Lifetime::with_end_point(0, 1),
Lifetime::with_end_point(0, 15),
Lifetime::with_end_point(1, 2),
Lifetime::with_end_point(2, 3),
Lifetime::with_end_point(3, 4),
Expand All @@ -231,6 +265,7 @@ mod tests {
Lifetime::with_end_point(11, 12),
Lifetime::with_end_point(12, 13),
Lifetime::with_end_point(13, 14),
Lifetime::with_end_point(14, 15),
];
assert_eq!(ir.lifetimes.len(), expected_lifetimes.len());
for (lhs, rhs) in ir.lifetimes.iter().zip(expected_lifetimes.iter()) {
Expand All @@ -249,6 +284,81 @@ mod tests {
for (lhs, rhs) in floats.iter().zip(expected_floats.iter()) {
assert_eq!(lhs, rhs);
}
assert_eq!(ir.const_map.get_strings().len(), 0);
let expected_strings = vec!["x"];
let strings = ir.const_map.get_strings();
assert_eq!(strings.len(), expected_strings.len());
for (lhs, rhs) in strings.iter().zip(expected_strings.iter()) {
assert_eq!(lhs, rhs);
}
}

#[test]
fn correctness_of_ssa_ir2() {
let pt = LuaParseTree::from_str(String::from("x = 1\ny = x"));
let ir = compile_to_ir(&pt.unwrap());
let expected_instrs = vec![
HLInstr(Opcode::LDI, 1, 0, 0),
HLInstr(Opcode::LDS, 2, 0, 0),
HLInstr(Opcode::SetAttr, 0, 2, 1),
HLInstr(Opcode::LDS, 3, 1, 0),
HLInstr(Opcode::SetAttr, 0, 3, 1),
];
assert_eq!(ir.instrs.len(), expected_instrs.len());
for (lhs, rhs) in ir.instrs.iter().zip(expected_instrs.iter()) {
assert_eq!(lhs, rhs);
}
// check that the IR is in SSA form
let mut regs = Vec::with_capacity(ir.instrs.len());
regs.resize(ir.instrs.len(), false);
for i in &ir.instrs {
regs[i.1] = !regs[i.1];
// if at any point this assertion fails, it means that a register has been
// assigned a value multiple times
if i.0 != Opcode::SetAttr {
assert!(regs[i.1]);
}
}
// check lifetimes
let expected_lifetimes = vec![
Lifetime::with_end_point(0, 4),
Lifetime::with_end_point(1, 4),
Lifetime::with_end_point(2, 3),
Lifetime::with_end_point(3, 4),
];
assert_eq!(ir.lifetimes.len(), expected_lifetimes.len());
for (lhs, rhs) in ir.lifetimes.iter().zip(expected_lifetimes.iter()) {
assert_eq!(lhs, rhs);
}
// check constats map
let expected_ints = vec![1];
let ints = ir.const_map.get_ints();
assert_eq!(ints.len(), expected_ints.len());
for (lhs, rhs) in ints.iter().zip(expected_ints.iter()) {
assert_eq!(lhs, rhs);
}
assert!(ir.const_map.get_floats().is_empty());
let expected_strings = vec!["x", "y"];
let strings = ir.const_map.get_strings();
assert_eq!(strings.len(), expected_strings.len());
for (lhs, rhs) in strings.iter().zip(expected_strings.iter()) {
assert_eq!(lhs, rhs);
}
}

#[test]
fn generates_get_attr_instr() {
let pt = LuaParseTree::from_str(String::from("x = y"));
let ir = compile_to_ir(&pt.unwrap());
let expected_instrs = vec![
HLInstr(Opcode::LDS, 1, 0, 0),
HLInstr(Opcode::GetAttr, 2, 0, 1),
HLInstr(Opcode::LDS, 3, 1, 0),
HLInstr(Opcode::SetAttr, 0, 3, 2),
];
assert_eq!(ir.instrs.len(), expected_instrs.len());
for (lhs, rhs) in ir.instrs.iter().zip(expected_instrs.iter()) {
assert_eq!(lhs, rhs);
}
}

}
Loading