From 6fd274d0c349c519119e6c72a0c3092fee02c758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=8D=9A=E6=96=87?= Date: Sat, 13 Apr 2024 19:56:52 +0800 Subject: [PATCH] feat: implements lua code generate --- lib/src/backend/lua/bytecode.rs | 42 +++++++++- lib/src/backend/lua/dump.rs | 13 ++- lib/src/backend/lua/mod.rs | 89 ++++++++++++++------- lib/src/backend/lua/utils.rs | 30 +++---- viewer/test_projects/example1/test_proj.xml | 4 +- 5 files changed, 129 insertions(+), 49 deletions(-) diff --git a/lib/src/backend/lua/bytecode.rs b/lib/src/backend/lua/bytecode.rs index bb45d27..14924a2 100644 --- a/lib/src/backend/lua/bytecode.rs +++ b/lib/src/backend/lua/bytecode.rs @@ -2,6 +2,9 @@ use indexmap::IndexSet; use std::fmt::{Display, Formatter, Write}; use std::hash::{Hash, Hasher}; +use crate::ast::SmallVec8; +use crate::parser::StString; + macro_rules! excess_k { ($v: expr, $k: expr) => { ($v as u32).wrapping_add(((2u32.pow($k) - 1) / 2)) & (2u32.pow($k) - 1) @@ -143,6 +146,14 @@ pub enum LuaConstants { Function(fn(&mut LuaExecState) -> i32), } +#[derive(Debug, Clone, Default)] +pub struct LuaUpValue { + pub name: Option, + pub stack: u8, + pub index: u8, + pub kind: u8, +} + impl Eq for LuaConstants {} impl Hash for LuaConstants { @@ -192,9 +203,15 @@ pub enum LuaByteCode { /// A sB k: if ((R[A] >= sB) ~= k) then pc++ Gei(u8, u8, u8), + /// A B C k: return R[A], ... ,R[A+B-2] + Return(bool, u8, u8, u8), + /// Call k, v: k is callee symbol position, v is argument count, return value not included /// A B C: R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) Call(u8, u8, u8), + + /// A (adjust vararg parameters) + VarArgPrep(u8), } impl LuaByteCode { @@ -210,6 +227,8 @@ impl LuaByteCode { LuaByteCode::Gei(..) => "GEI", LuaByteCode::Gti(..) => "GTI", LuaByteCode::Eq(..) => "EQ", + LuaByteCode::Return(..) => "RETURN", + LuaByteCode::VarArgPrep(..) => "VARARGPREP", } } @@ -225,6 +244,8 @@ impl LuaByteCode { LuaByteCode::Gei(..) => LuaOpCode::OP_GEI, LuaByteCode::Gti(..) => LuaOpCode::OP_GTI, LuaByteCode::Eq(..) => LuaOpCode::OP_EQ, + LuaByteCode::Return(..) => LuaOpCode::OP_RETURN, + LuaByteCode::VarArgPrep(..) => LuaOpCode::OP_VARARGPREP, } } @@ -237,13 +258,20 @@ impl LuaByteCode { | LuaByteCode::Gei(a, b, c) | LuaByteCode::Gti(a, b, c) | LuaByteCode::Eq(a, b, c) - | LuaByteCode::Add(a, b, c) => (c as u32) << 16 | (b as u32) << 8 | a as u32, + | LuaByteCode::Add(a, b, c) => (c as u32) << 17 | (b as u32) << 9 | a as u32, + // ABC with k + LuaByteCode::Return(k, a, b, c) => { + let k = if k { 1u32 } else { 0u32 }; + k << 15 | (c as u32) << 17 | (b as u32) << 9 | a as u32 + } // ABx LuaByteCode::LoadK(a, bx) => bx << 8 | a as u32, // AsBx LuaByteCode::LoadI(a, sbx) => excess_sBx!(sbx) << 8 | a as u32, // A B LuaByteCode::Move(a, b) => todo!(), + // A only + LuaByteCode::VarArgPrep(a) => (a as u32) << 8, }; let op = self.opcode() as u32; @@ -255,6 +283,7 @@ impl LuaByteCode { pub struct LuaCompiledCode { pub byte_codes: Vec, pub constants: IndexSet, + pub upvalues: SmallVec8, } impl LuaCompiledCode { @@ -285,6 +314,10 @@ impl LuaCompiledCode { | LuaByteCode::Add(a, b, c) => { write!(s, "{a} {b} {c}").unwrap(); } + // ABC with k + LuaByteCode::Return(_, a, b, c) => { + write!(s, "{a} {b} {c}").unwrap(); + } // ABx LuaByteCode::LoadK(a, bx) => { write!(s, "{a} {bx}").unwrap(); @@ -297,6 +330,10 @@ impl LuaCompiledCode { LuaByteCode::Move(a, b) => { write!(s, "{a} {b}").unwrap(); } + // A only + LuaByteCode::VarArgPrep(a) => { + write!(s, "{a}").unwrap(); + } } match code { @@ -347,5 +384,8 @@ mod test { let code = LuaByteCode::LoadI(3, -65535); assert_eq!(code.encode(), 0x00000181); + + let code = LuaByteCode::Return(false, 0, 1, 1); + assert_eq!(code.encode(), 0x01010046); } } diff --git a/lib/src/backend/lua/dump.rs b/lib/src/backend/lua/dump.rs index 2456083..9e16a44 100644 --- a/lib/src/backend/lua/dump.rs +++ b/lib/src/backend/lua/dump.rs @@ -84,7 +84,13 @@ fn lua_dump_function( // Dump Code for c in lua_code.byte_codes() { - w.write_all(&c.encode().to_le_bytes())?; + let data = c.encode(); + w.write_all(&[ + (data & 0xff) as u8, + ((data >> 8) & 0xff) as u8, + ((data >> 16) & 0xff) as u8, + ((data >> 24) & 0xff) as u8, + ])?; } // Dump size of constants @@ -93,9 +99,12 @@ fn lua_dump_function( // Dump Constants // Dump size of UpValues - lua_dump_size(w, 0)?; + lua_dump_size(w, lua_code.upvalues.len() as u64)?; // Dump UpValues + for upv in lua_code.upvalues.as_ref() { + w.write_all(&[upv.stack, upv.index, upv.kind])?; + } // Dump size of Protos lua_dump_size(w, 0)?; diff --git a/lib/src/backend/lua/mod.rs b/lib/src/backend/lua/mod.rs index 863e55a..02fc387 100644 --- a/lib/src/backend/lua/mod.rs +++ b/lib/src/backend/lua/mod.rs @@ -8,7 +8,7 @@ mod utils; mod vm; use crate::backend::lua::register::{RegisterId, RegisterManager}; -use crate::backend::lua::utils::try_fit_sbx; +use crate::backend::lua::utils::*; use crate::backend::*; use crate::parser::{LiteralValue, Operator}; use crate::prelude::*; @@ -27,7 +27,6 @@ bitflags! { const WRITE = 0b0000_0000_0000_0010; const PARAMETER = 0b0000_0000_0000_0100; const CALL = 0b0000_0000_0000_1000; - const LOAD = 0b0000_0000_0001_0000; } } @@ -60,12 +59,18 @@ pub struct LuaBackend { byte_codes: Vec, states: SmallVec<[LuaBackendStates; 32]>, local_function: Option, + local_proto: Option, constants: IndexSet, reg_mgr: RegisterManager, module_upvalues: SmallVec<[LuaConstants; 32]>, } impl LuaBackend { + fn push_code(&mut self, code: LuaByteCode) { + debug!("LuaBackend: Push Code {:?}", code); + self.byte_codes.push(code) + } + fn current_application(&self) -> ModuleContext { self.app.clone() } @@ -144,6 +149,7 @@ impl CodeGenBackend for LuaBackend { byte_codes: vec![], states: smallvec![], local_function: None, + local_proto: None, constants: IndexSet::new(), reg_mgr: RegisterManager::new(), module_upvalues: smallvec![], @@ -155,29 +161,48 @@ impl CodeGenBackend for LuaBackend { } fn gen_function(&mut self, func: usize) -> Result, CodeGenError> { - let f = self - .app - .read() + let app = self.app.read(); + let f = app .get_function(func) .ok_or(CodeGenError::FunctionNotDefined(func))? .clone(); + let p = app.get_declaration_by_id(func).cloned(); self.local_function = Some(f.clone()); + self.local_proto = p; + drop(app); let app_id = self.app.read().id(); let fun_scope = Scope::new(Some(self.mgr.clone()), Some(app_id), Some(func)); + // generate VarArgPrep + if let Some(p) = &self.local_proto { + if is_vararg(p) { + self.push_code(LuaByteCode::VarArgPrep(0)); + } + } + let mut fun = f.write(); self.push_attribute_with_scope(fun_scope); self.visit_statement_mut(fun.parse_tree_mut()); self.pop_attribute(); + // generate return + self.push_code(LuaByteCode::Return(false, 0, 1, 1)); + let byte_codes = mem::take(&mut self.byte_codes); let constants = mem::replace(&mut self.constants, IndexSet::new()); Ok(Box::new(LuaCompiledCode { byte_codes, constants, + // TODO:: upvalues + upvalues: smallvec![LuaUpValue { + name: None, + stack: 1, + index: 0, + kind: 0 + }], })) } @@ -212,7 +237,7 @@ impl AstVisitorMut for LuaBackend { .top_attribute() .register .unwrap_or_else(|| self.reg_mgr.alloc()); - self.byte_codes.push(LuaByteCode::LoadI(r as u8, v as i32)); + self.push_code(LuaByteCode::LoadI(r as u8, v)); self.top_attribute().register = Some(r); return; } @@ -245,18 +270,29 @@ impl AstVisitorMut for LuaBackend { var.and_then(|x| x.ty()) ); - // Callee process - if self.top_attribute().access_mode == LuaAccessMode::CALL { - self.top_attribute().constant_index = - Some(self.add_string_constant(var_expr.org_name())); - return; - } - - let scope = self.top_attribute().scope.as_ref().unwrap(); - if let Some(variable) = scope.find_variable(var_expr.name()) { - self.top_attribute().register = Some(self.reg_mgr.alloc()); - } else { - // TODO: variable not found error + let access_mode = self.top_attribute().access_mode; + match access_mode & (LuaAccessMode::READ | LuaAccessMode::WRITE | LuaAccessMode::CALL) { + // Callee process + LuaAccessMode::CALL => { + self.top_attribute().constant_index = + Some(self.add_string_constant(var_expr.org_name())) + } + // Write register into stack + LuaAccessMode::WRITE => {} + // Load into register + LuaAccessMode::READ => { + let scope = self.top_attribute().scope.as_ref().unwrap(); + if let Some(variable) = scope.find_variable(var_expr.name()) { + let reg = self.reg_mgr.alloc(); + self.top_attribute().register = Some(reg); + + // TODO: initialize + self.push_code(LuaByteCode::LoadI(reg as u8, 0)); + } else { + // TODO: variable not found error + } + } + _ => unreachable!("{:?}", access_mode), } } @@ -267,7 +303,7 @@ impl AstVisitorMut for LuaBackend { self.visit_expression_mut(call.callee_mut()); let callee_index = self.top_attribute().constant_index; self.pop_attribute(); - self.byte_codes.push(LuaByteCode::GetTabUp(0, 0, 0)); + self.push_code(LuaByteCode::GetTabUp(0, 0, 0)); // visit all arguments for arg in call.arguments_mut() { @@ -278,12 +314,11 @@ impl AstVisitorMut for LuaBackend { // Load argument if let Some(idx) = arg_value_index { - let load = LuaByteCode::LoadK(0, idx as u32); - self.byte_codes.push(load); + self.push_code(LuaByteCode::LoadK(0, idx as u32)); } } - self.byte_codes.push(LuaByteCode::Call( + self.push_code(LuaByteCode::Call( callee_index.unwrap() as u8, call.arguments().len() as u8, 0, @@ -334,7 +369,7 @@ impl AstVisitorMut for LuaBackend { // generate operators match op { // a + b - Operator::Plus => self.byte_codes.push(LuaByteCode::Add( + Operator::Plus => self.push_code(LuaByteCode::Add( dest_reg as u8, op0_reg as u8, op1_reg as u8, @@ -342,8 +377,7 @@ impl AstVisitorMut for LuaBackend { // a = b Operator::Equal => { // (op0 == op1) != 1 - self.byte_codes - .push(LuaByteCode::Eq(op0_reg as u8, op1_reg as u8, 1)) + self.push_code(LuaByteCode::Eq(op0_reg as u8, op1_reg as u8, 1)) } _ => unreachable!(), } @@ -360,7 +394,7 @@ impl AstVisitorMut for LuaBackend { fn visit_assign_expression_mut(&mut self, assign: &mut AssignExpression) { trace!("LuaGen: assignment expression: {}", assign); - self.push_access_attribute(LuaAccessMode::LOAD); + self.push_access_attribute(LuaAccessMode::READ); assign.left_mut().accept_mut(self); let lhs_reg = self.pop_attribute().register; @@ -378,8 +412,7 @@ impl AstVisitorMut for LuaBackend { if let Some(r) = rhs.register { // if rhs register is reused lhs_reg, ignore move if Some(r) != lhs_reg { - self.byte_codes - .push(LuaByteCode::Move(lhs.register.unwrap() as u8, r as u8)); + self.push_code(LuaByteCode::Move(lhs.register.unwrap() as u8, r as u8)); self.reg_mgr.free(&r) } diff --git a/lib/src/backend/lua/utils.rs b/lib/src/backend/lua/utils.rs index fe25007..b524304 100644 --- a/lib/src/backend/lua/utils.rs +++ b/lib/src/backend/lua/utils.rs @@ -12,40 +12,40 @@ const SBX_MIN_VALUE: i32 = 0 - 2_i32.pow(SBX_BIT_SIZE); const SBX_MASK: u32 = 0b0001_1111_1111_1111_1111; /// Returns true if literal can fit into sBx value -pub fn try_fit_sbx(literal: &LiteralValue) -> Option { - match literal { +pub fn try_fit_sbx(literal: &LiteralValue) -> Option { + match *literal { LiteralValue::Bit(BitValue::Zero) => Some(0), LiteralValue::Bit(BitValue::One) => Some(1), LiteralValue::Bool(false) => Some(0), LiteralValue::Bool(true) => Some(1), - LiteralValue::Byte(v) => Some(*v as u32), - LiteralValue::SInt(v) => Some((*v as i32) as u32 & SBX_MASK), - LiteralValue::Int(v) => Some((*v as i32) as u32 & SBX_MASK), - LiteralValue::UInt(v) => Some(*v as u32 & SBX_MASK), + LiteralValue::Byte(v) => Some(v as i32), + LiteralValue::SInt(v) => Some(v as i32), + LiteralValue::Int(v) => Some(v as i32), + LiteralValue::UInt(v) => Some(v as i32), LiteralValue::DInt(v) => { - if (SBX_MIN_VALUE..=SBX_MAX_VALUE).contains(v) { - Some(*v as u32 & SBX_MASK) + if (SBX_MIN_VALUE..=SBX_MAX_VALUE).contains(&v) { + Some(v) } else { None } } LiteralValue::UDInt(v) => { - if *v <= SBX_MAX_VALUE as u32 { - Some(v & SBX_MASK) + if v <= SBX_MAX_VALUE as u32 { + Some(v as i32) } else { None } } LiteralValue::LInt(v) => { - if (SBX_MIN_VALUE as i64..=SBX_MAX_VALUE as i64).contains(v) { - Some(*v as u32 & SBX_MASK) + if (SBX_MIN_VALUE as i64..=SBX_MAX_VALUE as i64).contains(&v) { + Some(v as i32) } else { None } } LiteralValue::ULInt(v) => { - if *v <= SBX_MAX_VALUE as u64 { - Some(*v as u32 & SBX_MASK) + if v <= SBX_MAX_VALUE as u64 { + Some(v as i32) } else { None } @@ -69,7 +69,7 @@ pub fn num_params(p: &Prototype) -> u8 { #[inline] pub fn is_vararg(_p: &Prototype) -> bool { // TODO: - false + true } #[inline] diff --git a/viewer/test_projects/example1/test_proj.xml b/viewer/test_projects/example1/test_proj.xml index 4f0ef7a..0f7617d 100644 --- a/viewer/test_projects/example1/test_proj.xml +++ b/viewer/test_projects/example1/test_proj.xml @@ -22,9 +22,7 @@ end_program ]]> - IF g1 = a THEN - b := b + 1; - END_IF + a := 1;