jit works
This commit is contained in:
@@ -1,24 +1,23 @@
|
||||
use crate::backend::error::BackendError;
|
||||
use crate::backend::Backend;
|
||||
use crate::eval::PrimitiveType;
|
||||
use crate::ir::{Expression, Primitive, Program, TopLevel, Type, Value, ValueOrRef, Variable};
|
||||
use crate::syntax::{ConstantType, Location};
|
||||
use crate::util::scoped_map::ScopedMap;
|
||||
use cranelift_codegen::ir::{
|
||||
self, entities, types, AbiParam, Function, GlobalValue, InstBuilder, Signature, UserFuncName,
|
||||
self, entities, types, AbiParam, Function, GlobalValue, InstBuilder, MemFlags, Signature, UserFuncName
|
||||
};
|
||||
use cranelift_codegen::isa::CallConv;
|
||||
use cranelift_codegen::Context;
|
||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||
use cranelift_module::{FuncId, Linkage, Module};
|
||||
use cranelift_module::{DataDescription, FuncId, Linkage, Module};
|
||||
use internment::ArcIntern;
|
||||
|
||||
use crate::backend::error::BackendError;
|
||||
use crate::backend::Backend;
|
||||
use std::collections::{hash_map, HashMap};
|
||||
|
||||
/// When we're talking about variables, it's handy to just have a table that points
|
||||
/// from a variable to "what to do if you want to reference this variable", which is
|
||||
/// agnostic about whether the variable is local, global, an argument, etc. Since
|
||||
/// the type of that function is a little bit annoying, we summarize it here.
|
||||
enum ReferenceBuilder {
|
||||
pub enum ReferenceBuilder {
|
||||
Global(ConstantType, GlobalValue),
|
||||
Local(ConstantType, cranelift_frontend::Variable),
|
||||
Argument(ConstantType, entities::Value),
|
||||
@@ -29,7 +28,8 @@ impl ReferenceBuilder {
|
||||
match self {
|
||||
ReferenceBuilder::Global(ty, gv) => {
|
||||
let cranelift_type = ir::Type::from(*ty);
|
||||
let value = builder.ins().symbol_value(cranelift_type, *gv);
|
||||
let ptr_value = builder.ins().symbol_value(types::I64, *gv);
|
||||
let value = builder.ins().load(cranelift_type, MemFlags::new(), ptr_value, 0);
|
||||
(value, *ty)
|
||||
}
|
||||
|
||||
@@ -81,11 +81,44 @@ impl<M: Module> Backend<M> {
|
||||
program: Program<Type>,
|
||||
) -> Result<FuncId, BackendError> {
|
||||
let mut generated_body = vec![];
|
||||
let mut variables = HashMap::new();
|
||||
|
||||
for (top_level_name, top_level_type) in program.get_top_level_variables() {
|
||||
match top_level_type {
|
||||
Type::Function(argument_types, return_type) => {
|
||||
let func_id = self.declare_function(
|
||||
top_level_name.as_str(),
|
||||
Linkage::Export,
|
||||
argument_types,
|
||||
*return_type,
|
||||
)?;
|
||||
self.defined_functions.insert(top_level_name, func_id);
|
||||
}
|
||||
|
||||
Type::Primitive(pt) => {
|
||||
let data_id = self.module.declare_data(
|
||||
top_level_name.as_str(),
|
||||
Linkage::Export,
|
||||
true,
|
||||
false,
|
||||
)?;
|
||||
self.module.define_data(data_id, &pt.blank_data())?;
|
||||
self.defined_symbols
|
||||
.insert(top_level_name, (data_id, pt.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let void = Type::Primitive(PrimitiveType::Void);
|
||||
let main_func_id =
|
||||
self.declare_function(function_name, Linkage::Export, vec![], void.clone())?;
|
||||
self.defined_functions
|
||||
.insert(ArcIntern::new(function_name.to_string()), main_func_id);
|
||||
|
||||
for item in program.items {
|
||||
match item {
|
||||
TopLevel::Function(name, args, rettype, body) => {
|
||||
self.compile_function(name.as_str(), &args, rettype, body)?;
|
||||
self.compile_function(&mut variables, name.as_str(), &args, rettype, body)?;
|
||||
}
|
||||
|
||||
TopLevel::Statement(stmt) => {
|
||||
@@ -94,8 +127,8 @@ impl<M: Module> Backend<M> {
|
||||
}
|
||||
}
|
||||
|
||||
let void = Type::Primitive(PrimitiveType::Void);
|
||||
self.compile_function(
|
||||
&mut variables,
|
||||
function_name,
|
||||
&[],
|
||||
void.clone(),
|
||||
@@ -103,14 +136,47 @@ impl<M: Module> Backend<M> {
|
||||
)
|
||||
}
|
||||
|
||||
fn declare_function(
|
||||
&mut self,
|
||||
name: &str,
|
||||
linkage: Linkage,
|
||||
argument_types: Vec<Type>,
|
||||
return_type: Type,
|
||||
) -> Result<FuncId, cranelift_module::ModuleError> {
|
||||
let basic_signature = Signature {
|
||||
params: argument_types
|
||||
.iter()
|
||||
.map(|t| self.translate_type(t))
|
||||
.collect(),
|
||||
returns: if return_type == Type::Primitive(PrimitiveType::Void) {
|
||||
vec![]
|
||||
} else {
|
||||
vec![self.translate_type(&return_type)]
|
||||
},
|
||||
call_conv: CallConv::triple_default(&self.platform),
|
||||
};
|
||||
|
||||
// this generates the handle for the function that we'll eventually want to
|
||||
// return to the user. For now, we declare all functions defined by this
|
||||
// function as public/global/exported, although we may want to reconsider
|
||||
// this decision later.
|
||||
self.module
|
||||
.declare_function(name, linkage, &basic_signature)
|
||||
}
|
||||
|
||||
/// Compile the given function.
|
||||
pub fn compile_function(
|
||||
&mut self,
|
||||
variables: &mut HashMap<Variable, ReferenceBuilder>,
|
||||
function_name: &str,
|
||||
arguments: &[(Variable, Type)],
|
||||
return_type: Type,
|
||||
body: Expression<Type>,
|
||||
) -> Result<FuncId, BackendError> {
|
||||
// reset the next variable counter. this value shouldn't matter; hopefully
|
||||
// we won't be using close to 2^32 variables!
|
||||
self.reset_local_variable_tracker();
|
||||
|
||||
let basic_signature = Signature {
|
||||
params: arguments
|
||||
.iter()
|
||||
@@ -124,17 +190,23 @@ impl<M: Module> Backend<M> {
|
||||
call_conv: CallConv::triple_default(&self.platform),
|
||||
};
|
||||
|
||||
// reset the next variable counter. this value shouldn't matter; hopefully
|
||||
// we won't be using close to 2^32 variables!
|
||||
self.reset_local_variable_tracker();
|
||||
|
||||
// this generates the handle for the function that we'll eventually want to
|
||||
// return to the user. For now, we declare all functions defined by this
|
||||
// function as public/global/exported, although we may want to reconsider
|
||||
// this decision later.
|
||||
let func_id =
|
||||
self.module
|
||||
.declare_function(function_name, Linkage::Export, &basic_signature)?;
|
||||
let interned_name = ArcIntern::new(function_name.to_string());
|
||||
let func_id = match self.defined_functions.entry(interned_name) {
|
||||
hash_map::Entry::Occupied(entry) => *entry.get(),
|
||||
hash_map::Entry::Vacant(vac) => {
|
||||
let func_id = self.module.declare_function(
|
||||
function_name,
|
||||
Linkage::Export,
|
||||
&basic_signature,
|
||||
)?;
|
||||
vac.insert(func_id);
|
||||
func_id
|
||||
}
|
||||
};
|
||||
|
||||
// Next we have to generate the compilation context for the rest of this
|
||||
// function. Currently, we generate a fresh context for every function.
|
||||
@@ -145,14 +217,6 @@ impl<M: Module> Backend<M> {
|
||||
let user_func_name = UserFuncName::user(0, func_id.as_u32());
|
||||
ctx.func = Function::with_name_signature(user_func_name, basic_signature);
|
||||
|
||||
// Let's start creating the variable table we'll use when we're dereferencing
|
||||
// them later. This table is a little interesting because instead of pointing
|
||||
// from data to data, we're going to point from data (the variable) to an
|
||||
// action to take if we encounter that variable at some later point. This
|
||||
// makes it nice and easy to have many different ways to access data, such
|
||||
// as globals, function arguments, etc.
|
||||
let mut variables: ScopedMap<ArcIntern<String>, ReferenceBuilder> = ScopedMap::new();
|
||||
|
||||
// At the outer-most scope of things, we'll put global variables we've defined
|
||||
// elsewhere in the program.
|
||||
for (name, (data_id, ty)) in self.defined_symbols.iter() {
|
||||
@@ -160,12 +224,6 @@ impl<M: Module> Backend<M> {
|
||||
variables.insert(name.clone(), ReferenceBuilder::Global(*ty, local_data));
|
||||
}
|
||||
|
||||
// Once we have these, we're going to actually push a level of scope and
|
||||
// add our arguments. We push scope because if there happen to be any with
|
||||
// the same name (their shouldn't be, but just in case), we want the arguments
|
||||
// to win.
|
||||
variables.new_scope();
|
||||
|
||||
// Finally (!), we generate the function builder that we're going to use to
|
||||
// make this function!
|
||||
let mut fctx = FunctionBuilderContext::new();
|
||||
@@ -192,7 +250,7 @@ impl<M: Module> Backend<M> {
|
||||
|
||||
builder.switch_to_block(main_block);
|
||||
|
||||
let (value, _) = self.compile_expression(body, &mut variables, &mut builder)?;
|
||||
let (value, _) = self.compile_expression(body, variables, &mut builder)?;
|
||||
|
||||
// Now that we're done, inject a return function (one with no actual value; basically
|
||||
// the equivalent of Rust's `return;`). We then seal the block (which lets Cranelift
|
||||
@@ -221,7 +279,7 @@ impl<M: Module> Backend<M> {
|
||||
fn compile_expression(
|
||||
&mut self,
|
||||
expr: Expression<Type>,
|
||||
variables: &mut ScopedMap<Variable, ReferenceBuilder>,
|
||||
variables: &mut HashMap<Variable, ReferenceBuilder>,
|
||||
builder: &mut FunctionBuilder,
|
||||
) -> Result<(entities::Value, ConstantType), BackendError> {
|
||||
match expr {
|
||||
@@ -282,6 +340,29 @@ impl<M: Module> Backend<M> {
|
||||
|
||||
(ConstantType::U64, Type::Primitive(PrimitiveType::U64)) => Ok((val, val_type)),
|
||||
|
||||
(ConstantType::Void, Type::Primitive(PrimitiveType::Void)) => {
|
||||
Ok((val, val_type))
|
||||
}
|
||||
|
||||
(ConstantType::U8, Type::Primitive(PrimitiveType::I16)) => {
|
||||
Ok((builder.ins().uextend(types::I16, val), ConstantType::I16))
|
||||
}
|
||||
(ConstantType::U8, Type::Primitive(PrimitiveType::I32)) => {
|
||||
Ok((builder.ins().uextend(types::I32, val), ConstantType::I32))
|
||||
}
|
||||
(ConstantType::U8, Type::Primitive(PrimitiveType::I64)) => {
|
||||
Ok((builder.ins().uextend(types::I64, val), ConstantType::I64))
|
||||
}
|
||||
(ConstantType::U16, Type::Primitive(PrimitiveType::I32)) => {
|
||||
Ok((builder.ins().uextend(types::I32, val), ConstantType::I32))
|
||||
}
|
||||
(ConstantType::U16, Type::Primitive(PrimitiveType::I64)) => {
|
||||
Ok((builder.ins().uextend(types::I64, val), ConstantType::I64))
|
||||
}
|
||||
(ConstantType::U32, Type::Primitive(PrimitiveType::I64)) => {
|
||||
Ok((builder.ins().uextend(types::I64, val), ConstantType::I64))
|
||||
}
|
||||
|
||||
_ => Err(BackendError::InvalidTypeCast {
|
||||
from: val_type.into(),
|
||||
to: target_type,
|
||||
@@ -327,7 +408,7 @@ impl<M: Module> Backend<M> {
|
||||
}
|
||||
|
||||
Expression::Block(_, _, mut exprs) => match exprs.pop() {
|
||||
None => Ok((builder.ins().iconst(types::I8, 0), ConstantType::I8)),
|
||||
None => Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)),
|
||||
Some(last) => {
|
||||
for inner in exprs {
|
||||
// we can ignore all of these return values and such, because we
|
||||
@@ -354,7 +435,7 @@ impl<M: Module> Backend<M> {
|
||||
// Look up the value for the variable. Because this might be a
|
||||
// global variable (and that requires special logic), we just turn
|
||||
// this into an `Expression` and re-use the logic in that implementation.
|
||||
let fake_ref = ValueOrRef::Ref(ann, Type::Primitive(PrimitiveType::U8), var);
|
||||
let fake_ref = ValueOrRef::Ref(ann, Type::Primitive(PrimitiveType::U8), var.clone());
|
||||
let (val, vtype) = self.compile_value_or_ref(fake_ref, variables, builder)?;
|
||||
|
||||
let vtype_repr = builder.ins().iconst(types::I64, vtype as i64);
|
||||
@@ -379,7 +460,7 @@ impl<M: Module> Backend<M> {
|
||||
print_func_ref,
|
||||
&[buffer_ptr, name_ptr, vtype_repr, casted_val],
|
||||
);
|
||||
Ok((builder.ins().iconst(types::I8, 0), ConstantType::I8))
|
||||
Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void))
|
||||
}
|
||||
|
||||
Expression::Bind(_, name, _, expr) => {
|
||||
@@ -390,7 +471,7 @@ impl<M: Module> Backend<M> {
|
||||
builder.declare_var(variable, ir_type);
|
||||
builder.def_var(variable, value);
|
||||
variables.insert(name, ReferenceBuilder::Local(value_type, variable));
|
||||
Ok((builder.ins().iconst(types::I8, 0), ConstantType::I8))
|
||||
Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,7 +481,7 @@ impl<M: Module> Backend<M> {
|
||||
fn compile_value_or_ref(
|
||||
&self,
|
||||
valref: ValueOrRef<Type>,
|
||||
variables: &ScopedMap<Variable, ReferenceBuilder>,
|
||||
variables: &HashMap<Variable, ReferenceBuilder>,
|
||||
builder: &mut FunctionBuilder,
|
||||
) -> Result<(entities::Value, ConstantType), BackendError> {
|
||||
match valref {
|
||||
@@ -453,3 +534,23 @@ impl<M: Module> Backend<M> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimitiveType {
|
||||
fn blank_data(&self) -> DataDescription {
|
||||
let (size, alignment) = match self {
|
||||
PrimitiveType::Void => (8, 8),
|
||||
PrimitiveType::U8 => (1, 1),
|
||||
PrimitiveType::U16 => (2, 2),
|
||||
PrimitiveType::U32 => (4, 4),
|
||||
PrimitiveType::U64 => (4, 4),
|
||||
PrimitiveType::I8 => (1, 1),
|
||||
PrimitiveType::I16 => (2, 2),
|
||||
PrimitiveType::I32 => (4, 4),
|
||||
PrimitiveType::I64 => (4, 4),
|
||||
};
|
||||
let mut result = DataDescription::new();
|
||||
result.define_zeroinit(size);
|
||||
result.set_align(alignment);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user