mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 03:42:17 +00:00
Merge remote-tracking branch 'origin/trunk' into remove-parse-def
This commit is contained in:
commit
a0fdb0b902
11 changed files with 307 additions and 296 deletions
|
@ -1,5 +1,5 @@
|
|||
use roc_gen_llvm::llvm::build::module_from_builtins;
|
||||
pub use roc_gen_llvm::llvm::build::FunctionIterator;
|
||||
use roc_gen_llvm::llvm::build::{module_from_builtins, LlvmBackendMode};
|
||||
use roc_load::{LoadedModule, MonomorphizedModule};
|
||||
use roc_module::symbol::{Interns, ModuleId};
|
||||
use roc_mono::ir::OptLevel;
|
||||
|
@ -255,9 +255,7 @@ pub fn gen_from_mono_module_llvm(
|
|||
interns: loaded.interns,
|
||||
module,
|
||||
target_info,
|
||||
// in gen_tests, the compiler provides roc_panic
|
||||
// and sets up the setjump/longjump exception handling
|
||||
is_gen_test: false,
|
||||
mode: LlvmBackendMode::Binary,
|
||||
exposed_to_host: loaded.exposed_to_host.values.keys().copied().collect(),
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use roc_parse::ast::{Collection, ExtractSpaces};
|
||||
use roc_parse::ast::{Collection, CommentOrNewline, ExtractSpaces};
|
||||
|
||||
use crate::{
|
||||
annotation::{Formattable, Newlines},
|
||||
spaces::{count_leading_newlines, fmt_comments_only, NewlineAt, INDENT},
|
||||
spaces::{fmt_comments_only, NewlineAt, INDENT},
|
||||
Buf,
|
||||
};
|
||||
|
||||
|
@ -41,24 +41,43 @@ pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable>(
|
|||
buf.push(start);
|
||||
|
||||
for (index, item) in items.iter().enumerate() {
|
||||
let item = item.extract_spaces();
|
||||
let is_first_item = index == 0;
|
||||
let item = item.extract_spaces();
|
||||
let is_only_newlines = item.before.iter().all(|s| s.is_newline());
|
||||
|
||||
buf.newline();
|
||||
if item.before.is_empty() || is_only_newlines {
|
||||
buf.ensure_ends_in_newline();
|
||||
} else {
|
||||
if is_first_item {
|
||||
// The first item in a multiline collection always begins with exactly
|
||||
// one newline (so the delimiter is at the end of its own line),
|
||||
// and that newline appears before the first comment (if there is one).
|
||||
buf.ensure_ends_in_newline();
|
||||
} else {
|
||||
if item.before.starts_with(&[CommentOrNewline::Newline]) {
|
||||
buf.ensure_ends_in_newline();
|
||||
}
|
||||
|
||||
if !item.before.is_empty() {
|
||||
let is_only_newlines = item.before.iter().all(|s| s.is_newline());
|
||||
if item
|
||||
.before
|
||||
.starts_with(&[CommentOrNewline::Newline, CommentOrNewline::Newline])
|
||||
{
|
||||
// If there's a comment, and it's not on the first item,
|
||||
// and it's preceded by at least one blank line, maintain 1 blank line.
|
||||
// (We already ensured that it ends in a newline, so this will turn that
|
||||
// into a blank line.)
|
||||
|
||||
if !is_first_item
|
||||
&& !is_only_newlines
|
||||
&& count_leading_newlines(item.before.iter()) > 1
|
||||
{
|
||||
buf.newline();
|
||||
buf.newline();
|
||||
}
|
||||
}
|
||||
|
||||
fmt_comments_only(buf, item.before.iter(), NewlineAt::Bottom, item_indent);
|
||||
fmt_comments_only(buf, item.before.iter(), NewlineAt::None, item_indent);
|
||||
|
||||
if !is_only_newlines {
|
||||
if item.before.ends_with(&[CommentOrNewline::Newline]) {
|
||||
buf.newline();
|
||||
}
|
||||
|
||||
if !is_only_newlines && count_leading_newlines(item.before.iter().rev()) > 0 {
|
||||
buf.newline();
|
||||
}
|
||||
}
|
||||
|
@ -68,21 +87,33 @@ pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable>(
|
|||
buf.push(',');
|
||||
|
||||
if !item.after.is_empty() {
|
||||
fmt_comments_only(buf, item.after.iter(), NewlineAt::Top, item_indent);
|
||||
if item.after.iter().any(|s| s.is_newline()) {
|
||||
buf.newline();
|
||||
}
|
||||
|
||||
fmt_comments_only(buf, item.after.iter(), NewlineAt::None, item_indent);
|
||||
}
|
||||
}
|
||||
|
||||
if count_leading_newlines(items.final_comments().iter()) > 1 {
|
||||
if items.final_comments().iter().any(|s| s.is_newline()) {
|
||||
buf.newline();
|
||||
}
|
||||
|
||||
if items
|
||||
.final_comments()
|
||||
.starts_with(&[CommentOrNewline::Newline, CommentOrNewline::Newline])
|
||||
{
|
||||
buf.newline();
|
||||
}
|
||||
|
||||
fmt_comments_only(
|
||||
buf,
|
||||
items.final_comments().iter(),
|
||||
NewlineAt::Top,
|
||||
NewlineAt::None,
|
||||
item_indent,
|
||||
);
|
||||
buf.newline();
|
||||
|
||||
buf.ensure_ends_in_newline();
|
||||
buf.indent(braces_indent);
|
||||
} else {
|
||||
// is_multiline == false
|
||||
|
|
|
@ -2347,8 +2347,7 @@ mod test_fmt {
|
|||
indoc!(
|
||||
r#"
|
||||
f : {
|
||||
x : Int *,
|
||||
# comment 1
|
||||
x : Int *, # comment 1
|
||||
# comment 2
|
||||
}
|
||||
|
||||
|
@ -4588,7 +4587,7 @@ mod test_fmt {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_tag_union_annotation() {
|
||||
fn multiline_tag_union_annotation_no_comments() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
b : [
|
||||
|
@ -4694,8 +4693,7 @@ mod test_fmt {
|
|||
b : [
|
||||
True,
|
||||
# comment 1
|
||||
False,
|
||||
# comment 2
|
||||
False, # comment 2
|
||||
# comment 3
|
||||
]
|
||||
|
||||
|
@ -5082,6 +5080,38 @@ mod test_fmt {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comments_in_multiline_tag_union_annotation() {
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
UnionAnn : [
|
||||
Foo, # comment 1
|
||||
Bar, # comment 2
|
||||
Baz, # comment 3
|
||||
# comment 4 line 1
|
||||
# comment 4 line 2
|
||||
]
|
||||
|
||||
0
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
UnionAnn : [
|
||||
Foo, # comment 1
|
||||
Bar, # comment 2
|
||||
Baz, # comment 3
|
||||
# comment 4 line 1
|
||||
# comment 4 line 2
|
||||
]
|
||||
|
||||
0
|
||||
"#
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Test that everything under examples/ is formatted correctly
|
||||
/// If this test fails on your diff, it probably means you need to re-format the examples.
|
||||
|
|
|
@ -164,6 +164,16 @@ impl<'a, 'ctx> Scope<'a, 'ctx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum LlvmBackendMode {
|
||||
/// Assumes primitives (roc_alloc, roc_panic, etc) are provided by the host
|
||||
Binary,
|
||||
/// Creates a test wrapper around the main roc function to catch and report panics.
|
||||
/// Provides a testing implementation of primitives (roc_alloc, roc_panic, etc)
|
||||
GenTest,
|
||||
WasmGenTest,
|
||||
}
|
||||
|
||||
pub struct Env<'a, 'ctx, 'env> {
|
||||
pub arena: &'a Bump,
|
||||
pub context: &'ctx Context,
|
||||
|
@ -173,7 +183,7 @@ pub struct Env<'a, 'ctx, 'env> {
|
|||
pub module: &'ctx Module<'ctx>,
|
||||
pub interns: Interns,
|
||||
pub target_info: TargetInfo,
|
||||
pub is_gen_test: bool,
|
||||
pub mode: LlvmBackendMode,
|
||||
pub exposed_to_host: MutSet<Symbol>,
|
||||
}
|
||||
|
||||
|
@ -3319,7 +3329,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx, 'env>(
|
|||
return_layout: Layout<'a>,
|
||||
c_function_name: &str,
|
||||
) -> FunctionValue<'ctx> {
|
||||
// NOTE we ingore env.is_gen_test here
|
||||
// NOTE we ingore env.mode here
|
||||
|
||||
let mut cc_argument_types = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||
for layout in arguments {
|
||||
|
@ -3411,8 +3421,8 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx, 'env>(
|
|||
|
||||
let arguments_for_call = &arguments_for_call.into_bump_slice();
|
||||
|
||||
let call_result = {
|
||||
if env.is_gen_test {
|
||||
let call_result = match env.mode {
|
||||
LlvmBackendMode::GenTest | LlvmBackendMode::WasmGenTest => {
|
||||
debug_assert_eq!(args.len(), roc_function.get_params().len());
|
||||
|
||||
let roc_wrapper_function = make_exception_catcher(env, roc_function, return_layout);
|
||||
|
@ -3425,7 +3435,8 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx, 'env>(
|
|||
|
||||
let wrapped_layout = roc_result_layout(env.arena, return_layout, env.target_info);
|
||||
call_roc_function(env, roc_function, &wrapped_layout, arguments_for_call)
|
||||
} else {
|
||||
}
|
||||
LlvmBackendMode::Binary => {
|
||||
call_roc_function(env, roc_function, &return_layout, arguments_for_call)
|
||||
}
|
||||
};
|
||||
|
@ -3695,15 +3706,19 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>(
|
|||
return_layout: Layout<'a>,
|
||||
c_function_name: &str,
|
||||
) -> FunctionValue<'ctx> {
|
||||
if env.is_gen_test {
|
||||
return expose_function_to_host_help_c_abi_gen_test(
|
||||
env,
|
||||
ident_string,
|
||||
roc_function,
|
||||
arguments,
|
||||
return_layout,
|
||||
c_function_name,
|
||||
);
|
||||
match env.mode {
|
||||
LlvmBackendMode::GenTest | LlvmBackendMode::WasmGenTest => {
|
||||
return expose_function_to_host_help_c_abi_gen_test(
|
||||
env,
|
||||
ident_string,
|
||||
roc_function,
|
||||
arguments,
|
||||
return_layout,
|
||||
c_function_name,
|
||||
)
|
||||
}
|
||||
|
||||
LlvmBackendMode::Binary => {}
|
||||
}
|
||||
|
||||
// a generic version that writes the result into a passed *u8 pointer
|
||||
|
@ -3749,11 +3764,12 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>(
|
|||
|
||||
debug_info_init!(env, size_function);
|
||||
|
||||
let return_type = if env.is_gen_test {
|
||||
roc_result_type(env, roc_function.get_type().get_return_type().unwrap()).into()
|
||||
} else {
|
||||
// roc_function.get_type().get_return_type().unwrap()
|
||||
basic_type_from_layout(env, &return_layout)
|
||||
let return_type = match env.mode {
|
||||
LlvmBackendMode::GenTest | LlvmBackendMode::WasmGenTest => {
|
||||
roc_result_type(env, roc_function.get_type().get_return_type().unwrap()).into()
|
||||
}
|
||||
|
||||
LlvmBackendMode::Binary => basic_type_from_layout(env, &return_layout),
|
||||
};
|
||||
|
||||
let size: BasicValueEnum = return_type.size_of().unwrap().into();
|
||||
|
@ -4494,40 +4510,45 @@ pub fn build_closure_caller<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
|
||||
if env.is_gen_test {
|
||||
let call_result = set_jump_and_catch_long_jump(
|
||||
env,
|
||||
function_value,
|
||||
evaluator,
|
||||
&evaluator_arguments,
|
||||
*return_layout,
|
||||
);
|
||||
match env.mode {
|
||||
LlvmBackendMode::GenTest | LlvmBackendMode::WasmGenTest => {
|
||||
let call_result = set_jump_and_catch_long_jump(
|
||||
env,
|
||||
function_value,
|
||||
evaluator,
|
||||
&evaluator_arguments,
|
||||
*return_layout,
|
||||
);
|
||||
|
||||
builder.build_store(output, call_result);
|
||||
} else {
|
||||
let call_result = call_roc_function(env, evaluator, return_layout, &evaluator_arguments);
|
||||
|
||||
if return_layout.is_passed_by_reference(env.target_info) {
|
||||
let align_bytes = return_layout.alignment_bytes(env.target_info);
|
||||
|
||||
if align_bytes > 0 {
|
||||
let size = env
|
||||
.ptr_int()
|
||||
.const_int(return_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
env.builder
|
||||
.build_memcpy(
|
||||
output,
|
||||
align_bytes,
|
||||
call_result.into_pointer_value(),
|
||||
align_bytes,
|
||||
size,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
} else {
|
||||
builder.build_store(output, call_result);
|
||||
}
|
||||
|
||||
LlvmBackendMode::Binary => {
|
||||
let call_result =
|
||||
call_roc_function(env, evaluator, return_layout, &evaluator_arguments);
|
||||
|
||||
if return_layout.is_passed_by_reference(env.target_info) {
|
||||
let align_bytes = return_layout.alignment_bytes(env.target_info);
|
||||
|
||||
if align_bytes > 0 {
|
||||
let size = env
|
||||
.ptr_int()
|
||||
.const_int(return_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
env.builder
|
||||
.build_memcpy(
|
||||
output,
|
||||
align_bytes,
|
||||
call_result.into_pointer_value(),
|
||||
align_bytes,
|
||||
size,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
} else {
|
||||
builder.build_store(output, call_result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
builder.build_return(None);
|
||||
|
|
|
@ -7,7 +7,7 @@ use inkwell::values::BasicValue;
|
|||
use inkwell::AddressSpace;
|
||||
use roc_builtins::bitcode;
|
||||
|
||||
use super::build::{get_sjlj_buffer, LLVM_LONGJMP};
|
||||
use super::build::{get_sjlj_buffer, LlvmBackendMode, LLVM_LONGJMP};
|
||||
|
||||
/// Define functions for roc_alloc, roc_realloc, and roc_dealloc
|
||||
/// which use libc implementations (malloc, realloc, and free)
|
||||
|
@ -19,129 +19,134 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
|
|||
let usize_type = env.ptr_int();
|
||||
let i8_ptr_type = ctx.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
// roc_alloc
|
||||
{
|
||||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_alloc").unwrap();
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let size_arg = params.next().unwrap();
|
||||
let _alignment_arg = params.next().unwrap();
|
||||
match env.mode {
|
||||
LlvmBackendMode::Binary => { /* externs are provided by the platform */ }
|
||||
LlvmBackendMode::WasmGenTest => { /* externs are provided by the wasm test platform */ }
|
||||
LlvmBackendMode::GenTest => {
|
||||
// roc_alloc
|
||||
{
|
||||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_alloc").unwrap();
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let size_arg = params.next().unwrap();
|
||||
let _alignment_arg = params.next().unwrap();
|
||||
|
||||
debug_assert!(params.next().is_none());
|
||||
debug_assert!(params.next().is_none());
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||
// Add a basic block for the entry point
|
||||
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
builder.position_at_end(entry);
|
||||
|
||||
// Call libc malloc()
|
||||
let retval = builder
|
||||
.build_array_malloc(ctx.i8_type(), size_arg.into_int_value(), "call_malloc")
|
||||
.unwrap();
|
||||
// Call libc malloc()
|
||||
let retval = builder
|
||||
.build_array_malloc(ctx.i8_type(), size_arg.into_int_value(), "call_malloc")
|
||||
.unwrap();
|
||||
|
||||
builder.build_return(Some(&retval));
|
||||
builder.build_return(Some(&retval));
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
}
|
||||
|
||||
// roc_realloc
|
||||
{
|
||||
let libc_realloc_val = {
|
||||
let fn_spec = FunctionSpec::cconv(
|
||||
env,
|
||||
CCReturn::Return,
|
||||
Some(i8_ptr_type.as_basic_type_enum()),
|
||||
&[
|
||||
// ptr: *void
|
||||
i8_ptr_type.into(),
|
||||
// size: usize
|
||||
usize_type.into(),
|
||||
],
|
||||
);
|
||||
let fn_val = add_func(env.context, module, "realloc", fn_spec, Linkage::External);
|
||||
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let ptr_arg = params.next().unwrap();
|
||||
let size_arg = params.next().unwrap();
|
||||
|
||||
debug_assert!(params.next().is_none());
|
||||
|
||||
ptr_arg.set_name("ptr");
|
||||
size_arg.set_name("size");
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
}
|
||||
|
||||
fn_val
|
||||
};
|
||||
// roc_realloc
|
||||
{
|
||||
let libc_realloc_val = {
|
||||
let fn_spec = FunctionSpec::cconv(
|
||||
env,
|
||||
CCReturn::Return,
|
||||
Some(i8_ptr_type.as_basic_type_enum()),
|
||||
&[
|
||||
// ptr: *void
|
||||
i8_ptr_type.into(),
|
||||
// size: usize
|
||||
usize_type.into(),
|
||||
],
|
||||
);
|
||||
let fn_val =
|
||||
add_func(env.context, module, "realloc", fn_spec, Linkage::External);
|
||||
|
||||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_realloc").unwrap();
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let ptr_arg = params.next().unwrap();
|
||||
let new_size_arg = params.next().unwrap();
|
||||
let _old_size_arg = params.next().unwrap();
|
||||
let _alignment_arg = params.next().unwrap();
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let ptr_arg = params.next().unwrap();
|
||||
let size_arg = params.next().unwrap();
|
||||
|
||||
debug_assert!(params.next().is_none());
|
||||
debug_assert!(params.next().is_none());
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||
ptr_arg.set_name("ptr");
|
||||
size_arg.set_name("size");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
|
||||
// Call libc realloc()
|
||||
let call = builder.build_call(
|
||||
libc_realloc_val,
|
||||
&[ptr_arg.into(), new_size_arg.into()],
|
||||
"call_libc_realloc",
|
||||
);
|
||||
fn_val
|
||||
};
|
||||
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_realloc").unwrap();
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let ptr_arg = params.next().unwrap();
|
||||
let new_size_arg = params.next().unwrap();
|
||||
let _old_size_arg = params.next().unwrap();
|
||||
let _alignment_arg = params.next().unwrap();
|
||||
|
||||
let retval = call.try_as_basic_value().left().unwrap();
|
||||
debug_assert!(params.next().is_none());
|
||||
|
||||
builder.build_return(Some(&retval));
|
||||
// Add a basic block for the entry point
|
||||
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
builder.position_at_end(entry);
|
||||
|
||||
// Call libc realloc()
|
||||
let call = builder.build_call(
|
||||
libc_realloc_val,
|
||||
&[ptr_arg.into(), new_size_arg.into()],
|
||||
"call_libc_realloc",
|
||||
);
|
||||
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
|
||||
let retval = call.try_as_basic_value().left().unwrap();
|
||||
|
||||
builder.build_return(Some(&retval));
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
}
|
||||
|
||||
// roc_dealloc
|
||||
{
|
||||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_dealloc").unwrap();
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let ptr_arg = params.next().unwrap();
|
||||
let _alignment_arg = params.next().unwrap();
|
||||
|
||||
debug_assert!(params.next().is_none());
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
// Call libc free()
|
||||
builder.build_free(ptr_arg.into_pointer_value());
|
||||
|
||||
builder.build_return(None);
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
}
|
||||
|
||||
add_sjlj_roc_panic(env)
|
||||
}
|
||||
}
|
||||
|
||||
// roc_dealloc
|
||||
{
|
||||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_dealloc").unwrap();
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let ptr_arg = params.next().unwrap();
|
||||
let _alignment_arg = params.next().unwrap();
|
||||
|
||||
debug_assert!(params.next().is_none());
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
// Call libc free()
|
||||
builder.build_free(ptr_arg.into_pointer_value());
|
||||
|
||||
builder.build_return(None);
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
}
|
||||
|
||||
if env.is_gen_test {
|
||||
add_sjlj_roc_panic(env)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) {
|
||||
|
|
|
@ -8958,6 +8958,7 @@ where
|
|||
ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy,
|
||||
{
|
||||
match lambda_set.runtime_representation() {
|
||||
Layout::VOID => empty_lambda_set_error(),
|
||||
Layout::Union(union_layout) => {
|
||||
let closure_tag_id_symbol = env.unique_symbol();
|
||||
|
||||
|
@ -9117,6 +9118,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn empty_lambda_set_error() -> Stmt<'static> {
|
||||
let msg = "a Lambda Set is empty. Most likely there is a type error in your program.";
|
||||
Stmt::RuntimeError(msg)
|
||||
}
|
||||
|
||||
/// Use the lambda set to figure out how to make a call-by-name
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn match_on_lambda_set<'a>(
|
||||
|
@ -9131,6 +9137,7 @@ fn match_on_lambda_set<'a>(
|
|||
hole: &'a Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
match lambda_set.runtime_representation() {
|
||||
Layout::VOID => empty_lambda_set_error(),
|
||||
Layout::Union(union_layout) => {
|
||||
let closure_tag_id_symbol = env.unique_symbol();
|
||||
|
||||
|
@ -9260,9 +9267,7 @@ fn union_lambda_set_to_switch<'a>(
|
|||
// there is really nothing we can do here. We generate a runtime error here which allows
|
||||
// code gen to proceed. We then assume that we hit another (more descriptive) error before
|
||||
// hitting this one
|
||||
|
||||
let msg = "a Lambda Set is empty. Most likely there is a type error in your program.";
|
||||
return Stmt::RuntimeError(msg);
|
||||
return empty_lambda_set_error();
|
||||
}
|
||||
|
||||
let join_point_id = JoinPointId(env.unique_symbol());
|
||||
|
|
|
@ -6,7 +6,7 @@ pub use helpers::platform_functions::*;
|
|||
|
||||
use bumpalo::Bump;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use roc_gen_llvm::{run_roc::RocCallResult, run_roc_dylib};
|
||||
use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult, run_roc_dylib};
|
||||
use roc_mono::ir::OptLevel;
|
||||
use roc_std::RocList;
|
||||
|
||||
|
@ -50,7 +50,7 @@ fn roc_function<'a, 'b>(
|
|||
source: &str,
|
||||
) -> libloading::Symbol<'a, Main<&'b Input, Output>> {
|
||||
let config = helpers::llvm::HelperConfig {
|
||||
is_gen_test: true,
|
||||
mode: LlvmBackendMode::GenTest,
|
||||
ignore_problems: false,
|
||||
add_debug_info: true,
|
||||
opt_level: OptLevel::Optimize,
|
||||
|
|
|
@ -240,26 +240,5 @@ macro_rules! assert_evals_to {
|
|||
};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_expect_failed {
|
||||
($src:expr, $expected:expr, $ty:ty, $failures:expr) => {{
|
||||
use bumpalo::Bump;
|
||||
use roc_gen_dev::run_jit_function_raw;
|
||||
let stdlib = roc_builtins::std::standard_stdlib();
|
||||
|
||||
let arena = Bump::new();
|
||||
let (main_fn_name, errors, lib) =
|
||||
$crate::helpers::dev::helper(&arena, $src, stdlib, true, true);
|
||||
|
||||
let transform = |success| {
|
||||
let expected = $expected;
|
||||
assert_eq!(&success, &expected);
|
||||
};
|
||||
run_jit_function_raw!(lib, main_fn_name, $ty, transform, errors);
|
||||
}};
|
||||
}
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_evals_to;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_expect_failed;
|
||||
|
|
|
@ -4,6 +4,7 @@ use libloading::Library;
|
|||
use roc_build::link::llvm_module_to_dylib;
|
||||
use roc_build::program::FunctionIterator;
|
||||
use roc_collections::all::MutSet;
|
||||
use roc_gen_llvm::llvm::build::LlvmBackendMode;
|
||||
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||
use roc_load::Threading;
|
||||
use roc_mono::ir::OptLevel;
|
||||
|
@ -11,6 +12,12 @@ use roc_region::all::LineInfo;
|
|||
use roc_reporting::report::RenderTarget;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
pub const OPT_LEVEL: OptLevel = if cfg!(debug_assertions) {
|
||||
OptLevel::Normal
|
||||
} else {
|
||||
OptLevel::Optimize
|
||||
};
|
||||
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
let mut buffer = String::from("app \"test\" provides [main] to \"./platform\"\n\nmain =\n");
|
||||
|
||||
|
@ -28,11 +35,9 @@ fn promote_expr_to_module(src: &str) -> String {
|
|||
fn create_llvm_module<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
src: &str,
|
||||
is_gen_test: bool,
|
||||
ignore_problems: bool,
|
||||
config: HelperConfig,
|
||||
context: &'a inkwell::context::Context,
|
||||
target: &Triple,
|
||||
opt_level: OptLevel,
|
||||
) -> (&'static str, String, &'a Module<'a>) {
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
|
@ -149,7 +154,7 @@ fn create_llvm_module<'a>(
|
|||
println!("{}", lines.join("\n"));
|
||||
|
||||
// only crash at this point if there were no delayed_errors
|
||||
if delayed_errors.is_empty() && !ignore_problems {
|
||||
if delayed_errors.is_empty() && !config.ignore_problems {
|
||||
assert_eq!(0, 1, "Mistakes were made");
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +164,7 @@ fn create_llvm_module<'a>(
|
|||
|
||||
let module = arena.alloc(module);
|
||||
let (module_pass, function_pass) =
|
||||
roc_gen_llvm::llvm::build::construct_optimization_passes(module, opt_level);
|
||||
roc_gen_llvm::llvm::build::construct_optimization_passes(module, config.opt_level);
|
||||
|
||||
let (dibuilder, compile_unit) = roc_gen_llvm::llvm::build::Env::new_debug_info(module);
|
||||
|
||||
|
@ -200,7 +205,7 @@ fn create_llvm_module<'a>(
|
|||
interns,
|
||||
module,
|
||||
target_info,
|
||||
is_gen_test,
|
||||
mode: config.mode,
|
||||
// important! we don't want any procedures to get the C calling convention
|
||||
exposed_to_host: MutSet::default(),
|
||||
};
|
||||
|
@ -214,7 +219,7 @@ fn create_llvm_module<'a>(
|
|||
|
||||
let (main_fn_name, main_fn) = roc_gen_llvm::llvm::build::build_procedures_return_main(
|
||||
&env,
|
||||
opt_level,
|
||||
config.opt_level,
|
||||
procedures,
|
||||
entry_point,
|
||||
);
|
||||
|
@ -248,7 +253,7 @@ fn create_llvm_module<'a>(
|
|||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct HelperConfig {
|
||||
pub is_gen_test: bool,
|
||||
pub mode: LlvmBackendMode,
|
||||
pub ignore_problems: bool,
|
||||
pub add_debug_info: bool,
|
||||
pub opt_level: OptLevel,
|
||||
|
@ -264,15 +269,8 @@ pub fn helper<'a>(
|
|||
) -> (&'static str, String, Library) {
|
||||
let target = target_lexicon::Triple::host();
|
||||
|
||||
let (main_fn_name, delayed_errors, module) = create_llvm_module(
|
||||
arena,
|
||||
src,
|
||||
config.is_gen_test,
|
||||
config.ignore_problems,
|
||||
context,
|
||||
&target,
|
||||
config.opt_level,
|
||||
);
|
||||
let (main_fn_name, delayed_errors, module) =
|
||||
create_llvm_module(arena, src, config, context, &target);
|
||||
|
||||
let res_lib = if config.add_debug_info {
|
||||
let module = annotate_with_debug_info(module, context);
|
||||
|
@ -338,29 +336,14 @@ fn wasm32_target_tripple() -> Triple {
|
|||
#[allow(dead_code)]
|
||||
pub fn helper_wasm<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
config: HelperConfig,
|
||||
src: &str,
|
||||
_is_gen_test: bool,
|
||||
ignore_problems: bool,
|
||||
context: &'a inkwell::context::Context,
|
||||
) -> wasmer::Instance {
|
||||
let target = wasm32_target_tripple();
|
||||
|
||||
let opt_level = if cfg!(debug_assertions) {
|
||||
OptLevel::Normal
|
||||
} else {
|
||||
OptLevel::Optimize
|
||||
};
|
||||
|
||||
let is_gen_test = false;
|
||||
let (_main_fn_name, _delayed_errors, llvm_module) = create_llvm_module(
|
||||
arena,
|
||||
src,
|
||||
is_gen_test,
|
||||
ignore_problems,
|
||||
context,
|
||||
&target,
|
||||
opt_level,
|
||||
);
|
||||
let (_main_fn_name, _delayed_errors, llvm_module) =
|
||||
create_llvm_module(arena, src, config, context, &target);
|
||||
|
||||
use inkwell::targets::{InitializationConfig, Target, TargetTriple};
|
||||
|
||||
|
@ -497,10 +480,14 @@ where
|
|||
let arena = bumpalo::Bump::new();
|
||||
let context = inkwell::context::Context::create();
|
||||
|
||||
let is_gen_test = true;
|
||||
let instance =
|
||||
crate::helpers::llvm::helper_wasm(&arena, src, is_gen_test, ignore_problems, &context);
|
||||
let config = HelperConfig {
|
||||
mode: LlvmBackendMode::WasmGenTest,
|
||||
add_debug_info: false,
|
||||
ignore_problems,
|
||||
opt_level: OPT_LEVEL,
|
||||
};
|
||||
|
||||
let instance = crate::helpers::llvm::helper_wasm(&arena, config, src, &context);
|
||||
let memory = instance.exports.get_memory("memory").unwrap();
|
||||
|
||||
crate::helpers::llvm::MEMORY.with(|f| {
|
||||
|
@ -557,23 +544,17 @@ macro_rules! assert_llvm_evals_to {
|
|||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => {
|
||||
use bumpalo::Bump;
|
||||
use inkwell::context::Context;
|
||||
use roc_gen_llvm::llvm::build::LlvmBackendMode;
|
||||
use roc_gen_llvm::run_jit_function;
|
||||
use roc_mono::ir::OptLevel;
|
||||
|
||||
let arena = Bump::new();
|
||||
let context = Context::create();
|
||||
|
||||
let opt_level = if cfg!(debug_assertions) {
|
||||
OptLevel::Normal
|
||||
} else {
|
||||
OptLevel::Optimize
|
||||
};
|
||||
|
||||
let config = $crate::helpers::llvm::HelperConfig {
|
||||
is_gen_test: true,
|
||||
mode: LlvmBackendMode::GenTest,
|
||||
add_debug_info: false,
|
||||
ignore_problems: $ignore_problems,
|
||||
opt_level,
|
||||
opt_level: $crate::helpers::llvm::OPT_LEVEL,
|
||||
};
|
||||
|
||||
let (main_fn_name, errors, lib) =
|
||||
|
@ -633,43 +614,6 @@ macro_rules! assert_evals_to {
|
|||
}};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_expect_failed {
|
||||
($src:expr, $expected:expr, $ty:ty) => {
|
||||
use bumpalo::Bump;
|
||||
use inkwell::context::Context;
|
||||
use roc_gen_llvm::run_jit_function;
|
||||
|
||||
let arena = Bump::new();
|
||||
let context = Context::create();
|
||||
|
||||
let is_gen_test = true;
|
||||
let (main_fn_name, errors, lib) =
|
||||
$crate::helpers::llvm::helper(&arena, $src, is_gen_test, false, &context);
|
||||
|
||||
let transform = |success| {
|
||||
let expected = $expected;
|
||||
assert_eq!(&success, &expected, "LLVM test failed");
|
||||
};
|
||||
|
||||
run_jit_function!(lib, main_fn_name, $ty, transform, errors)
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty) => {
|
||||
$crate::helpers::llvm::assert_llvm_evals_to!(
|
||||
$src,
|
||||
$expected,
|
||||
$ty,
|
||||
$crate::helpers::llvm::identity,
|
||||
false
|
||||
);
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
$crate::helpers::llvm::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! expect_runtime_error_panic {
|
||||
($src:expr) => {{
|
||||
#[cfg(feature = "wasm-cli-run")]
|
||||
|
@ -720,8 +664,6 @@ macro_rules! assert_non_opt_evals_to {
|
|||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_evals_to;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_expect_failed;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_llvm_evals_to;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_non_opt_evals_to;
|
||||
|
|
|
@ -5,7 +5,6 @@ procedure List.5 (#Attr.2, #Attr.3):
|
|||
|
||||
procedure Test.2 (Test.3):
|
||||
let Test.7 : {} = Struct {};
|
||||
let Test.8 : U8 = GetTagId Test.3;
|
||||
Error a Lambda Set is empty. Most likely there is a type error in your program.
|
||||
|
||||
procedure Test.0 ():
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue