From 788cfe87abe7e55ce89a29bca97d5ed853253461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=8D=9A=E6=96=87?= Date: Sun, 26 May 2024 15:50:54 +0800 Subject: [PATCH] Lua: implements simple Add instruction --- lib/src/backend/lua/bytecode.rs | 64 ++++++++-- lib/src/backend/lua/mod.rs | 123 +++++++++++++------- lib/src/backend/lua/test.rs | 94 ++++++++------- viewer/test_projects/example1/test_proj.xml | 5 +- 4 files changed, 186 insertions(+), 100 deletions(-) diff --git a/lib/src/backend/lua/bytecode.rs b/lib/src/backend/lua/bytecode.rs index ee3d889..00548f4 100644 --- a/lib/src/backend/lua/bytecode.rs +++ b/lib/src/backend/lua/bytecode.rs @@ -215,9 +215,19 @@ pub enum LuaByteCode { /// A B C: UpValue[A][K[B]:string] := RK(C) SetTabUp(Reg, u8, RK), + /// A B C: R[A] := R[B] + K[C]:number + AddK(Reg, Reg, ConstIdx), + /// A B C: R[A] := R[B] + R[C] Add(Reg, Reg, Reg), + /// A B C call C metamethod over R[A] and R[B] + MMBin(Reg, Reg, ConstIdx), + /// A sB C k call C metamethod over R[A] and sB + MMBinI(Reg, i32, ConstIdx), + /// A B C k call C metamethod over R[A] and K[B] + MMBinK(Reg, ConstIdx, ConstIdx), + /// A B k: if ((R[A] == R[B]) ~= k) then pc++ Eq(Reg, Reg, u8), @@ -247,6 +257,10 @@ impl LuaByteCode { LuaByteCode::Move(..) => "MOVE", LuaByteCode::LoadI(..) => "LOADI", LuaByteCode::Add(..) => "ADD", + LuaByteCode::AddK(..) => "ADDK", + LuaByteCode::MMBin(..) => "MMBIN", + LuaByteCode::MMBinI(..) => "MMBINI", + LuaByteCode::MMBinK(..) => "MMBINK", LuaByteCode::Gei(..) => "GEI", LuaByteCode::Gti(..) => "GTI", LuaByteCode::Eq(..) => "EQ", @@ -263,6 +277,10 @@ impl LuaByteCode { LuaByteCode::LoadK(..) => LuaOpCode::OP_LOADK, LuaByteCode::Move(..) => LuaOpCode::OP_MOVE, LuaByteCode::LoadI(..) => LuaOpCode::OP_LOADI, + LuaByteCode::AddK(..) => LuaOpCode::OP_ADDK, + LuaByteCode::MMBin(..) => LuaOpCode::OP_MMBIN, + LuaByteCode::MMBinI(..) => LuaOpCode::OP_MMBINI, + LuaByteCode::MMBinK(..) => LuaOpCode::OP_MMBINK, LuaByteCode::Add(..) => LuaOpCode::OP_ADD, LuaByteCode::Gei(..) => LuaOpCode::OP_GEI, LuaByteCode::Gti(..) => LuaOpCode::OP_GTI, @@ -281,13 +299,25 @@ impl LuaByteCode { // A B k LuaByteCode::Eq(a, b, k) => (k as u32) << 17 | (b.num() as u32) << 9 | a.num() as u32, // A sB k - | LuaByteCode::Gei(a, sb, c) + LuaByteCode::Gei(a, sb, c) | LuaByteCode::Gti(a, sb, c) | LuaByteCode::Call(a, sb, c) => (c as u32) << 17 | (sb as u32) << 9 | a.num() as u32, // A B K LuaByteCode::GetTabUp(a, upv, k) => { (k as u32) << 17 | (upv as u32) << 9 | a.num() as u32 } + // RA, KB, KC with k + LuaByteCode::MMBinK(ra, kb, kc) => { + (kc as u32) << 17 | (kb as u32) << 9 | ra.num() as u32 | 1u32 << 8 + } + // RA, sB, KC with k + LuaByteCode::MMBinI(ra, sb, k) => { + (k as u32) << 17 | excess_sbx!(sb) << 9 | ra.num() as u32 | 1u32 << 8 + } + // RA, RB, KC + LuaByteCode::AddK(ra, rb, k) | LuaByteCode::MMBin(ra, rb, k) => { + (k as u32) << 17 | (rb.num() as u32) << 9 | ra.num() as u32 + } // A B RK LuaByteCode::SetTabUp(a, upv, rk) => { let c = match rk { @@ -297,11 +327,9 @@ impl LuaByteCode { }; c | (upv as u32) << 9 | a.num() as u32 - }, - // A B C all literal - LuaByteCode::Return(a, b, c) => { - (c as u32) << 17 | (b as u32) << 9 | a as u32 } + // A B C all literal + LuaByteCode::Return(a, b, c) => (c as u32) << 17 | (b as u32) << 9 | a as u32, // ABx LuaByteCode::LoadK(a, bx) => (bx as u32) << 8 | a.num() as u32, // AsBx @@ -346,16 +374,26 @@ impl LuaCompiledCode { LuaByteCode::Add(a, b, c) => { write!(s, "R{} R{} R{}", a.num(), b.num(), c.num()).unwrap(); } + // RA, KB, KC with k + LuaByteCode::MMBinK(ra, kb, kc) => { + write!(s, "R{} {kb} {kc}", ra.num()).unwrap(); + } // A B k LuaByteCode::Eq(a, b, k) => { write!(s, "R{} R{} {k}", a.num(), b.num()).unwrap(); } // A sB k - LuaByteCode::Gti(a, b, c) - | LuaByteCode::Gei(a, b, c) - | LuaByteCode::Call(a, b, c) => { + LuaByteCode::Gti(a, b, c) | LuaByteCode::Gei(a, b, c) | LuaByteCode::Call(a, b, c) => { write!(s, "R{} {b} {c}", a.num()).unwrap(); } + // RA sB KC with k + LuaByteCode::MMBinI(ra, sb, kc) => { + write!(s, "R{} {sb} {kc}", ra.num()).unwrap(); + } + // RegA, RegB, K + LuaByteCode::AddK(ra, rb, k) | LuaByteCode::MMBin(ra, rb, k) => { + write!(s, "R{} R{} {k}", ra.num(), rb.num()).unwrap(); + } // Reg, Upv, K LuaByteCode::GetTabUp(reg, upv, k) => { write!(s, "R{} {upv} {k}", reg.num()).unwrap(); @@ -367,8 +405,9 @@ impl LuaCompiledCode { match rk { RK::R(r) => write!(s, "{}", r.num()), RK::K(k) => write!(s, "{}k", k), - }.unwrap(); - }, + } + .unwrap(); + } // ABC with k LuaByteCode::Return(a, b, c) => { write!(s, "{a} {b} {c}").unwrap(); @@ -392,6 +431,11 @@ impl LuaCompiledCode { } match code { + LuaByteCode::MMBin(_, _, k) + | LuaByteCode::MMBinI(_, _, k) + | LuaByteCode::MMBinK(_, _, k) => { + write!(s, " ; {}", self.constants[*k as usize]).unwrap(); + } LuaByteCode::LoadK(a, bx) => { write!(s, " ; K[{}] = {}", bx, self.constants[*bx as usize]).unwrap(); } diff --git a/lib/src/backend/lua/mod.rs b/lib/src/backend/lua/mod.rs index 4668d16..a62e845 100644 --- a/lib/src/backend/lua/mod.rs +++ b/lib/src/backend/lua/mod.rs @@ -22,7 +22,6 @@ use log::*; use smallvec::{smallvec, SmallVec}; use std::mem; use std::rc::Rc; -use crate::backend::lua::register::Reg::R; type ConstIdx = u8; @@ -82,6 +81,18 @@ pub struct LuaBackendStates { access_mode: LuaAccessMode, } +impl LuaBackendStates { + fn rk(&self) -> RK { + if !self.registers.is_empty() { + RK::R(self.registers[0]) + } else if let Some(k) = self.const_idx { + RK::K(k) + } else { + unreachable!() + } + } +} + impl Default for LuaBackendStates { fn default() -> Self { Self { @@ -117,7 +128,7 @@ impl LuaBackend { #[inline] fn code_settabup(&mut self, up_idx: u8, rk: RK) { - self.push_code(LuaByteCode::SetTabUp(R(0), up_idx, rk)); + self.push_code(LuaByteCode::SetTabUp(Reg::R(0), up_idx, rk)); } #[inline] @@ -147,6 +158,32 @@ impl LuaBackend { self.byte_codes.push(code) } + #[inline] + fn code_add(&mut self, dst: Reg, op0: RK, op1: RK) { + let addk = self.add_string_constant("__add"); + + match (op0, op1) { + (RK::K(k), RK::R(r)) | (RK::R(r), RK::K(k)) => { + self.push_code(LuaByteCode::AddK(dst, r, k)); + self.push_code(LuaByteCode::MMBinK(dst, k, addk)); + } + (RK::K(k1), RK::K(k2)) => { + self.push_code(LuaByteCode::LoadK(dst, k1)); + self.push_code(LuaByteCode::AddK(dst, dst, k2)); + self.push_code(LuaByteCode::MMBinK(dst, k2, addk)); + } + (RK::R(r1), RK::R(r2)) => { + self.push_code(LuaByteCode::Add(dst, r1, r2)); + self.push_code(LuaByteCode::MMBin(r1, r2, addk)); + } + } + } + + #[inline] + fn code_eq(&mut self, dst: Reg, op0: RK, op1: RK) { + todo!() + } + #[inline] fn add_constant(&mut self, v: &LiteralValue) -> ConstIdx { match v { @@ -278,11 +315,14 @@ impl CodeGenBackend for LuaBackend { // index: 0, // kind: 0, // }); - self.upvalue_table.insert(StString::empty(), LuaUpValue { - stack: 1, - index: 0, - kind: LuaVarKind::VDKREG, - }); + self.upvalue_table.insert( + StString::empty(), + LuaUpValue { + stack: 1, + index: 0, + kind: LuaVarKind::VDKREG, + }, + ); let mut fun = f.write(); self.push_attribute_with_scope(fun_scope); @@ -329,22 +369,21 @@ impl AstVisitorMut for LuaBackend { fn visit_literal_mut(&mut self, literal: &mut LiteralExpression) { trace!("LuaGen: literal expression: {:?}", literal); - // // Literals can't WRITE - // assert!(!self.top_attribute().access_mode != LuaAccessMode::Write); + let access = self.top_attribute().access_mode; - if matches!( - self.top_attribute().access_mode, - LuaAccessMode::ReadRegisterOnly - ) { - let r = match self.top_attribute().registers.first() { - Some(r) => *r, - _ => self.reg_mgr.alloc_hard(), - }; + // Literals can't WRITE + assert!(!matches!(access, LuaAccessMode::Write)); - self.code_load(r, literal.literal()); - } else { - self.top_attribute().const_idx = Some(self.add_constant(literal.literal())) - } + self.top_attribute().const_idx = Some(self.add_constant(literal.literal())) + // if matches!(access, LuaAccessMode::ReadRegisterOnly) { + // let r = match self.top_attribute().registers.first() { + // Some(r) => *r, + // _ => self.reg_mgr.alloc_hard(), + // }; + + // self.code_load(r, literal.literal()); + // } else { + // } } fn visit_variable_expression_mut(&mut self, var_expr: &mut VariableExpression) { @@ -365,11 +404,13 @@ impl AstVisitorMut for LuaBackend { let arg_regs = self.reg_mgr.alloc_hard_batch(arg_cnt); self.top_attribute().registers = arg_regs.into(); - self.top_attribute().const_idx = Some(self.add_string_constant(var_expr.org_name())); + self.top_attribute().const_idx = + Some(self.add_string_constant(var_expr.org_name())); } // Read Symbol LuaAccessMode::ReadSymbol => { - self.top_attribute().const_idx = Some(self.add_string_constant(var_expr.org_name())); + self.top_attribute().const_idx = + Some(self.add_string_constant(var_expr.org_name())); } LuaAccessMode::WriteRegister => { let dst = self.top_attribute().registers[0]; @@ -382,14 +423,11 @@ impl AstVisitorMut for LuaBackend { LuaAccessMode::LoadNewRegister => { let scope = self.top_attribute().scope.as_ref().unwrap(); if let Some(variable) = scope.find_variable(var_expr.name()) { - self.top_attribute().registers = - smallvec![self.reg_mgr.alloc_local_variable(variable.name())]; - - // let reg = self.reg_mgr.alloc_hard(); - // self.top_attribute().register = Some(reg); - // - // // TODO: initialize - // self.push_code(LuaByteCode::LoadI(reg, 0)); + let const_idx = self.add_string_constant(var_expr.org_name()); + let reg = self.reg_mgr.alloc_local_variable(variable.name()); + + self.code_gettabup(reg, const_idx); + self.top_attribute().registers = smallvec![reg]; } else { // TODO: variable not found error } @@ -415,7 +453,11 @@ impl AstVisitorMut for LuaBackend { let callee_reg = arg_regs[0]; // Load Callee from constant table into callee_reg - self.push_code(LuaByteCode::GetTabUp(callee_reg, 0, callee_attr.const_idx.unwrap())); + self.push_code(LuaByteCode::GetTabUp( + callee_reg, + 0, + callee_attr.const_idx.unwrap(), + )); // visit all arguments for (idx, arg) in call.arguments_mut().iter_mut().enumerate() { @@ -474,26 +516,23 @@ impl AstVisitorMut for LuaBackend { self.push_access_attribute(LuaAccessMode::LoadNewRegister); self.visit_expression_mut(&mut operands[0]); - let op0_reg = self.pop_attribute().registers[0]; + let rk0 = self.pop_attribute().rk(); self.push_access_attribute(LuaAccessMode::LoadNewRegister); self.visit_expression_mut(&mut operands[1]); - let op1_reg = self.pop_attribute().registers[0]; + let rk1 = self.pop_attribute().rk(); // generate operators match op { // a + b - Operator::Plus => self.push_code(LuaByteCode::Add(dest_reg, op0_reg, op1_reg)), + Operator::Plus => self.code_add(dest_reg, rk0, rk1), // a = b - Operator::Equal => { - // (op0 == op1) != 1 - self.push_code(LuaByteCode::Eq(op0_reg, op1_reg, 1)) - } + Operator::Equal => self.code_eq(dest_reg, rk0, rk1), _ => unreachable!(), } - self.reg_mgr.free(&op0_reg); - self.reg_mgr.free(&op1_reg); + // self.reg_mgr.free(&rk0); + // self.reg_mgr.free(&rk1); self.top_attribute().registers = smallvec![dest_reg]; } @@ -523,7 +562,7 @@ impl AstVisitorMut for LuaBackend { // assert_ne!(lhs_reg, rhs.registers[0]); self.reg_mgr.free(&rhs.registers[0]); - // self.code_move(rhs.registers[0], lhs_reg); + self.code_settabup(lhs_constant_index, RK::R(rhs.registers[0])); } } } diff --git a/lib/src/backend/lua/test.rs b/lib/src/backend/lua/test.rs index 75d2116..93a9767 100644 --- a/lib/src/backend/lua/test.rs +++ b/lib/src/backend/lua/test.rs @@ -1,15 +1,12 @@ -use std::{fs, io::Write, process::Command}; - -use tempfile::tempdir; +use std::process::Command; use crate::{ - analysis::TypeAnalyzer, backend::{CodeGenBackend, CodeGenDriver, LuaBackend}, parser::*, prelude::*, }; -fn eval_string, S2: AsRef>(decl: S1, body: S2) -> String { +fn eval_string, S2: AsRef>(decl: S1, body: S2) -> (String, String) { let mgr = UnitsManager::new(); let ctx = ModuleContext::new(ModuleContextScope::Application); let lexer = StLexerBuilder::new().build_str(decl.as_ref()); @@ -23,78 +20,85 @@ fn eval_string, S2: AsRef>(decl: S1, body: S2) -> String { mgr.write().add_context(ctx.clone()); mgr.write().set_active_application(Some(ctx.read().id())); - // Type analyze - let ctx_id = ctx.read().id(); - let ctx_read = ctx.read(); - let mut type_analyzer = TypeAnalyzer::new(); - for proto in ctx_read.declarations() { - let proto_read = proto.read().unwrap(); - let proto_id = proto_read.id(); - let fun = ctx_read.get_function(proto_read.id()); - - if let Some(f) = fun { - let mut f = f.write(); - - let scope = Scope::new(Some(mgr.clone()), Some(ctx_id), Some(proto_id)); - type_analyzer.analyze_statement(f.parse_tree_mut(), scope); - } - } - // Build + let ctx_id = ctx.read().id(); let mut code_gen: CodeGenDriver = CodeGenDriver::new(mgr.clone(), ctx_id).unwrap(); code_gen.build_application().expect("build app failed"); - let mut buf = vec![0u8; 0]; + // Write to temporary file + let mut f = tempfile::Builder::new() + .tempfile() + .expect("create temp file failed"); code_gen .backend() - .get_module_bytes(&mut buf) + .get_module_bytes(&mut f) .expect("get module bytes failed"); - // Write to temporary file - let dir = tempdir().unwrap(); - let fp = dir.path().join("test.o"); - let mut f = fs::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(&fp) - .unwrap(); - f.write_all(&buf).unwrap(); - // execute let lua_cmd = "lua"; let output = Command::new(lua_cmd) - .arg(fp) + .arg(f.path()) .output() .expect("lua exec failed"); // get outputs - let s = String::from_utf8_lossy(&output.stdout).to_string(); - drop(f); + let r = String::from_utf8_lossy(&output.stdout).to_string(); + let e = String::from_utf8_lossy(&output.stderr).to_string(); - s + (r, e) } #[test] fn test_print() { // assignment - let r = eval_string( + let (r, e) = eval_string( "PROGRAM main: VAR a: int; END_VAR END_PROGRAM", "a := 1; print(a);", ); - assert_eq!(r, "1\n"); + assert_eq!(r, "1\n", "Error: {}", e); // assignment twice - let r = eval_string( + let (r, e) = eval_string( "PROGRAM main: VAR a: int; END_VAR END_PROGRAM", "a := 3; a := 2; print(a);", ); - assert_eq!(r, "2\n"); + assert_eq!(r, "2\n", "Error: {}", e); // print string - let r = eval_string( + let (r, e) = eval_string( "PROGRAM main: VAR a: STRING; END_VAR END_PROGRAM", "a := \"abc\"; print(a);", ); - assert_eq!(r, "abc\n"); + assert_eq!(r, "abc\n", "Error: {}", e); +} + +#[test] +fn test_add() { + // R + R + let (r, e) = eval_string( + "PROGRAM main: VAR a: STRING; END_VAR END_PROGRAM", + "a := 2; a := a + a; print(a);", + ); + assert_eq!(r, "4\n", "Error: {}", e); + + // R + K + let (r, e) = eval_string( + "PROGRAM main: VAR a: STRING; END_VAR END_PROGRAM", + "a := 2; a := a + 1; print(a);", + ); + assert_eq!(r, "3\n", "Error: {}", e); + + // K + R + let (r, e) = eval_string( + "PROGRAM main: VAR a: STRING; END_VAR END_PROGRAM", + "a := 2; a := 1 + a; print(a);", + ); + assert_eq!(r, "3\n", "Error: {}", e); + + // K + K + let (r, e) = eval_string( + "PROGRAM main: VAR a: STRING; END_VAR END_PROGRAM", + "a := 2; a := 3 + 2; print(a);", + ); + assert_eq!(r, "5\n", "Error: {}", e); } diff --git a/viewer/test_projects/example1/test_proj.xml b/viewer/test_projects/example1/test_proj.xml index b6120b5..c9ed5f2 100644 --- a/viewer/test_projects/example1/test_proj.xml +++ b/viewer/test_projects/example1/test_proj.xml @@ -22,11 +22,10 @@ end_program ]]> - a := 1; + a := 2; + a := 3 + 2; - if a = 1 then print(a); - end_if