mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 15:51:12 +00:00
apply optimization passes
This commit is contained in:
parent
9e75252ddb
commit
8f7d842d41
5 changed files with 211 additions and 86 deletions
|
@ -1,7 +1,6 @@
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::execution_engine::JitFunction;
|
use inkwell::execution_engine::JitFunction;
|
||||||
use inkwell::passes::PassManager;
|
|
||||||
use inkwell::types::BasicType;
|
use inkwell::types::BasicType;
|
||||||
use inkwell::OptimizationLevel;
|
use inkwell::OptimizationLevel;
|
||||||
use roc_builtins::unique::uniq_stdlib;
|
use roc_builtins::unique::uniq_stdlib;
|
||||||
|
@ -209,13 +208,9 @@ pub fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<(String, S
|
||||||
}
|
}
|
||||||
|
|
||||||
let context = Context::create();
|
let context = Context::create();
|
||||||
let module = roc_gen::llvm::build::module_from_builtins(&context, "app");
|
let module = arena.alloc(roc_gen::llvm::build::module_from_builtins(&context, "app"));
|
||||||
let builder = context.create_builder();
|
let builder = context.create_builder();
|
||||||
let fpm = PassManager::create(&module);
|
let (mpm, fpm) = roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
||||||
|
|
||||||
roc_gen::llvm::build::add_passes(&fpm, opt_level);
|
|
||||||
|
|
||||||
fpm.initialize();
|
|
||||||
|
|
||||||
// pretty-print the expr type string for later.
|
// pretty-print the expr type string for later.
|
||||||
name_all_type_vars(var, &mut subs);
|
name_all_type_vars(var, &mut subs);
|
||||||
|
@ -243,7 +238,7 @@ pub fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<(String, S
|
||||||
builder: &builder,
|
builder: &builder,
|
||||||
context: &context,
|
context: &context,
|
||||||
interns,
|
interns,
|
||||||
module: arena.alloc(module),
|
module,
|
||||||
ptr_bytes,
|
ptr_bytes,
|
||||||
leak: false,
|
leak: false,
|
||||||
};
|
};
|
||||||
|
@ -355,6 +350,8 @@ pub fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<(String, S
|
||||||
panic!("Main function {} failed LLVM verification. Uncomment things near this error message for more details.", main_fn_name);
|
panic!("Main function {} failed LLVM verification. Uncomment things near this error message for more details.", main_fn_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mpm.run_on(module);
|
||||||
|
|
||||||
// Verify the module
|
// Verify the module
|
||||||
if let Err(errors) = env.module.verify() {
|
if let Err(errors) = env.module.verify() {
|
||||||
panic!("Errors defining module: {:?}", errors);
|
panic!("Errors defining module: {:?}", errors);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::module::Linkage;
|
use inkwell::module::Linkage;
|
||||||
use inkwell::passes::PassManager;
|
|
||||||
use inkwell::types::BasicType;
|
use inkwell::types::BasicType;
|
||||||
use inkwell::OptimizationLevel;
|
use inkwell::OptimizationLevel;
|
||||||
use roc_gen::layout_id::LayoutIds;
|
use roc_gen::layout_id::LayoutIds;
|
||||||
|
@ -127,13 +126,9 @@ pub fn gen(
|
||||||
// Generate the binary
|
// Generate the binary
|
||||||
|
|
||||||
let context = Context::create();
|
let context = Context::create();
|
||||||
let module = module_from_builtins(&context, "app");
|
let module = arena.alloc(module_from_builtins(&context, "app"));
|
||||||
let builder = context.create_builder();
|
let builder = context.create_builder();
|
||||||
let fpm = PassManager::create(&module);
|
let (mpm, fpm) = roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
||||||
|
|
||||||
roc_gen::llvm::build::add_passes(&fpm, opt_level);
|
|
||||||
|
|
||||||
fpm.initialize();
|
|
||||||
|
|
||||||
// Compute main_fn_type before moving subs to Env
|
// Compute main_fn_type before moving subs to Env
|
||||||
let layout = Layout::new(&arena, content, &subs).unwrap_or_else(|err| {
|
let layout = Layout::new(&arena, content, &subs).unwrap_or_else(|err| {
|
||||||
|
@ -154,7 +149,7 @@ pub fn gen(
|
||||||
builder: &builder,
|
builder: &builder,
|
||||||
context: &context,
|
context: &context,
|
||||||
interns: loaded.interns,
|
interns: loaded.interns,
|
||||||
module: arena.alloc(module),
|
module,
|
||||||
ptr_bytes,
|
ptr_bytes,
|
||||||
leak: false,
|
leak: false,
|
||||||
};
|
};
|
||||||
|
@ -317,6 +312,8 @@ pub fn gen(
|
||||||
panic!("Function {} failed LLVM verification.", main_fn_name);
|
panic!("Function {} failed LLVM verification.", main_fn_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mpm.run_on(module);
|
||||||
|
|
||||||
// Verify the module
|
// Verify the module
|
||||||
if let Err(errors) = env.module.verify() {
|
if let Err(errors) = env.module.verify() {
|
||||||
panic!("😱 LLVM errors when defining module: {:?}", errors);
|
panic!("😱 LLVM errors when defining module: {:?}", errors);
|
||||||
|
|
|
@ -31,6 +31,7 @@ const PRINT_FN_VERIFICATION_OUTPUT: bool = true;
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
const PRINT_FN_VERIFICATION_OUTPUT: bool = false;
|
const PRINT_FN_VERIFICATION_OUTPUT: bool = false;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum OptLevel {
|
pub enum OptLevel {
|
||||||
Normal,
|
Normal,
|
||||||
Optimize,
|
Optimize,
|
||||||
|
@ -154,14 +155,18 @@ fn add_intrinsic<'ctx>(
|
||||||
fn_val
|
fn_val
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_passes(fpm: &PassManager<FunctionValue<'_>>, opt_level: OptLevel) {
|
pub fn construct_optimization_passes<'a>(
|
||||||
|
module: &'a Module,
|
||||||
|
opt_level: OptLevel,
|
||||||
|
) -> (PassManager<Module<'a>>, PassManager<FunctionValue<'a>>) {
|
||||||
|
let mpm = PassManager::create(());
|
||||||
|
let fpm = PassManager::create(module);
|
||||||
|
|
||||||
// tail-call elimination is always on
|
// tail-call elimination is always on
|
||||||
fpm.add_instruction_combining_pass();
|
fpm.add_instruction_combining_pass();
|
||||||
fpm.add_tail_call_elimination_pass();
|
fpm.add_tail_call_elimination_pass();
|
||||||
|
|
||||||
let pmb = PassManagerBuilder::create();
|
let pmb = PassManagerBuilder::create();
|
||||||
|
|
||||||
// Enable more optimizations when running cargo test --release
|
|
||||||
match opt_level {
|
match opt_level {
|
||||||
OptLevel::Normal => {
|
OptLevel::Normal => {
|
||||||
pmb.set_optimization_level(OptimizationLevel::None);
|
pmb.set_optimization_level(OptimizationLevel::None);
|
||||||
|
@ -171,24 +176,35 @@ pub fn add_passes(fpm: &PassManager<FunctionValue<'_>>, opt_level: OptLevel) {
|
||||||
//
|
//
|
||||||
// See https://llvm.org/doxygen/CodeGen_8h_source.html
|
// See https://llvm.org/doxygen/CodeGen_8h_source.html
|
||||||
pmb.set_optimization_level(OptimizationLevel::Aggressive);
|
pmb.set_optimization_level(OptimizationLevel::Aggressive);
|
||||||
|
pmb.set_inliner_with_threshold(4);
|
||||||
|
|
||||||
// TODO figure out how enabling these individually differs from
|
// TODO figure out which of these actually help
|
||||||
// the broad "aggressive optimizations" setting.
|
|
||||||
|
|
||||||
// fpm.add_reassociate_pass();
|
// function passes
|
||||||
// fpm.add_basic_alias_analysis_pass();
|
fpm.add_basic_alias_analysis_pass();
|
||||||
// fpm.add_promote_memory_to_register_pass();
|
fpm.add_memcpy_optimize_pass();
|
||||||
// fpm.add_cfg_simplification_pass();
|
fpm.add_jump_threading_pass();
|
||||||
// fpm.add_gvn_pass();
|
fpm.add_instruction_combining_pass();
|
||||||
// TODO figure out why enabling any of these (even alone) causes LLVM to segfault
|
fpm.add_licm_pass();
|
||||||
// fpm.add_strip_dead_prototypes_pass();
|
fpm.add_loop_unroll_pass();
|
||||||
// fpm.add_dead_arg_elimination_pass();
|
fpm.add_scalar_repl_aggregates_pass_ssa();
|
||||||
// fpm.add_function_inlining_pass();
|
|
||||||
// pmb.set_inliner_with_threshold(4);
|
// module passes
|
||||||
|
mpm.add_cfg_simplification_pass();
|
||||||
|
mpm.add_jump_threading_pass();
|
||||||
|
mpm.add_instruction_combining_pass();
|
||||||
|
mpm.add_memcpy_optimize_pass();
|
||||||
|
mpm.add_promote_memory_to_register_pass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pmb.populate_module_pass_manager(&mpm);
|
||||||
pmb.populate_function_pass_manager(&fpm);
|
pmb.populate_function_pass_manager(&fpm);
|
||||||
|
|
||||||
|
fpm.initialize();
|
||||||
|
|
||||||
|
// For now, we have just one of each
|
||||||
|
(mpm, fpm)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_exp_literal<'a, 'ctx, 'env>(
|
pub fn build_exp_literal<'a, 'ctx, 'env>(
|
||||||
|
@ -493,23 +509,14 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
// This tricks comes from
|
// This tricks comes from
|
||||||
// https://github.com/raviqqe/ssf/blob/bc32aae68940d5bddf5984128e85af75ca4f4686/ssf-llvm/src/expression_compiler.rs#L116
|
// https://github.com/raviqqe/ssf/blob/bc32aae68940d5bddf5984128e85af75ca4f4686/ssf-llvm/src/expression_compiler.rs#L116
|
||||||
|
|
||||||
let array_type = ctx.i8_type().array_type(whole_size);
|
let internal_type =
|
||||||
|
basic_type_from_layout(env.arena, env.context, tag_layout, env.ptr_bytes);
|
||||||
|
|
||||||
let result = cast_basic_basic(
|
cast_basic_basic(
|
||||||
builder,
|
builder,
|
||||||
struct_val.into_struct_value().into(),
|
struct_val.into_struct_value().into(),
|
||||||
array_type.into(),
|
internal_type,
|
||||||
);
|
)
|
||||||
|
|
||||||
// For unclear reasons, we can't cast an array to a struct on the other side.
|
|
||||||
// the solution is to wrap the array in a struct (yea...)
|
|
||||||
let wrapper_type = ctx.struct_type(&[array_type.into()], false);
|
|
||||||
let mut wrapper_val = wrapper_type.const_zero().into();
|
|
||||||
wrapper_val = builder
|
|
||||||
.build_insert_value(wrapper_val, result, 0, "insert_field")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
wrapper_val.into_struct_value().into()
|
|
||||||
}
|
}
|
||||||
AccessAtIndex {
|
AccessAtIndex {
|
||||||
index,
|
index,
|
||||||
|
@ -805,18 +812,14 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
Dec(symbol, cont) => {
|
Dec(symbol, cont) => {
|
||||||
let (value, layout) = load_symbol_and_layout(env, scope, symbol);
|
let (value, layout) = load_symbol_and_layout(env, scope, symbol);
|
||||||
let layout = layout.clone();
|
let layout = layout.clone();
|
||||||
// TODO exclude unique lists in the future
|
|
||||||
match layout {
|
/*
|
||||||
Layout::Builtin(Builtin::List(_, _)) => decrement_refcount_list(
|
if layout.contains_refcounted() {
|
||||||
env,
|
decrement_refcount_layout(env, parent, value, &layout);
|
||||||
layout_ids,
|
|
||||||
scope,
|
|
||||||
parent,
|
|
||||||
cont,
|
|
||||||
value.into_struct_value(),
|
|
||||||
),
|
|
||||||
_ => build_exp_stmt(env, layout_ids, scope, parent, cont),
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
build_exp_stmt(env, layout_ids, scope, parent, cont)
|
||||||
}
|
}
|
||||||
_ => todo!("unsupported expr {:?}", stmt),
|
_ => todo!("unsupported expr {:?}", stmt),
|
||||||
}
|
}
|
||||||
|
@ -874,7 +877,113 @@ fn list_get_refcount_ptr<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
fn decrement_refcount_layout<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
value: BasicValueEnum<'ctx>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
) {
|
||||||
|
use Layout::*;
|
||||||
|
|
||||||
|
match layout {
|
||||||
|
Builtin(builtin) => decrement_refcount_builtin(env, parent, value, builtin),
|
||||||
|
Struct(layouts) => {
|
||||||
|
let wrapper_struct = value.into_struct_value();
|
||||||
|
|
||||||
|
for (i, field_layout) in layouts.iter().enumerate() {
|
||||||
|
if field_layout.contains_refcounted() {
|
||||||
|
let field_ptr = env
|
||||||
|
.builder
|
||||||
|
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
decrement_refcount_layout(env, parent, field_ptr, field_layout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Union(tags) => {
|
||||||
|
debug_assert!(!tags.is_empty());
|
||||||
|
let wrapper_struct = value.into_struct_value();
|
||||||
|
|
||||||
|
// read the tag_id
|
||||||
|
let tag_id = env
|
||||||
|
.builder
|
||||||
|
.build_extract_value(wrapper_struct, 0, "read_tag_id")
|
||||||
|
.unwrap()
|
||||||
|
.into_int_value();
|
||||||
|
|
||||||
|
// next, make a jump table for all possible values of the tag_id
|
||||||
|
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
|
||||||
|
|
||||||
|
let merge_block = env.context.append_basic_block(parent, "decrement_merge");
|
||||||
|
|
||||||
|
for (tag_id, field_layouts) in tags.iter().enumerate() {
|
||||||
|
let block = env.context.append_basic_block(parent, "tag_id_decrement");
|
||||||
|
env.builder.position_at_end(block);
|
||||||
|
|
||||||
|
for (i, field_layout) in field_layouts.iter().enumerate() {
|
||||||
|
if field_layout.contains_refcounted() {
|
||||||
|
let field_ptr = env
|
||||||
|
.builder
|
||||||
|
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
decrement_refcount_layout(env, parent, field_ptr, field_layout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env.builder.build_unconditional_branch(merge_block);
|
||||||
|
|
||||||
|
cases.push((env.context.i8_type().const_int(tag_id as u64, false), block));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, default_block) = cases.pop().unwrap();
|
||||||
|
|
||||||
|
env.builder.build_switch(tag_id, default_block, &cases);
|
||||||
|
|
||||||
|
env.builder.position_at_end(merge_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionPointer(_, _) | Pointer(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn decrement_refcount_builtin<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
value: BasicValueEnum<'ctx>,
|
||||||
|
builtin: &Builtin<'a>,
|
||||||
|
) {
|
||||||
|
use Builtin::*;
|
||||||
|
|
||||||
|
match builtin {
|
||||||
|
List(_, element_layout) => {
|
||||||
|
if element_layout.contains_refcounted() {
|
||||||
|
// TODO decrement all values
|
||||||
|
}
|
||||||
|
let wrapper_struct = value.into_struct_value();
|
||||||
|
decrement_refcount_list(env, parent, wrapper_struct);
|
||||||
|
}
|
||||||
|
Set(element_layout) => {
|
||||||
|
if element_layout.contains_refcounted() {
|
||||||
|
// TODO decrement all values
|
||||||
|
}
|
||||||
|
let wrapper_struct = value.into_struct_value();
|
||||||
|
decrement_refcount_list(env, parent, wrapper_struct);
|
||||||
|
}
|
||||||
|
Map(key_layout, value_layout) => {
|
||||||
|
if key_layout.contains_refcounted() || value_layout.contains_refcounted() {
|
||||||
|
// TODO decrement all values
|
||||||
|
}
|
||||||
|
|
||||||
|
let wrapper_struct = value.into_struct_value();
|
||||||
|
decrement_refcount_list(env, parent, wrapper_struct);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn increment_refcount_list<'a, 'ctx, 'env>(
|
fn increment_refcount_list<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
original_wrapper: StructValue<'ctx>,
|
original_wrapper: StructValue<'ctx>,
|
||||||
|
@ -900,15 +1009,11 @@ fn increment_refcount_list<'a, 'ctx, 'env>(
|
||||||
builder.build_store(refcount_ptr, decremented);
|
builder.build_store(refcount_ptr, decremented);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn decrement_refcount_list<'a, 'ctx, 'env>(
|
fn decrement_refcount_list<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
scope: &mut Scope<'a, 'ctx>,
|
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
stmt: &roc_mono::ir::Stmt<'a>,
|
|
||||||
original_wrapper: StructValue<'ctx>,
|
original_wrapper: StructValue<'ctx>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) {
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
|
|
||||||
|
@ -956,7 +1061,6 @@ fn decrement_refcount_list<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
// emit merge block
|
// emit merge block
|
||||||
builder.position_at_end(cont_block);
|
builder.position_at_end(cont_block);
|
||||||
build_exp_stmt(env, layout_ids, scope, parent, stmt)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_symbol<'a, 'ctx, 'env>(
|
fn load_symbol<'a, 'ctx, 'env>(
|
||||||
|
@ -987,6 +1091,17 @@ fn load_symbol_and_layout<'a, 'ctx, 'env, 'b>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_symbol_and_layout<'a, 'ctx, 'env, 'b>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
scope: &'b Scope<'a, 'ctx>,
|
||||||
|
symbol: &Symbol,
|
||||||
|
) -> (PointerValue<'ctx>, &'b Layout<'a>) {
|
||||||
|
match scope.get(symbol) {
|
||||||
|
Some((layout, ptr)) => (*ptr, layout),
|
||||||
|
None => panic!("There was no entry for {:?} in scope {:?}", symbol, scope),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Cast a struct to another struct of the same (or smaller?) size
|
/// Cast a struct to another struct of the same (or smaller?) size
|
||||||
fn cast_struct_struct<'ctx>(
|
fn cast_struct_struct<'ctx>(
|
||||||
builder: &Builder<'ctx>,
|
builder: &Builder<'ctx>,
|
||||||
|
@ -1012,7 +1127,7 @@ fn cast_basic_basic<'ctx>(
|
||||||
.build_bitcast(
|
.build_bitcast(
|
||||||
argument_pointer,
|
argument_pointer,
|
||||||
to_type.ptr_type(inkwell::AddressSpace::Generic),
|
to_type.ptr_type(inkwell::AddressSpace::Generic),
|
||||||
"",
|
"cast_basic_basic",
|
||||||
)
|
)
|
||||||
.into_pointer_value();
|
.into_pointer_value();
|
||||||
|
|
||||||
|
|
|
@ -112,12 +112,31 @@ pub fn basic_type_from_layout<'ctx>(
|
||||||
let ptr_size = std::mem::size_of::<i64>();
|
let ptr_size = std::mem::size_of::<i64>();
|
||||||
let union_size = layout.stack_size(ptr_size as u32);
|
let union_size = layout.stack_size(ptr_size as u32);
|
||||||
|
|
||||||
let array_type = context
|
// The memory layout of Union is a bit tricky.
|
||||||
.i8_type()
|
// We have tags with different memory layouts, that are part of the same type.
|
||||||
.array_type(union_size)
|
// For llvm, all tags must have the same memory layout.
|
||||||
.as_basic_type_enum();
|
//
|
||||||
|
// So, we convert all tags to a layout of bytes of some size.
|
||||||
|
// It turns out that encoding to i64 for as many elements as possible is
|
||||||
|
// a nice optimization, the remainder is encoded as bytes.
|
||||||
|
|
||||||
context.struct_type(&[array_type], false).into()
|
let num_i64 = union_size / 8;
|
||||||
|
let num_i8 = union_size % 8;
|
||||||
|
|
||||||
|
let i64_array_type = context.i64_type().array_type(num_i64).as_basic_type_enum();
|
||||||
|
|
||||||
|
if num_i8 == 0 {
|
||||||
|
// the object fits perfectly in some number of i64's
|
||||||
|
// (i.e. the size is a multiple of 8 bytes)
|
||||||
|
context.struct_type(&[i64_array_type], false).into()
|
||||||
|
} else {
|
||||||
|
// there are some trailing bytes at the end
|
||||||
|
let i8_array_type = context.i8_type().array_type(num_i8).as_basic_type_enum();
|
||||||
|
|
||||||
|
context
|
||||||
|
.struct_type(&[i64_array_type, i8_array_type], false)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Builtin(builtin) => match builtin {
|
Builtin(builtin) => match builtin {
|
||||||
|
|
|
@ -7,7 +7,6 @@ pub fn helper_without_uniqueness<'a>(
|
||||||
context: &'a inkwell::context::Context,
|
context: &'a inkwell::context::Context,
|
||||||
) -> (&'static str, inkwell::execution_engine::ExecutionEngine<'a>) {
|
) -> (&'static str, inkwell::execution_engine::ExecutionEngine<'a>) {
|
||||||
use crate::helpers::{can_expr, infer_expr, CanExprOut};
|
use crate::helpers::{can_expr, infer_expr, CanExprOut};
|
||||||
use inkwell::passes::PassManager;
|
|
||||||
use inkwell::types::BasicType;
|
use inkwell::types::BasicType;
|
||||||
use inkwell::OptimizationLevel;
|
use inkwell::OptimizationLevel;
|
||||||
use roc_gen::llvm::build::Scope;
|
use roc_gen::llvm::build::Scope;
|
||||||
|
@ -60,11 +59,10 @@ pub fn helper_without_uniqueness<'a>(
|
||||||
} else {
|
} else {
|
||||||
roc_gen::llvm::build::OptLevel::Optimize
|
roc_gen::llvm::build::OptLevel::Optimize
|
||||||
};
|
};
|
||||||
let fpm = PassManager::create(&module);
|
|
||||||
|
|
||||||
roc_gen::llvm::build::add_passes(&fpm, opt_level);
|
let module = arena.alloc(module);
|
||||||
|
let (module_pass, function_pass) =
|
||||||
fpm.initialize();
|
roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
||||||
|
|
||||||
// Compute main_fn_type before moving subs to Env
|
// Compute main_fn_type before moving subs to Env
|
||||||
let layout = Layout::new(&arena, content, &subs).unwrap_or_else(|err| {
|
let layout = Layout::new(&arena, content, &subs).unwrap_or_else(|err| {
|
||||||
|
@ -87,7 +85,7 @@ pub fn helper_without_uniqueness<'a>(
|
||||||
builder: &builder,
|
builder: &builder,
|
||||||
context: context,
|
context: context,
|
||||||
interns,
|
interns,
|
||||||
module: arena.alloc(module),
|
module,
|
||||||
ptr_bytes,
|
ptr_bytes,
|
||||||
leak: leak,
|
leak: leak,
|
||||||
};
|
};
|
||||||
|
@ -143,7 +141,7 @@ pub fn helper_without_uniqueness<'a>(
|
||||||
build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types);
|
build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types);
|
||||||
|
|
||||||
if fn_val.verify(true) {
|
if fn_val.verify(true) {
|
||||||
fpm.run_on(&fn_val);
|
function_pass.run_on(&fn_val);
|
||||||
} else {
|
} else {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"\n\nFunction {:?} failed LLVM verification in NON-OPTIMIZED build. Its content was:\n", fn_val.get_name().to_str().unwrap()
|
"\n\nFunction {:?} failed LLVM verification in NON-OPTIMIZED build. Its content was:\n", fn_val.get_name().to_str().unwrap()
|
||||||
|
@ -183,18 +181,20 @@ pub fn helper_without_uniqueness<'a>(
|
||||||
// env.module.print_to_stderr();
|
// env.module.print_to_stderr();
|
||||||
|
|
||||||
if main_fn.verify(true) {
|
if main_fn.verify(true) {
|
||||||
fpm.run_on(&main_fn);
|
function_pass.run_on(&main_fn);
|
||||||
} else {
|
} else {
|
||||||
panic!("Main function {} failed LLVM verification in NON-OPTIMIZED build. Uncomment things nearby to see more details.", main_fn_name);
|
panic!("Main function {} failed LLVM verification in NON-OPTIMIZED build. Uncomment things nearby to see more details.", main_fn_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module_pass.run_on(env.module);
|
||||||
|
|
||||||
// Verify the module
|
// Verify the module
|
||||||
if let Err(errors) = env.module.verify() {
|
if let Err(errors) = env.module.verify() {
|
||||||
panic!("Errors defining module: {:?}", errors);
|
panic!("Errors defining module: {:?}", errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uncomment this to see the module's optimized LLVM instruction output:
|
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||||
// env.module.print_to_stderr();
|
env.module.print_to_stderr();
|
||||||
|
|
||||||
(main_fn_name, execution_engine.clone())
|
(main_fn_name, execution_engine.clone())
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,6 @@ pub fn helper_with_uniqueness<'a>(
|
||||||
context: &'a inkwell::context::Context,
|
context: &'a inkwell::context::Context,
|
||||||
) -> (&'static str, inkwell::execution_engine::ExecutionEngine<'a>) {
|
) -> (&'static str, inkwell::execution_engine::ExecutionEngine<'a>) {
|
||||||
use crate::helpers::{infer_expr, uniq_expr};
|
use crate::helpers::{infer_expr, uniq_expr};
|
||||||
use inkwell::passes::PassManager;
|
|
||||||
use inkwell::types::BasicType;
|
use inkwell::types::BasicType;
|
||||||
use inkwell::OptimizationLevel;
|
use inkwell::OptimizationLevel;
|
||||||
use roc_gen::llvm::build::Scope;
|
use roc_gen::llvm::build::Scope;
|
||||||
|
@ -242,18 +241,14 @@ pub fn helper_with_uniqueness<'a>(
|
||||||
unify_problems
|
unify_problems
|
||||||
);
|
);
|
||||||
|
|
||||||
let module = roc_gen::llvm::build::module_from_builtins(context, "app");
|
let module = arena.alloc(roc_gen::llvm::build::module_from_builtins(context, "app"));
|
||||||
let builder = context.create_builder();
|
let builder = context.create_builder();
|
||||||
let opt_level = if cfg!(debug_assertions) {
|
let opt_level = if cfg!(debug_assertions) {
|
||||||
roc_gen::llvm::build::OptLevel::Normal
|
roc_gen::llvm::build::OptLevel::Normal
|
||||||
} else {
|
} else {
|
||||||
roc_gen::llvm::build::OptLevel::Optimize
|
roc_gen::llvm::build::OptLevel::Optimize
|
||||||
};
|
};
|
||||||
let fpm = PassManager::create(&module);
|
let (mpm, fpm) = roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
||||||
|
|
||||||
roc_gen::llvm::build::add_passes(&fpm, opt_level);
|
|
||||||
|
|
||||||
fpm.initialize();
|
|
||||||
|
|
||||||
// Compute main_fn_type before moving subs to Env
|
// Compute main_fn_type before moving subs to Env
|
||||||
let layout = Layout::new(&arena, content, &subs).unwrap_or_else(|err| {
|
let layout = Layout::new(&arena, content, &subs).unwrap_or_else(|err| {
|
||||||
|
@ -278,7 +273,7 @@ pub fn helper_with_uniqueness<'a>(
|
||||||
builder: &builder,
|
builder: &builder,
|
||||||
context: context,
|
context: context,
|
||||||
interns,
|
interns,
|
||||||
module: arena.alloc(module),
|
module,
|
||||||
ptr_bytes,
|
ptr_bytes,
|
||||||
leak: leak,
|
leak: leak,
|
||||||
};
|
};
|
||||||
|
@ -379,6 +374,8 @@ pub fn helper_with_uniqueness<'a>(
|
||||||
panic!("main function {} failed LLVM verification in OPTIMIZED build. Uncomment nearby statements to see more details.", main_fn_name);
|
panic!("main function {} failed LLVM verification in OPTIMIZED build. Uncomment nearby statements to see more details.", main_fn_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mpm.run_on(module);
|
||||||
|
|
||||||
// Verify the module
|
// Verify the module
|
||||||
if let Err(errors) = env.module.verify() {
|
if let Err(errors) = env.module.verify() {
|
||||||
panic!("Errors defining module: {:?}", errors);
|
panic!("Errors defining module: {:?}", errors);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue