Remove Cranelift for now.

This commit is contained in:
Richard Feldman 2020-03-25 20:18:57 -04:00
parent 9eed02ae4a
commit a561f343b2
6 changed files with 9 additions and 1496 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,52 +0,0 @@
use cranelift::prelude::AbiParam;
use cranelift_codegen::ir::{types, Signature, Type};
use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_module::{Backend, Module};
use roc_mono::layout::Layout;
pub fn type_from_layout(cfg: TargetFrontendConfig, layout: &Layout<'_>) -> Type {
use roc_mono::layout::Builtin::*;
use roc_mono::layout::Layout::*;
match layout {
FunctionPointer(_, _) | Pointer(_) | Struct(_) | Union(_) => cfg.pointer_type(),
Builtin(builtin) => match builtin {
Int64 => types::I64,
Float64 => types::F64,
Bool => types::I8,
Byte => types::I8,
Str | EmptyStr | Map(_, _) | EmptyMap | Set(_) | EmptySet | List(_) | EmptyList => {
cfg.pointer_type()
}
},
}
}
pub fn sig_from_layout<B: Backend>(
cfg: TargetFrontendConfig,
module: &mut Module<B>,
layout: &Layout<'_>,
) -> Signature {
match layout {
Layout::FunctionPointer(args, ret) => {
let ret_type = type_from_layout(cfg, &ret);
let mut sig = module.make_signature();
// Add return type to the signature
sig.returns.push(AbiParam::new(ret_type));
// Add params to the signature
for layout in args.iter() {
let arg_type = type_from_layout(cfg, &layout);
sig.params.push(AbiParam::new(arg_type));
}
sig
}
_ => {
panic!("Could not make Signature from Layout {:?}", layout);
}
}
}

View File

@ -1,117 +0,0 @@
use cranelift::prelude::{AbiParam, FunctionBuilder, FunctionBuilderContext};
use cranelift_codegen::ir::{ExternalName, Function, InstBuilder, Signature};
use cranelift_codegen::isa::CallConv;
use cranelift_codegen::Context;
use cranelift_module::{Backend, FuncId, Linkage, Module};
pub fn declare_malloc_header<B: Backend>(module: &mut Module<B>) -> (FuncId, Signature) {
let ptr_size_type = module.target_config().pointer_type();
let sig = Signature {
params: vec![AbiParam::new(ptr_size_type)],
returns: vec![AbiParam::new(ptr_size_type)],
call_conv: CallConv::SystemV, // TODO is this the calling convention we actually want?
};
// Declare the wrapper around malloc
let func_id = module
.declare_function("roc_malloc", Linkage::Local, &sig)
.unwrap();
(func_id, sig)
}
pub fn define_malloc_body<B: Backend>(
module: &mut Module<B>,
ctx: &mut Context,
sig: Signature,
func_id: FuncId,
) {
let ptr_size_type = module.target_config().pointer_type();
ctx.func = Function::with_name_signature(ExternalName::user(0, func_id.as_u32()), sig);
let mut func_ctx = FunctionBuilderContext::new();
{
let mut builder: FunctionBuilder = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
let block = builder.create_block();
builder.switch_to_block(block);
builder.append_block_params_for_function_params(block);
let mut malloc_sig = module.make_signature();
malloc_sig.params.push(AbiParam::new(ptr_size_type));
malloc_sig.returns.push(AbiParam::new(ptr_size_type));
let callee = module
.declare_function("malloc", Linkage::Import, &malloc_sig)
.expect("declare malloc");
let malloc = module.declare_func_in_func(callee, &mut builder.func);
let size = builder.block_params(block)[0];
let call = builder.ins().call(malloc, &[size]);
let ptr = builder.inst_results(call)[0];
builder.ins().return_(&[ptr]);
// TODO re-enable this once Switch stops making unsealed blocks, e.g.
// https://docs.rs/cranelift-frontend/0.59.0/src/cranelift_frontend/switch.rs.html#152
// builder.seal_block(block);
}
module.define_function(func_id, ctx).unwrap();
}
pub fn define_malloc<B: Backend>(module: &mut Module<B>, ctx: &mut Context) -> FuncId {
// TODO investigate whether we can remove this wrapper function somehow.
// It may get inlined away, but it seems like it shouldn't be
// necessary, and we should be able to return the FuncId of the imported malloc.
let ptr_size_type = module.target_config().pointer_type();
let sig = Signature {
params: vec![AbiParam::new(ptr_size_type)],
returns: vec![AbiParam::new(ptr_size_type)],
call_conv: CallConv::SystemV, // TODO is this the calling convention we actually want?
};
// Declare the wrapper around malloc
let func_id = module
.declare_function("roc_malloc", Linkage::Local, &sig)
.unwrap();
let ptr_size_type = module.target_config().pointer_type();
ctx.func = Function::with_name_signature(ExternalName::user(0, func_id.as_u32()), sig);
let mut func_ctx = FunctionBuilderContext::new();
{
let mut builder: FunctionBuilder = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
let block = builder.create_block();
builder.switch_to_block(block);
builder.append_block_params_for_function_params(block);
let mut malloc_sig = module.make_signature();
malloc_sig.params.push(AbiParam::new(ptr_size_type));
malloc_sig.returns.push(AbiParam::new(ptr_size_type));
let callee = module
.declare_function("malloc", Linkage::Import, &malloc_sig)
.expect("declare malloc");
let malloc = module.declare_func_in_func(callee, &mut builder.func);
let size = builder.block_params(block)[0];
let call = builder.ins().call(malloc, &[size]);
let ptr = builder.inst_results(call)[0];
builder.ins().return_(&[ptr]);
builder.seal_block(block);
builder.finalize();
}
module.define_function(func_id, ctx).unwrap();
module.clear_context(ctx);
func_id
}

View File

@ -1,3 +0,0 @@
pub mod build;
pub mod convert;
pub mod imports;

View File

@ -10,5 +10,5 @@
// and encouraging shortcuts here creates bad incentives. I would rather temporarily
// re-enable this when working on performance optimizations than have it block PRs.
#![allow(clippy::large_enum_variant)]
pub mod crane;
pub mod llvm;

View File

@ -14,181 +14,23 @@ mod helpers;
mod test_gen {
use crate::helpers::{can_expr, infer_expr, uniq_expr, CanExprOut};
use bumpalo::Bump;
use cranelift::prelude::{AbiParam, ExternalName, FunctionBuilder, FunctionBuilderContext};
use cranelift_codegen::ir::{ArgumentPurpose, InstBuilder};
use cranelift_codegen::settings;
use cranelift_codegen::verifier::verify_function;
use cranelift_module::{default_libcall_names, Linkage, Module};
use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder};
use inkwell::context::Context;
use inkwell::execution_engine::JitFunction;
use inkwell::passes::PassManager;
use inkwell::types::BasicType;
use inkwell::OptimizationLevel;
use roc_collections::all::ImMap;
use roc_gen::crane::build::{declare_proc, define_proc_body, ScopeEntry};
use roc_gen::crane::convert::type_from_layout;
use roc_gen::crane::imports::define_malloc;
use roc_gen::llvm::build::{build_proc, build_proc_header};
use roc_gen::llvm::convert::basic_type_from_layout;
use roc_mono::expr::{Expr, Procs};
use roc_mono::layout::Layout;
use roc_types::subs::Subs;
use std::ffi::{CStr, CString};
use std::mem;
use std::os::raw::c_char;
// Pointer size on 64-bit platforms
const POINTER_SIZE: u32 = std::mem::size_of::<u64>() as u32;
macro_rules! assert_crane_evals_to {
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
let arena = Bump::new();
let CanExprOut { loc_expr, var_store, var, constraint, home, interns, .. } = can_expr($src);
let subs = Subs::new(var_store.into());
let mut unify_problems = Vec::new();
let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
let shared_builder = settings::builder();
let shared_flags = settings::Flags::new(shared_builder);
let mut module: Module<SimpleJITBackend> =
Module::new(SimpleJITBuilder::new(default_libcall_names()));
let cfg = module.target_config();
let mut ctx = module.make_context();
let malloc = define_malloc(&mut module, &mut ctx);
let mut func_ctx = FunctionBuilderContext::new();
let main_fn_name = "$Test.main";
// Compute main_fn_ret_type before moving subs to Env
let layout = Layout::from_content(&arena, content, &subs, POINTER_SIZE)
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert content to layout. Err was {:?} and Subs were {:?}", err, subs));
// Compile and add all the Procs before adding main
let mut procs = Procs::default();
let mut env = roc_gen::crane::build::Env {
arena: &arena,
interns,
cfg,
malloc,
variable_counter: &mut 0
};
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
let mono_expr = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, POINTER_SIZE);
// Put this module's ident_ids back in the interns
env.interns.all_ident_ids.insert(home, ident_ids);
let mut scope = ImMap::default();
let mut declared = Vec::with_capacity(procs.len());
// Declare all the Procs, then insert them into scope so their bodies
// can look up their Funcs in scope later when calling each other by value.
for (name, opt_proc) in procs.as_map().into_iter() {
if let Some(proc) = opt_proc {
let (func_id, sig) = declare_proc(&mut env, &mut module, name, &proc);
declared.push((proc.clone(), sig.clone(), func_id));
scope.insert(name.clone(), ScopeEntry::Func { func_id, sig });
}
}
for (proc, sig, fn_id) in declared {
define_proc_body(
&mut env,
&mut ctx,
&mut module,
fn_id,
&scope,
sig,
arena.alloc(proc),
&procs,
);
// Verify the function we just defined
if let Err(errors) = verify_function(&ctx.func, &shared_flags) {
// NOTE: We don't include proc here because it's already
// been moved. If you need to know which proc failed, go back
// and add some logging.
panic!("Errors defining proc: {}", errors);
}
}
// Add main itself
let mut sig = module.make_signature();
// Add return type to the signature.
// If it is a struct, give it a special return type.
// Otherwise, Cranelift will return a raw pointer to the struct
// instead of using a proper struct return.
match layout {
Layout::Struct(fields) => {
for field_layout in fields {
let ret_type = type_from_layout(cfg, &field_layout);
let abi_param = AbiParam::special(ret_type, ArgumentPurpose::StructReturn);
sig.returns.push(abi_param);
}
},
_ => {
let main_ret_type = type_from_layout(cfg, &layout);
sig.returns.push(AbiParam::new(main_ret_type));
}
};
let main_fn = module
.declare_function(main_fn_name, Linkage::Local, &sig)
.unwrap();
ctx.func.signature = sig;
ctx.func.name = ExternalName::user(0, main_fn.as_u32());
{
let mut builder: FunctionBuilder =
FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
let block = builder.create_block();
builder.switch_to_block(block);
// TODO try deleting this line and seeing if everything still works.
builder.append_block_params_for_function_params(block);
let main_body =
roc_gen::crane::build::build_expr(&mut env, &scope, &mut module, &mut builder, &mono_expr, &procs);
builder.ins().return_(&[main_body]);
// TODO re-enable this once Switch stops making unsealed blocks, e.g.
// https://docs.rs/cranelift-frontend/0.59.0/src/cranelift_frontend/switch.rs.html#152
// builder.seal_block(block);
builder.seal_all_blocks();
builder.finalize();
}
module.define_function(main_fn, &mut ctx).expect("crane declare main");
module.clear_context(&mut ctx);
// Perform linking
module.finalize_definitions();
// Verify the main function
if let Err(errors) = verify_function(&ctx.func, &shared_flags) {
panic!("Errors defining {} - {}", main_fn_name, errors);
}
let main_ptr = module.get_finalized_function(main_fn);
unsafe {
let run_main = mem::transmute::<_, fn() -> $ty>(main_ptr) ;
assert_eq!($transform(run_main()), $expected);
}
};
}
macro_rules! assert_llvm_evals_to {
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
let arena = Bump::new();
@ -469,13 +311,10 @@ mod test_gen {
macro_rules! assert_evals_to {
($src:expr, $expected:expr, $ty:ty) => {
// Run Cranelift tests, then LLVM tests, in separate scopes.
// Run un-optimized tests, and then optimized tests, in separate scopes.
// These each rebuild everything from scratch, starting with
// parsing the source, so that there's no chance their passing
// or failing depends on leftover state from the previous one.
{
assert_crane_evals_to!($src, $expected, $ty, (|val| val));
}
{
assert_llvm_evals_to!($src, $expected, $ty, (|val| val));
}
@ -485,9 +324,6 @@ mod test_gen {
};
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
// Same as above, except with an additional transformation argument.
{
assert_crane_evals_to!($src, $expected, $ty, $transform);
}
{
assert_llvm_evals_to!($src, $expected, $ty, $transform);
}
@ -567,12 +403,12 @@ mod test_gen {
//
#[test]
fn empty_list_literal() {
assert_opt_evals_to!("[]", &[], &'static [i64], |x| x);
assert_evals_to!("[]", &[], &'static [i64], |x| x);
}
#[test]
fn int_list_literal() {
assert_opt_evals_to!("[ 12, 9, 6, 3 ]", &[12, 9, 6, 3], &'static [i64], |x| x);
assert_evals_to!("[ 12, 9, 6, 3 ]", &[12, 9, 6, 3], &'static [i64], |x| x);
}
#[test]
@ -592,7 +428,7 @@ mod test_gen {
#[test]
fn set_unique_int_list() {
assert_opt_evals_to!(
assert_evals_to!(
"List.set [ 12, 9, 7, 1, 5 ] 2 33",
&[12, 9, 33, 1, 5],
&'static [i64],
@ -602,7 +438,7 @@ mod test_gen {
#[test]
fn set_unique_list_oob() {
assert_opt_evals_to!(
assert_evals_to!(
"List.set [ 3, 17, 4.1 ] 1337 9.25",
&[3.0, 17.0, 4.1],
&'static [f64],
@ -612,7 +448,7 @@ mod test_gen {
#[test]
fn set_shared_int_list() {
assert_opt_evals_to!(
assert_evals_to!(
indoc!(
r#"
shared = [ 2.1, 4.3 ]
@ -631,7 +467,7 @@ mod test_gen {
#[test]
fn set_shared_list_oob() {
assert_opt_evals_to!(
assert_evals_to!(
indoc!(
r#"
shared = [ 2, 4 ]
@ -1933,7 +1769,7 @@ mod test_gen {
#[test]
fn i64_record_literal() {
assert_opt_evals_to!(
assert_evals_to!(
indoc!(
r#"
{ x: 3, y: 5 }