mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
improve how we generate the mainForHost function
This commit is contained in:
parent
5b5b9e5eb8
commit
7e28d557ec
1 changed files with 134 additions and 132 deletions
|
@ -3554,6 +3554,93 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx, 'env>(
|
||||||
c_function
|
c_function
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expose_function_to_host_help_c_abi_xx<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
roc_function: FunctionValue<'ctx>,
|
||||||
|
arguments: &[Layout<'a>],
|
||||||
|
return_layout: Layout<'a>,
|
||||||
|
c_function_name: &str,
|
||||||
|
) -> FunctionValue<'ctx> {
|
||||||
|
let it = arguments.iter().map(|l| basic_type_from_layout(env, l));
|
||||||
|
let argument_types = Vec::from_iter_in(it, env.arena);
|
||||||
|
let return_type = basic_type_from_layout(env, &return_layout);
|
||||||
|
|
||||||
|
let cc_return = to_cc_return(env, &return_layout);
|
||||||
|
let roc_return = RocReturn::from_layout(env, &return_layout);
|
||||||
|
|
||||||
|
let c_function_type = cc_return.to_signature(env, return_type, argument_types.as_slice());
|
||||||
|
|
||||||
|
let c_function = add_func(
|
||||||
|
env.module,
|
||||||
|
c_function_name,
|
||||||
|
c_function_type,
|
||||||
|
Linkage::External,
|
||||||
|
C_CALL_CONV,
|
||||||
|
);
|
||||||
|
|
||||||
|
let subprogram = env.new_subprogram(c_function_name);
|
||||||
|
c_function.set_subprogram(subprogram);
|
||||||
|
|
||||||
|
// STEP 2: build the exposed function's body
|
||||||
|
let builder = env.builder;
|
||||||
|
let context = env.context;
|
||||||
|
|
||||||
|
let entry = context.append_basic_block(c_function, "entry");
|
||||||
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
|
let params = c_function.get_params();
|
||||||
|
|
||||||
|
let param_types = Vec::from_iter_in(roc_function.get_type().get_param_types(), env.arena);
|
||||||
|
|
||||||
|
// drop the "return pointer" if it exists on the roc function
|
||||||
|
// and the c function does not return via pointer
|
||||||
|
let param_types = match (&roc_return, &cc_return) {
|
||||||
|
(RocReturn::ByPointer, CCReturn::Return) => ¶m_types[1..],
|
||||||
|
_ => ¶m_types,
|
||||||
|
};
|
||||||
|
|
||||||
|
debug_assert_eq!(params.len(), param_types.len());
|
||||||
|
|
||||||
|
let it = params.iter().zip(param_types).map(|(arg, fastcc_type)| {
|
||||||
|
let arg_type = arg.get_type();
|
||||||
|
if arg_type == *fastcc_type {
|
||||||
|
// the C and Fast calling conventions agree
|
||||||
|
*arg
|
||||||
|
} else {
|
||||||
|
complex_bitcast_check_size(env, *arg, *fastcc_type, "to_fastcc_type")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let arguments = Vec::from_iter_in(it, env.arena);
|
||||||
|
|
||||||
|
let value = call_roc_function(env, roc_function, &return_layout, arguments.as_slice());
|
||||||
|
|
||||||
|
match cc_return {
|
||||||
|
CCReturn::Return => match roc_return {
|
||||||
|
RocReturn::Return => {
|
||||||
|
env.builder.build_return(Some(&value));
|
||||||
|
}
|
||||||
|
RocReturn::ByPointer => {
|
||||||
|
let loaded = env
|
||||||
|
.builder
|
||||||
|
.build_load(value.into_pointer_value(), "load_result");
|
||||||
|
env.builder.build_return(Some(&loaded));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CCReturn::ByPointer => {
|
||||||
|
let out_ptr = c_function.get_nth_param(0).unwrap().into_pointer_value();
|
||||||
|
|
||||||
|
env.builder.build_store(out_ptr, value);
|
||||||
|
env.builder.build_return(None);
|
||||||
|
}
|
||||||
|
CCReturn::Void => {
|
||||||
|
env.builder.build_return(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c_function
|
||||||
|
}
|
||||||
|
|
||||||
fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>(
|
fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
ident_string: &str,
|
ident_string: &str,
|
||||||
|
@ -3582,122 +3669,14 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>(
|
||||||
&format!("{}_generic", c_function_name),
|
&format!("{}_generic", c_function_name),
|
||||||
);
|
);
|
||||||
|
|
||||||
let wrapper_return_type = if env.is_gen_test {
|
let c_function = expose_function_to_host_help_c_abi_xx(
|
||||||
roc_result_type(env, roc_function.get_type().get_return_type().unwrap()).into()
|
env,
|
||||||
} else {
|
roc_function,
|
||||||
// roc_function.get_type().get_return_type().unwrap()
|
arguments,
|
||||||
basic_type_from_layout(env, &return_layout)
|
return_layout,
|
||||||
};
|
|
||||||
|
|
||||||
let mut cc_argument_types = Vec::with_capacity_in(arguments.len(), env.arena);
|
|
||||||
for layout in arguments {
|
|
||||||
cc_argument_types.push(to_cc_type(env, layout));
|
|
||||||
}
|
|
||||||
|
|
||||||
// STEP 1: turn `f : a,b,c -> d` into `f : a,b,c, &d -> {}` if the C abi demands it
|
|
||||||
let mut argument_types = cc_argument_types;
|
|
||||||
let return_type = wrapper_return_type;
|
|
||||||
|
|
||||||
let cc_return = to_cc_return(env, &return_layout);
|
|
||||||
|
|
||||||
let c_function_type = match cc_return {
|
|
||||||
CCReturn::Void => env
|
|
||||||
.context
|
|
||||||
.void_type()
|
|
||||||
.fn_type(&function_arguments(env, &argument_types), false),
|
|
||||||
CCReturn::Return => return_type.fn_type(&function_arguments(env, &argument_types), false),
|
|
||||||
CCReturn::ByPointer => {
|
|
||||||
let output_type = return_type.ptr_type(AddressSpace::Generic);
|
|
||||||
argument_types.push(output_type.into());
|
|
||||||
env.context
|
|
||||||
.void_type()
|
|
||||||
.fn_type(&function_arguments(env, &argument_types), false)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let c_function = add_func(
|
|
||||||
env.module,
|
|
||||||
c_function_name,
|
c_function_name,
|
||||||
c_function_type,
|
|
||||||
Linkage::External,
|
|
||||||
C_CALL_CONV,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let subprogram = env.new_subprogram(c_function_name);
|
|
||||||
c_function.set_subprogram(subprogram);
|
|
||||||
|
|
||||||
// STEP 2: build the exposed function's body
|
|
||||||
let builder = env.builder;
|
|
||||||
let context = env.context;
|
|
||||||
|
|
||||||
let entry = context.append_basic_block(c_function, "entry");
|
|
||||||
|
|
||||||
builder.position_at_end(entry);
|
|
||||||
|
|
||||||
debug_info_init!(env, c_function);
|
|
||||||
|
|
||||||
// drop the final argument, which is the pointer we write the result into
|
|
||||||
let args_vector = c_function.get_params();
|
|
||||||
let mut args = args_vector.as_slice();
|
|
||||||
let args_length = args.len();
|
|
||||||
|
|
||||||
match cc_return {
|
|
||||||
CCReturn::Return => {
|
|
||||||
debug_assert_eq!(args.len(), roc_function.get_params().len());
|
|
||||||
}
|
|
||||||
CCReturn::Void => {
|
|
||||||
debug_assert_eq!(args.len(), roc_function.get_params().len());
|
|
||||||
}
|
|
||||||
CCReturn::ByPointer => match RocReturn::from_layout(env, &return_layout) {
|
|
||||||
RocReturn::ByPointer => {
|
|
||||||
debug_assert_eq!(args.len(), roc_function.get_params().len());
|
|
||||||
}
|
|
||||||
RocReturn::Return => {
|
|
||||||
args = &args[..args.len() - 1];
|
|
||||||
debug_assert_eq!(args.len(), roc_function.get_params().len());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut arguments_for_call = Vec::with_capacity_in(args.len(), env.arena);
|
|
||||||
|
|
||||||
let it = args.iter().zip(roc_function.get_type().get_param_types());
|
|
||||||
for (arg, fastcc_type) in it {
|
|
||||||
let arg_type = arg.get_type();
|
|
||||||
if arg_type == fastcc_type {
|
|
||||||
// the C and Fast calling conventions agree
|
|
||||||
arguments_for_call.push(*arg);
|
|
||||||
} else {
|
|
||||||
let cast = complex_bitcast_check_size(env, *arg, fastcc_type, "to_fastcc_type");
|
|
||||||
arguments_for_call.push(cast);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let arguments_for_call = arguments_for_call.into_bump_slice();
|
|
||||||
|
|
||||||
let call_result = call_roc_function(env, roc_function, &return_layout, arguments_for_call);
|
|
||||||
|
|
||||||
match cc_return {
|
|
||||||
CCReturn::Void => {
|
|
||||||
// TODO return empty struct here?
|
|
||||||
builder.build_return(None);
|
|
||||||
}
|
|
||||||
CCReturn::Return => {
|
|
||||||
builder.build_return(Some(&call_result));
|
|
||||||
}
|
|
||||||
CCReturn::ByPointer => {
|
|
||||||
let output_arg_index = args_length - 1;
|
|
||||||
|
|
||||||
let output_arg = c_function
|
|
||||||
.get_nth_param(output_arg_index as u32)
|
|
||||||
.unwrap()
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
builder.build_store(output_arg, call_result);
|
|
||||||
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
|
||||||
let size_function_type = env.context.i64_type().fn_type(&[], false);
|
let size_function_type = env.context.i64_type().fn_type(&[], false);
|
||||||
let size_function_name: String = format!("roc__{}_size", ident_string);
|
let size_function_name: String = format!("roc__{}_size", ident_string);
|
||||||
|
@ -3713,14 +3692,21 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>(
|
||||||
let subprogram = env.new_subprogram(&size_function_name);
|
let subprogram = env.new_subprogram(&size_function_name);
|
||||||
size_function.set_subprogram(subprogram);
|
size_function.set_subprogram(subprogram);
|
||||||
|
|
||||||
let entry = context.append_basic_block(size_function, "entry");
|
let entry = env.context.append_basic_block(size_function, "entry");
|
||||||
|
|
||||||
builder.position_at_end(entry);
|
env.builder.position_at_end(entry);
|
||||||
|
|
||||||
debug_info_init!(env, size_function);
|
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 size: BasicValueEnum = return_type.size_of().unwrap().into();
|
let size: BasicValueEnum = return_type.size_of().unwrap().into();
|
||||||
builder.build_return(Some(&size));
|
env.builder.build_return(Some(&size));
|
||||||
|
|
||||||
c_function
|
c_function
|
||||||
}
|
}
|
||||||
|
@ -6181,6 +6167,7 @@ impl RocReturn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
enum CCReturn {
|
enum CCReturn {
|
||||||
/// Return as normal
|
/// Return as normal
|
||||||
Return,
|
Return,
|
||||||
|
@ -6192,6 +6179,36 @@ enum CCReturn {
|
||||||
Void,
|
Void,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CCReturn {
|
||||||
|
fn to_signature<'a, 'ctx, 'env>(
|
||||||
|
&self,
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
return_type: BasicTypeEnum<'ctx>,
|
||||||
|
argument_types: &[BasicTypeEnum<'ctx>],
|
||||||
|
) -> FunctionType<'ctx> {
|
||||||
|
match self {
|
||||||
|
CCReturn::ByPointer => {
|
||||||
|
// turn the output type into a pointer type. Make it the first argument to the function
|
||||||
|
let output_type = return_type.ptr_type(AddressSpace::Generic);
|
||||||
|
let mut arguments: Vec<'_, BasicTypeEnum> =
|
||||||
|
bumpalo::vec![in env.arena; output_type.into()];
|
||||||
|
arguments.extend(argument_types);
|
||||||
|
|
||||||
|
let arguments = function_arguments(env, &arguments);
|
||||||
|
env.context.void_type().fn_type(&arguments, false)
|
||||||
|
}
|
||||||
|
CCReturn::Return => {
|
||||||
|
let arguments = function_arguments(env, argument_types);
|
||||||
|
return_type.fn_type(&arguments, false)
|
||||||
|
}
|
||||||
|
CCReturn::Void => {
|
||||||
|
let arguments = function_arguments(env, argument_types);
|
||||||
|
env.context.void_type().fn_type(&arguments, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// According to the C ABI, how should we return a value with the given layout?
|
/// According to the C ABI, how should we return a value with the given layout?
|
||||||
fn to_cc_return<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>) -> CCReturn {
|
fn to_cc_return<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>) -> CCReturn {
|
||||||
let return_size = layout.stack_size(env.ptr_bytes);
|
let return_size = layout.stack_size(env.ptr_bytes);
|
||||||
|
@ -6268,22 +6285,7 @@ fn build_foreign_symbol<'a, 'ctx, 'env>(
|
||||||
arguments.push(value);
|
arguments.push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let cc_type = match cc_return {
|
let cc_type = cc_return.to_signature(env, return_type, cc_argument_types.as_slice());
|
||||||
CCReturn::Void => env
|
|
||||||
.context
|
|
||||||
.void_type()
|
|
||||||
.fn_type(&function_arguments(env, &cc_argument_types), false),
|
|
||||||
CCReturn::ByPointer => {
|
|
||||||
cc_argument_types.insert(0, return_type.ptr_type(AddressSpace::Generic).into());
|
|
||||||
env.context
|
|
||||||
.void_type()
|
|
||||||
.fn_type(&function_arguments(env, &cc_argument_types), false)
|
|
||||||
}
|
|
||||||
CCReturn::Return => {
|
|
||||||
return_type.fn_type(&function_arguments(env, &cc_argument_types), false)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let cc_function = get_foreign_symbol(env, foreign.clone(), cc_type);
|
let cc_function = get_foreign_symbol(env, foreign.clone(), cc_type);
|
||||||
|
|
||||||
let fastcc_type = match roc_return {
|
let fastcc_type = match roc_return {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue