Clean up primitive handling, finally.

This commit is contained in:
2024-04-16 16:20:31 -07:00
parent 763a895285
commit 7d4f182a67
19 changed files with 399 additions and 550 deletions

View File

@@ -374,43 +374,6 @@ impl<M: Module> Backend<M> {
}
}
Expression::Primitive(_, ret_type, prim, mut vals) => {
let mut values = vec![];
let mut first_type = None;
for val in vals.drain(..) {
let (compiled, compiled_type) =
self.compile_value_or_ref(val, variables, builder)?;
if let Some(leftmost_type) = first_type {
assert_eq!(leftmost_type, compiled_type);
} else {
first_type = Some(compiled_type);
}
values.push(compiled);
}
let first_type = first_type.expect("primitive op has at least one argument");
// 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(values[0], values[1]), first_type)),
Primitive::Minus if values.len() == 2 => {
Ok((builder.ins().isub(values[0], values[1]), first_type))
}
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 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)),
}
}
Expression::Construct(_, ty, _, fields) => {
let Type::Structure(type_fields) = ty else {
panic!("Got to backend with non-structure type in structure construction?!");
@@ -493,60 +456,6 @@ impl<M: Module> Backend<M> {
}
},
Expression::Print(_, var) => {
// Get the output buffer (or null) from our general compilation context.
let buffer_ptr = self.output_buffer_ptr();
let buffer_ptr = builder.ins().iconst(types::I64, buffer_ptr as i64);
// Get a reference to the string we want to print.
let var_name = match var {
ValueOrRef::Ref(_, _, ref name) => name.as_ref(),
ValueOrRef::Value(_, _, _) => "<unknown>",
};
let string_data_id = self.string_reference(var_name)?;
let local_name_ref = self
.module
.declare_data_in_func(string_data_id, builder.func);
let name_ptr = builder.ins().symbol_value(types::I64, local_name_ref);
let var_type = var.type_of();
let (val, _) = self.compile_value_or_ref(var, variables, builder)?;
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 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",
&mut self.module,
builder.func,
)?;
builder.ins().call(
print_func_ref,
&[buffer_ptr, name_ptr, vtype_repr, casted_val],
);
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 variable = self.generate_local();
@@ -576,8 +485,15 @@ impl<M: Module> Backend<M> {
Ok((value, value_type))
}
Expression::Call(_, _, function, args) => {
let (arguments, _argument_types): (Vec<_>, Vec<_>) = args
Expression::Call(_, final_type, function, args) => {
// Get a reference to the string we want to print.
let var_name = match args[0] {
ValueOrRef::Ref(_, _, ref name) => name.to_string(),
ValueOrRef::Value(_, _, _) => "<unknown>".to_string(),
ValueOrRef::Primitive(_, _, n) => format!("<primitive {}>", n),
};
let var_type = args[0].type_of();
let (arguments, argument_types): (Vec<_>, Vec<_>) = args
.into_iter()
.map(|x| self.compile_value_or_ref(x, variables, builder))
.collect::<Result<Vec<(_, _)>, BackendError>>()?
@@ -608,6 +524,109 @@ impl<M: Module> Backend<M> {
}
}
}
ValueOrRef::Primitive(_, _, prim) => match prim {
Primitive::Plus => {
assert_eq!(2, arguments.len());
Ok((
builder.ins().iadd(arguments[0], arguments[1]),
argument_types[0],
))
}
Primitive::Minus => {
assert_eq!(2, arguments.len());
Ok((
builder.ins().isub(arguments[0], arguments[1]),
argument_types[0],
))
}
Primitive::Times => {
assert_eq!(2, arguments.len());
Ok((
builder.ins().imul(arguments[0], arguments[1]),
argument_types[0],
))
}
Primitive::Divide if final_type.is_signed() => {
assert_eq!(2, arguments.len());
Ok((
builder.ins().sdiv(arguments[0], arguments[1]),
argument_types[0],
))
}
Primitive::Divide => {
assert_eq!(2, arguments.len());
Ok((
builder.ins().udiv(arguments[0], arguments[1]),
argument_types[0],
))
}
Primitive::Negate => {
assert_eq!(1, arguments.len());
Ok((
builder.ins().ineg(arguments[0]),
argument_types[0],
))
}
Primitive::Print => {
// Get the output buffer (or null) from our general compilation context.
let buffer_ptr = self.output_buffer_ptr();
let buffer_ptr = builder.ins().iconst(types::I64, buffer_ptr as i64);
assert_eq!(1, arguments.len());
let string_data_id = self.string_reference(&var_name)?;
let local_name_ref = self
.module
.declare_data_in_func(string_data_id, builder.func);
let name_ptr = builder.ins().symbol_value(types::I64, local_name_ref);
let (repr_val, casted_val) = match var_type {
Type::Structure(_) => (ConstantType::I64 as i64, arguments[0]),
Type::Function(_, _) => (ConstantType::I64 as i64, arguments[0]),
Type::Primitive(pt) => {
let constant_type = pt.into();
let new_val = match constant_type {
ConstantType::U64
| ConstantType::I64
| ConstantType::Void => arguments[0],
ConstantType::I8
| ConstantType::I16
| ConstantType::I32 => {
builder.ins().sextend(types::I64, arguments[0])
}
ConstantType::U8
| ConstantType::U16
| ConstantType::U32 => {
builder.ins().uextend(types::I64, arguments[0])
}
};
(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",
&mut self.module,
builder.func,
)?;
builder.ins().call(
print_func_ref,
&[buffer_ptr, name_ptr, vtype_repr, casted_val],
);
Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE))
}
},
}
}
}
@@ -668,6 +687,9 @@ impl<M: Module> Backend<M> {
Ok((value, *ctype))
}
},
ValueOrRef::Primitive(_, _, _) => {
unimplemented!()
}
}
}
}