Get to the point of needing to construct/reference fields.

This commit is contained in:
2024-03-30 21:17:11 -07:00
parent 854fd60132
commit e1e798ef8e
6 changed files with 176 additions and 152 deletions

View File

@@ -1,4 +1,4 @@
use crate::{backend::runtime::RuntimeFunctionError, eval::PrimitiveType, ir::Type};
use crate::{backend::runtime::RuntimeFunctionError, ir::Type};
use codespan_reporting::diagnostic::Diagnostic;
use cranelift_codegen::{isa::LookupError, settings::SetError, CodegenError};
use cranelift_module::ModuleError;
@@ -40,7 +40,10 @@ pub enum BackendError {
#[error(transparent)]
Write(#[from] cranelift_object::object::write::Error),
#[error("Invalid type cast from {from} to {to}")]
InvalidTypeCast { from: PrimitiveType, to: Type },
InvalidTypeCast {
from: cranelift_codegen::ir::types::Type,
to: Type,
},
#[error("Unknown string constant '{0}")]
UnknownString(ArcIntern<String>),
#[error("Compiler doesn't currently support function arguments")]

View File

@@ -14,14 +14,16 @@ use cranelift_module::{DataDescription, FuncId, Linkage, Module};
use internment::ArcIntern;
use std::collections::{hash_map, HashMap};
const VOID_REPR_TYPE: types::Type = types::I64;
/// 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.
pub enum ReferenceBuilder {
Global(ConstantType, GlobalValue),
Local(ConstantType, cranelift_frontend::Variable),
Argument(ConstantType, entities::Value),
Global(types::Type, GlobalValue),
Local(types::Type, cranelift_frontend::Variable),
Argument(types::Type, entities::Value),
}
impl<M: Module> Backend<M> {
@@ -29,13 +31,10 @@ impl<M: Module> Backend<M> {
/// best as possible.
fn translate_type(&self, t: &Type) -> AbiParam {
let (value_type, extension) = match t {
Type::Function(_, _) => (
Type::Function(_, _) | Type::Structure(_) => (
types::Type::triple_pointer_type(&self.platform),
ir::ArgumentExtension::None,
),
Type::Structure(_) => {
unimplemented!()
}
Type::Primitive(PrimitiveType::Void) => (types::I8, ir::ArgumentExtension::None), // FIXME?
Type::Primitive(PrimitiveType::I8) => (types::I8, ir::ArgumentExtension::Sext),
Type::Primitive(PrimitiveType::I16) => (types::I16, ir::ArgumentExtension::Sext),
@@ -88,12 +87,23 @@ impl<M: Module> Backend<M> {
)?;
tracing::info!(name = %top_level_name, data_type = %pt, "defining top-level data");
self.module.define_data(data_id, &pt.blank_data())?;
let constant_type = ConstantType::from(pt);
self.defined_symbols
.insert(top_level_name, (data_id, pt.into()));
.insert(top_level_name, (data_id, constant_type.into()));
}
Type::Structure(_) => {
unimplemented!()
Type::Structure(mut fields) => {
let data_id = self.module.declare_data(
top_level_name.as_str(),
Linkage::Export,
true,
false,
)?;
tracing::info!(name = %top_level_name, "defining top-level data structure");
self.module.define_data(data_id, fields.blank_data())?;
let pointer = self.module.target_config().pointer_type();
self.defined_symbols
.insert(top_level_name, (data_id, pointer));
}
}
}
@@ -226,13 +236,8 @@ impl<M: Module> Backend<M> {
let main_block = builder.create_block();
// add the block parameters, which should be the function parameters
for (name, ty) in arguments.iter() {
let constant_type = ty
.try_into()
.map_err(|_| BackendError::NoFunctionArguments {
function_name: function_name.to_string(),
argument_name: name.to_string(),
})?;
let value = builder.append_block_param(main_block, ir::Type::from(constant_type));
let constant_type = self.translate_type(ty).value_type;
let value = builder.append_block_param(main_block, constant_type);
variables.insert(
name.clone(),
ReferenceBuilder::Argument(constant_type, value),
@@ -273,96 +278,98 @@ impl<M: Module> Backend<M> {
expr: Expression<Type>,
variables: &mut HashMap<Variable, ReferenceBuilder>,
builder: &mut FunctionBuilder,
) -> Result<(entities::Value, ConstantType), BackendError> {
) -> Result<(entities::Value, types::Type), BackendError> {
match expr {
Expression::Atomic(x) => self.compile_value_or_ref(x, variables, builder),
Expression::Cast(_, target_type, valref) => {
let val_is_signed = valref.type_of().is_signed();
let (val, val_type) = self.compile_value_or_ref(valref, variables, builder)?;
match (val_type, &target_type) {
(ConstantType::I8, Type::Primitive(PrimitiveType::I8)) => Ok((val, val_type)),
(ConstantType::I8, Type::Primitive(PrimitiveType::I16)) => {
Ok((builder.ins().sextend(types::I16, val), ConstantType::I16))
(types::I8, Type::Primitive(PrimitiveType::I8)) => Ok((val, val_type)),
(types::I8, Type::Primitive(PrimitiveType::I16)) => {
if val_is_signed {
Ok((builder.ins().sextend(types::I16, val), types::I16))
} else {
Ok((builder.ins().uextend(types::I16, val), types::I16))
}
}
(ConstantType::I8, Type::Primitive(PrimitiveType::I32)) => {
Ok((builder.ins().sextend(types::I32, val), ConstantType::I32))
(types::I8, Type::Primitive(PrimitiveType::I32)) => {
if val_is_signed {
Ok((builder.ins().sextend(types::I32, val), types::I32))
} else {
Ok((builder.ins().uextend(types::I32, val), types::I32))
}
}
(ConstantType::I8, Type::Primitive(PrimitiveType::I64)) => {
Ok((builder.ins().sextend(types::I64, val), ConstantType::I64))
(types::I8, Type::Primitive(PrimitiveType::I64)) => {
if val_is_signed {
Ok((builder.ins().sextend(types::I64, val), types::I64))
} else {
Ok((builder.ins().uextend(types::I64, val), types::I64))
}
}
(ConstantType::I16, Type::Primitive(PrimitiveType::I16)) => Ok((val, val_type)),
(ConstantType::I16, Type::Primitive(PrimitiveType::I32)) => {
Ok((builder.ins().sextend(types::I32, val), ConstantType::I32))
(types::I16, Type::Primitive(PrimitiveType::I16)) => Ok((val, val_type)),
(types::I16, Type::Primitive(PrimitiveType::I32)) => {
if val_is_signed {
Ok((builder.ins().sextend(types::I32, val), types::I32))
} else {
Ok((builder.ins().uextend(types::I32, val), types::I32))
}
}
(ConstantType::I16, Type::Primitive(PrimitiveType::I64)) => {
Ok((builder.ins().sextend(types::I64, val), ConstantType::I64))
(types::I16, Type::Primitive(PrimitiveType::I64)) => {
if val_is_signed {
Ok((builder.ins().sextend(types::I64, val), types::I64))
} else {
Ok((builder.ins().uextend(types::I64, val), types::I64))
}
}
(ConstantType::I32, Type::Primitive(PrimitiveType::I32)) => Ok((val, val_type)),
(ConstantType::I32, Type::Primitive(PrimitiveType::I64)) => {
Ok((builder.ins().sextend(types::I64, val), ConstantType::I64))
(types::I32, Type::Primitive(PrimitiveType::I32)) => Ok((val, val_type)),
(types::I32, Type::Primitive(PrimitiveType::I64)) => {
if val_is_signed {
Ok((builder.ins().sextend(types::I64, val), types::I64))
} else {
Ok((builder.ins().uextend(types::I64, val), types::I64))
}
}
(ConstantType::I64, Type::Primitive(PrimitiveType::I64)) => Ok((val, val_type)),
(types::I64, Type::Primitive(PrimitiveType::I64)) => Ok((val, val_type)),
(ConstantType::U8, Type::Primitive(PrimitiveType::U8)) => Ok((val, val_type)),
(ConstantType::U8, Type::Primitive(PrimitiveType::U16)) => {
Ok((builder.ins().uextend(types::I16, val), ConstantType::U16))
(types::I8, Type::Primitive(PrimitiveType::U8)) => Ok((val, val_type)),
(types::I8, Type::Primitive(PrimitiveType::U16)) => {
Ok((builder.ins().uextend(types::I16, val), types::I16))
}
(ConstantType::U8, Type::Primitive(PrimitiveType::U32)) => {
Ok((builder.ins().uextend(types::I32, val), ConstantType::U32))
(types::I8, Type::Primitive(PrimitiveType::U32)) => {
Ok((builder.ins().uextend(types::I32, val), types::I32))
}
(ConstantType::U8, Type::Primitive(PrimitiveType::U64)) => {
Ok((builder.ins().uextend(types::I64, val), ConstantType::U64))
(types::I8, Type::Primitive(PrimitiveType::U64)) => {
Ok((builder.ins().uextend(types::I64, val), types::I64))
}
(ConstantType::U16, Type::Primitive(PrimitiveType::U16)) => Ok((val, val_type)),
(ConstantType::U16, Type::Primitive(PrimitiveType::U32)) => {
Ok((builder.ins().uextend(types::I32, val), ConstantType::U32))
(types::I16, Type::Primitive(PrimitiveType::U16)) => Ok((val, val_type)),
(types::I16, Type::Primitive(PrimitiveType::U32)) => {
Ok((builder.ins().uextend(types::I32, val), types::I32))
}
(ConstantType::U16, Type::Primitive(PrimitiveType::U64)) => {
Ok((builder.ins().uextend(types::I64, val), ConstantType::U64))
(types::I16, Type::Primitive(PrimitiveType::U64)) => {
Ok((builder.ins().uextend(types::I64, val), types::I64))
}
(ConstantType::U32, Type::Primitive(PrimitiveType::U32)) => Ok((val, val_type)),
(ConstantType::U32, Type::Primitive(PrimitiveType::U64)) => {
Ok((builder.ins().uextend(types::I64, val), ConstantType::U64))
(types::I32, Type::Primitive(PrimitiveType::U32)) => Ok((val, val_type)),
(types::I32, Type::Primitive(PrimitiveType::U64)) => {
Ok((builder.ins().uextend(types::I64, val), types::I64))
}
(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))
}
(types::I64, Type::Primitive(PrimitiveType::U64)) => Ok((val, val_type)),
_ => Err(BackendError::InvalidTypeCast {
from: val_type.into(),
from: val_type,
to: target_type,
}),
}
}
Expression::Primitive(_, _, prim, mut vals) => {
Expression::Primitive(_, ret_type, prim, mut vals) => {
let mut values = vec![];
let mut first_type = None;
@@ -392,7 +399,7 @@ impl<M: Module> Backend<M> {
}
Primitive::Minus => Ok((builder.ins().ineg(values[0]), first_type)),
Primitive::Times => Ok((builder.ins().imul(values[0], values[1]), first_type)),
Primitive::Divide if first_type.is_signed() => {
Primitive::Divide if ret_type.is_signed() => {
Ok((builder.ins().sdiv(values[0], values[1]), first_type))
}
Primitive::Divide => Ok((builder.ins().udiv(values[0], values[1]), first_type)),
@@ -403,7 +410,7 @@ impl<M: Module> Backend<M> {
Expression::FieldRef(_, _, _, _) => unimplemented!(),
Expression::Block(_, _, mut exprs) => match exprs.pop() {
None => Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)),
None => Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)),
Some(last) => {
for inner in exprs {
// we can ignore all of these return values and such, because we
@@ -431,20 +438,31 @@ impl<M: Module> Backend<M> {
.declare_data_in_func(string_data_id, builder.func);
let name_ptr = builder.ins().symbol_value(types::I64, local_name_ref);
let (val, vtype) = self.compile_value_or_ref(var, variables, builder)?;
let var_type = var.type_of();
let (val, _) = self.compile_value_or_ref(var, variables, builder)?;
let vtype_repr = builder.ins().iconst(types::I64, vtype as i64);
let (repr_val, casted_val) = match var_type {
Type::Structure(_) => (ConstantType::I64 as i64, val),
Type::Function(_, _) => (ConstantType::I64 as i64, val),
Type::Primitive(pt) => {
let constant_type = pt.into();
let casted_val = match vtype {
ConstantType::U64 | ConstantType::I64 | ConstantType::Void => 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)
let new_val = match constant_type {
ConstantType::U64 | ConstantType::I64 | ConstantType::Void => 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)
}
};
(constant_type as i64, new_val)
}
};
let vtype_repr = builder.ins().iconst(types::I64, repr_val);
// Finally, we can generate the call to print.
let print_func_ref = self.runtime_functions.include_runtime_function(
"print",
@@ -455,12 +473,11 @@ impl<M: Module> Backend<M> {
print_func_ref,
&[buffer_ptr, name_ptr, vtype_repr, casted_val],
);
Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void))
Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE))
}
Expression::Bind(_, name, _, expr) => {
let (value, value_type) = self.compile_expression(*expr, variables, builder)?;
let ir_type = ir::Type::from(value_type);
let variable = self.generate_local();
match variables.get(&name) {
@@ -468,7 +485,7 @@ impl<M: Module> Backend<M> {
let pointer = self.module.target_config().pointer_type();
let pointer_to = builder.ins().symbol_value(pointer, *global_value);
builder.ins().store(MemFlags::new(), value, pointer_to, 0);
Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void))
Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE))
}
Some(ReferenceBuilder::Argument(_, _)) => {
@@ -480,10 +497,10 @@ impl<M: Module> Backend<M> {
}
None => {
builder.declare_var(variable, ir_type);
builder.declare_var(variable, value_type);
builder.def_var(variable, value);
variables.insert(name, ReferenceBuilder::Local(value_type, variable));
Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void))
Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE))
}
}
}
@@ -511,21 +528,10 @@ impl<M: Module> Backend<M> {
let results = builder.inst_results(call);
match results {
[] => Ok((
builder.ins().iconst(types::I64, 0),
ConstantType::Void,
)),
[result] => match result_type {
Type::Primitive(ct) => Ok((*result, ct.into())),
Type::Function(_, rt) => match *rt {
Type::Function(_, _) => {
panic!("function returns a function?")
}
Type::Structure(_) => unimplemented!(),
Type::Primitive(ct) => Ok((*result, ct.into())),
},
Type::Structure(_) => unimplemented!(),
},
[] => Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)),
[result] => {
Ok((*result, self.translate_type(&result_type).value_type))
}
_ => panic!("don't support multi-value returns yet"),
}
}
@@ -544,7 +550,7 @@ impl<M: Module> Backend<M> {
value_or_ref: ValueOrRef<Type>,
variables: &HashMap<Variable, ReferenceBuilder>,
builder: &mut FunctionBuilder,
) -> Result<(entities::Value, ConstantType), BackendError> {
) -> Result<(entities::Value, types::Type), BackendError> {
match value_or_ref {
ValueOrRef::Value(_, _, val) => match val {
Value::I8(_, v) => {
@@ -555,49 +561,31 @@ impl<M: Module> Backend<M> {
// negative number for us. Which sets the high bits, which makes Cranelift unhappy.
// So first we cast the i8 as u8, to get rid of the whole concept of sign extension,
// and *then* we cast to i64.
Ok((
builder.ins().iconst(types::I8, v as u8 as i64),
ConstantType::I8,
))
Ok((builder.ins().iconst(types::I8, v as u8 as i64), types::I8))
}
Value::I16(_, v) => Ok((
// see above note for the "... as ... as"
builder.ins().iconst(types::I16, v as u16 as i64),
ConstantType::I16,
types::I16,
)),
Value::I32(_, v) => Ok((
// see above note for the "... as ... as"
builder.ins().iconst(types::I32, v as u32 as i64),
ConstantType::I32,
types::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,
)),
Value::Void => Ok((builder.ins().iconst(types::I64, 0i64), ConstantType::Void)),
Value::I64(_, v) => Ok((builder.ins().iconst(types::I64, v), types::I64)),
Value::U8(_, v) => Ok((builder.ins().iconst(types::I8, v as i64), types::I8)),
Value::U16(_, v) => Ok((builder.ins().iconst(types::I16, v as i64), types::I16)),
Value::U32(_, v) => Ok((builder.ins().iconst(types::I32, v as i64), types::I32)),
Value::U64(_, v) => Ok((builder.ins().iconst(types::I64, v as i64), types::I64)),
Value::Void => Ok((builder.ins().iconst(types::I64, 0i64), VOID_REPR_TYPE)),
},
ValueOrRef::Ref(_, _, name) => match variables.get(&name) {
None => Err(BackendError::VariableLookupFailure(name)),
Some(ReferenceBuilder::Global(ty, gv)) => {
let pointer_to = self.module.target_config().pointer_type();
let pointer_value = builder.ins().symbol_value(pointer_to, *gv);
let cranelift_type = ir::Type::from(*ty);
let value =
builder
.ins()
.load(cranelift_type, MemFlags::new(), pointer_value, 0);
let value = builder.ins().load(*ty, MemFlags::new(), pointer_value, 0);
Ok((value, *ty))
}