Skip to content

Commit

Permalink
feat: implements lua code generate
Browse files Browse the repository at this point in the history
  • Loading branch information
sbwtw committed Apr 13, 2024
1 parent 63611bd commit 6fd274d
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 49 deletions.
42 changes: 41 additions & 1 deletion lib/src/backend/lua/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -143,6 +146,14 @@ pub enum LuaConstants {
Function(fn(&mut LuaExecState) -> i32),
}

#[derive(Debug, Clone, Default)]
pub struct LuaUpValue {
pub name: Option<StString>,
pub stack: u8,
pub index: u8,
pub kind: u8,
}

impl Eq for LuaConstants {}

impl Hash for LuaConstants {
Expand Down Expand Up @@ -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 {
Expand All @@ -210,6 +227,8 @@ impl LuaByteCode {
LuaByteCode::Gei(..) => "GEI",
LuaByteCode::Gti(..) => "GTI",
LuaByteCode::Eq(..) => "EQ",
LuaByteCode::Return(..) => "RETURN",
LuaByteCode::VarArgPrep(..) => "VARARGPREP",
}
}

Expand All @@ -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,
}
}

Expand All @@ -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;
Expand All @@ -255,6 +283,7 @@ impl LuaByteCode {
pub struct LuaCompiledCode {
pub byte_codes: Vec<LuaByteCode>,
pub constants: IndexSet<LuaConstants>,
pub upvalues: SmallVec8<LuaUpValue>,
}

impl LuaCompiledCode {
Expand Down Expand Up @@ -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();
Expand All @@ -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 {
Expand Down Expand Up @@ -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);
}
}
13 changes: 11 additions & 2 deletions lib/src/backend/lua/dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)?;
Expand Down
89 changes: 61 additions & 28 deletions lib/src/backend/lua/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -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;
}
}

Expand Down Expand Up @@ -60,12 +59,18 @@ pub struct LuaBackend {
byte_codes: Vec<LuaByteCode>,
states: SmallVec<[LuaBackendStates; 32]>,
local_function: Option<Function>,
local_proto: Option<Prototype>,
constants: IndexSet<LuaConstants>,
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()
}
Expand Down Expand Up @@ -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![],
Expand All @@ -155,29 +161,48 @@ impl CodeGenBackend for LuaBackend {
}

fn gen_function(&mut self, func: usize) -> Result<Box<dyn CompiledCode>, 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
}],
}))
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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),
}
}

Expand All @@ -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() {
Expand All @@ -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,
Expand Down Expand Up @@ -334,16 +369,15 @@ 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,
)),
// 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!(),
}
Expand All @@ -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;

Expand All @@ -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)
}
Expand Down
Loading

0 comments on commit 6fd274d

Please sign in to comment.