mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-25 04:52:32 +00:00
Update test function catcher to take records by reference
This commit is contained in:
parent
de44e90fda
commit
7b1dc7eff0
2 changed files with 174 additions and 116 deletions
|
@ -2466,89 +2466,10 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
||||||
Ret(symbol) => {
|
Ret(symbol) => {
|
||||||
let (value, layout) = scope.load_symbol_and_layout(symbol);
|
let (value, layout) = scope.load_symbol_and_layout(symbol);
|
||||||
|
|
||||||
match RocReturn::from_layout(layout_interner, layout) {
|
build_return(env, layout_interner, layout, value, parent);
|
||||||
RocReturn::Return => {
|
|
||||||
if let Some(block) = env.builder.get_insert_block() {
|
|
||||||
if block.get_terminator().is_none() {
|
|
||||||
env.builder.build_return(Some(&value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
RocReturn::ByPointer => {
|
|
||||||
// we need to write our value into the final argument of the current function
|
|
||||||
let parameters = parent.get_params();
|
|
||||||
let out_parameter = parameters.last().unwrap();
|
|
||||||
debug_assert!(out_parameter.is_pointer_value());
|
|
||||||
|
|
||||||
// store_roc_value(env, *layout, out_parameter.into_pointer_value(), value);
|
|
||||||
|
|
||||||
let destination = out_parameter.into_pointer_value();
|
|
||||||
if layout_interner.is_passed_by_reference(layout) {
|
|
||||||
let align_bytes = layout_interner.alignment_bytes(layout);
|
|
||||||
|
|
||||||
if align_bytes > 0 {
|
|
||||||
debug_assert!(
|
|
||||||
value.is_pointer_value(),
|
|
||||||
"{:?}: {:?}\n{:?}",
|
|
||||||
parent.get_name(),
|
|
||||||
value,
|
|
||||||
layout
|
|
||||||
);
|
|
||||||
|
|
||||||
// What we want to do here is
|
|
||||||
//
|
|
||||||
// let value_ptr = value.into_pointer_value();
|
|
||||||
// if value_ptr.get_first_use().is_some() {
|
|
||||||
// value_ptr.replace_all_uses_with(destination);
|
|
||||||
//
|
|
||||||
// In other words, if the source pointer is used,
|
|
||||||
// then we just subsitute the source for the input pointer, done.
|
|
||||||
//
|
|
||||||
// Only that does not work if the source is not written to.
|
|
||||||
// A simple example is the identity function
|
|
||||||
//
|
|
||||||
// A slightly more complex case that will also make the above not
|
|
||||||
// work is when the source pointer is only incremented, but not
|
|
||||||
// written to. Then there is a first_use, but it's still invalid to
|
|
||||||
// subsitute source with destination
|
|
||||||
//
|
|
||||||
// Hence, we explicitly memcpy source to destination, and rely on
|
|
||||||
// LLVM optimizing away any inefficiencies.
|
|
||||||
let width = layout_interner.stack_size(layout);
|
|
||||||
let size = env.ptr_int().const_int(width as _, false);
|
|
||||||
|
|
||||||
env.builder
|
|
||||||
.build_memcpy(
|
|
||||||
destination,
|
|
||||||
align_bytes,
|
|
||||||
value.into_pointer_value(),
|
|
||||||
align_bytes,
|
|
||||||
size,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
env.builder.build_store(destination, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(block) = env.builder.get_insert_block() {
|
|
||||||
match block.get_terminator() {
|
|
||||||
None => {
|
|
||||||
env.builder.build_return(None);
|
|
||||||
}
|
|
||||||
Some(terminator) => {
|
|
||||||
terminator.remove_from_basic_block();
|
|
||||||
env.builder.build_return(None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
env.context.i8_type().const_zero().into()
|
env.context.i8_type().const_zero().into()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Switch {
|
Switch {
|
||||||
branches,
|
branches,
|
||||||
|
@ -2968,6 +2889,93 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_return<'a, 'ctx>(
|
||||||
|
env: &Env<'a, 'ctx, '_>,
|
||||||
|
layout_interner: &mut STLayoutInterner<'a>,
|
||||||
|
layout: InLayout<'a>,
|
||||||
|
value: BasicValueEnum<'ctx>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
) {
|
||||||
|
match RocReturn::from_layout(layout_interner, layout) {
|
||||||
|
RocReturn::Return => {
|
||||||
|
if let Some(block) = env.builder.get_insert_block() {
|
||||||
|
if block.get_terminator().is_none() {
|
||||||
|
env.builder.build_return(Some(&value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RocReturn::ByPointer => {
|
||||||
|
// we need to write our value into the final argument of the current function
|
||||||
|
let parameters = parent.get_params();
|
||||||
|
let out_parameter = parameters.last().unwrap();
|
||||||
|
debug_assert!(out_parameter.is_pointer_value());
|
||||||
|
|
||||||
|
// store_roc_value(env, *layout, out_parameter.into_pointer_value(), value);
|
||||||
|
|
||||||
|
let destination = out_parameter.into_pointer_value();
|
||||||
|
if layout_interner.is_passed_by_reference(layout) {
|
||||||
|
let align_bytes = layout_interner.alignment_bytes(layout);
|
||||||
|
|
||||||
|
if align_bytes > 0 {
|
||||||
|
debug_assert!(
|
||||||
|
value.is_pointer_value(),
|
||||||
|
"{:?}: {:?}\n{:?}",
|
||||||
|
parent.get_name(),
|
||||||
|
value,
|
||||||
|
layout
|
||||||
|
);
|
||||||
|
|
||||||
|
// What we want to do here is
|
||||||
|
//
|
||||||
|
// let value_ptr = value.into_pointer_value();
|
||||||
|
// if value_ptr.get_first_use().is_some() {
|
||||||
|
// value_ptr.replace_all_uses_with(destination);
|
||||||
|
//
|
||||||
|
// In other words, if the source pointer is used,
|
||||||
|
// then we just subsitute the source for the input pointer, done.
|
||||||
|
//
|
||||||
|
// Only that does not work if the source is not written to.
|
||||||
|
// A simple example is the identity function
|
||||||
|
//
|
||||||
|
// A slightly more complex case that will also make the above not
|
||||||
|
// work is when the source pointer is only incremented, but not
|
||||||
|
// written to. Then there is a first_use, but it's still invalid to
|
||||||
|
// subsitute source with destination
|
||||||
|
//
|
||||||
|
// Hence, we explicitly memcpy source to destination, and rely on
|
||||||
|
// LLVM optimizing away any inefficiencies.
|
||||||
|
let width = layout_interner.stack_size(layout);
|
||||||
|
let size = env.ptr_int().const_int(width as _, false);
|
||||||
|
|
||||||
|
env.builder
|
||||||
|
.build_memcpy(
|
||||||
|
destination,
|
||||||
|
align_bytes,
|
||||||
|
value.into_pointer_value(),
|
||||||
|
align_bytes,
|
||||||
|
size,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
env.builder.build_store(destination, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(block) = env.builder.get_insert_block() {
|
||||||
|
match block.get_terminator() {
|
||||||
|
None => {
|
||||||
|
env.builder.build_return(None);
|
||||||
|
}
|
||||||
|
Some(terminator) => {
|
||||||
|
terminator.remove_from_basic_block();
|
||||||
|
env.builder.build_return(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn equivalent_type_constructors(t1: &BasicTypeEnum, t2: &BasicTypeEnum) -> bool {
|
fn equivalent_type_constructors(t1: &BasicTypeEnum, t2: &BasicTypeEnum) -> bool {
|
||||||
use BasicTypeEnum::*;
|
use BasicTypeEnum::*;
|
||||||
match (t1, t2) {
|
match (t1, t2) {
|
||||||
|
@ -3750,7 +3758,7 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>(
|
||||||
|
|
||||||
let arguments_for_call = &arguments_for_call.into_bump_slice();
|
let arguments_for_call = &arguments_for_call.into_bump_slice();
|
||||||
|
|
||||||
let call_result = {
|
let (call_result, call_result_layout) = {
|
||||||
let last_block = builder.get_insert_block().unwrap();
|
let last_block = builder.get_insert_block().unwrap();
|
||||||
|
|
||||||
let roc_wrapper_function =
|
let roc_wrapper_function =
|
||||||
|
@ -3764,13 +3772,15 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>(
|
||||||
env.target_info,
|
env.target_info,
|
||||||
));
|
));
|
||||||
|
|
||||||
call_roc_function(
|
let roc_value = call_roc_function(
|
||||||
env,
|
env,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
roc_wrapper_function,
|
roc_wrapper_function,
|
||||||
wrapper_result,
|
wrapper_result,
|
||||||
arguments_for_call,
|
arguments_for_call,
|
||||||
)
|
);
|
||||||
|
|
||||||
|
(roc_value, wrapper_result)
|
||||||
};
|
};
|
||||||
|
|
||||||
let output_arg_index = args_length - 1;
|
let output_arg_index = args_length - 1;
|
||||||
|
@ -3780,7 +3790,13 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>(
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_pointer_value();
|
.into_pointer_value();
|
||||||
|
|
||||||
builder.build_store(output_arg, call_result);
|
store_roc_value(
|
||||||
|
env,
|
||||||
|
layout_interner,
|
||||||
|
call_result_layout,
|
||||||
|
output_arg,
|
||||||
|
call_result,
|
||||||
|
);
|
||||||
builder.build_return(None);
|
builder.build_return(None);
|
||||||
|
|
||||||
// STEP 3: build a {} -> u64 function that gives the size of the return type
|
// STEP 3: build a {} -> u64 function that gives the size of the return type
|
||||||
|
@ -4271,14 +4287,23 @@ fn set_jump_and_catch_long_jump<'a, 'ctx>(
|
||||||
env: &Env<'a, 'ctx, '_>,
|
env: &Env<'a, 'ctx, '_>,
|
||||||
layout_interner: &mut STLayoutInterner<'a>,
|
layout_interner: &mut STLayoutInterner<'a>,
|
||||||
parent: FunctionValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
|
// The roc function to call
|
||||||
roc_function: FunctionValue<'ctx>,
|
roc_function: FunctionValue<'ctx>,
|
||||||
arguments: &[BasicValueEnum<'ctx>],
|
roc_arguments: &[BasicValueEnum<'ctx>],
|
||||||
return_layout: InLayout<'a>,
|
roc_return_layout: InLayout<'a>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let context = env.context;
|
let context = env.context;
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
let return_type = basic_type_from_layout(env, layout_interner, return_layout);
|
let return_type = basic_type_from_layout(env, layout_interner, roc_return_layout);
|
||||||
|
let call_result_return_conv = {
|
||||||
|
let layout = layout_interner.insert_direct_no_semantic(roc_call_result_layout(
|
||||||
|
env.arena,
|
||||||
|
roc_return_layout,
|
||||||
|
env.target_info,
|
||||||
|
));
|
||||||
|
RocReturn::from_layout(layout_interner, layout)
|
||||||
|
};
|
||||||
let call_result_type = roc_call_result_type(env, return_type.as_basic_type_enum());
|
let call_result_type = roc_call_result_type(env, return_type.as_basic_type_enum());
|
||||||
let result_alloca = builder.build_alloca(call_result_type, "result");
|
let result_alloca = builder.build_alloca(call_result_type, "result");
|
||||||
|
|
||||||
|
@ -4301,10 +4326,16 @@ fn set_jump_and_catch_long_jump<'a, 'ctx>(
|
||||||
{
|
{
|
||||||
builder.position_at_end(then_block);
|
builder.position_at_end(then_block);
|
||||||
|
|
||||||
let call_result =
|
let call_result = call_roc_function(
|
||||||
call_roc_function(env, layout_interner, roc_function, return_layout, arguments);
|
env,
|
||||||
|
layout_interner,
|
||||||
|
roc_function,
|
||||||
|
roc_return_layout,
|
||||||
|
roc_arguments,
|
||||||
|
);
|
||||||
|
|
||||||
let return_value = make_good_roc_result(env, layout_interner, return_layout, call_result);
|
let return_value =
|
||||||
|
make_good_roc_result(env, layout_interner, roc_return_layout, call_result);
|
||||||
|
|
||||||
builder.build_store(result_alloca, return_value);
|
builder.build_store(result_alloca, return_value);
|
||||||
|
|
||||||
|
@ -4342,11 +4373,14 @@ fn set_jump_and_catch_long_jump<'a, 'ctx>(
|
||||||
|
|
||||||
env.builder.position_at_end(cont_block);
|
env.builder.position_at_end(cont_block);
|
||||||
|
|
||||||
builder.new_build_load(
|
match call_result_return_conv {
|
||||||
|
RocReturn::Return => builder.new_build_load(
|
||||||
call_result_type,
|
call_result_type,
|
||||||
result_alloca,
|
result_alloca,
|
||||||
"set_jump_and_catch_long_jump_load_result",
|
"set_jump_and_catch_long_jump_load_result",
|
||||||
)
|
),
|
||||||
|
RocReturn::ByPointer => result_alloca.into(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_exception_catcher<'a, 'ctx>(
|
fn make_exception_catcher<'a, 'ctx>(
|
||||||
|
@ -4375,11 +4409,12 @@ fn roc_call_result_layout<'a>(
|
||||||
return_layout: InLayout<'a>,
|
return_layout: InLayout<'a>,
|
||||||
target_info: TargetInfo,
|
target_info: TargetInfo,
|
||||||
) -> LayoutRepr<'a> {
|
) -> LayoutRepr<'a> {
|
||||||
let elements = [Layout::U64, Layout::usize(target_info), return_layout];
|
let elements = [Layout::U64, Layout::STR_PTR, return_layout];
|
||||||
|
|
||||||
LayoutRepr::struct_(arena.alloc(elements))
|
LayoutRepr::struct_(arena.alloc(elements))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: coalesce with `roc_call_result_layout`?
|
||||||
fn roc_call_result_type<'ctx>(
|
fn roc_call_result_type<'ctx>(
|
||||||
env: &Env<'_, 'ctx, '_>,
|
env: &Env<'_, 'ctx, '_>,
|
||||||
return_type: BasicTypeEnum<'ctx>,
|
return_type: BasicTypeEnum<'ctx>,
|
||||||
|
@ -4445,12 +4480,25 @@ fn make_exception_catching_wrapper<'a, 'ctx>(
|
||||||
let context = env.context;
|
let context = env.context;
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
|
// TODO: pass these, and the roc function, in directly?
|
||||||
|
let wrapper_return_layout = layout_interner.insert_direct_no_semantic(roc_call_result_layout(
|
||||||
|
env.arena,
|
||||||
|
return_layout,
|
||||||
|
env.target_info,
|
||||||
|
));
|
||||||
|
|
||||||
|
let wrapper_return_type = roc_call_result_type(
|
||||||
|
env,
|
||||||
|
basic_type_from_layout(env, layout_interner, return_layout),
|
||||||
|
);
|
||||||
|
|
||||||
let roc_function_type = roc_function.get_type();
|
let roc_function_type = roc_function.get_type();
|
||||||
let argument_types = match RocReturn::from_layout(layout_interner, return_layout) {
|
let argument_types = match RocReturn::from_layout(layout_interner, return_layout) {
|
||||||
RocReturn::Return => roc_function_type.get_param_types(),
|
RocReturn::Return => roc_function_type.get_param_types(),
|
||||||
RocReturn::ByPointer => {
|
RocReturn::ByPointer => {
|
||||||
// Our fastcc passes the return pointer as the last parameter.
|
// Our fastcc passes the return pointer as the last parameter. Remove it from the
|
||||||
// Remove the return pointer since we now intend to return the result by value.
|
// argument types used for the wrapper, since the wrapper's return type will go here
|
||||||
|
// when we build the wrapper function spec below.
|
||||||
let mut types = roc_function_type.get_param_types();
|
let mut types = roc_function_type.get_param_types();
|
||||||
types.pop();
|
types.pop();
|
||||||
|
|
||||||
|
@ -4458,19 +4506,13 @@ fn make_exception_catching_wrapper<'a, 'ctx>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let wrapper_return_type = roc_call_result_type(
|
let wrapper_return_conv = RocReturn::from_layout(layout_interner, wrapper_return_layout);
|
||||||
env,
|
|
||||||
basic_type_from_layout(env, layout_interner, return_layout),
|
|
||||||
);
|
|
||||||
|
|
||||||
// argument_types.push(wrapper_return_type.ptr_type(AddressSpace::default()).into());
|
let wrapper_function_spec = FunctionSpec::fastcc(
|
||||||
|
|
||||||
// let wrapper_function_type = env.context.void_type().fn_type(&argument_types, false);
|
|
||||||
let wrapper_function_spec = FunctionSpec::cconv(
|
|
||||||
env,
|
env,
|
||||||
CCReturn::Return,
|
wrapper_return_conv,
|
||||||
Some(wrapper_return_type.as_basic_type_enum()),
|
wrapper_return_type.into(),
|
||||||
&argument_types,
|
Vec::from_iter_in(argument_types, env.arena),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add main to the module.
|
// Add main to the module.
|
||||||
|
@ -4485,27 +4527,42 @@ fn make_exception_catching_wrapper<'a, 'ctx>(
|
||||||
let subprogram = env.new_subprogram(wrapper_function_name);
|
let subprogram = env.new_subprogram(wrapper_function_name);
|
||||||
wrapper_function.set_subprogram(subprogram);
|
wrapper_function.set_subprogram(subprogram);
|
||||||
|
|
||||||
// our exposed main function adheres to the C calling convention
|
// The exposed main function must adhere to the C calling convention, but the wrapper can still be fastcc.
|
||||||
wrapper_function.set_call_conventions(FAST_CALL_CONV);
|
wrapper_function.set_call_conventions(FAST_CALL_CONV);
|
||||||
|
|
||||||
// invoke instead of call, so that we can catch any exceptions thrown in Roc code
|
// invoke instead of call, so that we can catch any exceptions thrown in Roc code
|
||||||
let arguments = wrapper_function.get_params();
|
let roc_function_arguments = {
|
||||||
|
let mut params = wrapper_function.get_params();
|
||||||
|
match wrapper_return_conv {
|
||||||
|
RocReturn::Return => { /* passthrough */ }
|
||||||
|
RocReturn::ByPointer => {
|
||||||
|
params.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
params
|
||||||
|
};
|
||||||
|
|
||||||
let basic_block = context.append_basic_block(wrapper_function, "entry");
|
let basic_block = context.append_basic_block(wrapper_function, "entry");
|
||||||
builder.position_at_end(basic_block);
|
builder.position_at_end(basic_block);
|
||||||
|
|
||||||
debug_info_init!(env, wrapper_function);
|
debug_info_init!(env, wrapper_function);
|
||||||
|
|
||||||
let result = set_jump_and_catch_long_jump(
|
let wrapper_return_result = set_jump_and_catch_long_jump(
|
||||||
env,
|
env,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
wrapper_function,
|
wrapper_function,
|
||||||
roc_function,
|
roc_function,
|
||||||
&arguments,
|
&roc_function_arguments,
|
||||||
return_layout,
|
return_layout,
|
||||||
);
|
);
|
||||||
|
|
||||||
builder.build_return(Some(&result));
|
build_return(
|
||||||
|
env,
|
||||||
|
layout_interner,
|
||||||
|
wrapper_return_layout,
|
||||||
|
wrapper_return_result,
|
||||||
|
wrapper_function,
|
||||||
|
);
|
||||||
|
|
||||||
wrapper_function
|
wrapper_function
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,8 @@ impl<'ctx> From<BasicValueEnum<'ctx>> for RocStruct<'ctx> {
|
||||||
fn from(basic_value: BasicValueEnum<'ctx>) -> Self {
|
fn from(basic_value: BasicValueEnum<'ctx>) -> Self {
|
||||||
match basic_value {
|
match basic_value {
|
||||||
BasicValueEnum::StructValue(struct_value) => RocStruct::ByValue(struct_value),
|
BasicValueEnum::StructValue(struct_value) => RocStruct::ByValue(struct_value),
|
||||||
_ => panic!("Expected struct value"),
|
BasicValueEnum::PointerValue(struct_ptr) => RocStruct::ByReference(struct_ptr),
|
||||||
|
_ => panic!("Expected struct or pointer value"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue