forked from M-Labs/nac3
David Mak
31dcd2dde9
In LLVM, i1 represents a 1-byte integer with a single valid bit; The rest of the 7 upper bits are undefined. This causes problems when using these variables in memory operations (e.g. memcpy/memmove as needed by List slicing and assignment). We fix this by treating all local boolean variables as i8 so that they are well-defined for memory operations. Function ABIs will continue to use i1, as memory operations cannot be directly performed on function arguments or return types, instead they are always converted back into local boolean variables (which are i8s anyways). Fixes #315.
230 lines
6.7 KiB
Rust
230 lines
6.7 KiB
Rust
use crate::{
|
|
codegen::{expr::*, stmt::*, bool_to_i1, bool_to_i8, CodeGenContext},
|
|
symbol_resolver::ValueEnum,
|
|
toplevel::{DefinitionId, TopLevelDef},
|
|
typecheck::typedef::{FunSignature, Type},
|
|
};
|
|
use inkwell::{
|
|
context::Context,
|
|
types::{BasicTypeEnum, IntType},
|
|
values::{BasicValueEnum, IntValue, PointerValue},
|
|
};
|
|
use nac3parser::ast::{Expr, Stmt, StrRef};
|
|
|
|
pub trait CodeGenerator {
|
|
/// Return the module name for the code generator.
|
|
fn get_name(&self) -> &str;
|
|
|
|
fn get_size_type<'ctx>(&self, ctx: &'ctx Context) -> IntType<'ctx>;
|
|
|
|
/// Generate function call and returns the function return value.
|
|
/// - obj: Optional object for method call.
|
|
/// - fun: Function signature and definition ID.
|
|
/// - params: Function parameters. Note that this does not include the object even if the
|
|
/// function is a class method.
|
|
fn gen_call<'ctx, 'a>(
|
|
&mut self,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
obj: Option<(Type, ValueEnum<'ctx>)>,
|
|
fun: (&FunSignature, DefinitionId),
|
|
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
|
) -> Result<Option<BasicValueEnum<'ctx>>, String>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
gen_call(self, ctx, obj, fun, params)
|
|
}
|
|
|
|
/// Generate object constructor and returns the constructed object.
|
|
/// - signature: Function signature of the constructor.
|
|
/// - def: Class definition for the constructor class.
|
|
/// - params: Function parameters.
|
|
fn gen_constructor<'ctx, 'a>(
|
|
&mut self,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
signature: &FunSignature,
|
|
def: &TopLevelDef,
|
|
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
|
) -> Result<BasicValueEnum<'ctx>, String>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
gen_constructor(self, ctx, signature, def, params)
|
|
}
|
|
|
|
/// Generate a function instance.
|
|
/// - obj: Optional object for method call.
|
|
/// - fun: Function signature, definition ID and the substitution key.
|
|
/// - params: Function parameters. Note that this does not include the object even if the
|
|
/// function is a class method.
|
|
/// Note that this function should check if the function is generated in another thread (due to
|
|
/// possible race condition), see the default implementation for an example.
|
|
fn gen_func_instance<'ctx, 'a>(
|
|
&mut self,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
obj: Option<(Type, ValueEnum<'ctx>)>,
|
|
fun: (&FunSignature, &mut TopLevelDef, String),
|
|
id: usize,
|
|
) -> Result<String, String> {
|
|
gen_func_instance(ctx, obj, fun, id)
|
|
}
|
|
|
|
/// Generate the code for an expression.
|
|
fn gen_expr<'ctx, 'a>(
|
|
&mut self,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
expr: &Expr<Option<Type>>,
|
|
) -> Result<Option<ValueEnum<'ctx>>, String>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
gen_expr(self, ctx, expr)
|
|
}
|
|
|
|
/// Allocate memory for a variable and return a pointer pointing to it.
|
|
/// The default implementation places the allocations at the start of the function.
|
|
fn gen_var_alloc<'ctx, 'a>(
|
|
&mut self,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
ty: BasicTypeEnum<'ctx>,
|
|
name: Option<&str>,
|
|
) -> Result<PointerValue<'ctx>, String> {
|
|
gen_var(ctx, ty, name)
|
|
}
|
|
|
|
/// Return a pointer pointing to the target of the expression.
|
|
fn gen_store_target<'ctx, 'a>(
|
|
&mut self,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
pattern: &Expr<Option<Type>>,
|
|
name: Option<&str>,
|
|
) -> Result<PointerValue<'ctx>, String>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
gen_store_target(self, ctx, pattern, name)
|
|
}
|
|
|
|
/// Generate code for an assignment expression.
|
|
fn gen_assign<'ctx, 'a>(
|
|
&mut self,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
target: &Expr<Option<Type>>,
|
|
value: ValueEnum<'ctx>,
|
|
) -> Result<(), String>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
gen_assign(self, ctx, target, value)
|
|
}
|
|
|
|
/// Generate code for a while expression.
|
|
/// Return true if the while loop must early return
|
|
fn gen_while<'ctx, 'a>(
|
|
&mut self,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
stmt: &Stmt<Option<Type>>,
|
|
) -> Result<(), String>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
gen_while(self, ctx, stmt)
|
|
}
|
|
|
|
/// Generate code for a while expression.
|
|
/// Return true if the while loop must early return
|
|
fn gen_for<'ctx, 'a>(
|
|
&mut self,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
stmt: &Stmt<Option<Type>>,
|
|
) -> Result<(), String>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
gen_for(self, ctx, stmt)
|
|
}
|
|
|
|
/// Generate code for an if expression.
|
|
/// Return true if the statement must early return
|
|
fn gen_if<'ctx, 'a>(
|
|
&mut self,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
stmt: &Stmt<Option<Type>>,
|
|
) -> Result<(), String>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
gen_if(self, ctx, stmt)
|
|
}
|
|
|
|
fn gen_with<'ctx, 'a>(
|
|
&mut self,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
stmt: &Stmt<Option<Type>>,
|
|
) -> Result<(), String>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
gen_with(self, ctx, stmt)
|
|
}
|
|
|
|
/// Generate code for a statement
|
|
/// Return true if the statement must early return
|
|
fn gen_stmt<'ctx, 'a>(
|
|
&mut self,
|
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
stmt: &Stmt<Option<Type>>,
|
|
) -> Result<(), String>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
gen_stmt(self, ctx, stmt)
|
|
}
|
|
|
|
/// Converts the value of [a boolean-like value][bool_value] into an `i1`.
|
|
fn bool_to_i1<'ctx, 'a>(
|
|
&self,
|
|
ctx: &CodeGenContext<'ctx, 'a>,
|
|
bool_value: IntValue<'ctx>
|
|
) -> IntValue<'ctx> {
|
|
bool_to_i1(&ctx.builder, bool_value)
|
|
}
|
|
|
|
/// Converts the value of [a boolean-like value][bool_value] into an `i8`.
|
|
fn bool_to_i8<'ctx, 'a>(
|
|
&self,
|
|
ctx: &CodeGenContext<'ctx, 'a>,
|
|
bool_value: IntValue<'ctx>
|
|
) -> IntValue<'ctx> {
|
|
bool_to_i8(&ctx.builder, &ctx.ctx, bool_value)
|
|
}
|
|
}
|
|
|
|
pub struct DefaultCodeGenerator {
|
|
name: String,
|
|
size_t: u32,
|
|
}
|
|
|
|
impl DefaultCodeGenerator {
|
|
pub fn new(name: String, size_t: u32) -> DefaultCodeGenerator {
|
|
assert!(size_t == 32 || size_t == 64);
|
|
DefaultCodeGenerator { name, size_t }
|
|
}
|
|
}
|
|
|
|
impl CodeGenerator for DefaultCodeGenerator {
|
|
fn get_name(&self) -> &str {
|
|
&self.name
|
|
}
|
|
|
|
fn get_size_type<'ctx>(&self, ctx: &'ctx Context) -> IntType<'ctx> {
|
|
// it should be unsigned, but we don't really need unsigned and this could save us from
|
|
// having to do a bit cast...
|
|
if self.size_t == 32 {
|
|
ctx.i32_type()
|
|
} else {
|
|
ctx.i64_type()
|
|
}
|
|
}
|
|
}
|