mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-22 00:09:33 +03:00
Remove Cranelift for now.
This commit is contained in:
parent
9eed02ae4a
commit
a561f343b2
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
pub mod build;
|
||||
pub mod convert;
|
||||
pub mod imports;
|
@ -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;
|
||||
|
@ -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 }
|
||||
|
Loading…
Reference in New Issue
Block a user