Merge branch 'trunk' into integrate-linker

This commit is contained in:
Brendan Hansknecht 2021-09-16 16:27:50 -07:00
commit 8a30b4803a
18 changed files with 352 additions and 88 deletions

2
Cargo.lock generated
View file

@ -3402,7 +3402,9 @@ dependencies = [
"roc_can", "roc_can",
"roc_collections", "roc_collections",
"roc_constrain", "roc_constrain",
"roc_gen_dev",
"roc_gen_llvm", "roc_gen_llvm",
"roc_gen_wasm",
"roc_load", "roc_load",
"roc_module", "roc_module",
"roc_mono", "roc_mono",

View file

@ -62,10 +62,7 @@ pub fn build_file<'a>(
let subs_by_module = MutMap::default(); let subs_by_module = MutMap::default();
// Release builds use uniqueness optimizations // Release builds use uniqueness optimizations
let stdlib = match opt_level { let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
OptLevel::Normal => arena.alloc(roc_builtins::std::standard_stdlib()),
OptLevel::Optimize => arena.alloc(roc_builtins::std::standard_stdlib()),
};
let loaded = roc_load::file::load_and_monomorphize( let loaded = roc_load::file::load_and_monomorphize(
arena, arena,
@ -78,16 +75,7 @@ pub fn build_file<'a>(
)?; )?;
use target_lexicon::Architecture; use target_lexicon::Architecture;
let emit_wasm = match target.architecture { let emit_wasm = matches!(target.architecture, Architecture::Wasm32);
Architecture::X86_64 => false,
Architecture::Aarch64(_) => false,
Architecture::Wasm32 => true,
Architecture::X86_32(_) => false,
_ => panic!(
"TODO gracefully handle unsupported architecture: {:?}",
target.architecture
),
};
// TODO wasm host extension should be something else ideally // TODO wasm host extension should be something else ideally
// .bc does not seem to work because // .bc does not seem to work because
@ -180,15 +168,21 @@ pub fn build_file<'a>(
let loaded = loaded; let loaded = loaded;
let binary_path = cwd.join(&*loaded.output_path); // TODO should join ".exe" on Windows let binary_path = cwd.join(&*loaded.output_path); // TODO should join ".exe" on Windows
let code_gen_timing = program::gen_from_mono_module(
arena, let code_gen_timing = match opt_level {
loaded, OptLevel::Normal | OptLevel::Optimize => program::gen_from_mono_module_llvm(
&roc_file_path, arena,
target, loaded,
app_o_file, &roc_file_path,
opt_level, target,
emit_debug_info, app_o_file,
); opt_level,
emit_debug_info,
),
OptLevel::Development => {
program::gen_from_mono_module_dev(arena, loaded, target, app_o_file)
}
};
buf.push('\n'); buf.push('\n');
buf.push_str(" "); buf.push_str(" ");

View file

@ -28,6 +28,7 @@ pub const CMD_EDIT: &str = "edit";
pub const CMD_DOCS: &str = "docs"; pub const CMD_DOCS: &str = "docs";
pub const FLAG_DEBUG: &str = "debug"; pub const FLAG_DEBUG: &str = "debug";
pub const FLAG_DEV: &str = "dev";
pub const FLAG_OPTIMIZE: &str = "optimize"; pub const FLAG_OPTIMIZE: &str = "optimize";
pub const FLAG_LIB: &str = "lib"; pub const FLAG_LIB: &str = "lib";
pub const FLAG_BACKEND: &str = "backend"; pub const FLAG_BACKEND: &str = "backend";
@ -55,6 +56,12 @@ pub fn build_app<'a>() -> App<'a> {
.help("Optimize your compiled Roc program to run faster. (Optimization takes time to complete.)") .help("Optimize your compiled Roc program to run faster. (Optimization takes time to complete.)")
.required(false), .required(false),
) )
.arg(
Arg::with_name(FLAG_DEV)
.long(FLAG_DEV)
.help("Make compilation as fast as possible. (Runtime performance may suffer)")
.required(false),
)
.arg( .arg(
Arg::with_name(FLAG_BACKEND) Arg::with_name(FLAG_BACKEND)
.long(FLAG_BACKEND) .long(FLAG_BACKEND)
@ -98,6 +105,12 @@ pub fn build_app<'a>() -> App<'a> {
.help("Optimize the compiled program to run faster. (Optimization takes time to complete.)") .help("Optimize the compiled program to run faster. (Optimization takes time to complete.)")
.required(false), .required(false),
) )
.arg(
Arg::with_name(FLAG_DEV)
.long(FLAG_DEV)
.help("Make compilation as fast as possible. (Runtime performance may suffer)")
.required(false),
)
.arg( .arg(
Arg::with_name(FLAG_DEBUG) Arg::with_name(FLAG_DEBUG)
.long(FLAG_DEBUG) .long(FLAG_DEBUG)
@ -137,6 +150,12 @@ pub fn build_app<'a>() -> App<'a> {
.requires(ROC_FILE) .requires(ROC_FILE)
.required(false), .required(false),
) )
.arg(
Arg::with_name(FLAG_DEV)
.long(FLAG_DEV)
.help("Make compilation as fast as possible. (Runtime performance may suffer)")
.required(false),
)
.arg( .arg(
Arg::with_name(FLAG_DEBUG) Arg::with_name(FLAG_DEBUG)
.long(FLAG_DEBUG) .long(FLAG_DEBUG)
@ -223,10 +242,14 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
let filename = matches.value_of(ROC_FILE).unwrap(); let filename = matches.value_of(ROC_FILE).unwrap();
let original_cwd = std::env::current_dir()?; let original_cwd = std::env::current_dir()?;
let opt_level = if matches.is_present(FLAG_OPTIMIZE) { let opt_level = match (
OptLevel::Optimize matches.is_present(FLAG_OPTIMIZE),
} else { matches.is_present(FLAG_DEV),
OptLevel::Normal ) {
(true, false) => OptLevel::Optimize,
(true, true) => panic!("development cannot be optimized!"),
(false, true) => OptLevel::Development,
(false, false) => OptLevel::Normal,
}; };
let emit_debug_info = matches.is_present(FLAG_DEBUG); let emit_debug_info = matches.is_present(FLAG_DEBUG);
let emit_timings = matches.is_present(FLAG_TIME); let emit_timings = matches.is_present(FLAG_TIME);
@ -429,9 +452,7 @@ enum Backend {
Host, Host,
X86_32, X86_32,
X86_64, X86_64,
Dev,
Wasm32, Wasm32,
Wasm32Dev,
} }
impl Default for Backend { impl Default for Backend {
@ -446,9 +467,7 @@ impl Backend {
Backend::Host => "host", Backend::Host => "host",
Backend::X86_32 => "x86_32", Backend::X86_32 => "x86_32",
Backend::X86_64 => "x86_64", Backend::X86_64 => "x86_64",
Backend::Dev => "dev",
Backend::Wasm32 => "wasm32", Backend::Wasm32 => "wasm32",
Backend::Wasm32Dev => "wasm32_dev",
} }
} }
@ -457,9 +476,7 @@ impl Backend {
Backend::Host.as_str(), Backend::Host.as_str(),
Backend::X86_32.as_str(), Backend::X86_32.as_str(),
Backend::X86_64.as_str(), Backend::X86_64.as_str(),
Backend::Dev.as_str(),
Backend::Wasm32.as_str(), Backend::Wasm32.as_str(),
Backend::Wasm32Dev.as_str(),
]; ];
fn to_triple(&self) -> Triple { fn to_triple(&self) -> Triple {
@ -482,8 +499,7 @@ impl Backend {
triple triple
} }
Backend::Dev => todo!(), Backend::Wasm32 => {
Backend::Wasm32 | Backend::Wasm32Dev => {
triple.architecture = Architecture::Wasm32; triple.architecture = Architecture::Wasm32;
triple.binary_format = BinaryFormat::Wasm; triple.binary_format = BinaryFormat::Wasm;
@ -507,9 +523,7 @@ impl std::str::FromStr for Backend {
"host" => Ok(Backend::Host), "host" => Ok(Backend::Host),
"x86_32" => Ok(Backend::X86_32), "x86_32" => Ok(Backend::X86_32),
"x86_64" => Ok(Backend::X86_64), "x86_64" => Ok(Backend::X86_64),
"dev" => Ok(Backend::Dev),
"wasm32" => Ok(Backend::Wasm32), "wasm32" => Ok(Backend::Wasm32),
"wasm32_dev" => Ok(Backend::Wasm32Dev),
_ => Err(()), _ => Err(()),
} }
} }

View file

@ -157,6 +157,16 @@ mod cli_run {
let example = $example; let example = $example;
let file_name = example_file(dir_name, example.filename); let file_name = example_file(dir_name, example.filename);
match example.filename {
"Fib.roc" => {
// it is broken because the dev and normal backend don't generate the
// same name for main. The dev version is expected here.
eprintln!("WARNING: skipping testing example {} because the test is broken right now!", example.filename);
return;
}
_ => {}
}
// Check with and without optimizations // Check with and without optimizations
check_output_with_stdin( check_output_with_stdin(
&file_name, &file_name,
@ -224,6 +234,13 @@ mod cli_run {
expected_ending:"Hello, World!\n", expected_ending:"Hello, World!\n",
use_valgrind: true, use_valgrind: true,
}, },
fib:"fib" => Example {
filename: "Fib.roc",
executable_filename: "fib",
stdin: &[],
expected_ending:"55\n",
use_valgrind: true,
},
quicksort:"quicksort" => Example { quicksort:"quicksort" => Example {
filename: "Quicksort.roc", filename: "Quicksort.roc",
executable_filename: "quicksort", executable_filename: "quicksort",

View file

@ -20,6 +20,8 @@ roc_solve = { path = "../solve" }
roc_mono = { path = "../mono" } roc_mono = { path = "../mono" }
roc_load = { path = "../load" } roc_load = { path = "../load" }
roc_gen_llvm = { path = "../gen_llvm", optional = true } roc_gen_llvm = { path = "../gen_llvm", optional = true }
roc_gen_wasm = { path = "../gen_wasm" }
roc_gen_dev = { path = "../gen_dev" }
roc_reporting = { path = "../reporting" } roc_reporting = { path = "../reporting" }
roc_std = { path = "../../roc_std" } roc_std = { path = "../../roc_std" }
im = "14" # im and im-rc should always have the same version! im = "14" # im and im-rc should always have the same version!

View file

@ -10,6 +10,8 @@ use roc_mono::ir::OptLevel;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::time::Duration; use std::time::Duration;
use roc_collections::all::{MutMap, MutSet};
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub struct CodeGenTiming { pub struct CodeGenTiming {
pub code_gen: Duration, pub code_gen: Duration,
@ -142,7 +144,7 @@ pub fn report_problems(loaded: &mut MonomorphizedModule) -> usize {
// TODO this should probably use more helper functions // TODO this should probably use more helper functions
// TODO make this polymorphic in the llvm functions so it can be reused for another backend. // TODO make this polymorphic in the llvm functions so it can be reused for another backend.
#[cfg(feature = "llvm")] #[cfg(feature = "llvm")]
pub fn gen_from_mono_module( pub fn gen_from_mono_module_llvm(
arena: &bumpalo::Bump, arena: &bumpalo::Bump,
loaded: MonomorphizedModule, loaded: MonomorphizedModule,
roc_file_path: &Path, roc_file_path: &Path,
@ -371,3 +373,78 @@ pub fn gen_from_mono_module(
emit_o_file, emit_o_file,
} }
} }
pub fn gen_from_mono_module_dev(
arena: &bumpalo::Bump,
loaded: MonomorphizedModule,
target: &target_lexicon::Triple,
app_o_file: &Path,
) -> CodeGenTiming {
use target_lexicon::Architecture;
match target.architecture {
Architecture::Wasm32 => gen_from_mono_module_dev_wasm32(arena, loaded, app_o_file),
Architecture::X86_64 => {
gen_from_mono_module_dev_assembly(arena, loaded, target, app_o_file)
}
_ => todo!(),
}
}
fn gen_from_mono_module_dev_wasm32(
arena: &bumpalo::Bump,
loaded: MonomorphizedModule,
app_o_file: &Path,
) -> CodeGenTiming {
let mut procedures = MutMap::default();
for (key, proc) in loaded.procedures {
procedures.insert(key, proc);
}
let exposed_to_host = loaded
.exposed_to_host
.keys()
.copied()
.collect::<MutSet<_>>();
let env = roc_gen_wasm::Env {
arena,
interns: loaded.interns,
exposed_to_host,
};
let bytes = roc_gen_wasm::build_module(&env, procedures).unwrap();
std::fs::write(&app_o_file, &bytes).expect("failed to write object to file");
CodeGenTiming::default()
}
fn gen_from_mono_module_dev_assembly(
arena: &bumpalo::Bump,
loaded: MonomorphizedModule,
target: &target_lexicon::Triple,
app_o_file: &Path,
) -> CodeGenTiming {
let lazy_literals = true;
let generate_allocators = false; // provided by the platform
let env = roc_gen_dev::Env {
arena,
interns: loaded.interns,
exposed_to_host: loaded.exposed_to_host.keys().copied().collect(),
lazy_literals,
generate_allocators,
};
let module_object = roc_gen_dev::build_module(&env, target, loaded.procedures)
.expect("failed to compile module");
let module_out = module_object
.write()
.expect("failed to build output object");
std::fs::write(&app_o_file, module_out).expect("failed to write object to file");
CodeGenTiming::default()
}

View file

@ -106,7 +106,7 @@ pub fn target_machine(
#[cfg(feature = "llvm")] #[cfg(feature = "llvm")]
pub fn convert_opt_level(level: OptLevel) -> OptimizationLevel { pub fn convert_opt_level(level: OptLevel) -> OptimizationLevel {
match level { match level {
OptLevel::Normal => OptimizationLevel::None, OptLevel::Development | OptLevel::Normal => OptimizationLevel::None,
OptLevel::Optimize => OptimizationLevel::Aggressive, OptLevel::Optimize => OptimizationLevel::Aggressive,
} }
} }

View file

@ -191,10 +191,16 @@ fn build_object<'a, B: Backend<'a>>(
let mut layout_ids = roc_mono::layout::LayoutIds::default(); let mut layout_ids = roc_mono::layout::LayoutIds::default();
let mut procs = Vec::with_capacity_in(procedures.len(), env.arena); let mut procs = Vec::with_capacity_in(procedures.len(), env.arena);
for ((sym, layout), proc) in procedures { for ((sym, layout), proc) in procedures {
let fn_name = layout_ids let base_name = layout_ids
.get_toplevel(sym, &layout) .get_toplevel(sym, &layout)
.to_symbol_string(sym, &env.interns); .to_symbol_string(sym, &env.interns);
let fn_name = if env.exposed_to_host.contains(&sym) {
format!("roc_{}_exposed", base_name)
} else {
base_name
};
let section_id = output.add_section( let section_id = output.add_section(
output.segment_name(StandardSegment::Text).to_vec(), output.segment_name(StandardSegment::Text).to_vec(),
format!(".text.{:x}", sym.as_u64()).as_bytes().to_vec(), format!(".text.{:x}", sym.as_u64()).as_bytes().to_vec(),

View file

@ -94,10 +94,12 @@ pub fn helper<'a>(
let main_fn_layout = loaded.entry_point.layout; let main_fn_layout = loaded.entry_point.layout;
let mut layout_ids = roc_mono::layout::LayoutIds::default(); let mut layout_ids = roc_mono::layout::LayoutIds::default();
let main_fn_name = layout_ids let main_fn_name_base = layout_ids
.get_toplevel(main_fn_symbol, &main_fn_layout) .get_toplevel(main_fn_symbol, &main_fn_layout)
.to_symbol_string(main_fn_symbol, &interns); .to_symbol_string(main_fn_symbol, &interns);
let main_fn_name = format!("roc_{}_exposed", main_fn_name_base);
let mut lines = Vec::new(); let mut lines = Vec::new();
// errors whose reporting we delay (so we can see that code gen generates runtime errors) // errors whose reporting we delay (so we can see that code gen generates runtime errors)
let mut delayed_errors = Vec::new(); let mut delayed_errors = Vec::new();

View file

@ -10,7 +10,7 @@ use inkwell::types::{BasicType, BasicTypeEnum};
use inkwell::values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue}; use inkwell::values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue};
use inkwell::AddressSpace; use inkwell::AddressSpace;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::layout::{Layout, LayoutIds, UnionLayout}; use roc_mono::layout::{LambdaSet, Layout, LayoutIds, UnionLayout};
pub fn call_bitcode_fn<'a, 'ctx, 'env>( pub fn call_bitcode_fn<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
@ -189,7 +189,7 @@ fn build_has_tag_id_help<'a, 'ctx, 'env>(
pub fn build_transform_caller<'a, 'ctx, 'env>( pub fn build_transform_caller<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
function: FunctionValue<'ctx>, function: FunctionValue<'ctx>,
closure_data_layout: Layout<'a>, closure_data_layout: LambdaSet<'a>,
argument_layouts: &[Layout<'a>], argument_layouts: &[Layout<'a>],
) -> FunctionValue<'ctx> { ) -> FunctionValue<'ctx> {
let fn_name: &str = &format!( let fn_name: &str = &format!(
@ -212,7 +212,7 @@ pub fn build_transform_caller<'a, 'ctx, 'env>(
fn build_transform_caller_help<'a, 'ctx, 'env>( fn build_transform_caller_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
roc_function: FunctionValue<'ctx>, roc_function: FunctionValue<'ctx>,
closure_data_layout: Layout<'a>, closure_data_layout: LambdaSet<'a>,
argument_layouts: &[Layout<'a>], argument_layouts: &[Layout<'a>],
fn_name: &str, fn_name: &str,
) -> FunctionValue<'ctx> { ) -> FunctionValue<'ctx> {
@ -270,7 +270,7 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
arguments_cast.push(argument); arguments_cast.push(argument);
} }
match closure_data_layout { match closure_data_layout.runtime_representation() {
Layout::Struct(&[]) => { Layout::Struct(&[]) => {
// nothing to add // nothing to add
} }
@ -529,7 +529,7 @@ pub fn build_eq_wrapper<'a, 'ctx, 'env>(
pub fn build_compare_wrapper<'a, 'ctx, 'env>( pub fn build_compare_wrapper<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
roc_function: FunctionValue<'ctx>, roc_function: FunctionValue<'ctx>,
closure_data_layout: Layout<'a>, closure_data_layout: LambdaSet<'a>,
layout: &Layout<'a>, layout: &Layout<'a>,
) -> FunctionValue<'ctx> { ) -> FunctionValue<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function"); let block = env.builder.get_insert_block().expect("to be in a function");
@ -595,7 +595,7 @@ pub fn build_compare_wrapper<'a, 'ctx, 'env>(
let default = [value1, value2]; let default = [value1, value2];
let arguments_cast = match closure_data_layout { let arguments_cast = match closure_data_layout.runtime_representation() {
Layout::Struct(&[]) => { Layout::Struct(&[]) => {
// nothing to add // nothing to add
&default &default

View file

@ -638,7 +638,7 @@ pub fn construct_optimization_passes<'a>(
let pmb = PassManagerBuilder::create(); let pmb = PassManagerBuilder::create();
match opt_level { match opt_level {
OptLevel::Normal => { OptLevel::Development | OptLevel::Normal => {
pmb.set_optimization_level(OptimizationLevel::None); pmb.set_optimization_level(OptimizationLevel::None);
} }
OptLevel::Optimize => { OptLevel::Optimize => {
@ -2609,6 +2609,18 @@ pub fn load_symbol_and_layout<'a, 'ctx, 'b>(
None => panic!("There was no entry for {:?} in scope {:?}", symbol, scope), None => panic!("There was no entry for {:?} in scope {:?}", symbol, scope),
} }
} }
pub fn load_symbol_and_lambda_set<'a, 'ctx, 'b>(
scope: &'b Scope<'a, 'ctx>,
symbol: &Symbol,
) -> (BasicValueEnum<'ctx>, LambdaSet<'a>) {
match scope.get(symbol) {
Some((Layout::LambdaSet(lambda_set), ptr)) => (*ptr, *lambda_set),
Some((other, ptr)) => panic!("Not a lambda set: {:?}, {:?}", other, ptr),
None => panic!("There was no entry for {:?} in scope {:?}", symbol, scope),
}
}
fn access_index_struct_value<'ctx>( fn access_index_struct_value<'ctx>(
builder: &Builder<'ctx>, builder: &Builder<'ctx>,
from_value: StructValue<'ctx>, from_value: StructValue<'ctx>,
@ -4102,31 +4114,26 @@ fn roc_function_call<'a, 'ctx, 'env>(
layout_ids: &mut LayoutIds<'a>, layout_ids: &mut LayoutIds<'a>,
transform: FunctionValue<'ctx>, transform: FunctionValue<'ctx>,
closure_data: BasicValueEnum<'ctx>, closure_data: BasicValueEnum<'ctx>,
closure_data_layout: Layout<'a>, lambda_set: LambdaSet<'a>,
closure_data_is_owned: bool, closure_data_is_owned: bool,
argument_layouts: &[Layout<'a>], argument_layouts: &[Layout<'a>],
) -> RocFunctionCall<'ctx> { ) -> RocFunctionCall<'ctx> {
use crate::llvm::bitcode::{build_inc_n_wrapper, build_transform_caller}; use crate::llvm::bitcode::{build_inc_n_wrapper, build_transform_caller};
let closure_data_layout = match closure_data_layout {
Layout::LambdaSet(lambda_set) => lambda_set.runtime_representation(),
_ => panic!("closure argument is not a lambda set!"),
};
let closure_data_ptr = env let closure_data_ptr = env
.builder .builder
.build_alloca(closure_data.get_type(), "closure_data_ptr"); .build_alloca(closure_data.get_type(), "closure_data_ptr");
env.builder.build_store(closure_data_ptr, closure_data); env.builder.build_store(closure_data_ptr, closure_data);
let stepper_caller = let stepper_caller = build_transform_caller(env, transform, lambda_set, argument_layouts)
build_transform_caller(env, transform, closure_data_layout, argument_layouts)
.as_global_value()
.as_pointer_value();
let inc_closure_data = build_inc_n_wrapper(env, layout_ids, &closure_data_layout)
.as_global_value() .as_global_value()
.as_pointer_value(); .as_pointer_value();
let inc_closure_data =
build_inc_n_wrapper(env, layout_ids, &lambda_set.runtime_representation())
.as_global_value()
.as_pointer_value();
let closure_data_is_owned = env let closure_data_is_owned = env
.context .context
.bool_type() .bool_type()
@ -4180,7 +4187,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
let function = passed_function_at_index!(2); let function = passed_function_at_index!(2);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]); let (closure, closure_layout) = load_symbol_and_lambda_set(scope, &args[3]);
match list_layout { match list_layout {
Layout::Builtin(Builtin::EmptyList) => default, Layout::Builtin(Builtin::EmptyList) => default,
@ -4192,7 +4199,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
layout_ids, layout_ids,
function, function,
closure, closure,
*closure_layout, closure_layout,
function_owns_closure_data, function_owns_closure_data,
argument_layouts, argument_layouts,
); );
@ -4222,7 +4229,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
let function = passed_function_at_index!(1); let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); let (closure, closure_layout) = load_symbol_and_lambda_set(scope, &args[2]);
match (list_layout, return_layout) { match (list_layout, return_layout) {
(Layout::Builtin(Builtin::EmptyList), _) => empty_list(env), (Layout::Builtin(Builtin::EmptyList), _) => empty_list(env),
@ -4237,7 +4244,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
layout_ids, layout_ids,
function, function,
closure, closure,
*closure_layout, closure_layout,
function_owns_closure_data, function_owns_closure_data,
argument_layouts, argument_layouts,
); );
@ -4254,7 +4261,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
let (list2, list2_layout) = load_symbol_and_layout(scope, &args[1]); let (list2, list2_layout) = load_symbol_and_layout(scope, &args[1]);
let function = passed_function_at_index!(2); let function = passed_function_at_index!(2);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]); let (closure, closure_layout) = load_symbol_and_lambda_set(scope, &args[3]);
match (list1_layout, list2_layout, return_layout) { match (list1_layout, list2_layout, return_layout) {
( (
@ -4269,7 +4276,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
layout_ids, layout_ids,
function, function,
closure, closure,
*closure_layout, closure_layout,
function_owns_closure_data, function_owns_closure_data,
argument_layouts, argument_layouts,
); );
@ -4298,7 +4305,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
let (list3, list3_layout) = load_symbol_and_layout(scope, &args[2]); let (list3, list3_layout) = load_symbol_and_layout(scope, &args[2]);
let function = passed_function_at_index!(3); let function = passed_function_at_index!(3);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[4]); let (closure, closure_layout) = load_symbol_and_lambda_set(scope, &args[4]);
match (list1_layout, list2_layout, list3_layout, return_layout) { match (list1_layout, list2_layout, list3_layout, return_layout) {
( (
@ -4315,7 +4322,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
layout_ids, layout_ids,
function, function,
closure, closure,
*closure_layout, closure_layout,
function_owns_closure_data, function_owns_closure_data,
argument_layouts, argument_layouts,
); );
@ -4347,7 +4354,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
let function = passed_function_at_index!(1); let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); let (closure, closure_layout) = load_symbol_and_lambda_set(scope, &args[2]);
match (list_layout, return_layout) { match (list_layout, return_layout) {
(Layout::Builtin(Builtin::EmptyList), _) => empty_list(env), (Layout::Builtin(Builtin::EmptyList), _) => empty_list(env),
@ -4362,7 +4369,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
layout_ids, layout_ids,
function, function,
closure, closure,
*closure_layout, closure_layout,
function_owns_closure_data, function_owns_closure_data,
argument_layouts, argument_layouts,
); );
@ -4380,7 +4387,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
let function = passed_function_at_index!(1); let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); let (closure, closure_layout) = load_symbol_and_lambda_set(scope, &args[2]);
match list_layout { match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env), Layout::Builtin(Builtin::EmptyList) => empty_list(env),
@ -4392,7 +4399,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
layout_ids, layout_ids,
function, function,
closure, closure,
*closure_layout, closure_layout,
function_owns_closure_data, function_owns_closure_data,
argument_layouts, argument_layouts,
); );
@ -4410,7 +4417,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
let function = passed_function_at_index!(1); let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); let (closure, closure_layout) = load_symbol_and_lambda_set(scope, &args[2]);
match (list_layout, return_layout) { match (list_layout, return_layout) {
(_, Layout::Builtin(Builtin::EmptyList)) (_, Layout::Builtin(Builtin::EmptyList))
@ -4426,7 +4433,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
layout_ids, layout_ids,
function, function,
closure, closure,
*closure_layout, closure_layout,
function_owns_closure_data, function_owns_closure_data,
argument_layouts, argument_layouts,
); );
@ -4454,7 +4461,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
let function = passed_function_at_index!(1); let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); let (closure, closure_layout) = load_symbol_and_lambda_set(scope, &args[2]);
match (list_layout, return_layout) { match (list_layout, return_layout) {
(_, Layout::Builtin(Builtin::EmptyList)) (_, Layout::Builtin(Builtin::EmptyList))
@ -4470,7 +4477,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
layout_ids, layout_ids,
function, function,
closure, closure,
*closure_layout, closure_layout,
function_owns_closure_data, function_owns_closure_data,
argument_layouts, argument_layouts,
); );
@ -4507,7 +4514,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
let function = passed_function_at_index!(1); let function = passed_function_at_index!(1);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[2]); let (closure, closure_layout) = load_symbol_and_lambda_set(scope, &args[2]);
match list_layout { match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env), Layout::Builtin(Builtin::EmptyList) => empty_list(env),
@ -4516,13 +4523,8 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
let argument_layouts = &[**element_layout, **element_layout]; let argument_layouts = &[**element_layout, **element_layout];
let closure_data_layout = match closure_layout {
Layout::LambdaSet(lambda_set) => lambda_set.runtime_representation(),
_ => panic!("closure argument is not a lambda set!"),
};
let compare_wrapper = let compare_wrapper =
build_compare_wrapper(env, function, closure_data_layout, element_layout) build_compare_wrapper(env, function, closure_layout, element_layout)
.as_global_value() .as_global_value()
.as_pointer_value(); .as_pointer_value();
@ -4531,7 +4533,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
layout_ids, layout_ids,
function, function,
closure, closure,
*closure_layout, closure_layout,
function_owns_closure_data, function_owns_closure_data,
argument_layouts, argument_layouts,
); );
@ -4553,7 +4555,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
let (dict, dict_layout) = load_symbol_and_layout(scope, &args[0]); let (dict, dict_layout) = load_symbol_and_layout(scope, &args[0]);
let (default, default_layout) = load_symbol_and_layout(scope, &args[1]); let (default, default_layout) = load_symbol_and_layout(scope, &args[1]);
let function = passed_function_at_index!(2); let function = passed_function_at_index!(2);
let (closure, closure_layout) = load_symbol_and_layout(scope, &args[3]); let (closure, closure_layout) = load_symbol_and_lambda_set(scope, &args[3]);
match dict_layout { match dict_layout {
Layout::Builtin(Builtin::EmptyDict) => { Layout::Builtin(Builtin::EmptyDict) => {
@ -4568,7 +4570,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
layout_ids, layout_ids,
function, function,
closure, closure,
*closure_layout, closure_layout,
function_owns_closure_data, function_owns_closure_data,
argument_layouts, argument_layouts,
); );

View file

@ -589,9 +589,9 @@ fn call_spec(
let index = builder.add_make_tuple(block, &[])?; let index = builder.add_make_tuple(block, &[])?;
let argument = if closure_env_layout.is_none() { let argument = if closure_env_layout.is_none() {
builder.add_make_tuple(block, &[first, index])? builder.add_make_tuple(block, &[index, first])?
} else { } else {
builder.add_make_tuple(block, &[first, index, closure_env])? builder.add_make_tuple(block, &[index, first, closure_env])?
}; };
builder.add_call(block, spec_var, module, name, argument)?; builder.add_call(block, spec_var, module, name, argument)?;
} }

View file

@ -54,6 +54,7 @@ macro_rules! return_on_layout_error_help {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum OptLevel { pub enum OptLevel {
Development,
Normal, Normal,
Optimize, Optimize,
} }

View file

@ -2017,3 +2017,17 @@ fn lists_with_incompatible_type_param_in_if() {
RocStr RocStr
); );
} }
#[test]
fn map_with_index_multi_record() {
// see https://github.com/rtfeldman/roc/issues/1700
assert_evals_to!(
indoc!(
r#"
List.mapWithIndex [ { x: {}, y: {} } ] \_, _ -> {}
"#
),
RocList::from_slice(&[((), ())]),
RocList<((), ())>
);
}

1
examples/fib/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
add

27
examples/fib/Fib.roc Normal file
View file

@ -0,0 +1,27 @@
app "fib"
packages { base: "platform" }
imports []
provides [ main ] to base
main = \n -> fib n
fib = \n ->
if n == 0 then
0
else if n == 1 then
1
else
(fib (n - 1)) + (fib (n - 2))
# the clever implementation requires join points
# fib = \n, a, b ->
# if n == 0 then
# a
#
# else
# fib (n - 1) b (a + b)
#
# fib n 0 1

View file

@ -0,0 +1,10 @@
platform examples/add
requires {}{ main : I64 -> I64 }
exposes []
packages {}
imports []
provides [ mainForHost ]
effects fx.Effect {}
mainForHost : I64 -> I64
mainForHost = \a -> main a

View file

@ -0,0 +1,95 @@
const std = @import("std");
const testing = std.testing;
const expectEqual = testing.expectEqual;
const expect = testing.expect;
comptime {
// This is a workaround for https://github.com/ziglang/zig/issues/8218
// which is only necessary on macOS.
//
// Once that issue is fixed, we can undo the changes in
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
// -fcompiler-rt in link.rs instead of doing this. Note that this
// workaround is present in many host.zig files, so make sure to undo
// it everywhere!
if (std.builtin.os.tag == .macos) {
_ = @import("compiler_rt");
}
}
const mem = std.mem;
const Allocator = mem.Allocator;
extern fn roc__mainForHost_1_exposed(i64) i64;
const Align = extern struct { a: usize, b: usize };
extern fn malloc(size: usize) callconv(.C) ?*align(@alignOf(Align)) c_void;
extern fn realloc(c_ptr: [*]align(@alignOf(Align)) u8, size: usize) callconv(.C) ?*c_void;
extern fn free(c_ptr: [*]align(@alignOf(Align)) u8) callconv(.C) void;
const DEBUG: bool = false;
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void {
if (DEBUG) {
var ptr = malloc(size);
const stdout = std.io.getStdOut().writer();
stdout.print("alloc: {d} (alignment {d}, size {d})\n", .{ ptr, alignment, size }) catch unreachable;
return ptr;
} else {
return malloc(size);
}
}
export fn roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*c_void {
if (DEBUG) {
const stdout = std.io.getStdOut().writer();
stdout.print("realloc: {d} (alignment {d}, old_size {d})\n", .{ c_ptr, alignment, old_size }) catch unreachable;
}
return realloc(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)), new_size);
}
export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
if (DEBUG) {
const stdout = std.io.getStdOut().writer();
stdout.print("dealloc: {d} (alignment {d})\n", .{ c_ptr, alignment }) catch unreachable;
}
free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)));
}
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
_ = tag_id;
const stderr = std.io.getStdErr().writer();
const msg = @ptrCast([*:0]const u8, c_ptr);
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
std.process.exit(0);
}
pub export fn main() u8 {
const stdout = std.io.getStdOut().writer();
const stderr = std.io.getStdErr().writer();
// start time
var ts1: std.os.timespec = undefined;
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts1) catch unreachable;
const result = roc__mainForHost_1_exposed(10);
stdout.print("{d}\n", .{result}) catch unreachable;
// end time
var ts2: std.os.timespec = undefined;
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts2) catch unreachable;
const delta = to_seconds(ts2) - to_seconds(ts1);
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
return 0;
}
fn to_seconds(tms: std.os.timespec) f64 {
return @intToFloat(f64, tms.tv_sec) + (@intToFloat(f64, tms.tv_nsec) / 1_000_000_000.0);
}