You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
785 lines
26 KiB
785 lines
26 KiB
/** |
|
* Copyright 2021 Alexey Yerin |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* https://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
use super::{Generator, GeneratorResult}; |
|
use crate::ast::types::Type; |
|
use crate::ast::*; |
|
use std::collections::HashMap; |
|
|
|
pub struct QbeGenerator { |
|
/// Counter for unique temporary names |
|
tmp_counter: u32, |
|
/// Block-scoped variable -> temporary mappings |
|
scopes: Vec<HashMap<String, (qbe::Type, qbe::Value)>>, |
|
/// Structure -> (type, meta data, size) mappings |
|
struct_map: HashMap<String, (qbe::Type, StructMeta, u64)>, |
|
/// Label prefix of loop scopes |
|
loop_labels: Vec<String>, |
|
/// Data defintions collected during generation |
|
datadefs: Vec<qbe::DataDef>, |
|
/// Type defintions collected during generation |
|
typedefs: Vec<qbe::TypeDef>, |
|
} |
|
|
|
/// Mapping of field -> (type, offset) |
|
type StructMeta = HashMap<String, (qbe::Type, u64)>; |
|
|
|
impl Generator for QbeGenerator { |
|
fn generate(prog: Module) -> GeneratorResult<String> { |
|
let mut generator = QbeGenerator { |
|
tmp_counter: 0, |
|
scopes: Vec::new(), |
|
struct_map: HashMap::new(), |
|
loop_labels: Vec::new(), |
|
datadefs: Vec::new(), |
|
typedefs: Vec::new(), |
|
}; |
|
|
|
// TODO: use `qbe::Module` API instead of writing to the buffer directly |
|
let mut buf = String::new(); |
|
|
|
for def in &prog.structs { |
|
let structure = generator.generate_struct(def)?; |
|
|
|
#[cfg(debug_assertions)] |
|
{ |
|
// Just in case it incorrectly calculates offsets |
|
let (_, meta, size) = generator.struct_map.get(&def.name).unwrap(); |
|
buf.push_str(&format!("# size: {}\n", size)); |
|
buf.push_str(&format!("# meta: {:?}\n", meta)); |
|
} |
|
buf.push_str(&format!("{}\n", structure)); |
|
} |
|
|
|
for func in &prog.func { |
|
let func = generator.generate_function(func)?; |
|
buf.push_str(&format!("{}\n", func)); |
|
} |
|
|
|
for def in &generator.typedefs { |
|
buf.push_str(&format!("{}\n", def)); |
|
} |
|
|
|
for def in &generator.datadefs { |
|
buf.push_str(&format!("{}\n", def)); |
|
} |
|
|
|
Ok(buf) |
|
} |
|
} |
|
|
|
impl QbeGenerator { |
|
/// Returns an aggregate type for a structure (note: has side effects) |
|
fn generate_struct(&mut self, def: &StructDef) -> GeneratorResult<qbe::TypeDef> { |
|
self.tmp_counter += 1; |
|
let mut typedef = qbe::TypeDef { |
|
name: format!("struct.{}", self.tmp_counter), |
|
align: None, |
|
items: Vec::new(), |
|
}; |
|
let mut meta: StructMeta = StructMeta::new(); |
|
let mut offset = 0_u64; |
|
|
|
for field in &def.fields { |
|
let ty = self.get_type( |
|
field |
|
.ty |
|
.as_ref() |
|
.ok_or_else(|| "Structure field must have a type".to_owned())? |
|
.to_owned(), |
|
)?; |
|
|
|
meta.insert(field.name.clone(), (ty.clone(), offset)); |
|
typedef.items.push((ty.clone(), 1)); |
|
|
|
offset += ty.size(); |
|
} |
|
self.struct_map.insert( |
|
def.name.clone(), |
|
(qbe::Type::Aggregate(typedef.name.clone()), meta, offset), |
|
); |
|
|
|
Ok(typedef) |
|
} |
|
|
|
fn generate_function(&mut self, func: &Function) -> GeneratorResult<qbe::Function> { |
|
// Function argument scope |
|
self.scopes.push(HashMap::new()); |
|
|
|
let mut arguments: Vec<(qbe::Type, qbe::Value)> = Vec::new(); |
|
for arg in &func.arguments { |
|
let ty = self.get_type( |
|
arg.ty |
|
.as_ref() |
|
.ok_or("Function arguments must have a type")? |
|
.to_owned(), |
|
)?; |
|
let tmp = self.new_var(&ty, &arg.name)?; |
|
|
|
arguments.push((ty.into_abi(), tmp)); |
|
} |
|
|
|
let return_ty = if let Some(ty) = &func.ret_type { |
|
Some(self.get_type(ty.to_owned())?.into_abi()) |
|
} else { |
|
None |
|
}; |
|
|
|
let mut qfunc = qbe::Function { |
|
linkage: qbe::Linkage::public(), |
|
name: func.name.clone(), |
|
arguments, |
|
return_ty, |
|
blocks: Vec::new(), |
|
}; |
|
|
|
qfunc.add_block("start".to_owned()); |
|
|
|
self.generate_statement(&mut qfunc, &func.body)?; |
|
|
|
let returns = qfunc.last_block().statements.last().map_or(false, |i| { |
|
matches!(i, qbe::Statement::Volatile(qbe::Instr::Ret(_))) |
|
}); |
|
// Automatically add return in void functions unless it already returns, |
|
// non-void functions raise an error |
|
if !returns { |
|
if func.ret_type.is_none() { |
|
qfunc.add_instr(qbe::Instr::Ret(None)); |
|
} else { |
|
return Err(format!( |
|
"Function '{}' does not return in all code paths", |
|
&func.name |
|
)); |
|
} |
|
} |
|
|
|
self.scopes.pop(); |
|
|
|
Ok(qfunc) |
|
} |
|
|
|
/// Generates a statement |
|
fn generate_statement( |
|
&mut self, |
|
func: &mut qbe::Function, |
|
stmt: &Statement, |
|
) -> GeneratorResult<()> { |
|
match stmt { |
|
Statement::Block { |
|
statements, |
|
scope: _, |
|
} => { |
|
self.scopes.push(HashMap::new()); |
|
for stmt in statements.iter() { |
|
self.generate_statement(func, stmt)?; |
|
} |
|
self.scopes.pop(); |
|
} |
|
Statement::Declare { variable, value } => { |
|
let ty = self.get_type( |
|
variable |
|
.ty |
|
.as_ref() |
|
.ok_or_else(|| format!("Missing type for variable '{}'", &variable.name))? |
|
.to_owned(), |
|
)?; |
|
let tmp = self.new_var(&ty, &variable.name)?; |
|
|
|
if let Some(expr) = value { |
|
let (ty, result) = self.generate_expression(func, expr)?; |
|
func.assign_instr(tmp, ty, qbe::Instr::Copy(result)); |
|
} |
|
} |
|
Statement::Assign { lhs, rhs } => { |
|
let (_, rhs) = self.generate_expression(func, rhs)?; |
|
// TODO: type check |
|
self.generate_assignment(func, lhs, rhs)?; |
|
} |
|
Statement::Return(val) => match val { |
|
Some(expr) => { |
|
let (_, result) = self.generate_expression(func, expr)?; |
|
// TODO: Cast to function return type |
|
func.add_instr(qbe::Instr::Ret(Some(result))); |
|
} |
|
None => func.add_instr(qbe::Instr::Ret(None)), |
|
}, |
|
Statement::If { |
|
condition, |
|
body, |
|
else_branch, |
|
} => { |
|
self.generate_if(func, condition, body, else_branch)?; |
|
} |
|
Statement::While { condition, body } => { |
|
self.generate_while(func, condition, body)?; |
|
} |
|
Statement::Break => { |
|
if let Some(label) = &self.loop_labels.last() { |
|
func.add_instr(qbe::Instr::Jmp(format!("{}.end", label))); |
|
} else { |
|
return Err("break used outside of a loop".to_owned()); |
|
} |
|
} |
|
Statement::Continue => { |
|
if let Some(label) = &self.loop_labels.last() { |
|
func.add_instr(qbe::Instr::Jmp(format!("{}.cond", label))); |
|
} else { |
|
return Err("continue used outside of a loop".to_owned()); |
|
} |
|
} |
|
Statement::Exp(expr) => { |
|
self.generate_expression(func, expr)?; |
|
} |
|
_ => todo!("statement: {:?}", stmt), |
|
} |
|
Ok(()) |
|
} |
|
|
|
/// Generates an expression |
|
fn generate_expression( |
|
&mut self, |
|
func: &mut qbe::Function, |
|
expr: &Expression, |
|
) -> GeneratorResult<(qbe::Type, qbe::Value)> { |
|
match expr { |
|
Expression::Int(literal) => { |
|
let tmp = self.new_temporary(); |
|
func.assign_instr( |
|
tmp.clone(), |
|
qbe::Type::Word, |
|
qbe::Instr::Copy(qbe::Value::Const(*literal as u64)), |
|
); |
|
|
|
Ok((qbe::Type::Word, tmp)) |
|
} |
|
Expression::Str(string) => self.generate_string(string), |
|
Expression::Bool(literal) => { |
|
let tmp = self.new_temporary(); |
|
func.assign_instr( |
|
tmp.clone(), |
|
qbe::Type::Word, |
|
qbe::Instr::Copy(qbe::Value::Const(if *literal { 1 } else { 0 })), |
|
); |
|
|
|
Ok((qbe::Type::Word, tmp)) |
|
} |
|
Expression::Array { capacity, elements } => { |
|
self.generate_array(func, *capacity, elements) |
|
} |
|
Expression::FunctionCall { fn_name, args } => { |
|
let mut new_args: Vec<(qbe::Type, qbe::Value)> = Vec::new(); |
|
for arg in args.iter() { |
|
new_args.push(self.generate_expression(func, arg)?); |
|
} |
|
|
|
let tmp = self.new_temporary(); |
|
func.assign_instr( |
|
tmp.clone(), |
|
// TODO: get that type properly |
|
qbe::Type::Word, |
|
qbe::Instr::Call(fn_name.clone(), new_args), |
|
); |
|
|
|
Ok((qbe::Type::Word, tmp)) |
|
} |
|
Expression::Variable(name) => self.get_var(name).map(|v| v.to_owned()), |
|
Expression::BinOp { lhs, op, rhs } => self.generate_binop(func, lhs, op, rhs), |
|
Expression::StructInitialization { name, fields } => { |
|
self.generate_struct_init(func, name, fields) |
|
} |
|
Expression::FieldAccess { expr, field } => { |
|
self.generate_field_access(func, expr, field) |
|
} |
|
_ => todo!("expression: {:?}", expr), |
|
} |
|
} |
|
|
|
/// Generates an `if` statement |
|
fn generate_if( |
|
&mut self, |
|
func: &mut qbe::Function, |
|
cond: &Expression, |
|
if_clause: &Statement, |
|
else_clause: &Option<Box<Statement>>, |
|
) -> GeneratorResult<()> { |
|
let (_, result) = self.generate_expression(func, cond)?; |
|
|
|
self.tmp_counter += 1; |
|
let if_label = format!("cond.{}.if", self.tmp_counter); |
|
let else_label = format!("cond.{}.else", self.tmp_counter); |
|
let end_label = format!("cond.{}.end", self.tmp_counter); |
|
|
|
func.add_instr(qbe::Instr::Jnz( |
|
result, |
|
if_label.clone(), |
|
if else_clause.is_some() { |
|
else_label.clone() |
|
} else { |
|
end_label.clone() |
|
}, |
|
)); |
|
|
|
func.add_block(if_label); |
|
self.generate_statement(func, if_clause)?; |
|
|
|
if let Some(else_clause) = else_clause { |
|
// Jump over to the end to prevent fallthrough into else |
|
// clause, unless the last block already jumps |
|
if !func.blocks.last().map_or(false, |b| b.jumps()) { |
|
func.add_instr(qbe::Instr::Jmp(end_label.clone())); |
|
} |
|
|
|
func.add_block(else_label); |
|
self.generate_statement(func, else_clause)?; |
|
} |
|
|
|
func.add_block(end_label); |
|
|
|
Ok(()) |
|
} |
|
|
|
/// Generates a `while` statement |
|
fn generate_while( |
|
&mut self, |
|
func: &mut qbe::Function, |
|
cond: &Expression, |
|
body: &Statement, |
|
) -> GeneratorResult<()> { |
|
self.tmp_counter += 1; |
|
let cond_label = format!("loop.{}.cond", self.tmp_counter); |
|
let body_label = format!("loop.{}.body", self.tmp_counter); |
|
let end_label = format!("loop.{}.end", self.tmp_counter); |
|
|
|
self.loop_labels.push(format!("loop.{}", self.tmp_counter)); |
|
|
|
func.add_block(cond_label.clone()); |
|
|
|
let (_, result) = self.generate_expression(func, cond)?; |
|
func.add_instr(qbe::Instr::Jnz( |
|
result, |
|
body_label.clone(), |
|
end_label.clone(), |
|
)); |
|
|
|
func.add_block(body_label); |
|
self.generate_statement(func, body)?; |
|
|
|
if !func.blocks.last().map_or(false, |b| b.jumps()) { |
|
func.add_instr(qbe::Instr::Jmp(cond_label)); |
|
} |
|
|
|
func.add_block(end_label); |
|
|
|
self.loop_labels.pop(); |
|
|
|
Ok(()) |
|
} |
|
|
|
/// Generates a string |
|
fn generate_string(&mut self, string: &str) -> GeneratorResult<(qbe::Type, qbe::Value)> { |
|
self.tmp_counter += 1; |
|
let name = format!("string.{}", self.tmp_counter); |
|
|
|
let mut items: Vec<(qbe::Type, qbe::DataItem)> = Vec::new(); |
|
let mut buf = String::new(); |
|
for ch in string.chars() { |
|
if ch.is_ascii() && !ch.is_ascii_control() && ch != '"' { |
|
buf.push(ch) |
|
} else { |
|
if !buf.is_empty() { |
|
items.push((qbe::Type::Byte, qbe::DataItem::Str(buf.clone()))); |
|
buf.clear(); |
|
} |
|
|
|
let mut buf = [0; 4]; |
|
let len = ch.encode_utf8(&mut buf).len(); |
|
|
|
for b in buf.iter().take(len) { |
|
items.push((qbe::Type::Byte, qbe::DataItem::Const(*b as u64))); |
|
} |
|
continue; |
|
} |
|
} |
|
if !buf.is_empty() { |
|
items.push((qbe::Type::Byte, qbe::DataItem::Str(buf))); |
|
} |
|
// NUL terminator |
|
items.push((qbe::Type::Byte, qbe::DataItem::Const(0))); |
|
|
|
self.datadefs.push(qbe::DataDef { |
|
linkage: qbe::Linkage::public(), |
|
name: name.clone(), |
|
align: None, |
|
items, |
|
}); |
|
|
|
Ok((qbe::Type::Long, qbe::Value::Global(name))) |
|
} |
|
|
|
/// Returns the result of a binary operation (e.g. `+` or `*=`). |
|
fn generate_binop( |
|
&mut self, |
|
func: &mut qbe::Function, |
|
lhs: &Expression, |
|
op: &BinOp, |
|
rhs: &Expression, |
|
) -> GeneratorResult<(qbe::Type, qbe::Value)> { |
|
let (_, lhs_val) = self.generate_expression(func, lhs)?; |
|
let (_, rhs_val) = self.generate_expression(func, rhs)?; |
|
let tmp = self.new_temporary(); |
|
|
|
// TODO: take the biggest |
|
let ty = qbe::Type::Word; |
|
|
|
func.assign_instr( |
|
tmp.clone(), |
|
ty.clone(), |
|
match op { |
|
BinOp::Addition | BinOp::AddAssign => qbe::Instr::Add(lhs_val, rhs_val), |
|
BinOp::Subtraction | BinOp::SubtractAssign => qbe::Instr::Sub(lhs_val, rhs_val), |
|
BinOp::Multiplication | BinOp::MultiplyAssign => qbe::Instr::Mul(lhs_val, rhs_val), |
|
BinOp::Division | BinOp::DivideAssign => qbe::Instr::Div(lhs_val, rhs_val), |
|
BinOp::Modulus => qbe::Instr::Rem(lhs_val, rhs_val), |
|
|
|
BinOp::And => qbe::Instr::And(lhs_val, rhs_val), |
|
BinOp::Or => qbe::Instr::Or(lhs_val, rhs_val), |
|
|
|
// Others should be comparisons |
|
cmp => qbe::Instr::Cmp( |
|
ty.clone(), |
|
match cmp { |
|
BinOp::LessThan => qbe::Cmp::Slt, |
|
BinOp::LessThanOrEqual => qbe::Cmp::Sle, |
|
BinOp::GreaterThan => qbe::Cmp::Sgt, |
|
BinOp::GreaterThanOrEqual => qbe::Cmp::Sge, |
|
BinOp::Equal => qbe::Cmp::Eq, |
|
BinOp::NotEqual => qbe::Cmp::Ne, |
|
_ => unreachable!(), |
|
}, |
|
lhs_val, |
|
rhs_val, |
|
), |
|
}, |
|
); |
|
|
|
// *Assign BinOps work just like normal ones except that here the |
|
// result is assigned to the left hand side. This essentially makes |
|
// `a += 1` the same as `a = a + 1`. |
|
match op { |
|
BinOp::AddAssign |
|
| BinOp::SubtractAssign |
|
| BinOp::MultiplyAssign |
|
| BinOp::DivideAssign => { |
|
self.generate_assignment(func, lhs, tmp.clone())?; |
|
} |
|
_ => {} |
|
}; |
|
|
|
Ok((ty, tmp)) |
|
} |
|
|
|
/// Generates an assignment to either a variable, field access or array |
|
/// access |
|
fn generate_assignment( |
|
&mut self, |
|
func: &mut qbe::Function, |
|
lhs: &Expression, |
|
rhs: qbe::Value, |
|
) -> GeneratorResult<()> { |
|
match lhs { |
|
Expression::Variable(name) => { |
|
let (vty, tmp) = self.get_var(name)?; |
|
func.assign_instr( |
|
tmp.to_owned(), |
|
vty.to_owned(), |
|
qbe::Instr::Copy(rhs), |
|
); |
|
} |
|
Expression::FieldAccess { expr, field } => { |
|
let (src, ty, offset) = self.resolve_field_access(expr, field)?; |
|
|
|
let field_ptr = self.new_temporary(); |
|
func.assign_instr( |
|
field_ptr.clone(), |
|
qbe::Type::Long, |
|
qbe::Instr::Add(src, qbe::Value::Const(offset)), |
|
); |
|
|
|
func.add_instr(qbe::Instr::Store(ty, field_ptr, rhs)); |
|
} |
|
Expression::ArrayAccess { name: _, index: _ } => todo!(), |
|
_ => return Err("Left side of an assignment must be either a variable, field access or array access".to_owned()), |
|
} |
|
|
|
Ok(()) |
|
} |
|
|
|
/// Generates struct initialization |
|
fn generate_struct_init( |
|
&mut self, |
|
func: &mut qbe::Function, |
|
name: &str, |
|
fields: &HashMap<String, Box<Expression>>, |
|
) -> GeneratorResult<(qbe::Type, qbe::Value)> { |
|
let base = self.new_temporary(); |
|
let (ty, meta, size) = self |
|
.struct_map |
|
.get(name) |
|
.ok_or_else(|| format!("Initialization of undeclared struct '{}'", name))? |
|
.to_owned(); |
|
|
|
func.assign_instr( |
|
base.clone(), |
|
qbe::Type::Long, |
|
// XXX: Always align to 8 bytes? |
|
qbe::Instr::Alloc8(size), |
|
); |
|
|
|
for (name, expr) in fields { |
|
let (_, offset) = meta |
|
.get(name) |
|
.ok_or_else(|| format!("Unknown field '{}'", name))?; |
|
|
|
let (ty, expr_tmp) = self.generate_expression(func, expr)?; |
|
|
|
let field_tmp = self.new_temporary(); |
|
func.assign_instr( |
|
field_tmp.clone(), |
|
qbe::Type::Long, |
|
qbe::Instr::Add(base.clone(), qbe::Value::Const(*offset)), |
|
); |
|
|
|
func.add_instr(qbe::Instr::Store(ty, field_tmp, expr_tmp)); |
|
} |
|
|
|
Ok((ty, base)) |
|
} |
|
|
|
/// Retrieves the result of struct field access |
|
fn generate_field_access( |
|
&mut self, |
|
func: &mut qbe::Function, |
|
obj: &Expression, |
|
field: &Expression, |
|
) -> GeneratorResult<(qbe::Type, qbe::Value)> { |
|
let (src, ty, offset) = self.resolve_field_access(obj, field)?; |
|
|
|
let field_ptr = self.new_temporary(); |
|
func.assign_instr( |
|
field_ptr.clone(), |
|
qbe::Type::Long, |
|
qbe::Instr::Add(src, qbe::Value::Const(offset)), |
|
); |
|
|
|
let tmp = self.new_temporary(); |
|
func.assign_instr( |
|
tmp.clone(), |
|
ty.clone(), |
|
qbe::Instr::Load(ty.clone(), field_ptr), |
|
); |
|
|
|
Ok((ty, tmp)) |
|
} |
|
|
|
/// Retrieves `(source, offset)` from field access expression |
|
fn resolve_field_access( |
|
&mut self, |
|
obj: &Expression, |
|
field: &Expression, |
|
) -> GeneratorResult<(qbe::Value, qbe::Type, u64)> { |
|
let (ty, src) = match obj { |
|
Expression::Variable(var) => self.get_var(var)?.to_owned(), |
|
Expression::FieldAccess { .. } => todo!("nested field access"), |
|
Expression::Selff => unimplemented!("methods"), |
|
other => { |
|
return Err(format!( |
|
"Invalid field access type: expected variable, field access or 'self', got {:?}", |
|
other, |
|
)); |
|
} |
|
}; |
|
let field = match field { |
|
Expression::Variable(v) => v, |
|
Expression::FunctionCall { |
|
fn_name: _, |
|
args: _, |
|
} => unimplemented!("methods"), |
|
// Parser should ensure this won't happen |
|
_ => unreachable!(), |
|
}; |
|
|
|
// XXX: this is very hacky and inefficient |
|
let (name, meta) = self |
|
.struct_map |
|
.iter() |
|
.filter_map( |
|
|(name, (sty, meta, _))| { |
|
if ty == *sty { |
|
Some((name, meta)) |
|
} else { |
|
None |
|
} |
|
}, |
|
) |
|
.next() |
|
.unwrap(); |
|
|
|
let (ty, offset) = meta |
|
.get(field) |
|
.ok_or_else(|| format!("No field '{}' on struct {}", field, name))? |
|
.to_owned(); |
|
|
|
Ok((src, ty, offset)) |
|
} |
|
|
|
/// Generates an array literal |
|
fn generate_array( |
|
&mut self, |
|
func: &mut qbe::Function, |
|
len: usize, |
|
items: &[Expression], |
|
) -> GeneratorResult<(qbe::Type, qbe::Value)> { |
|
let mut first_type: Option<qbe::Type> = None; |
|
let mut results: Vec<qbe::Value> = Vec::new(); |
|
|
|
for item in items.iter() { |
|
let (ty, result) = self.generate_expression(func, item)?; |
|
results.push(result); |
|
|
|
if let Some(first_type) = first_type.clone() { |
|
if ty != first_type { |
|
return Err(format!( |
|
"Inconsistent array types {:?} and {:?} (possibly more)", |
|
first_type, ty |
|
)); |
|
} |
|
} else { |
|
first_type = Some(ty); |
|
} |
|
} |
|
|
|
// Arrays have the following in-memory representation: |
|
// { |
|
// length (long), |
|
// values... |
|
// } |
|
let tmp = self.new_temporary(); |
|
func.assign_instr( |
|
tmp.clone(), |
|
qbe::Type::Long, |
|
qbe::Instr::Alloc8( |
|
qbe::Type::Long.size() |
|
+ if let Some(ref ty) = first_type { |
|
ty.size() * (len as u64) |
|
} else { |
|
0 |
|
}, |
|
), |
|
); |
|
func.add_instr(qbe::Instr::Store( |
|
qbe::Type::Long, |
|
tmp.clone(), |
|
qbe::Value::Const(len as u64), |
|
)); |
|
|
|
for (i, value) in results.iter().enumerate() { |
|
let value_ptr = self.new_temporary(); |
|
func.assign_instr( |
|
value_ptr.clone(), |
|
qbe::Type::Long, |
|
qbe::Instr::Add( |
|
tmp.clone(), |
|
qbe::Value::Const( |
|
qbe::Type::Long.size() + (i as u64) * first_type.as_ref().unwrap().size(), |
|
), |
|
), |
|
); |
|
|
|
func.add_instr(qbe::Instr::Store( |
|
first_type.as_ref().unwrap().clone(), |
|
value_ptr, |
|
value.to_owned(), |
|
)); |
|
} |
|
|
|
self.tmp_counter += 1; |
|
let name = format!("array.{}", self.tmp_counter); |
|
let typedef = qbe::TypeDef { |
|
name: name.clone(), |
|
align: None, |
|
items: if let Some(ty) = first_type { |
|
vec![(qbe::Type::Long, 1), (ty, len)] |
|
} else { |
|
// No elements |
|
vec![(qbe::Type::Long, 1)] |
|
}, |
|
}; |
|
self.typedefs.push(typedef); |
|
|
|
Ok((qbe::Type::Aggregate(name), tmp)) |
|
} |
|
|
|
/// Returns a new unique temporary |
|
fn new_temporary(&mut self) -> qbe::Value { |
|
self.tmp_counter += 1; |
|
qbe::Value::Temporary(format!("tmp.{}", self.tmp_counter)) |
|
} |
|
|
|
/// Returns a new temporary bound to a variable |
|
fn new_var(&mut self, ty: &qbe::Type, name: &str) -> GeneratorResult<qbe::Value> { |
|
if self.get_var(name).is_ok() { |
|
return Err(format!("Re-declaration of variable '{}'", name)); |
|
} |
|
|
|
let tmp = self.new_temporary(); |
|
|
|
let scope = self |
|
.scopes |
|
.last_mut() |
|
.expect("expected last scope to be present"); |
|
scope.insert(name.to_owned(), (ty.to_owned(), tmp.to_owned())); |
|
|
|
Ok(tmp) |
|
} |
|
|
|
/// Returns a temporary accociated to a variable |
|
fn get_var(&self, name: &str) -> GeneratorResult<&(qbe::Type, qbe::Value)> { |
|
self.scopes |
|
.iter() |
|
.rev() |
|
.filter_map(|s| s.get(name)) |
|
.next() |
|
.ok_or_else(|| format!("Undefined variable '{}'", name)) |
|
} |
|
|
|
/// Returns a QBE type for the given AST type |
|
fn get_type(&self, ty: Type) -> GeneratorResult<qbe::Type> { |
|
match ty { |
|
Type::Any => Err("'any' type is not supported".into()), |
|
Type::Int => Ok(qbe::Type::Word), |
|
Type::Bool => Ok(qbe::Type::Byte), |
|
Type::Str => Ok(qbe::Type::Long), |
|
Type::Struct(name) => { |
|
let (ty, ..) = self |
|
.struct_map |
|
.get(&name) |
|
.ok_or_else(|| format!("Use of undeclared struct '{}'", name))? |
|
.to_owned(); |
|
Ok(ty) |
|
} |
|
Type::Array(..) => Ok(qbe::Type::Long), |
|
} |
|
} |
|
}
|
|
|