mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
add wasm test mode that influences test wrapper codegen
This commit is contained in:
parent
7b308d9efe
commit
cef4dc6b9f
7 changed files with 216 additions and 270 deletions
|
@ -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<'_, '_, '_>) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue