todo: arbitrary ir

This commit is contained in:
2023-12-03 17:32:37 -08:00
parent 93cac44a99
commit 2c2268925a
16 changed files with 298 additions and 163 deletions

View File

@@ -1,5 +1,3 @@
use std::collections::HashMap;
use crate::eval::PrimitiveType;
use crate::ir::{Expression, Primitive, Program, TopLevel, Type, Value, ValueOrRef, Variable};
use crate::syntax::{ConstantType, Location};
@@ -16,14 +14,6 @@ use internment::ArcIntern;
use crate::backend::error::BackendError;
use crate::backend::Backend;
/// When we're compiling, we might need to reference some of the strings built into
/// the source code; to do so, we need a `GlobalValue`. Perhaps unexpectedly, given
/// the name, `GlobalValue`s are specific to a single function we're compiling, so
/// we end up computing this table for every function.
///
/// This just a handy type alias to avoid a lot of confusion in the functions.
type StringTable = HashMap<ArcIntern<String>, GlobalValue>;
/// 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
@@ -36,7 +26,9 @@ struct ReferenceBuilder {
impl ReferenceBuilder {
fn refer_to(&self, builder: &mut FunctionBuilder) -> (entities::Value, ConstantType) {
let value = builder.ins().symbol_value(self.cranelift_type, self.local_data);
let value = builder
.ins()
.symbol_value(self.cranelift_type, self.local_data);
(value, self.ir_type)
}
}
@@ -83,7 +75,7 @@ impl<M: Module> Backend<M> {
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(name.as_str(), &args, rettype, body)?;
}
TopLevel::Statement(stmt) => {
@@ -139,18 +131,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);
// In the future, we might want to see what runtime functions the function
// we were given uses, and then only include those functions that we care
// about. Presumably, we'd use some sort of lookup table like we do for
// strings. But for now, we only have one runtime function, and we're pretty
// sure we're always going to use it, so we just declare it (and reference
// it) directly.
let print_func_ref = self.runtime_functions.include_runtime_function(
"print",
&mut self.module,
&mut ctx.func,
)?;
// 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
@@ -166,7 +146,11 @@ impl<M: Module> Backend<M> {
let cranelift_type = ir::Type::from(*ty);
variables.insert(
name.clone(),
ReferenceBuilder { cranelift_type, local_data, ir_type: *ty },
ReferenceBuilder {
cranelift_type,
local_data,
ir_type: *ty,
},
);
}
@@ -176,9 +160,6 @@ impl<M: Module> Backend<M> {
// to win.
variables.new_scope();
// FIXME: Add arguments
let mut next_var_num = 1;
// Finally (!), we generate the function builder that we're going to use to
// make this function!
let mut fctx = FunctionBuilderContext::new();
@@ -326,7 +307,7 @@ impl<M: Module> Backend<M> {
for inner in exprs {
// we can ignore all of these return values and such, because we
// don't actually use them anywhere
self.compile_expression(inner, variables, builder);
self.compile_expression(inner, variables, builder)?;
}
// instead, we just return the last one
self.compile_expression(last, variables, builder)