Merge remote-tracking branch 'origin/trunk' into list-str-capacity

This commit is contained in:
Folkert 2022-03-09 14:36:34 +01:00
commit 07063a8e18
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
458 changed files with 20371 additions and 8316 deletions

View file

@ -1,7 +1,8 @@
/// Helpers for interacting with the zig that generates bitcode
use crate::debug_info_init;
use crate::llvm::build::{
load_roc_value, struct_from_fields, Env, C_CALL_CONV, FAST_CALL_CONV, TAG_DATA_INDEX,
complex_bitcast_check_size, load_roc_value, struct_from_fields, to_cc_return, CCReturn, Env,
C_CALL_CONV, FAST_CALL_CONV, TAG_DATA_INDEX,
};
use crate::llvm::convert::basic_type_from_layout;
use crate::llvm::refcounting::{
@ -11,11 +12,14 @@ use inkwell::attributes::{Attribute, AttributeLoc};
use inkwell::types::{BasicType, BasicTypeEnum};
use inkwell::values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue};
use inkwell::AddressSpace;
use roc_error_macros::internal_error;
use roc_module::symbol::Symbol;
use roc_mono::layout::{LambdaSet, Layout, LayoutIds, UnionLayout};
use super::build::create_entry_block_alloca;
use std::convert::TryInto;
pub fn call_bitcode_fn<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
args: &[BasicValueEnum<'ctx>],
@ -112,6 +116,63 @@ fn call_bitcode_fn_help<'a, 'ctx, 'env>(
call
}
pub fn call_bitcode_fn_fixing_for_convention<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
args: &[BasicValueEnum<'ctx>],
return_layout: &Layout<'_>,
fn_name: &str,
) -> BasicValueEnum<'ctx> {
// Calling zig bitcode, so we must follow C calling conventions.
let cc_return = to_cc_return(env, return_layout);
match cc_return {
CCReturn::Return => {
// We'll get a return value
call_bitcode_fn(env, args, fn_name)
}
CCReturn::ByPointer => {
// We need to pass the return value by pointer.
let roc_return_type = basic_type_from_layout(env, return_layout);
let cc_ptr_return_type = env
.module
.get_function(fn_name)
.unwrap()
.get_type()
.get_param_types()[0]
.into_pointer_type();
let cc_return_type: BasicTypeEnum<'ctx> = cc_ptr_return_type
.get_element_type()
.try_into()
.expect("Zig bitcode return type is not a basic type!");
let cc_return_value_ptr = env.builder.build_alloca(cc_return_type, "return_value");
let fixed_args: Vec<BasicValueEnum<'ctx>> = [cc_return_value_ptr.into()]
.iter()
.chain(args)
.copied()
.collect();
call_void_bitcode_fn(env, &fixed_args, fn_name);
let cc_return_value = env.builder.build_load(cc_return_value_ptr, "read_result");
if roc_return_type.size_of() == cc_return_type.size_of() {
cc_return_value
} else {
// We need to convert the C-callconv return type, which may be larger than the Roc
// return type, into the Roc return type.
complex_bitcast_check_size(
env,
cc_return_value,
roc_return_type,
"c_value_to_roc_value",
)
}
}
CCReturn::Void => {
internal_error!("Tried to call valued bitcode function, but it has no return type")
}
}
}
const ARGUMENT_SYMBOLS: [Symbol; 8] = [
Symbol::ARG_1,
Symbol::ARG_2,
@ -333,7 +394,9 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
}
match closure_data_layout.runtime_representation() {
Layout::Struct(&[]) => {
Layout::Struct {
field_layouts: &[], ..
} => {
// nothing to add
}
other => {
@ -653,7 +716,9 @@ pub fn build_compare_wrapper<'a, 'ctx, 'env>(
let default = [value1.into(), value2.into()];
let arguments_cast = match closure_data_layout.runtime_representation() {
Layout::Struct(&[]) => {
Layout::Struct {
field_layouts: &[], ..
} => {
// nothing to add
&default
}

View file

@ -1,7 +1,9 @@
use std::convert::TryFrom;
use std::path::Path;
use crate::llvm::bitcode::{call_bitcode_fn, call_void_bitcode_fn};
use crate::llvm::bitcode::{
call_bitcode_fn, call_bitcode_fn_fixing_for_convention, call_void_bitcode_fn,
};
use crate::llvm::build_dict::{
self, dict_contains, dict_difference, dict_empty, dict_get, dict_insert, dict_intersection,
dict_keys, dict_len, dict_remove, dict_union, dict_values, dict_walk, set_from_list,
@ -11,7 +13,7 @@ use crate::llvm::build_list::{
self, allocate_list, empty_polymorphic_list, list_all, list_any, list_append, list_concat,
list_contains, list_drop_at, list_find_unsafe, list_get_unsafe, list_join, list_keep_errs,
list_keep_if, list_keep_oks, list_len, list_map, list_map2, list_map3, list_map4,
list_map_with_index, list_prepend, list_range, list_repeat, list_reverse, list_set,
list_map_with_index, list_prepend, list_range, list_repeat, list_replace_unsafe, list_reverse,
list_single, list_sort_with, list_sublist, list_swap, list_to_c_abi,
};
use crate::llvm::build_str::{
@ -53,7 +55,7 @@ use morphic_lib::{
CalleeSpecVar, FuncName, FuncSpec, FuncSpecSolutions, ModSolutions, UpdateMode, UpdateModeVar,
};
use roc_builtins::bitcode::{self, FloatWidth, IntWidth, IntrinsicName};
use roc_builtins::{float_intrinsic, int_intrinsic};
use roc_builtins::{float_intrinsic, llvm_int_intrinsic};
use roc_collections::all::{ImMap, MutMap, MutSet};
use roc_error_macros::internal_error;
use roc_module::low_level::LowLevel;
@ -609,14 +611,14 @@ static LLVM_SETJMP: &str = "llvm.eh.sjlj.setjmp";
pub static LLVM_LONGJMP: &str = "llvm.eh.sjlj.longjmp";
const LLVM_ADD_WITH_OVERFLOW: IntrinsicName =
int_intrinsic!("llvm.sadd.with.overflow", "llvm.uadd.with.overflow");
llvm_int_intrinsic!("llvm.sadd.with.overflow", "llvm.uadd.with.overflow");
const LLVM_SUB_WITH_OVERFLOW: IntrinsicName =
int_intrinsic!("llvm.ssub.with.overflow", "llvm.usub.with.overflow");
llvm_int_intrinsic!("llvm.ssub.with.overflow", "llvm.usub.with.overflow");
const LLVM_MUL_WITH_OVERFLOW: IntrinsicName =
int_intrinsic!("llvm.smul.with.overflow", "llvm.umul.with.overflow");
llvm_int_intrinsic!("llvm.smul.with.overflow", "llvm.umul.with.overflow");
const LLVM_ADD_SATURATED: IntrinsicName = int_intrinsic!("llvm.sadd.sat", "llvm.uadd.sat");
const LLVM_SUB_SATURATED: IntrinsicName = int_intrinsic!("llvm.ssub.sat", "llvm.usub.sat");
const LLVM_ADD_SATURATED: IntrinsicName = llvm_int_intrinsic!("llvm.sadd.sat", "llvm.uadd.sat");
const LLVM_SUB_SATURATED: IntrinsicName = llvm_int_intrinsic!("llvm.ssub.sat", "llvm.usub.sat");
fn add_intrinsic<'ctx>(
module: &Module<'ctx>,
@ -655,35 +657,43 @@ pub fn construct_optimization_passes<'a>(
OptLevel::Development | OptLevel::Normal => {
pmb.set_optimization_level(OptimizationLevel::None);
}
OptLevel::Size => {
pmb.set_optimization_level(OptimizationLevel::Default);
// TODO: For some usecase, like embedded, it is useful to expose this and tune it.
pmb.set_inliner_with_threshold(50);
}
OptLevel::Optimize => {
pmb.set_optimization_level(OptimizationLevel::Aggressive);
// this threshold seems to do what we want
pmb.set_inliner_with_threshold(275);
// TODO figure out which of these actually help
// function passes
fpm.add_cfg_simplification_pass();
mpm.add_cfg_simplification_pass();
fpm.add_jump_threading_pass();
mpm.add_jump_threading_pass();
fpm.add_memcpy_optimize_pass(); // this one is very important
fpm.add_licm_pass();
// turn invoke into call
mpm.add_prune_eh_pass();
// remove unused global values (often the `_wrapper` can be removed)
mpm.add_global_dce_pass();
mpm.add_function_inlining_pass();
}
}
// Add optimization passes for Size and Optimize.
if matches!(opt_level, OptLevel::Size | OptLevel::Optimize) {
// TODO figure out which of these actually help
// function passes
fpm.add_cfg_simplification_pass();
mpm.add_cfg_simplification_pass();
fpm.add_jump_threading_pass();
mpm.add_jump_threading_pass();
fpm.add_memcpy_optimize_pass(); // this one is very important
fpm.add_licm_pass();
// turn invoke into call
mpm.add_prune_eh_pass();
// remove unused global values (often the `_wrapper` can be removed)
mpm.add_global_dce_pass();
mpm.add_function_inlining_pass();
}
pmb.populate_module_pass_manager(&mpm);
pmb.populate_function_pass_manager(&fpm);
@ -700,7 +710,7 @@ fn promote_to_main_function<'a, 'ctx, 'env>(
top_level: ProcLayout<'a>,
) -> (&'static str, FunctionValue<'ctx>) {
let it = top_level.arguments.iter().copied();
let bytes = roc_mono::alias_analysis::func_name_bytes_help(symbol, it, &top_level.result);
let bytes = roc_alias_analysis::func_name_bytes_help(symbol, it, &top_level.result);
let func_name = FuncName(&bytes);
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
@ -712,8 +722,7 @@ fn promote_to_main_function<'a, 'ctx, 'env>(
);
// NOTE fake layout; it is only used for debug prints
let roc_main_fn =
function_value_by_func_spec(env, *func_spec, symbol, &[], &Layout::Struct(&[]));
let roc_main_fn = function_value_by_func_spec(env, *func_spec, symbol, &[], &Layout::UNIT);
let main_fn_name = "$Test.main";
@ -785,11 +794,13 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
_ => panic!("Invalid layout for float literal = {:?}", layout),
},
Decimal(int) => env
.context
.i128_type()
.const_int(int.0 as u64, false)
.into(),
Decimal(int) => {
let (upper_bits, lower_bits) = int.as_bits();
env.context
.i128_type()
.const_int_arbitrary_precision(&[lower_bits, upper_bits as u64])
.into()
}
Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(),
Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(),
Str(str_literal) => {
@ -1158,8 +1169,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
// extract field from a record
match (value, layout) {
(StructValue(argument), Layout::Struct(fields)) => {
debug_assert!(!fields.is_empty());
(StructValue(argument), Layout::Struct { field_layouts, .. }) => {
debug_assert!(!field_layouts.is_empty());
let field_value = env
.builder
@ -1171,14 +1182,14 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
)
.unwrap();
let field_layout = fields[*index as usize];
let field_layout = field_layouts[*index as usize];
use_roc_value(env, field_layout, field_value, "struct_field_tag")
}
(
PointerValue(argument),
Layout::Union(UnionLayout::NonNullableUnwrapped(fields)),
) => {
let struct_layout = Layout::Struct(fields);
let struct_layout = Layout::struct_no_name_order(fields);
let struct_type = basic_type_from_layout(env, &struct_layout);
let cast_argument = env
@ -1262,7 +1273,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
)
}
UnionLayout::NonNullableUnwrapped(field_layouts) => {
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let struct_type = basic_type_from_layout(env, &struct_layout);
@ -1311,7 +1322,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
debug_assert_ne!(*tag_id != 0, *nullable_id);
let field_layouts = other_fields;
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let struct_type = basic_type_from_layout(env, &struct_layout);
@ -1994,7 +2005,7 @@ fn lookup_at_index_ptr2<'a, 'ctx, 'env>(
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let struct_type = basic_type_from_layout(env, &struct_layout);
let wrapper_type = env
@ -2893,7 +2904,7 @@ pub fn complex_bitcast<'ctx>(
/// Check the size of the input and output types. Pretending we have more bytes at a pointer than
/// we actually do can lead to faulty optimizations and weird segfaults/crashes
fn complex_bitcast_check_size<'a, 'ctx, 'env>(
pub fn complex_bitcast_check_size<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
from_value: BasicValueEnum<'ctx>,
to_type: BasicTypeEnum<'ctx>,
@ -3492,7 +3503,7 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx, 'env>(
call_roc_function(
env,
roc_wrapper_function,
&Layout::Struct(&[Layout::u64(), return_layout]),
&Layout::struct_no_name_order(&[Layout::u64(), return_layout]),
arguments_for_call,
)
};
@ -3582,7 +3593,12 @@ fn expose_function_to_host_help_c_abi_v2<'a, 'ctx, 'env>(
_ => (&params[..], &param_types[..]),
};
debug_assert_eq!(params.len(), param_types.len());
debug_assert!(
params.len() == param_types.len(),
"when exposing a function to the host, params.len() was {}, but param_types.len() was {}",
params.len(),
param_types.len()
);
let it = params.iter().zip(param_types).map(|(arg, fastcc_type)| {
let arg_type = arg.get_type();
@ -3873,7 +3889,7 @@ fn roc_result_layout<'a>(
) -> Layout<'a> {
let elements = [Layout::u64(), Layout::usize(target_info), return_layout];
Layout::Struct(arena.alloc(elements))
Layout::struct_no_name_order(arena.alloc(elements))
}
fn roc_result_type<'a, 'ctx, 'env>(
@ -4003,7 +4019,7 @@ pub fn build_proc_headers<'a, 'ctx, 'env>(
// Populate Procs further and get the low-level Expr from the canonical Expr
let mut headers = Vec::with_capacity_in(procedures.len(), env.arena);
for ((symbol, layout), proc) in procedures {
let name_bytes = roc_mono::alias_analysis::func_name_bytes(&proc);
let name_bytes = roc_alias_analysis::func_name_bytes(&proc);
let func_name = FuncName(&name_bytes);
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
@ -4045,7 +4061,13 @@ pub fn build_procedures_return_main<'a, 'ctx, 'env>(
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
entry_point: EntryPoint<'a>,
) -> (&'static str, FunctionValue<'ctx>) {
let mod_solutions = build_procedures_help(env, opt_level, procedures, entry_point, None);
let mod_solutions = build_procedures_help(
env,
opt_level,
procedures,
entry_point,
Some(Path::new("/tmp/test.ll")),
);
promote_to_main_function(env, mod_solutions, entry_point.symbol, entry_point.layout)
}
@ -4062,7 +4084,7 @@ fn build_procedures_help<'a, 'ctx, 'env>(
let it = procedures.iter().map(|x| x.1);
let solutions = match roc_mono::alias_analysis::spec_program(opt_level, entry_point, it) {
let solutions = match roc_alias_analysis::spec_program(opt_level, entry_point, it) {
Err(e) => panic!("Error in alias analysis: {}", e),
Ok(solutions) => solutions,
};
@ -4070,7 +4092,7 @@ fn build_procedures_help<'a, 'ctx, 'env>(
let solutions = env.arena.alloc(solutions);
let mod_solutions = solutions
.mod_solutions(roc_mono::alias_analysis::MOD_APP)
.mod_solutions(roc_alias_analysis::MOD_APP)
.unwrap();
// Add all the Proc headers to the module.
@ -4422,11 +4444,8 @@ pub fn build_proc<'a, 'ctx, 'env>(
// * roc__mainForHost_1_Update_result_size() -> i64
let it = top_level.arguments.iter().copied();
let bytes = roc_mono::alias_analysis::func_name_bytes_help(
symbol,
it,
&top_level.result,
);
let bytes =
roc_alias_analysis::func_name_bytes_help(symbol, it, &top_level.result);
let func_name = FuncName(&bytes);
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
@ -5325,7 +5344,7 @@ fn run_low_level<'a, 'ctx, 'env>(
debug_assert_eq!(args.len(), 1);
let number_layout = match layout {
Layout::Struct(fields) => fields[0], // TODO: why is it sometimes a struct?
Layout::Struct { field_layouts, .. } => field_layouts[0], // TODO: why is it sometimes a struct?
_ => unreachable!(),
};
@ -5608,12 +5627,12 @@ fn run_low_level<'a, 'ctx, 'env>(
wrapper_struct,
)
}
ListSet => {
ListReplaceUnsafe => {
let list = load_symbol(scope, &args[0]);
let index = load_symbol(scope, &args[1]);
let (element, element_layout) = load_symbol_and_layout(scope, &args[2]);
list_set(
list_replace_unsafe(
env,
layout_ids,
list,
@ -5642,7 +5661,8 @@ fn run_low_level<'a, 'ctx, 'env>(
}
}
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumLogUnchecked | NumSin | NumCos
| NumCeiling | NumFloor | NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin => {
| NumCeiling | NumFloor | NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin
| NumToIntChecked => {
debug_assert_eq!(args.len(), 1);
let (arg, arg_layout) = load_symbol_and_layout(scope, &args[0]);
@ -5654,7 +5674,14 @@ fn run_low_level<'a, 'ctx, 'env>(
match arg_builtin {
Int(int_width) => {
let int_type = convert::int_type_from_int_width(env, *int_width);
build_int_unary_op(env, arg.into_int_value(), int_type, op, layout)
build_int_unary_op(
env,
arg.into_int_value(),
*int_width,
int_type,
op,
layout,
)
}
Float(float_width) => build_float_unary_op(
env,
@ -6132,7 +6159,7 @@ impl RocReturn {
}
#[derive(Debug)]
enum CCReturn {
pub enum CCReturn {
/// Return as normal
Return,
/// require an extra argument, a pointer
@ -6174,7 +6201,7 @@ impl CCReturn {
}
/// 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 {
pub fn to_cc_return<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>) -> CCReturn {
let return_size = layout.stack_size(env.target_info);
let pass_result_by_pointer = return_size > 2 * env.target_info.ptr_width() as u32;
@ -6868,7 +6895,8 @@ fn int_type_signed_min(int_type: IntType) -> IntValue {
fn build_int_unary_op<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
arg: IntValue<'ctx>,
int_type: IntType<'ctx>,
arg_width: IntWidth,
arg_int_type: IntType<'ctx>,
op: LowLevel,
return_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
@ -6879,11 +6907,11 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
match op {
NumNeg => {
// integer abs overflows when applied to the minimum value of a signed type
int_neg_raise_on_overflow(env, arg, int_type)
int_neg_raise_on_overflow(env, arg, arg_int_type)
}
NumAbs => {
// integer abs overflows when applied to the minimum value of a signed type
int_abs_raise_on_overflow(env, arg, int_type)
int_abs_raise_on_overflow(env, arg, arg_int_type)
}
NumToFloat => {
// This is an Int, so we need to convert it.
@ -6902,6 +6930,75 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
"i64_to_f64",
)
}
NumToIntChecked => {
// return_layout : Result N [ OutOfBounds ]* ~ { result: N, out_of_bounds: bool }
let target_int_width = match return_layout {
Layout::Struct { field_layouts, .. } if field_layouts.len() == 2 => {
debug_assert!(matches!(field_layouts[1], Layout::Builtin(Builtin::Bool)));
match field_layouts[0] {
Layout::Builtin(Builtin::Int(iw)) => iw,
layout => internal_error!(
"There can only be an int layout here, found {:?}!",
layout
),
}
}
layout => internal_error!(
"There can only be a result layout here, found {:?}!",
layout
),
};
let arg_always_fits_in_target = (arg_width.stack_size() < target_int_width.stack_size()
&& (
// If the arg is unsigned, it will always fit in either a signed or unsigned
// int of a larger width.
!arg_width.is_signed()
||
// Otherwise if the arg is signed, it will always fit in a signed int of a
// larger width.
(target_int_width.is_signed() )
) )
|| // Or if the two types are the same, they trivially fit.
arg_width == target_int_width;
if arg_always_fits_in_target {
// This is guaranteed to succeed so we can just make it an int cast and let LLVM
// optimize it away.
let target_int_type = convert::int_type_from_int_width(env, target_int_width);
let target_int_val: BasicValueEnum<'ctx> = env
.builder
.build_int_cast(arg, target_int_type, "int_cast")
.into();
let return_type =
convert::basic_type_from_layout(env, return_layout).into_struct_type();
let r = return_type.const_zero();
let r = bd
.build_insert_value(r, target_int_val, 0, "converted_int")
.unwrap();
let r = bd
.build_insert_value(r, env.context.bool_type().const_zero(), 1, "out_of_bounds")
.unwrap();
r.into_struct_value().into()
} else {
let bitcode_fn = if !arg_width.is_signed() {
// We are trying to convert from unsigned to signed/unsigned of same or lesser width, e.g.
// u16 -> i16, u16 -> i8, or u16 -> u8. We only need to check that the argument
// value fits in the MAX target type value.
&bitcode::NUM_INT_TO_INT_CHECKING_MAX[target_int_width][arg_width]
} else {
// We are trying to convert from signed to signed/unsigned of same or lesser width, e.g.
// i16 -> u16, i16 -> i8, or i16 -> u8. We need to check that the argument value fits in
// the MAX and MIN target type.
&bitcode::NUM_INT_TO_INT_CHECKING_MAX_AND_MIN[target_int_width][arg_width]
};
call_bitcode_fn_fixing_for_convention(env, &[arg.into()], return_layout, bitcode_fn)
}
}
_ => {
unreachable!("Unrecognized int unary operation: {:?}", op);
}

View file

@ -728,8 +728,7 @@ pub fn set_from_list<'a, 'ctx, 'env>(
let result_alloca = builder.build_alloca(zig_dict_type(env), "result_alloca");
let alignment =
Alignment::from_key_value_layout(key_layout, &Layout::Struct(&[]), env.target_info);
let alignment = Alignment::from_key_value_layout(key_layout, &Layout::UNIT, env.target_info);
let alignment_iv = alignment.as_int_value(env.context);
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);

View file

@ -50,10 +50,10 @@ fn build_hash_layout<'a, 'ctx, 'env>(
hash_builtin(env, layout_ids, seed, val, layout, builtin, when_recursive)
}
Layout::Struct(fields) => build_hash_struct(
Layout::Struct { field_layouts, .. } => build_hash_struct(
env,
layout_ids,
fields,
field_layouts,
when_recursive,
seed,
val.into_struct_value(),
@ -166,7 +166,7 @@ fn build_hash_struct<'a, 'ctx, 'env>(
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let symbol = Symbol::GENERIC_HASH;
let fn_name = layout_ids
@ -248,7 +248,7 @@ fn hash_struct<'a, 'ctx, 'env>(
) -> IntValue<'ctx> {
let ptr_bytes = env.target_info;
let layout = Layout::Struct(field_layouts);
let layout = Layout::struct_no_name_order(field_layouts);
// Optimization: if the bit representation of equal values is the same
// just hash the bits. Caveat here is tags: e.g. `Nothing` in `Just a`
@ -818,7 +818,7 @@ fn hash_ptr_to_struct<'a, 'ctx, 'env>(
.build_struct_gep(wrapper_ptr, TAG_DATA_INDEX, "get_tag_data")
.unwrap();
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let struct_type = basic_type_from_layout(env, &struct_layout);
let struct_ptr = env
.builder

View file

@ -318,52 +318,70 @@ pub fn list_drop_at<'a, 'ctx, 'env>(
)
}
/// List.set : List elem, Nat, elem -> List elem
pub fn list_set<'a, 'ctx, 'env>(
/// List.replace_unsafe : List elem, Nat, elem -> { list: List elem, value: elem }
pub fn list_replace_unsafe<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
_layout_ids: &mut LayoutIds<'a>,
list: BasicValueEnum<'ctx>,
index: IntValue<'ctx>,
element: BasicValueEnum<'ctx>,
element_layout: &Layout<'a>,
update_mode: UpdateMode,
) -> BasicValueEnum<'ctx> {
let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout);
let element_type = basic_type_from_layout(env, element_layout);
let element_ptr = env
.builder
.build_alloca(element_type, "output_element_as_opaque");
let (length, bytes) = load_list(
env.builder,
list.into_struct_value(),
env.context.i8_type().ptr_type(AddressSpace::Generic),
);
let new_bytes = match update_mode {
UpdateMode::InPlace => call_bitcode_fn(
// Assume the bounds have already been checked earlier
// (e.g. by List.replace or List.set, which wrap List.#replaceUnsafe)
let new_list = match update_mode {
UpdateMode::InPlace => call_list_bitcode_fn(
env,
&[
bytes.into(),
list_to_c_abi(env, list).into(),
index.into(),
pass_element_as_opaque(env, element, *element_layout),
layout_width(env, element_layout),
dec_element_fn.as_global_value().as_pointer_value().into(),
pass_as_opaque(env, element_ptr),
],
bitcode::LIST_SET_IN_PLACE,
bitcode::LIST_REPLACE_IN_PLACE,
),
UpdateMode::Immutable => call_bitcode_fn(
UpdateMode::Immutable => call_list_bitcode_fn(
env,
&[
bytes.into(),
length.into(),
list_to_c_abi(env, list).into(),
env.alignment_intvalue(element_layout),
index.into(),
pass_element_as_opaque(env, element, *element_layout),
layout_width(env, element_layout),
dec_element_fn.as_global_value().as_pointer_value().into(),
pass_as_opaque(env, element_ptr),
],
bitcode::LIST_SET,
bitcode::LIST_REPLACE,
),
};
store_list(env, new_bytes.into_pointer_value(), length)
// Load the element and returned list into a struct.
let old_element = env.builder.build_load(element_ptr, "load_element");
let result = env
.context
.struct_type(
&[super::convert::zig_list_type(env).into(), element_type],
false,
)
.const_zero();
let result = env
.builder
.build_insert_value(result, new_list, 0, "insert_list")
.unwrap();
env.builder
.build_insert_value(result, old_element, 1, "insert_value")
.unwrap()
.into_struct_value()
.into()
}
fn bounds_check_comparison<'ctx>(

View file

@ -161,10 +161,10 @@ fn build_eq<'a, 'ctx, 'env>(
build_eq_builtin(env, layout_ids, lhs_val, rhs_val, builtin, when_recursive)
}
Layout::Struct(fields) => build_struct_eq(
Layout::Struct { field_layouts, .. } => build_struct_eq(
env,
layout_ids,
fields,
field_layouts,
when_recursive,
lhs_val.into_struct_value(),
rhs_val.into_struct_value(),
@ -330,11 +330,11 @@ fn build_neq<'a, 'ctx, 'env>(
build_neq_builtin(env, layout_ids, lhs_val, rhs_val, builtin, when_recursive)
}
Layout::Struct(fields) => {
Layout::Struct { field_layouts, .. } => {
let is_equal = build_struct_eq(
env,
layout_ids,
fields,
field_layouts,
when_recursive,
lhs_val.into_struct_value(),
rhs_val.into_struct_value(),
@ -587,7 +587,7 @@ fn build_struct_eq<'a, 'ctx, 'env>(
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let symbol = Symbol::GENERIC_EQ;
let fn_name = layout_ids
@ -1208,7 +1208,7 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
tag1: PointerValue<'ctx>,
tag2: PointerValue<'ctx>,
) -> IntValue<'ctx> {
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let wrapper_type = basic_type_from_layout(env, &struct_layout);
debug_assert!(wrapper_type.is_struct_type());

View file

@ -28,7 +28,10 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
use Layout::*;
match layout {
Struct(sorted_fields) => basic_type_from_record(env, sorted_fields),
Struct {
field_layouts: sorted_fields,
..
} => basic_type_from_record(env, sorted_fields),
LambdaSet(lambda_set) => basic_type_from_layout(env, &lambda_set.runtime_representation()),
Union(union_layout) => {
use UnionLayout::*;
@ -86,7 +89,10 @@ pub fn basic_type_from_layout_1<'a, 'ctx, 'env>(
use Layout::*;
match layout {
Struct(sorted_fields) => basic_type_from_record(env, sorted_fields),
Struct {
field_layouts: sorted_fields,
..
} => basic_type_from_record(env, sorted_fields),
LambdaSet(lambda_set) => {
basic_type_from_layout_1(env, &lambda_set.runtime_representation())
}

View file

@ -280,7 +280,7 @@ fn modify_refcount_struct<'a, 'ctx, 'env>(
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let layout = Layout::Struct(layouts);
let layout = Layout::struct_no_name_order(layouts);
let (_, fn_name) = function_name_from_mode(
layout_ids,
@ -440,7 +440,7 @@ fn modify_refcount_builtin<'a, 'ctx, 'env>(
}
Set(element_layout) => {
let key_layout = element_layout;
let value_layout = &Layout::Struct(&[]);
let value_layout = &Layout::UNIT;
let function = modify_refcount_dict(
env,
@ -619,8 +619,9 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>(
}
}
Struct(layouts) => {
let function = modify_refcount_struct(env, layout_ids, layouts, mode, when_recursive);
Struct { field_layouts, .. } => {
let function =
modify_refcount_struct(env, layout_ids, field_layouts, mode, when_recursive);
Some(function)
}
@ -1313,7 +1314,8 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
env.builder.position_at_end(block);
let wrapper_type = basic_type_from_layout(env, &Layout::Struct(field_layouts));
let wrapper_type =
basic_type_from_layout(env, &Layout::struct_no_name_order(field_layouts));
// cast the opaque pointer to a pointer of the correct shape
let struct_ptr = env
@ -1721,7 +1723,8 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
let block = env.context.append_basic_block(parent, "tag_id_modify");
env.builder.position_at_end(block);
let wrapper_type = basic_type_from_layout(env, &Layout::Struct(field_layouts));
let wrapper_type =
basic_type_from_layout(env, &Layout::struct_no_name_order(field_layouts));
debug_assert!(wrapper_type.is_struct_type());
let opaque_tag_data_ptr = env