Clean up primitive handling, finally.
This commit is contained in:
@@ -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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user