todo: arbitrary ir
This commit is contained in:
@@ -26,7 +26,7 @@ impl Backend<JITModule> {
|
||||
/// library do. So, if you're validating equivalence between them, you'll want to weed
|
||||
/// out examples that overflow/underflow before checking equivalence. (This is the behavior
|
||||
/// of the built-in test systems.)
|
||||
pub fn eval(program: Program<Type>) -> Result<String, EvalError> {
|
||||
pub fn eval(program: Program<Type>) -> Result<String, EvalError<Expression<Type>>> {
|
||||
let mut jitter = Backend::jit(Some(String::new()))?;
|
||||
let mut function_map = HashMap::new();
|
||||
let mut main_function_body = vec![];
|
||||
@@ -80,7 +80,7 @@ impl Backend<ObjectModule> {
|
||||
/// library do. So, if you're validating equivalence between them, you'll want to weed
|
||||
/// out examples that overflow/underflow before checking equivalence. (This is the behavior
|
||||
/// of the built-in test systems.)
|
||||
pub fn eval(program: Program<Type>) -> Result<String, EvalError> {
|
||||
pub fn eval(program: Program<Type>) -> Result<String, EvalError<Expression<Type>>> {
|
||||
//use pretty::{Arena, Pretty};
|
||||
//let allocator = Arena::<()>::new();
|
||||
//program.pretty(&allocator).render(80, &mut std::io::stdout())?;
|
||||
@@ -147,7 +147,7 @@ impl Backend<ObjectModule> {
|
||||
/// This function assumes that this compilation and linking should run without any
|
||||
/// output, so changes to the RTS should make 100% sure that they do not generate
|
||||
/// any compiler warnings.
|
||||
fn link(object_file: &Path, executable_path: &Path) -> Result<(), EvalError> {
|
||||
fn link(object_file: &Path, executable_path: &Path) -> Result<(), EvalError<Expression<Type>>> {
|
||||
use std::path::PathBuf;
|
||||
|
||||
let output = std::process::Command::new("clang")
|
||||
@@ -179,7 +179,7 @@ proptest::proptest! {
|
||||
fn static_backend(program in Program::arbitrary_with(GenerationEnvironment::new(false))) {
|
||||
use crate::eval::PrimOpError;
|
||||
|
||||
let basic_result = program.eval();
|
||||
let basic_result = program.eval().map(|(_,x)| x);
|
||||
|
||||
// windows `printf` is going to terminate lines with "\r\n", so we need to adjust
|
||||
// our test result here.
|
||||
@@ -219,7 +219,7 @@ proptest::proptest! {
|
||||
// .expect("rendering works");
|
||||
|
||||
|
||||
let basic_result = program.eval();
|
||||
let basic_result = program.eval().map(|(_,x)| x);
|
||||
|
||||
if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) {
|
||||
let compiled_result = Backend::<JITModule>::eval(program);
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user