Add support for multiple integer types.
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::ir::{Expression, Primitive, Program, Statement, Value, ValueOrRef};
|
||||
use crate::syntax::ConstantType;
|
||||
use cranelift_codegen::entity::EntityRef;
|
||||
use cranelift_codegen::ir::{
|
||||
entities, types, Function, GlobalValue, InstBuilder, MemFlags, Signature, UserFuncName,
|
||||
self, entities, types, Function, GlobalValue, InstBuilder, MemFlags, Signature, UserFuncName,
|
||||
};
|
||||
use cranelift_codegen::isa::CallConv;
|
||||
use cranelift_codegen::Context;
|
||||
@@ -85,12 +86,12 @@ impl<M: Module> Backend<M> {
|
||||
// Just like with strings, generating the `GlobalValue`s we need can potentially
|
||||
// be a little tricky to do on the fly, so we generate the complete list right
|
||||
// here and then use it later.
|
||||
let pre_defined_symbols: HashMap<String, GlobalValue> = self
|
||||
let pre_defined_symbols: HashMap<String, (GlobalValue, ConstantType)> = self
|
||||
.defined_symbols
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
.map(|(k, (v, t))| {
|
||||
let local_data = self.module.declare_data_in_func(*v, &mut ctx.func);
|
||||
(k.clone(), local_data)
|
||||
(k.clone(), (local_data, *t))
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -135,31 +136,47 @@ 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 val = Expression::Reference(ann, var).into_crane(
|
||||
let (val, vtype) = Expression::Reference(ann, var).into_crane(
|
||||
&mut builder,
|
||||
&variable_table,
|
||||
&pre_defined_symbols,
|
||||
)?;
|
||||
|
||||
let vtype_repr = builder.ins().iconst(types::I64, vtype as i64);
|
||||
|
||||
let casted_val = match vtype {
|
||||
ConstantType::U64 | ConstantType::I64 => val,
|
||||
ConstantType::I8 | ConstantType::I16 | ConstantType::I32 => {
|
||||
builder.ins().sextend(types::I64, val)
|
||||
}
|
||||
ConstantType::U8 | ConstantType::U16 | ConstantType::U32 => {
|
||||
builder.ins().uextend(types::I64, val)
|
||||
}
|
||||
};
|
||||
|
||||
// Finally, we can generate the call to print.
|
||||
builder
|
||||
.ins()
|
||||
.call(print_func_ref, &[buffer_ptr, name_ptr, val]);
|
||||
builder.ins().call(
|
||||
print_func_ref,
|
||||
&[buffer_ptr, name_ptr, vtype_repr, casted_val],
|
||||
);
|
||||
}
|
||||
|
||||
// Variable binding is a little more con
|
||||
Statement::Binding(_, var_name, value) => {
|
||||
// Kick off to the `Expression` implementation to see what value we're going
|
||||
// to bind to this variable.
|
||||
let val =
|
||||
let (val, etype) =
|
||||
value.into_crane(&mut builder, &variable_table, &pre_defined_symbols)?;
|
||||
|
||||
// Now the question is: is this a local variable, or a global one?
|
||||
if let Some(global_id) = pre_defined_symbols.get(var_name.as_str()) {
|
||||
if let Some((global_id, ctype)) = pre_defined_symbols.get(var_name.as_str()) {
|
||||
// It's a global variable! In this case, we assume that someone has already
|
||||
// dedicated some space in memory to store this value. We look this location
|
||||
// up, and then tell Cranelift to store the value there.
|
||||
let val_ptr = builder.ins().symbol_value(types::I64, *global_id);
|
||||
assert_eq!(etype, *ctype);
|
||||
let val_ptr = builder
|
||||
.ins()
|
||||
.symbol_value(ir::Type::from(*ctype), *global_id);
|
||||
builder.ins().store(MemFlags::new(), val, val_ptr, 0);
|
||||
} else {
|
||||
// It's a local variable! In this case, we need to allocate a new Cranelift
|
||||
@@ -171,12 +188,10 @@ impl<M: Module> Backend<M> {
|
||||
next_var_num += 1;
|
||||
|
||||
// We can add the variable directly to our local variable map; it's `Copy`.
|
||||
variable_table.insert(var_name, var);
|
||||
variable_table.insert(var_name, (var, etype));
|
||||
|
||||
// Now we tell Cranelift about our new variable, which has type I64 because
|
||||
// everything we have at this point is of type I64. Once it's declare, we
|
||||
// define it as having the value we computed above.
|
||||
builder.declare_var(var, types::I64);
|
||||
// Now we tell Cranelift about our new variable!
|
||||
builder.declare_var(var, ir::Type::from(etype));
|
||||
builder.def_var(var, val);
|
||||
}
|
||||
}
|
||||
@@ -231,26 +246,60 @@ impl Expression {
|
||||
fn into_crane(
|
||||
self,
|
||||
builder: &mut FunctionBuilder,
|
||||
local_variables: &HashMap<ArcIntern<String>, Variable>,
|
||||
global_variables: &HashMap<String, GlobalValue>,
|
||||
) -> Result<entities::Value, BackendError> {
|
||||
local_variables: &HashMap<ArcIntern<String>, (Variable, ConstantType)>,
|
||||
global_variables: &HashMap<String, (GlobalValue, ConstantType)>,
|
||||
) -> Result<(entities::Value, ConstantType), BackendError> {
|
||||
match self {
|
||||
// Values are pretty straightforward to compile, mostly because we only
|
||||
// have one type of variable, and it's an integer type.
|
||||
Expression::Value(_, Value::Number(_, v)) => Ok(builder.ins().iconst(types::I64, v)),
|
||||
Expression::Value(_, val) => match val {
|
||||
Value::I8(_, v) => {
|
||||
Ok((builder.ins().iconst(types::I8, v as i64), ConstantType::I8))
|
||||
}
|
||||
Value::I16(_, v) => Ok((
|
||||
builder.ins().iconst(types::I16, v as i64),
|
||||
ConstantType::I16,
|
||||
)),
|
||||
Value::I32(_, v) => Ok((
|
||||
builder.ins().iconst(types::I32, v as i64),
|
||||
ConstantType::I32,
|
||||
)),
|
||||
Value::I64(_, v) => Ok((builder.ins().iconst(types::I64, v), ConstantType::I64)),
|
||||
Value::U8(_, v) => {
|
||||
Ok((builder.ins().iconst(types::I8, v as i64), ConstantType::U8))
|
||||
}
|
||||
Value::U16(_, v) => Ok((
|
||||
builder.ins().iconst(types::I16, v as i64),
|
||||
ConstantType::U16,
|
||||
)),
|
||||
Value::U32(_, v) => Ok((
|
||||
builder.ins().iconst(types::I32, v as i64),
|
||||
ConstantType::U32,
|
||||
)),
|
||||
Value::U64(_, v) => Ok((
|
||||
builder.ins().iconst(types::I64, v as i64),
|
||||
ConstantType::U64,
|
||||
)),
|
||||
},
|
||||
|
||||
Expression::Reference(_, name) => {
|
||||
// first we see if this is a local variable (which is nicer, from an
|
||||
// optimization point of view.)
|
||||
if let Some(local_var) = local_variables.get(&name) {
|
||||
return Ok(builder.use_var(*local_var));
|
||||
if let Some((local_var, etype)) = local_variables.get(&name) {
|
||||
return Ok((builder.use_var(*local_var), *etype));
|
||||
}
|
||||
|
||||
// then we check to see if this is a global reference, which requires us to
|
||||
// first lookup where the value is stored, and then load it.
|
||||
if let Some(global_var) = global_variables.get(name.as_ref()) {
|
||||
let val_ptr = builder.ins().symbol_value(types::I64, *global_var);
|
||||
return Ok(builder.ins().load(types::I64, MemFlags::new(), val_ptr, 0));
|
||||
if let Some((global_var, etype)) = global_variables.get(name.as_ref()) {
|
||||
let cranelift_type = ir::Type::from(*etype);
|
||||
let val_ptr = builder.ins().symbol_value(cranelift_type, *global_var);
|
||||
return Ok((
|
||||
builder
|
||||
.ins()
|
||||
.load(cranelift_type, MemFlags::new(), val_ptr, 0),
|
||||
*etype,
|
||||
));
|
||||
}
|
||||
|
||||
// this should never happen, because we should have made sure that there are
|
||||
@@ -260,25 +309,27 @@ impl Expression {
|
||||
|
||||
Expression::Primitive(_, prim, mut vals) => {
|
||||
// we're going to use `pop`, so we're going to pull and compile the right value ...
|
||||
let right =
|
||||
let (right, rtype) =
|
||||
vals.pop()
|
||||
.unwrap()
|
||||
.into_crane(builder, local_variables, global_variables)?;
|
||||
// ... and then the left.
|
||||
let left =
|
||||
let (left, ltype) =
|
||||
vals.pop()
|
||||
.unwrap()
|
||||
.into_crane(builder, local_variables, global_variables)?;
|
||||
|
||||
assert_eq!(rtype, ltype, "primitive argument types match");
|
||||
// then we just need to tell Cranelift how to do each of our primitives! Much
|
||||
// like Statements, above, we probably want to eventually shuffle this off into
|
||||
// a separate function (maybe something off `Primitive`), but for now it's simple
|
||||
// enough that we just do the `match` here.
|
||||
match prim {
|
||||
Primitive::Plus => Ok(builder.ins().iadd(left, right)),
|
||||
Primitive::Minus => Ok(builder.ins().isub(left, right)),
|
||||
Primitive::Times => Ok(builder.ins().imul(left, right)),
|
||||
Primitive::Divide => Ok(builder.ins().sdiv(left, right)),
|
||||
Primitive::Plus => Ok((builder.ins().iadd(left, right), ltype)),
|
||||
Primitive::Minus => Ok((builder.ins().isub(left, right), ltype)),
|
||||
Primitive::Times => Ok((builder.ins().imul(left, right), ltype)),
|
||||
Primitive::Divide if rtype.is_signed() => Ok((builder.ins().sdiv(left, right), ltype)),
|
||||
Primitive::Divide => Ok((builder.ins().udiv(left, right), ltype)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,9 +342,9 @@ impl ValueOrRef {
|
||||
fn into_crane(
|
||||
self,
|
||||
builder: &mut FunctionBuilder,
|
||||
local_variables: &HashMap<ArcIntern<String>, Variable>,
|
||||
global_variables: &HashMap<String, GlobalValue>,
|
||||
) -> Result<entities::Value, BackendError> {
|
||||
local_variables: &HashMap<ArcIntern<String>, (Variable, ConstantType)>,
|
||||
global_variables: &HashMap<String, (GlobalValue, ConstantType)>,
|
||||
) -> Result<(entities::Value, ConstantType), BackendError> {
|
||||
Expression::from(self).into_crane(builder, local_variables, global_variables)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user