Merge branch 'main' of github.com:roc-lang/roc into clippy-1.74

This commit is contained in:
Anton-4 2023-12-25 17:05:37 +01:00
commit cd632fe549
No known key found for this signature in database
GPG key ID: 0971D718C0A9B937
271 changed files with 7741 additions and 7417 deletions

View file

@ -30,12 +30,25 @@ pub fn call_bitcode_fn<'ctx>(
args: &[BasicValueEnum<'ctx>],
fn_name: &str,
) -> BasicValueEnum<'ctx> {
call_bitcode_fn_help(env, args, fn_name)
let ret = call_bitcode_fn_help(env, args, fn_name)
.try_as_basic_value()
.left()
.unwrap_or_else(|| {
panic!("LLVM error: Did not get return value from bitcode function {fn_name:?}")
})
});
if env.target_info.operating_system == roc_target::OperatingSystem::Windows {
// On windows zig uses a vector type <2xi64> instead of a i128 value
let vec_type = env.context.i64_type().vec_type(2);
if ret.get_type() == vec_type.into() {
return env
.builder
.build_bitcast(ret, env.context.i128_type(), "return_i128")
.unwrap();
}
}
ret
}
pub fn call_void_bitcode_fn<'ctx>(
@ -54,7 +67,35 @@ fn call_bitcode_fn_help<'ctx>(
args: &[BasicValueEnum<'ctx>],
fn_name: &str,
) -> CallSiteValue<'ctx> {
let it = args.iter().map(|x| (*x).into());
let it = args
.iter()
.map(|x| {
if env.target_info.operating_system == roc_target::OperatingSystem::Windows {
if x.get_type() == env.context.i128_type().into() {
let parent = env
.builder
.get_insert_block()
.and_then(|b| b.get_parent())
.unwrap();
let alloca = create_entry_block_alloca(
env,
parent,
x.get_type(),
"pass_u128_by_reference",
);
env.builder.build_store(alloca, *x).unwrap();
alloca.into()
} else {
*x
}
} else {
*x
}
})
.map(|x| (x).into());
let arguments = bumpalo::collections::Vec::from_iter_in(it, env.arena);
let fn_val = env

View file

@ -710,7 +710,7 @@ impl LlvmBackendMode {
match self {
LlvmBackendMode::Binary => false,
LlvmBackendMode::BinaryDev => false,
LlvmBackendMode::BinaryGlue => false,
LlvmBackendMode::BinaryGlue => true,
LlvmBackendMode::GenTest => true,
LlvmBackendMode::WasmGenTest => true,
LlvmBackendMode::CliTest => true,
@ -910,16 +910,19 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
&self,
env: &Env<'a, 'ctx, 'env>,
location: BasicValueEnum<'ctx>,
source: BasicValueEnum<'ctx>,
message: BasicValueEnum<'ctx>,
) {
let function = self.module.get_function("roc_dbg").unwrap();
let loc = self.string_to_arg(env, location);
let src = self.string_to_arg(env, source);
let msg = self.string_to_arg(env, message);
let call = self
.builder
.new_build_call(function, &[loc.into(), msg.into()], "roc_dbg");
// TODO: at some point it will be a breaking change, but flip order to (loc, src, msg)
let call =
self.builder
.new_build_call(function, &[loc.into(), msg.into(), src.into()], "roc_dbg");
call.set_call_convention(C_CALL_CONV);
}
@ -1055,6 +1058,55 @@ pub fn module_from_builtins<'ctx>(
let module = Module::parse_bitcode_from_buffer(&memory_buffer, ctx)
.unwrap_or_else(|err| panic!("Unable to import builtins bitcode. LLVM error: {err:?}"));
// In testing, this adds about 20ms extra to compilation.
// Long term it would be best if we could do this on the zig side.
// The core issue is that we have to properly labael certain functions as private and DCE them.
// Otherwise, now that zig bundles all of compiler-rt, we would optimize and compile the entire library.
// Anything not depended on by a `roc_builtin.` function could already by DCE'd theoretically.
// That said, this workaround is good enough and fixes compilations times.
// Also, must_keep is the functions we depend on that would normally be provide by libc.
// They are magically linked to by llvm builtins, so we must specify that they can't be DCE'd.
let must_keep = [
"_fltused",
"floorf",
"memcpy",
"memset",
// I have no idea why this function is special.
// Without it, some tests hang on M1 mac outside of nix.
"__muloti4",
// fixes `Undefined Symbol in relocation`
"__udivti3",
// Roc special functions
"__roc_force_longjmp",
"__roc_force_setjmp",
"set_shared_buffer",
];
for func in module.get_functions() {
let has_definition = func.count_basic_blocks() > 0;
let name = func.get_name().to_string_lossy();
if has_definition
&& !name.starts_with("roc_builtins.")
&& !must_keep.contains(&name.as_ref())
{
func.set_linkage(Linkage::Private);
}
}
// Note, running DCE here is faster then waiting until full app DCE.
let mpm = PassManager::create(());
mpm.add_global_dce_pass();
mpm.run_on(&module);
// Now that the unused compiler-rt functions have been removed,
// mark that the builtin functions are allowed to be DCE'd if they aren't used.
for func in module.get_functions() {
let name = func.get_name().to_string_lossy();
if name.starts_with("roc_builtins.") {
func.set_linkage(Linkage::Private);
}
}
// Add LLVM intrinsics.
add_intrinsics(ctx, &module);
@ -1227,6 +1279,8 @@ fn promote_to_wasm_test_wrapper<'a, 'ctx>(
let subprogram = env.new_subprogram(main_fn_name);
c_function.set_subprogram(subprogram);
debug_info_init!(env, c_function);
// STEP 2: build the exposed function's body
let builder = env.builder;
let context = env.context;
@ -3527,17 +3581,17 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
}
Dbg {
source_location,
source,
symbol,
variable: _,
remainder,
} => {
if env.mode.runs_expects() {
// TODO: Change location to `filename:line_number`
// let region = unsafe { std::mem::transmute::<_, roc_region::all::Region>(*symbol) };
let location =
build_string_literal(env, parent, symbol.module_string(&env.interns));
let location = build_string_literal(env, parent, source_location);
let source = build_string_literal(env, parent, source);
let message = scope.load_symbol(symbol);
env.call_dbg(env, location, message);
env.call_dbg(env, location, source, message);
}
build_exp_stmt(
@ -4363,6 +4417,8 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
let subprogram = env.new_subprogram(c_function_name);
c_function.set_subprogram(subprogram);
debug_info_init!(env, c_function);
// STEP 2: build the exposed function's body
let builder = env.builder;
let context = env.context;
@ -4371,8 +4427,6 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
builder.position_at_end(entry);
debug_info_init!(env, c_function);
// drop the first argument, which is the pointer we write the result into
let args_vector = c_function.get_params();
let mut args = args_vector.as_slice();
@ -4413,29 +4467,68 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
}
}
let arguments_for_call = &arguments_for_call.into_bump_slice();
let call_result = if env.mode.returns_roc_result() {
debug_assert_eq!(args.len(), roc_function.get_params().len());
if args.len() == roc_function.get_params().len() {
let arguments_for_call = &arguments_for_call.into_bump_slice();
let roc_wrapper_function =
make_exception_catcher(env, layout_interner, roc_function, return_layout);
debug_assert_eq!(
arguments_for_call.len(),
roc_wrapper_function.get_params().len()
);
let dbg_loc = builder.get_current_debug_location().unwrap();
let roc_wrapper_function =
make_exception_catcher(env, layout_interner, roc_function, return_layout);
debug_assert_eq!(
arguments_for_call.len(),
roc_wrapper_function.get_params().len()
);
builder.position_at_end(entry);
builder.position_at_end(entry);
builder.set_current_debug_location(dbg_loc);
let wrapped_layout = roc_call_result_layout(env.arena, return_layout);
call_direct_roc_function(
env,
layout_interner,
roc_function,
wrapped_layout,
arguments_for_call,
)
let wrapped_layout = roc_call_result_layout(env.arena, return_layout);
call_direct_roc_function(
env,
layout_interner,
roc_function,
wrapped_layout,
arguments_for_call,
)
} else {
debug_assert_eq!(args.len() + 1, roc_function.get_params().len());
arguments_for_call.push(args[0]);
let arguments_for_call = &arguments_for_call.into_bump_slice();
let dbg_loc = builder.get_current_debug_location().unwrap();
let roc_wrapper_function =
make_exception_catcher(env, layout_interner, roc_function, return_layout);
builder.position_at_end(entry);
builder.set_current_debug_location(dbg_loc);
let wrapped_layout = roc_call_result_layout(env.arena, return_layout);
let call_result = call_direct_roc_function(
env,
layout_interner,
roc_wrapper_function,
wrapped_layout,
arguments_for_call,
);
let output_arg_index = 0;
let output_arg = c_function
.get_nth_param(output_arg_index as u32)
.unwrap()
.into_pointer_value();
env.builder.new_build_store(output_arg, call_result);
builder.new_build_return(None);
return c_function;
}
} else {
let arguments_for_call = &arguments_for_call.into_bump_slice();
call_direct_roc_function(
env,
layout_interner,
@ -4459,6 +4552,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
output_arg,
call_result,
);
builder.new_build_return(None);
c_function
@ -4511,6 +4605,8 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>(
let subprogram = env.new_subprogram(c_function_name);
c_function.set_subprogram(subprogram);
debug_info_init!(env, c_function);
// STEP 2: build the exposed function's body
let builder = env.builder;
let context = env.context;
@ -4519,8 +4615,6 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>(
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();
@ -4567,10 +4661,12 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>(
let (call_result, call_result_layout) = {
let last_block = builder.get_insert_block().unwrap();
let dbg_loc = builder.get_current_debug_location().unwrap();
let roc_wrapper_function =
make_exception_catcher(env, layout_interner, roc_function, return_layout);
builder.position_at_end(last_block);
builder.set_current_debug_location(dbg_loc);
let wrapper_result = roc_call_result_layout(env.arena, return_layout);
@ -4622,12 +4718,12 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>(
let subprogram = env.new_subprogram(&size_function_name);
size_function.set_subprogram(subprogram);
debug_info_init!(env, size_function);
let entry = context.append_basic_block(size_function, "entry");
builder.position_at_end(entry);
debug_info_init!(env, size_function);
let size: BasicValueEnum = return_type.size_of().unwrap().into();
builder.new_build_return(Some(&size));
@ -4713,6 +4809,8 @@ fn expose_function_to_host_help_c_abi_v2<'a, 'ctx>(
let subprogram = env.new_subprogram(c_function_name);
c_function.set_subprogram(subprogram);
debug_info_init!(env, c_function);
// STEP 2: build the exposed function's body
let builder = env.builder;
let context = env.context;
@ -4941,12 +5039,12 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx>(
let subprogram = env.new_subprogram(&size_function_name);
size_function.set_subprogram(subprogram);
debug_info_init!(env, size_function);
let entry = env.context.append_basic_block(size_function, "entry");
env.builder.position_at_end(entry);
debug_info_init!(env, size_function);
let return_type = match env.mode {
LlvmBackendMode::GenTest | LlvmBackendMode::WasmGenTest | LlvmBackendMode::CliTest => {
roc_call_result_type(env, roc_function.get_type().get_return_type().unwrap()).into()
@ -5343,6 +5441,8 @@ fn make_exception_catching_wrapper<'a, 'ctx>(
let subprogram = env.new_subprogram(wrapper_function_name);
wrapper_function.set_subprogram(subprogram);
debug_info_init!(env, wrapper_function);
// 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);
@ -5819,6 +5919,8 @@ fn build_proc_header<'a, 'ctx>(
let subprogram = env.new_subprogram(&fn_name);
fn_val.set_subprogram(subprogram);
debug_info_init!(env, fn_val);
if env.exposed_to_host.contains(&symbol) {
let arguments = Vec::from_iter_in(proc.args.iter().map(|(layout, _)| *layout), env.arena);
expose_function_to_host(

View file

@ -450,7 +450,7 @@ pub(crate) fn list_capacity_or_ref_ptr<'ctx>(
// Gets a pointer to just after the refcount for a list or seamless slice.
// The value is just after the refcount so that normal lists and seamless slices can share code paths easily.
pub(crate) fn list_refcount_ptr<'ctx>(
pub(crate) fn list_allocation_ptr<'ctx>(
env: &Env<'_, 'ctx, '_>,
wrapper_struct: StructValue<'ctx>,
) -> PointerValue<'ctx> {
@ -459,7 +459,7 @@ pub(crate) fn list_refcount_ptr<'ctx>(
&[wrapper_struct],
&[],
BitcodeReturns::Basic,
bitcode::LIST_REFCOUNT_PTR,
bitcode::LIST_ALLOCATION_PTR,
)
.into_pointer_value()
}
@ -864,7 +864,7 @@ pub(crate) fn decref<'ctx>(
wrapper_struct: StructValue<'ctx>,
alignment: u32,
) {
let refcount_ptr = list_refcount_ptr(env, wrapper_struct);
let refcount_ptr = list_allocation_ptr(env, wrapper_struct);
crate::llvm::refcounting::decref_pointer_check_null(env, refcount_ptr, alignment);
}

View file

@ -48,7 +48,7 @@ pub(crate) fn str_equal<'ctx>(
// Gets a pointer to just after the refcount for a list or seamless slice.
// The value is just after the refcount so that normal lists and seamless slices can share code paths easily.
pub(crate) fn str_refcount_ptr<'ctx>(
pub(crate) fn str_allocation_ptr<'ctx>(
env: &Env<'_, 'ctx, '_>,
value: BasicValueEnum<'ctx>,
) -> PointerValue<'ctx> {
@ -57,7 +57,7 @@ pub(crate) fn str_refcount_ptr<'ctx>(
&[value],
&[],
BitcodeReturns::Basic,
bitcode::STR_REFCOUNT_PTR,
bitcode::STR_ALLOCATION_PTR,
)
.into_pointer_value()
}

View file

@ -469,6 +469,8 @@ fn build_clone_tag<'a, 'ctx>(
let subprogram = env.new_subprogram(&fn_name);
function_value.set_subprogram(subprogram);
debug_info_init!(env, function_value);
env.dibuilder.finalize();
build_clone_tag_help(

View file

@ -1,3 +1,4 @@
use crate::debug_info_init;
use crate::llvm::bitcode::call_void_bitcode_fn;
use crate::llvm::build::{add_func, get_panic_msg_ptr, get_panic_tag_ptr, BuilderExt, C_CALL_CONV};
use crate::llvm::build::{CCReturn, Env, FunctionSpec};
@ -160,8 +161,39 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
}
}
// TODO: generate a valid impl of dbg here.
unreachable_function(env, "roc_dbg");
// roc_dbg
{
// 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_dbg").unwrap();
let mut params = fn_val.get_param_iter();
let loc_arg = params.next().unwrap();
let msg_arg = params.next().unwrap();
let src_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 utils.dbg_impl()
let dbg_impl = module.get_function(bitcode::UTILS_DBG_IMPL).unwrap();
let call = builder.new_build_call(
dbg_impl,
&[loc_arg.into(), msg_arg.into(), src_arg.into()],
"call_utils_dbg_impl",
);
call.set_call_convention(C_CALL_CONV);
builder.new_build_return(None);
if cfg!(debug_assertions) {
crate::llvm::build::verify_fn(fn_val);
}
}
match env.target_info.operating_system {
roc_target::OperatingSystem::Windows => {
@ -223,6 +255,8 @@ pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) {
let subprogram = env.new_subprogram("roc_panic");
fn_val.set_subprogram(subprogram);
debug_info_init!(env, fn_val);
env.dibuilder.finalize();
// Add a basic block for the entry point

View file

@ -80,10 +80,6 @@ pub(crate) fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
let i32_type = ctx.i32_type();
let void_type = ctx.void_type();
if let Some(func) = module.get_function("__muloti4") {
func.set_linkage(Linkage::WeakAny);
}
add_intrinsic(
ctx,
module,

View file

@ -1100,13 +1100,22 @@ pub(crate) fn run_low_level<'a, 'ctx>(
NumBytesToU128 => {
arguments!(list, position);
call_list_bitcode_fn(
let ret = call_list_bitcode_fn(
env,
&[list.into_struct_value()],
&[position],
BitcodeReturns::Basic,
bitcode::NUM_BYTES_TO_U128,
)
);
if env.target_info.operating_system == roc_target::OperatingSystem::Windows {
// On windows the return type is not a i128, likely due to alignment
env.builder
.build_bitcast(ret, env.context.i128_type(), "empty_string")
.unwrap()
} else {
ret
}
}
NumCompare => {
arguments_with_layouts!((lhs_arg, lhs_layout), (rhs_arg, rhs_layout));
@ -1535,7 +1544,7 @@ fn build_int_binop<'ctx>(
)
.into_struct_value();
throw_on_overflow(env, parent, result, "integer addition overflowed!")
throw_on_overflow(env, parent, result, "Integer addition overflowed!")
}
NumAddWrap => bd.new_build_int_add(lhs, rhs, "add_int_wrap").into(),
NumAddChecked => {
@ -1566,7 +1575,7 @@ fn build_int_binop<'ctx>(
)
.into_struct_value();
throw_on_overflow(env, parent, result, "integer subtraction overflowed!")
throw_on_overflow(env, parent, result, "Integer subtraction overflowed!")
}
NumSubWrap => bd.new_build_int_sub(lhs, rhs, "sub_int").into(),
NumSubChecked => {
@ -1597,7 +1606,7 @@ fn build_int_binop<'ctx>(
)
.into_struct_value();
throw_on_overflow(env, parent, result, "integer multiplication overflowed!")
throw_on_overflow(env, parent, result, "Integer multiplication overflowed!")
}
NumMulWrap => bd.new_build_int_mul(lhs, rhs, "mul_int").into(),
NumMulSaturated => call_bitcode_fn(
@ -2350,7 +2359,7 @@ fn build_dec_binop<'a, 'ctx>(
bitcode::DEC_ADD_WITH_OVERFLOW,
lhs,
rhs,
"decimal addition overflowed",
"Decimal addition overflowed",
),
NumSub => build_dec_binop_throw_on_overflow(
env,
@ -2358,7 +2367,7 @@ fn build_dec_binop<'a, 'ctx>(
bitcode::DEC_SUB_WITH_OVERFLOW,
lhs,
rhs,
"decimal subtraction overflowed",
"Decimal subtraction overflowed",
),
NumMul => build_dec_binop_throw_on_overflow(
env,
@ -2366,7 +2375,7 @@ fn build_dec_binop<'a, 'ctx>(
bitcode::DEC_MUL_WITH_OVERFLOW,
lhs,
rhs,
"decimal multiplication overflowed",
"Decimal multiplication overflowed",
),
NumDivFrac => dec_binop_with_unchecked(env, bitcode::DEC_DIV, lhs, rhs),
@ -2596,7 +2605,16 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
}
}
PtrWidth::Bytes8 => {
if target_int_width.stack_size() as usize > env.target_info.ptr_size() {
let return_by_pointer = {
if env.target_info.operating_system
== roc_target::OperatingSystem::Windows
{
target_int_width.stack_size() as usize >= env.target_info.ptr_size()
} else {
target_int_width.stack_size() as usize > env.target_info.ptr_size()
}
};
if return_by_pointer {
let bitcode_return_type =
zig_to_int_checked_result_type(env, target_int_width.type_name());
@ -2659,7 +2677,7 @@ fn int_neg_raise_on_overflow<'ctx>(
throw_internal_exception(
env,
parent,
"integer negation overflowed because its argument is the minimum value",
"Integer negation overflowed because its argument is the minimum value",
);
builder.position_at_end(else_block);
@ -2690,7 +2708,7 @@ fn int_abs_raise_on_overflow<'ctx>(
throw_internal_exception(
env,
parent,
"integer absolute overflowed because its argument is the minimum value",
"Integer absolute overflowed because its argument is the minimum value",
);
builder.position_at_end(else_block);

View file

@ -5,9 +5,9 @@ use crate::llvm::build::{
add_func, cast_basic_basic, get_tag_id, tag_pointer_clear_tag_id, Env, FAST_CALL_CONV,
};
use crate::llvm::build_list::{
incrementing_elem_loop, list_capacity_or_ref_ptr, list_refcount_ptr, load_list,
incrementing_elem_loop, list_allocation_ptr, list_capacity_or_ref_ptr, load_list,
};
use crate::llvm::build_str::str_refcount_ptr;
use crate::llvm::build_str::str_allocation_ptr;
use crate::llvm::convert::{basic_type_from_layout, zig_str_type, RocUnion};
use crate::llvm::struct_::RocStruct;
use bumpalo::collections::Vec;
@ -156,6 +156,8 @@ impl<'ctx> PointerToRefcount<'ctx> {
let subprogram = env.new_subprogram(fn_name);
function_value.set_subprogram(subprogram);
debug_info_init!(env, function_value);
Self::build_decrement_function_body(env, function_value, alignment);
function_value
@ -864,7 +866,7 @@ fn modify_refcount_list_help<'a, 'ctx>(
}
let refcount_ptr =
PointerToRefcount::from_ptr_to_data(env, list_refcount_ptr(env, original_wrapper));
PointerToRefcount::from_ptr_to_data(env, list_allocation_ptr(env, original_wrapper));
let call_mode = mode_to_call_mode(fn_val, mode);
refcount_ptr.modify(call_mode, layout, env, layout_interner);
@ -971,7 +973,7 @@ fn modify_refcount_str_help<'a, 'ctx>(
builder.new_build_conditional_branch(is_big_and_non_empty, modification_block, cont_block);
builder.position_at_end(modification_block);
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, str_refcount_ptr(env, arg_val));
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, str_allocation_ptr(env, arg_val));
let call_mode = mode_to_call_mode(fn_val, mode);
refcount_ptr.modify(
call_mode,
@ -1049,6 +1051,8 @@ pub fn build_header_help<'ctx>(
let subprogram = env.new_subprogram(fn_name);
fn_val.set_subprogram(subprogram);
debug_info_init!(env, fn_val);
env.dibuilder.finalize();
fn_val