mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
centralize llvm logic
This commit is contained in:
parent
8f5d1fa48f
commit
ab8abb8f11
4 changed files with 120 additions and 170 deletions
|
@ -8,7 +8,7 @@ use roc_can::builtins::builtin_defs_map;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_fmt::annotation::Formattable;
|
use roc_fmt::annotation::Formattable;
|
||||||
use roc_fmt::annotation::{Newlines, Parens};
|
use roc_fmt::annotation::{Newlines, Parens};
|
||||||
use roc_gen::llvm::build::{build_proc, build_proc_header, OptLevel};
|
use roc_gen::llvm::build::OptLevel;
|
||||||
use roc_gen::llvm::externs::add_default_roc_externs;
|
use roc_gen::llvm::externs::add_default_roc_externs;
|
||||||
use roc_load::file::LoadingProblem;
|
use roc_load::file::LoadingProblem;
|
||||||
use roc_parse::parser::SyntaxError;
|
use roc_parse::parser::SyntaxError;
|
||||||
|
@ -68,7 +68,7 @@ pub fn gen_and_eval<'a>(
|
||||||
|
|
||||||
use roc_load::file::MonomorphizedModule;
|
use roc_load::file::MonomorphizedModule;
|
||||||
let MonomorphizedModule {
|
let MonomorphizedModule {
|
||||||
mut procedures,
|
procedures,
|
||||||
interns,
|
interns,
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
mut subs,
|
mut subs,
|
||||||
|
@ -185,63 +185,10 @@ pub fn gen_and_eval<'a>(
|
||||||
exposed_to_host: MutSet::default(),
|
exposed_to_host: MutSet::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut layout_ids = roc_mono::layout::LayoutIds::default();
|
let (main_fn_name, main_fn) = roc_gen::llvm::build::build_procedures_return_main(
|
||||||
let mut headers = Vec::with_capacity(procedures.len());
|
|
||||||
|
|
||||||
// Add all the Proc headers to the module.
|
|
||||||
// We have to do this in a separate pass first,
|
|
||||||
// because their bodies may reference each other.
|
|
||||||
let mut scope = roc_gen::llvm::build::Scope::default();
|
|
||||||
for ((symbol, layout), proc) in procedures.drain() {
|
|
||||||
let fn_val = build_proc_header(&env, &mut layout_ids, symbol, layout, &proc);
|
|
||||||
|
|
||||||
if proc.args.is_empty() {
|
|
||||||
// this is a 0-argument thunk, i.e. a top-level constant definition
|
|
||||||
// it must be in-scope everywhere in the module!
|
|
||||||
scope.insert_top_level_thunk(symbol, arena.alloc(layout), fn_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
headers.push((proc, fn_val));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build each proc using its header info.
|
|
||||||
for (proc, fn_val) in headers {
|
|
||||||
let mut current_scope = scope.clone();
|
|
||||||
|
|
||||||
// only have top-level thunks for this proc's module in scope
|
|
||||||
// this retain is not needed for correctness, but will cause less confusion when debugging
|
|
||||||
let home = proc.name.module_id();
|
|
||||||
current_scope.retain_top_level_thunks_for_module(home);
|
|
||||||
|
|
||||||
build_proc(&env, &mut layout_ids, scope.clone(), proc, fn_val);
|
|
||||||
|
|
||||||
// call finalize() before any code generation/verification
|
|
||||||
env.dibuilder.finalize();
|
|
||||||
|
|
||||||
if fn_val.verify(true) {
|
|
||||||
function_pass.run_on(&fn_val);
|
|
||||||
} else {
|
|
||||||
let mode = "NON-OPTIMIZED";
|
|
||||||
|
|
||||||
eprintln!(
|
|
||||||
"\n\nFunction {:?} failed LLVM verification in {} build. Its content was:\n",
|
|
||||||
fn_val.get_name().to_str().unwrap(),
|
|
||||||
mode,
|
|
||||||
);
|
|
||||||
|
|
||||||
fn_val.print_to_stderr();
|
|
||||||
|
|
||||||
panic!(
|
|
||||||
"The preceding code was from {:?}, which failed LLVM verification in {} build.",
|
|
||||||
fn_val.get_name().to_str().unwrap(),
|
|
||||||
mode,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (main_fn_name, main_fn) = roc_gen::llvm::build::promote_to_main_function(
|
|
||||||
&env,
|
&env,
|
||||||
&mut layout_ids,
|
opt_level,
|
||||||
|
procedures,
|
||||||
main_fn_symbol,
|
main_fn_symbol,
|
||||||
main_fn_layout,
|
main_fn_layout,
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,9 +3,8 @@ use bumpalo::Bump;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::targets::{CodeModel, FileType, RelocMode};
|
use inkwell::targets::{CodeModel, FileType, RelocMode};
|
||||||
pub use roc_gen::llvm::build::FunctionIterator;
|
pub use roc_gen::llvm::build::FunctionIterator;
|
||||||
use roc_gen::llvm::build::{build_proc, build_proc_header, module_from_builtins, OptLevel, Scope};
|
use roc_gen::llvm::build::{module_from_builtins, OptLevel};
|
||||||
use roc_load::file::MonomorphizedModule;
|
use roc_load::file::MonomorphizedModule;
|
||||||
use roc_mono::layout::LayoutIds;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::Triple;
|
||||||
|
@ -121,7 +120,7 @@ pub fn gen_from_mono_module(
|
||||||
|
|
||||||
let builder = context.create_builder();
|
let builder = context.create_builder();
|
||||||
let (dibuilder, compile_unit) = roc_gen::llvm::build::Env::new_debug_info(module);
|
let (dibuilder, compile_unit) = roc_gen::llvm::build::Env::new_debug_info(module);
|
||||||
let (mpm, fpm) = roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
let (mpm, _fpm) = roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
// Compile and add all the Procs before adding main
|
||||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||||
|
@ -138,55 +137,7 @@ pub fn gen_from_mono_module(
|
||||||
exposed_to_host: loaded.exposed_to_host.keys().copied().collect(),
|
exposed_to_host: loaded.exposed_to_host.keys().copied().collect(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Populate Procs further and get the low-level Expr from the canonical Expr
|
roc_gen::llvm::build::build_procedures(&env, opt_level, loaded.procedures);
|
||||||
let mut headers = Vec::with_capacity(loaded.procedures.len());
|
|
||||||
|
|
||||||
// Add all the Proc headers to the module.
|
|
||||||
// We have to do this in a separate pass first,
|
|
||||||
// because their bodies may reference each other.
|
|
||||||
let mut layout_ids = LayoutIds::default();
|
|
||||||
|
|
||||||
let mut scope = Scope::default();
|
|
||||||
for ((symbol, layout), proc) in loaded.procedures {
|
|
||||||
let fn_val = build_proc_header(&env, &mut layout_ids, symbol, layout, &proc);
|
|
||||||
|
|
||||||
if proc.args.is_empty() {
|
|
||||||
// this is a 0-argument thunk, i.e. a top-level constant definition
|
|
||||||
// it must be in-scope everywhere in the module!
|
|
||||||
scope.insert_top_level_thunk(symbol, arena.alloc(layout), fn_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
headers.push((proc, fn_val));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build each proc using its header info.
|
|
||||||
for (proc, fn_val) in headers {
|
|
||||||
// NOTE: This is here to be uncommented in case verification fails.
|
|
||||||
// (This approach means we don't have to defensively clone name here.)
|
|
||||||
//
|
|
||||||
// println!("\n\nBuilding and then verifying function {:?}\n\n", proc);
|
|
||||||
build_proc(&env, &mut layout_ids, scope.clone(), proc, fn_val);
|
|
||||||
|
|
||||||
// call finalize() before any code generation/verification
|
|
||||||
env.dibuilder.finalize();
|
|
||||||
|
|
||||||
if fn_val.verify(true) {
|
|
||||||
fpm.run_on(&fn_val);
|
|
||||||
} else {
|
|
||||||
fn_val.print_to_stderr();
|
|
||||||
|
|
||||||
// write the ll code to a file, so we can modify it
|
|
||||||
env.module.print_to_file(&app_ll_file).unwrap();
|
|
||||||
|
|
||||||
// env.module.print_to_stderr();
|
|
||||||
// NOTE: If this fails, uncomment the above println to debug.
|
|
||||||
panic!(
|
|
||||||
r"Non-main function {:?} failed LLVM verification. I wrote the full LLVM IR to {:?}",
|
|
||||||
fn_val.get_name(),
|
|
||||||
app_ll_file,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
env.dibuilder.finalize();
|
env.dibuilder.finalize();
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ use inkwell::values::{
|
||||||
use inkwell::OptimizationLevel;
|
use inkwell::OptimizationLevel;
|
||||||
use inkwell::{AddressSpace, IntPredicate};
|
use inkwell::{AddressSpace, IntPredicate};
|
||||||
use roc_builtins::bitcode;
|
use roc_builtins::bitcode;
|
||||||
use roc_collections::all::{ImMap, MutSet};
|
use roc_collections::all::{ImMap, MutMap, MutSet};
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
|
@ -2985,7 +2985,113 @@ fn make_exception_catching_wrapper<'a, 'ctx, 'env>(
|
||||||
wrapper_function
|
wrapper_function
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_proc_header<'a, 'ctx, 'env>(
|
pub fn build_proc_headers<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
procedures: MutMap<(Symbol, TopLevelFunctionLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||||
|
scope: &mut Scope<'a, 'ctx>,
|
||||||
|
// alias_analysis_solutions: AliasAnalysisSolutions,
|
||||||
|
) -> std::vec::Vec<(roc_mono::ir::Proc<'a>, FunctionValue<'ctx>)> {
|
||||||
|
// Populate Procs further and get the low-level Expr from the canonical Expr
|
||||||
|
let mut headers = std::vec::Vec::with_capacity(procedures.len());
|
||||||
|
for ((symbol, layout), proc) in procedures {
|
||||||
|
let fn_val = build_proc_header(env, layout_ids, symbol, layout, &proc);
|
||||||
|
|
||||||
|
if proc.args.is_empty() {
|
||||||
|
// this is a 0-argument thunk, i.e. a top-level constant definition
|
||||||
|
// it must be in-scope everywhere in the module!
|
||||||
|
scope.insert_top_level_thunk(symbol, env.arena.alloc(layout), fn_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
headers.push((proc, fn_val));
|
||||||
|
}
|
||||||
|
|
||||||
|
headers
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_procedures<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
opt_level: OptLevel,
|
||||||
|
procedures: MutMap<(Symbol, TopLevelFunctionLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||||
|
// alias_analysis_solutions: AliasAnalysisSolutions,
|
||||||
|
) {
|
||||||
|
build_procedures_help(env, opt_level, procedures, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_procedures_return_main<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
opt_level: OptLevel,
|
||||||
|
procedures: MutMap<(Symbol, TopLevelFunctionLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||||
|
// alias_analysis_solutions: AliasAnalysisSolutions,
|
||||||
|
main_fn_symbol: Symbol,
|
||||||
|
main_fn_layout: TopLevelFunctionLayout<'a>,
|
||||||
|
) -> (&'static str, FunctionValue<'ctx>) {
|
||||||
|
build_procedures_help(
|
||||||
|
env,
|
||||||
|
opt_level,
|
||||||
|
procedures,
|
||||||
|
Some((main_fn_symbol, main_fn_layout)),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_procedures_help<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
opt_level: OptLevel,
|
||||||
|
procedures: MutMap<(Symbol, TopLevelFunctionLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||||
|
main_data: Option<(Symbol, TopLevelFunctionLayout<'a>)>,
|
||||||
|
) -> Option<(&'static str, FunctionValue<'ctx>)> {
|
||||||
|
let mut layout_ids = roc_mono::layout::LayoutIds::default();
|
||||||
|
let mut scope = Scope::default();
|
||||||
|
|
||||||
|
// Add all the Proc headers to the module.
|
||||||
|
// We have to do this in a separate pass first,
|
||||||
|
// because their bodies may reference each other.
|
||||||
|
let headers = build_proc_headers(env, &mut layout_ids, procedures, &mut scope);
|
||||||
|
|
||||||
|
let (_, function_pass) = construct_optimization_passes(env.module, opt_level);
|
||||||
|
|
||||||
|
for (proc, fn_val) in headers {
|
||||||
|
let mut current_scope = scope.clone();
|
||||||
|
|
||||||
|
// only have top-level thunks for this proc's module in scope
|
||||||
|
// this retain is not needed for correctness, but will cause less confusion when debugging
|
||||||
|
let home = proc.name.module_id();
|
||||||
|
current_scope.retain_top_level_thunks_for_module(home);
|
||||||
|
|
||||||
|
build_proc(&env, &mut layout_ids, scope.clone(), proc, fn_val);
|
||||||
|
|
||||||
|
// call finalize() before any code generation/verification
|
||||||
|
env.dibuilder.finalize();
|
||||||
|
|
||||||
|
if fn_val.verify(true) {
|
||||||
|
function_pass.run_on(&fn_val);
|
||||||
|
} else {
|
||||||
|
let mode = "NON-OPTIMIZED";
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
|
"\n\nFunction {:?} failed LLVM verification in {} build. Its content was:\n",
|
||||||
|
fn_val.get_name().to_str().unwrap(),
|
||||||
|
mode,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn_val.print_to_stderr();
|
||||||
|
// module.print_to_stderr();
|
||||||
|
|
||||||
|
panic!(
|
||||||
|
"The preceding code was from {:?}, which failed LLVM verification in {} build.",
|
||||||
|
fn_val.get_name().to_str().unwrap(),
|
||||||
|
mode,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main_data.map(|(main_fn_symbol, main_fn_layout)| {
|
||||||
|
promote_to_main_function(env, &mut layout_ids, main_fn_symbol, main_fn_layout)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_proc_header<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
|
|
|
@ -36,7 +36,6 @@ pub fn helper<'a>(
|
||||||
ignore_problems: bool,
|
ignore_problems: bool,
|
||||||
context: &'a inkwell::context::Context,
|
context: &'a inkwell::context::Context,
|
||||||
) -> (&'static str, String, Library) {
|
) -> (&'static str, String, Library) {
|
||||||
use roc_gen::llvm::build::{build_proc, build_proc_header, Scope};
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
let filename = PathBuf::from("Test.roc");
|
let filename = PathBuf::from("Test.roc");
|
||||||
|
@ -79,7 +78,7 @@ pub fn helper<'a>(
|
||||||
|
|
||||||
use roc_load::file::MonomorphizedModule;
|
use roc_load::file::MonomorphizedModule;
|
||||||
let MonomorphizedModule {
|
let MonomorphizedModule {
|
||||||
mut procedures,
|
procedures,
|
||||||
interns,
|
interns,
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
..
|
..
|
||||||
|
@ -236,63 +235,10 @@ pub fn helper<'a>(
|
||||||
exposed_to_host: MutSet::default(),
|
exposed_to_host: MutSet::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut layout_ids = roc_mono::layout::LayoutIds::default();
|
let (main_fn_name, main_fn) = roc_gen::llvm::build::build_procedures_return_main(
|
||||||
let mut headers = Vec::with_capacity(procedures.len());
|
|
||||||
|
|
||||||
// Add all the Proc headers to the module.
|
|
||||||
// We have to do this in a separate pass first,
|
|
||||||
// because their bodies may reference each other.
|
|
||||||
let mut scope = Scope::default();
|
|
||||||
for ((symbol, layout), proc) in procedures.drain() {
|
|
||||||
let fn_val = build_proc_header(&env, &mut layout_ids, symbol, layout, &proc);
|
|
||||||
|
|
||||||
if proc.args.is_empty() {
|
|
||||||
// this is a 0-argument thunk, i.e. a top-level constant definition
|
|
||||||
// it must be in-scope everywhere in the module!
|
|
||||||
scope.insert_top_level_thunk(symbol, arena.alloc(layout), fn_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
headers.push((proc, fn_val));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build each proc using its header info.
|
|
||||||
for (proc, fn_val) in headers {
|
|
||||||
let mut current_scope = scope.clone();
|
|
||||||
|
|
||||||
// only have top-level thunks for this proc's module in scope
|
|
||||||
// this retain is not needed for correctness, but will cause less confusion when debugging
|
|
||||||
let home = proc.name.module_id();
|
|
||||||
current_scope.retain_top_level_thunks_for_module(home);
|
|
||||||
|
|
||||||
build_proc(&env, &mut layout_ids, scope.clone(), proc, fn_val);
|
|
||||||
|
|
||||||
// call finalize() before any code generation/verification
|
|
||||||
env.dibuilder.finalize();
|
|
||||||
|
|
||||||
if fn_val.verify(true) {
|
|
||||||
function_pass.run_on(&fn_val);
|
|
||||||
} else {
|
|
||||||
let mode = "NON-OPTIMIZED";
|
|
||||||
|
|
||||||
eprintln!(
|
|
||||||
"\n\nFunction {:?} failed LLVM verification in {} build. Its content was:\n",
|
|
||||||
fn_val.get_name().to_str().unwrap(),
|
|
||||||
mode,
|
|
||||||
);
|
|
||||||
|
|
||||||
fn_val.print_to_stderr();
|
|
||||||
// module.print_to_stderr();
|
|
||||||
|
|
||||||
panic!(
|
|
||||||
"The preceding code was from {:?}, which failed LLVM verification in {} build.",
|
|
||||||
fn_val.get_name().to_str().unwrap(),
|
|
||||||
mode,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let (main_fn_name, main_fn) = roc_gen::llvm::build::promote_to_main_function(
|
|
||||||
&env,
|
&env,
|
||||||
&mut layout_ids,
|
opt_level,
|
||||||
|
procedures,
|
||||||
main_fn_symbol,
|
main_fn_symbol,
|
||||||
main_fn_layout,
|
main_fn_layout,
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue