blob: 86be83b5a05c040cb1e6f5dd1ce981c916ecc9a4 [file] [log] [blame]
use std::prelude::v1::*;
use std::error::Error;
use std::fmt;
use hexagon::opcode::{OpCode, SelectType};
use ast::{Block, Expr, Stmt, Lhs, GetEscapeInfo};
use codegen::{ModuleBuilder, FunctionBuilder, BasicBlockBuilder, LoopControlInfo, VarLocation};
#[derive(Debug)]
pub struct CodegenError {
desc: String
}
impl<'a> From<&'a str> for CodegenError {
fn from(other: &'a str) -> CodegenError {
CodegenError {
desc: other.to_string()
}
}
}
impl Default for CodegenError {
fn default() -> Self {
CodegenError {
desc: "Error while generating code".into()
}
}
}
impl Error for CodegenError {
fn description(&self) -> &str {
self.desc.as_str()
}
}
impl fmt::Display for CodegenError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "CodegenError: {}", self.desc)
}
}
impl Lhs {
fn build_set(&self, fb: &mut FunctionBuilder) -> Result<(), CodegenError> {
match *self {
Lhs::Id(ref id) => {
let loc = fb.get_var_location(id);
loc.build_set(fb)?;
},
Lhs::Index(ref target, ref index) => {
index.restricted_generate_code(fb)?;
target.restricted_generate_code(fb)?;
fb.write_index_set()?;
}
}
Ok(())
}
fn build_new_local(&self, fb: &mut FunctionBuilder) -> Result<(), CodegenError> {
match *self {
Lhs::Id(ref id) => {
let loc = fb.create_local(id);
loc.build_set(fb)?;
},
_ => return Err("build_new_local: Unexpected lvalue".into())
}
Ok(())
}
}
pub trait RestrictedGenerateCode {
fn restricted_generate_code(&self, fb: &mut FunctionBuilder) -> Result<(), CodegenError>;
}
pub trait UnrestrictedGenerateCode {
fn unrestricted_generate_code(&self, fb: &mut FunctionBuilder) -> Result<(), CodegenError>;
}
impl UnrestrictedGenerateCode for Block {
fn unrestricted_generate_code(&self, fb: &mut FunctionBuilder) -> Result<(), CodegenError> {
for stmt in self.statements() {
stmt.unrestricted_generate_code(fb)?;
}
Ok(())
}
}
impl UnrestrictedGenerateCode for Stmt {
fn unrestricted_generate_code(&self, fb: &mut FunctionBuilder) -> Result<(), CodegenError> {
match *self {
Stmt::Do(ref stmts) => {
fb.scoped(|fb| -> Result<(), CodegenError> {
for stmt in stmts {
stmt.unrestricted_generate_code(fb)?;
}
Ok(())
})?;
},
Stmt::Set(ref lhs, ref exprs) => {
if lhs.len() != exprs.len() {
return Err("Set: lhs & exprs length mismatch".into());
}
for i in 0..lhs.len() {
exprs[i].restricted_generate_code(fb)?;
lhs[i].build_set(fb)?;
}
},
Stmt::While(ref expr, ref blk) => {
fb.scoped(|fb| -> Result<(), CodegenError> {
let expr_check_bb_id = fb.current_basic_block + 1;
fb.get_current_bb().opcodes.push(OpCode::Branch(expr_check_bb_id));
fb.move_forward();
expr.restricted_generate_code(fb)?;
let break_point_bb_id = fb.current_basic_block + 1;
fb.move_forward();
let body_begin_bb_id = fb.current_basic_block + 1;
fb.move_forward();
fb.with_lci(LoopControlInfo {
break_point: break_point_bb_id,
continue_point: expr_check_bb_id
}, |fb| blk.unrestricted_generate_code(fb))?;
fb.get_current_bb().opcodes.push(OpCode::Branch(expr_check_bb_id));
let end_bb_id = fb.current_basic_block + 1;
fb.move_forward();
fb.basic_blocks[break_point_bb_id].opcodes.push(OpCode::Branch(end_bb_id));
fb.basic_blocks[expr_check_bb_id].opcodes.push(OpCode::ConditionalBranch(
body_begin_bb_id,
end_bb_id
));
Ok(())
})?;
},
Stmt::Repeat(ref blk, ref expr) => {
fb.scoped(|fb| -> Result<(), CodegenError> {
let before_bb_id = fb.current_basic_block;
let continue_point_bb_id = fb.current_basic_block + 1;
fb.move_forward();
let break_point_bb_id = fb.current_basic_block + 1;
fb.move_forward();
let body_begin_bb_id = fb.current_basic_block + 1;
fb.move_forward();
fb.basic_blocks[before_bb_id].opcodes.push(OpCode::Branch(body_begin_bb_id));
fb.with_lci(LoopControlInfo {
break_point: break_point_bb_id,
continue_point: continue_point_bb_id
}, |fb| blk.unrestricted_generate_code(fb))?;
let body_end_bb_id = fb.current_basic_block;
let expr_check_bb_id = fb.current_basic_block + 1;
fb.move_forward();
expr.restricted_generate_code(fb)?;
let end_bb_id = fb.current_basic_block + 1;
fb.move_forward();
fb.basic_blocks[continue_point_bb_id].opcodes.push(OpCode::Branch(expr_check_bb_id));
fb.basic_blocks[break_point_bb_id].opcodes.push(OpCode::Branch(end_bb_id));
fb.basic_blocks[body_end_bb_id].opcodes.push(OpCode::Branch(expr_check_bb_id));
fb.basic_blocks[expr_check_bb_id].opcodes.push(OpCode::ConditionalBranch(
end_bb_id,
body_begin_bb_id
));
Ok(())
})?;
},
Stmt::If(ref branches, ref else_branch) => {
let before_bb_id = fb.current_basic_block;
fb.move_forward();
let terminator_bb_id = fb.current_basic_block;
let mut branch_begin_bbs: Vec<usize> = Vec::new();
for &(ref expr, _) in branches {
fb.move_forward();
expr.restricted_generate_code(fb)?;
branch_begin_bbs.push(fb.current_basic_block);
}
if let Some(ref else_blk) = *else_branch {
fb.move_forward();
let else_begin = fb.current_basic_block;
fb.scoped(|fb| else_blk.unrestricted_generate_code(fb))?;
fb.get_current_bb().opcodes.push(OpCode::Branch(terminator_bb_id));
branch_begin_bbs.push(else_begin);
} else {
branch_begin_bbs.push(terminator_bb_id);
}
assert!(branch_begin_bbs.len() == branches.len() + 1);
fb.basic_blocks[before_bb_id].opcodes.push(OpCode::Branch(branch_begin_bbs[0]));
for i in 0..branches.len() {
let &(_, ref blk) = &branches[i];
fb.move_forward();
let current_bb = fb.current_basic_block;
let checker_bb = branch_begin_bbs[i];
fb.basic_blocks[checker_bb].opcodes.push(OpCode::ConditionalBranch(
current_bb,
branch_begin_bbs[i + 1]
));
fb.scoped(|fb| blk.unrestricted_generate_code(fb))?;
fb.get_current_bb().opcodes.push(OpCode::Branch(terminator_bb_id));
}
fb.move_forward();
let end_bb_id = fb.current_basic_block;
fb.basic_blocks[terminator_bb_id].opcodes.push(OpCode::Branch(end_bb_id));
},
Stmt::Local(ref lhs, ref exprs) => {
if lhs.len() != exprs.len() {
return Err("Local: lhs & exprs length mismatch".into());
}
for i in 0..lhs.len() {
exprs[i].restricted_generate_code(fb)?;
lhs[i].build_new_local(fb)?;
}
},
Stmt::Call(ref target, ref args) => {
Expr::Call(Box::new(target.clone()), args.clone()).restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Pop);
},
Stmt::Return(ref v) => {
if v.len() == 0 {
fb.get_current_bb().opcodes.push(OpCode::LoadNull);
fb.get_current_bb().opcodes.push(OpCode::Return);
fb.move_forward();
} else if v.len() == 1 {
v[0].restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Return);
fb.move_forward();
} else {
return Err("Multiple return values is not supported for now".into());
}
},
Stmt::Break => {
fb.write_break()?;
},
_ => return Err("Not implemented".into())
}
Ok(())
}
}
impl RestrictedGenerateCode for Expr {
fn restricted_generate_code(&self, fb: &mut FunctionBuilder) -> Result<(), CodegenError> {
match *self {
Expr::Nil => fb.get_current_bb().opcodes.push(OpCode::LoadNull),
Expr::Boolean(v) => fb.get_current_bb().opcodes.push(OpCode::LoadBool(v)),
Expr::Number(v) => fb.get_current_bb().opcodes.push(OpCode::LoadFloat(v)),
Expr::String(ref s) => fb.get_current_bb().opcodes.push(OpCode::LoadString(s.clone())),
Expr::Function(ref vlhs, ref blk) => {
let mut new_builder = fb.get_module_builder().new_function();
let mut arg_names: Vec<String> = Vec::new();
for lhs in vlhs {
if let Some(id) = lhs.id() {
arg_names.push(id.to_string());
} else {
return Err("Expecting id in function signature".into());
}
}
let fn_id = new_builder.build(blk, arg_names)?;
fb.write_function_load(fn_id)?;
},
Expr::Table(ref elems) => {
fb.write_array_create()?;
for (i, v) in elems.iter().enumerate() {
fb.get_current_bb().opcodes.push(OpCode::Dup);
v.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.write_array_push()?;
}
fb.write_table_create()?;
fb.get_current_bb().opcodes.extend(vec! [
OpCode::Dup,
OpCode::Rotate3,
OpCode::Rotate2,
OpCode::LoadString("__copy_from_array__".into()),
OpCode::LoadNull,
OpCode::Rotate3,
OpCode::CallField(1),
OpCode::Pop
]);
},
Expr::Add(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.get_current_bb().opcodes.push(OpCode::Add);
},
Expr::Sub(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.get_current_bb().opcodes.push(OpCode::Sub);
},
Expr::Mul(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.get_current_bb().opcodes.push(OpCode::Mul);
},
Expr::Div(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.get_current_bb().opcodes.push(OpCode::Div);
},
Expr::Idiv(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.get_current_bb().opcodes.push(OpCode::IntDiv);
},
Expr::Mod(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.get_current_bb().opcodes.push(OpCode::Mod);
},
Expr::Pow(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.get_current_bb().opcodes.push(OpCode::Pow);
},
Expr::Concat(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.write_concat()?;
},
Expr::Eq(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.get_current_bb().opcodes.push(OpCode::TestEq);
},
Expr::Ne(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.get_current_bb().opcodes.push(OpCode::TestNe);
},
Expr::Lt(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.get_current_bb().opcodes.push(OpCode::TestLt);
},
Expr::Gt(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.get_current_bb().opcodes.push(OpCode::TestGt);
},
Expr::Le(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.get_current_bb().opcodes.push(OpCode::TestLe);
},
Expr::Ge(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.get_current_bb().opcodes.push(OpCode::TestGe);
},
Expr::Not(ref v) => {
v.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Not);
},
Expr::Unm(ref v) => {
v.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::LoadFloat(0.0));
fb.get_current_bb().opcodes.push(OpCode::Sub);
},
Expr::And(ref left, ref right) => {
let begin = fb.get_current_bb().opcodes.len();
left.restricted_generate_code(fb)?;
let left_opcodes = fb.get_current_bb().detach_opcodes(begin);
assert!(begin == fb.get_current_bb().opcodes.len());
right.restricted_generate_code(fb)?;
let right_opcodes = fb.get_current_bb().detach_opcodes(begin);
assert!(begin == fb.get_current_bb().opcodes.len());
fb.get_current_bb().opcodes.push(OpCode::Select(
SelectType::And,
left_opcodes,
right_opcodes
));
},
Expr::Or(ref left, ref right) => {
let begin = fb.get_current_bb().opcodes.len();
left.restricted_generate_code(fb)?;
let left_opcodes = fb.get_current_bb().detach_opcodes(begin);
assert!(begin == fb.get_current_bb().opcodes.len());
right.restricted_generate_code(fb)?;
let right_opcodes = fb.get_current_bb().detach_opcodes(begin);
assert!(begin == fb.get_current_bb().opcodes.len());
fb.get_current_bb().opcodes.push(OpCode::Select(
SelectType::Or,
left_opcodes,
right_opcodes
));
},
Expr::Call(ref target, ref args) => {
for arg in args {
arg.restricted_generate_code(fb)?;
}
if args.len() > 0 {
fb.get_current_bb().opcodes.push(OpCode::RotateReverse(args.len()));
}
fb.get_current_bb().opcodes.push(OpCode::LoadNull);
target.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Call(args.len()));
},
Expr::Pair(ref left, ref right) => {
left.restricted_generate_code(fb)?;
right.restricted_generate_code(fb)?;
fb.get_current_bb().opcodes.push(OpCode::Rotate2);
fb.write_pair_create()?;
},
Expr::Id(ref k) => {
let loc = fb.get_var_location(k.as_str());
loc.build_get(fb)?;
},
Expr::Index(ref target, ref index) => {
index.restricted_generate_code(fb)?;
target.restricted_generate_code(fb)?;
fb.write_index_get()?;
},
Expr::Dots => {
return Err("Dots: Not implemented".into());
}
}
Ok(())
}
}