diff --git a/compiler/gen/src/crane/build.rs b/compiler/gen/src/crane/build.rs index 996cb82e26..7ca673800c 100644 --- a/compiler/gen/src/crane/build.rs +++ b/compiler/gen/src/crane/build.rs @@ -422,18 +422,19 @@ pub fn build_expr<'a, B: Backend>( elems_ptr }; - // Store the pointer in slot 0 - builder - .ins() - .stack_store(elems_ptr, slot, Offset32::new(0)); + // Store the pointer + { + let offset = Offset32::new((Builtin::WRAPPER_PTR * ptr_bytes) as i32); - // Store the length in slot 1 + builder.ins().stack_store(elems_ptr, slot, offset); + } + + // Store the length { let length = builder.ins().iconst(env.ptr_sized_int(), elems.len() as i64); + let offset = Offset32::new((Builtin::WRAPPER_LEN * ptr_bytes) as i32); - builder - .ins() - .stack_store(length, slot, Offset32::new(ptr_bytes as i32)); + builder.ins().stack_store(length, slot, offset); } // Return the pointer to the wrapper @@ -797,14 +798,13 @@ fn call_by_name<'a, B: Backend>( debug_assert!(args.len() == 1); let list_ptr = build_arg(&args[0], env, scope, module, builder, procs); + let ptr_bytes = env.cfg.pointer_bytes() as u32; + let offset = Offset32::new((Builtin::WRAPPER_LEN * ptr_bytes) as i32); // Get the usize list length - builder.ins().load( - env.ptr_sized_int(), - MemFlags::new(), - list_ptr, - Offset32::new(env.cfg.pointer_bytes() as i32), - ) + builder + .ins() + .load(env.ptr_sized_int(), MemFlags::new(), list_ptr, offset) } Symbol::INT_EQ_I64 | Symbol::INT_EQ_I8 | Symbol::INT_EQ_I1 => { debug_assert!(args.len() == 2); @@ -853,12 +853,12 @@ fn call_by_name<'a, B: Backend>( let (_list_expr, list_layout) = &args[0]; // Get the usize list length - let _list_len = builder.ins().load( - env.ptr_sized_int(), - MemFlags::new(), - wrapper_ptr, - Offset32::new(env.cfg.pointer_bytes() as i32), - ); + let ptr_bytes = env.cfg.pointer_bytes() as u32; + let offset = Offset32::new((Builtin::WRAPPER_LEN * ptr_bytes) as i32); + let _list_len = + builder + .ins() + .load(env.ptr_sized_int(), MemFlags::new(), wrapper_ptr, offset); // TODO compare elem_index to _list_len to do array bounds checking. @@ -898,6 +898,16 @@ fn call_by_name<'a, B: Backend>( let wrapper_ptr = build_arg(&args[0], env, scope, module, builder, procs); let (_list_expr, list_layout) = &args[0]; + // Get the usize list length + let ptr_bytes = env.cfg.pointer_bytes() as u32; + let offset = Offset32::new((Builtin::WRAPPER_LEN * ptr_bytes) as i32); + let _list_len = + builder + .ins() + .load(env.ptr_sized_int(), MemFlags::new(), wrapper_ptr, offset); + + // TODO do array bounds checking, and early return the original List if out of bounds + match list_layout { Layout::Builtin(Builtin::List(elem_layout)) => { let wrapper_ptr = clone_list(env, builder, module, wrapper_ptr, elem_layout); @@ -918,6 +928,17 @@ fn call_by_name<'a, B: Backend>( // set : List elem, Int, elem -> List elem debug_assert!(args.len() == 3); + // Get the usize list length + let wrapper_ptr = build_arg(&args[0], env, scope, module, builder, procs); + let ptr_bytes = env.cfg.pointer_bytes() as u32; + let offset = Offset32::new((Builtin::WRAPPER_LEN * ptr_bytes) as i32); + let _list_len = + builder + .ins() + .load(env.ptr_sized_int(), MemFlags::new(), wrapper_ptr, offset); + + // TODO do array bounds checking, and early return the original List if out of bounds + let (list_expr, list_layout) = &args[0]; let list_val = build_expr(env, scope, module, builder, list_expr, procs); @@ -1014,22 +1035,28 @@ fn clone_list( elem_layout: &Layout<'_>, ) -> Value { let cfg = env.cfg; + let ptr_bytes = env.cfg.pointer_bytes() as u32; // Load the pointer we got to the wrapper struct - let elems_ptr = builder.ins().load( - cfg.pointer_type(), - MemFlags::new(), - src_wrapper_ptr, - Offset32::new(0), - ); + let elems_ptr = { + let offset = Offset32::new((Builtin::WRAPPER_PTR * ptr_bytes) as i32); + + builder + .ins() + .load(cfg.pointer_type(), MemFlags::new(), src_wrapper_ptr, offset) + }; // Get the usize list length - let list_len = builder.ins().load( - env.ptr_sized_int(), - MemFlags::new(), - src_wrapper_ptr, - Offset32::new(env.cfg.pointer_bytes() as i32), - ); + let list_len = { + let offset = Offset32::new((Builtin::WRAPPER_LEN * ptr_bytes) as i32); + + builder.ins().load( + env.ptr_sized_int(), + MemFlags::new(), + src_wrapper_ptr, + offset, + ) + }; // Calculate the number of bytes we'll need to allocate. let elem_bytes = builder.ins().iconst( diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index ba6cb312fc..35ea443b69 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -3,13 +3,13 @@ use bumpalo::Bump; use inkwell::builder::Builder; use inkwell::context::Context; use inkwell::module::{Linkage, Module}; -use inkwell::types::{BasicTypeEnum, StructType}; +use inkwell::types::{BasicTypeEnum, IntType, StructType}; use inkwell::values::BasicValueEnum::{self, *}; use inkwell::values::{FunctionValue, IntValue, PointerValue, StructValue}; use inkwell::{AddressSpace, FloatPredicate, IntPredicate}; use crate::llvm::convert::{ - basic_type_from_layout, collection_wrapper, get_array_type, get_fn_type, + basic_type_from_layout, collection_wrapper, get_array_type, get_fn_type, ptr_int, }; use roc_collections::all::ImMap; use roc_module::symbol::{Interns, Symbol}; @@ -32,7 +32,13 @@ pub struct Env<'a, 'ctx, 'env> { pub builder: &'env Builder<'ctx>, pub module: &'ctx Module<'ctx>, pub interns: Interns, - pub pointer_bytes: u32, + pub ptr_bytes: u32, +} + +impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { + pub fn ptr_int(&self) -> IntType<'ctx> { + ptr_int(self.context, self.ptr_bytes) + } } #[allow(clippy::cognitive_complexity)] @@ -76,7 +82,8 @@ pub fn build_expr<'a, 'ctx, 'env>( ret_layout, cond_layout, } => { - let ret_type = basic_type_from_layout(env.arena, env.context, &ret_layout); + let ret_type = + basic_type_from_layout(env.arena, env.context, &ret_layout, env.ptr_bytes); let switch_args = SwitchArgs { cond_layout: cond_layout.clone(), cond_expr: cond, @@ -93,7 +100,7 @@ pub fn build_expr<'a, 'ctx, 'env>( for (symbol, layout, expr) in stores.iter() { let val = build_expr(env, &scope, parent, &expr, procs); - let expr_bt = basic_type_from_layout(env.arena, context, &layout); + let expr_bt = basic_type_from_layout(env.arena, context, &layout, env.ptr_bytes); let alloca = create_entry_block_alloca( env, parent, @@ -146,14 +153,14 @@ pub fn build_expr<'a, 'ctx, 'env>( build_basic_phi2(env, parent, comparison, then_val, else_val, ret_type) } _ => { - let mut arg_vals: Vec = + let mut arg_tuples: Vec<(BasicValueEnum, &'a Layout<'a>)> = Vec::with_capacity_in(args.len(), env.arena); - for (arg, _layout) in args.iter() { - arg_vals.push(build_expr(env, scope, parent, arg, procs)); + for (arg, layout) in args.iter() { + arg_tuples.push((build_expr(env, scope, parent, arg, procs), layout)); } - call_with_args(*symbol, arg_vals.into_bump_slice(), env) + call_with_args(*symbol, arg_tuples.into_bump_slice(), env) } }, FunctionPointer(symbol) => { @@ -189,7 +196,6 @@ pub fn build_expr<'a, 'ctx, 'env>( .left() .unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer.")) } - Load(symbol) => match scope.get(symbol) { Some((_, ptr)) => env .builder @@ -233,13 +239,13 @@ pub fn build_expr<'a, 'ctx, 'env>( } Array { elem_layout, elems } => { let ctx = env.context; - let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout); + let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes); let builder = env.builder; if elems.is_empty() { let array_type = get_array_type(&elem_type, 0); let ptr_type = array_type.ptr_type(AddressSpace::Generic); - let struct_type = collection_wrapper(ctx, ptr_type); + let struct_type = collection_wrapper(ctx, ptr_type, env.ptr_bytes); // The first field in the struct should be the pointer. let struct_val = builder @@ -254,11 +260,12 @@ pub fn build_expr<'a, 'ctx, 'env>( BasicValueEnum::StructValue(struct_val.into_struct_value()) } else { let len_u64 = elems.len() as u64; - let elem_bytes = elem_layout.stack_size(env.pointer_bytes) as u64; + let elem_bytes = elem_layout.stack_size(env.ptr_bytes) as u64; let ptr = { let bytes_len = elem_bytes * len_u64; - let len = ctx.i32_type().const_int(bytes_len, false); + let len_type = env.ptr_int(); + let len = len_type.const_int(bytes_len, false); env.builder .build_array_malloc(elem_type, len, "create_list_ptr") @@ -276,8 +283,8 @@ pub fn build_expr<'a, 'ctx, 'env>( } let ptr_val = BasicValueEnum::PointerValue(ptr); - let struct_type = collection_wrapper(ctx, ptr.get_type()); - let len = BasicValueEnum::IntValue(ctx.i32_type().const_int(len_u64, false)); + let struct_type = collection_wrapper(ctx, ptr.get_type(), env.ptr_bytes); + let len = BasicValueEnum::IntValue(env.ptr_int().const_int(len_u64, false)); let mut struct_val; // Field 0: pointer @@ -295,16 +302,6 @@ pub fn build_expr<'a, 'ctx, 'env>( .build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len") .unwrap(); - // Field 2: capacity (initially set to length) - struct_val = builder - .build_insert_value( - struct_val, - len, - Builtin::WRAPPER_CAPACITY, - "insert_capacity", - ) - .unwrap(); - BasicValueEnum::StructValue(struct_val.into_struct_value()) } } @@ -320,7 +317,8 @@ pub fn build_expr<'a, 'ctx, 'env>( for (field_expr, field_layout) in sorted_fields.iter() { let val = build_expr(env, &scope, parent, field_expr, procs); - let field_type = basic_type_from_layout(env.arena, env.context, &field_layout); + let field_type = + basic_type_from_layout(env.arena, env.context, &field_layout, env.ptr_bytes); field_types.push(field_type); field_vals.push(val); @@ -356,7 +354,8 @@ pub fn build_expr<'a, 'ctx, 'env>( for (field_expr, field_layout) in it { let val = build_expr(env, &scope, parent, field_expr, procs); - let field_type = basic_type_from_layout(env.arena, env.context, &field_layout); + let field_type = + basic_type_from_layout(env.arena, env.context, &field_layout, env.ptr_bytes); field_types.push(field_type); field_vals.push(val); @@ -382,7 +381,7 @@ pub fn build_expr<'a, 'ctx, 'env>( tag_id, .. } => { - let ptr_size = env.pointer_bytes; + let ptr_size = env.ptr_bytes; let whole_size = tag_layout.stack_size(ptr_size); let mut filler = tag_layout.stack_size(ptr_size); @@ -414,7 +413,8 @@ pub fn build_expr<'a, 'ctx, 'env>( for (field_expr, field_layout) in arguments.iter() { let val = build_expr(env, &scope, parent, field_expr, procs); - let field_type = basic_type_from_layout(env.arena, env.context, &field_layout); + let field_type = + basic_type_from_layout(env.arena, env.context, &field_layout, ptr_size); field_types.push(field_type); field_vals.push(val); @@ -559,9 +559,11 @@ pub fn build_expr<'a, 'ctx, 'env>( // Determine types, assumes the descriminant is in the field layouts let num_fields = field_layouts.len(); let mut field_types = Vec::with_capacity_in(num_fields, env.arena); + let ptr_bytes = env.ptr_bytes; for field_layout in field_layouts.iter() { - let field_type = basic_type_from_layout(env.arena, env.context, &field_layout); + let field_type = + basic_type_from_layout(env.arena, env.context, &field_layout, ptr_bytes); field_types.push(field_type); } @@ -648,7 +650,7 @@ fn build_branch2<'a, 'ctx, 'env>( procs: &Procs<'a>, ) -> BasicValueEnum<'ctx> { let ret_layout = cond.ret_layout; - let ret_type = basic_type_from_layout(env.arena, env.context, &ret_layout); + let ret_type = basic_type_from_layout(env.arena, env.context, &ret_layout, env.ptr_bytes); let cond_expr = build_expr(env, scope, parent, cond.cond, procs); @@ -877,12 +879,12 @@ pub fn build_proc_header<'a, 'ctx, 'env>( let args = proc.args; let arena = env.arena; let context = &env.context; - let ret_type = basic_type_from_layout(arena, context, &proc.ret_layout); + let ret_type = basic_type_from_layout(arena, context, &proc.ret_layout, env.ptr_bytes); let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena); let mut arg_symbols = Vec::new_in(arena); for (layout, arg_symbol) in args.iter() { - let arg_type = basic_type_from_layout(arena, env.context, &layout); + let arg_type = basic_type_from_layout(arena, env.context, &layout, env.ptr_bytes); arg_basic_types.push(arg_type); arg_symbols.push(arg_symbol); @@ -950,7 +952,7 @@ pub fn verify_fn(fn_val: FunctionValue<'_>) { #[allow(clippy::cognitive_complexity)] fn call_with_args<'a, 'ctx, 'env>( symbol: Symbol, - args: &[BasicValueEnum<'ctx>], + args: &[(BasicValueEnum<'ctx>, &'a Layout<'a>)], env: &Env<'a, 'ctx, 'env>, ) -> BasicValueEnum<'ctx> { match symbol { @@ -958,8 +960,8 @@ fn call_with_args<'a, 'ctx, 'env>( debug_assert!(args.len() == 2); let int_val = env.builder.build_int_add( - args[0].into_int_value(), - args[1].into_int_value(), + args[0].0.into_int_value(), + args[1].0.into_int_value(), "add_i64", ); @@ -969,8 +971,8 @@ fn call_with_args<'a, 'ctx, 'env>( debug_assert!(args.len() == 2); let float_val = env.builder.build_float_add( - args[0].into_float_value(), - args[1].into_float_value(), + args[0].0.into_float_value(), + args[1].0.into_float_value(), "add_f64", ); @@ -980,8 +982,8 @@ fn call_with_args<'a, 'ctx, 'env>( debug_assert!(args.len() == 2); let int_val = env.builder.build_int_sub( - args[0].into_int_value(), - args[1].into_int_value(), + args[0].0.into_int_value(), + args[1].0.into_int_value(), "sub_I64", ); @@ -991,8 +993,8 @@ fn call_with_args<'a, 'ctx, 'env>( debug_assert!(args.len() == 2); let float_val = env.builder.build_float_sub( - args[0].into_float_value(), - args[1].into_float_value(), + args[0].0.into_float_value(), + args[1].0.into_float_value(), "sub_f64", ); @@ -1002,8 +1004,8 @@ fn call_with_args<'a, 'ctx, 'env>( debug_assert!(args.len() == 2); let int_val = env.builder.build_int_mul( - args[0].into_int_value(), - args[1].into_int_value(), + args[0].0.into_int_value(), + args[1].0.into_int_value(), "mul_i64", ); @@ -1014,29 +1016,26 @@ fn call_with_args<'a, 'ctx, 'env>( let int_val = env .builder - .build_int_neg(args[0].into_int_value(), "negate_i64"); + .build_int_neg(args[0].0.into_int_value(), "negate_i64"); BasicValueEnum::IntValue(int_val) } Symbol::LIST_LEN => { debug_assert!(args.len() == 1); - let wrapper_struct = args[0].into_struct_value(); + let wrapper_struct = args[0].0.into_struct_value(); let builder = env.builder; - // Get the 32-bit int length - let i32_val = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "unwrapped_list_len").unwrap().into_int_value(); - - // cast the 32-bit length to a 64-bit int - BasicValueEnum::IntValue(builder.build_int_cast(i32_val, env.context.i64_type(), "i32_to_i64")) + // Get the usize int length + builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "unwrapped_list_len").unwrap().into_int_value().into() } Symbol::LIST_IS_EMPTY => { debug_assert!(args.len() == 1); - let list_struct = args[0].into_struct_value(); + let list_struct = args[0].0.into_struct_value(); let builder = env.builder; let list_len = builder.build_extract_value(list_struct, 1, "unwrapped_list_len").unwrap().into_int_value(); - let zero = env.context.i32_type().const_zero(); + let zero = env.ptr_int().const_zero(); let answer = builder.build_int_compare(IntPredicate::EQ, list_len, zero, "is_zero"); BasicValueEnum::IntValue(answer) @@ -1046,8 +1045,8 @@ fn call_with_args<'a, 'ctx, 'env>( let int_val = env.builder.build_int_compare( IntPredicate::EQ, - args[0].into_int_value(), - args[1].into_int_value(), + args[0].0.into_int_value(), + args[1].0.into_int_value(), "cmp_i64", ); @@ -1058,8 +1057,8 @@ fn call_with_args<'a, 'ctx, 'env>( let int_val = env.builder.build_int_compare( IntPredicate::EQ, - args[0].into_int_value(), - args[1].into_int_value(), + args[0].0.into_int_value(), + args[1].0.into_int_value(), "cmp_i1", ); @@ -1070,8 +1069,8 @@ fn call_with_args<'a, 'ctx, 'env>( let int_val = env.builder.build_int_compare( IntPredicate::EQ, - args[0].into_int_value(), - args[1].into_int_value(), + args[0].0.into_int_value(), + args[1].0.into_int_value(), "cmp_i8", ); @@ -1082,8 +1081,8 @@ fn call_with_args<'a, 'ctx, 'env>( let int_val = env.builder.build_float_compare( FloatPredicate::OEQ, - args[0].into_float_value(), - args[1].into_float_value(), + args[0].0.into_float_value(), + args[1].0.into_float_value(), "cmp_f64", ); @@ -1095,36 +1094,45 @@ fn call_with_args<'a, 'ctx, 'env>( // List.get : List elem, Int -> Result elem [ OutOfBounds ]* debug_assert!(args.len() == 2); - let wrapper_struct = args[0].into_struct_value(); - let elem_index = args[1].into_int_value(); + let (_list_expr, list_layout) = &args[0]; - // Slot 1 in the wrapper struct is the length + let wrapper_struct = args[0].0.into_struct_value(); + let elem_index = args[1].0.into_int_value(); + + // Get the length from the wrapper struct let _list_len = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "unwrapped_list_len").unwrap().into_int_value(); // TODO here, check to see if the requested index exceeds the length of the array. - // Slot 0 in the wrapper struct is the pointer to the array data - let array_data_ptr = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "unwrapped_list_ptr").unwrap().into_pointer_value(); + match list_layout { + Layout::Builtin(Builtin::List(elem_layout)) => { + // Get the pointer to the array data + let array_data_ptr = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "unwrapped_list_ptr").unwrap().into_pointer_value(); - let elem_bytes = 8; // TODO Look this size up instead of hardcoding it! - let elem_size = env.context.i64_type().const_int(elem_bytes, false); + let elem_bytes = elem_layout.stack_size(env.ptr_bytes) as u64; + let elem_size = env.context.i64_type().const_int(elem_bytes, false); - // Calculate the offset at runtime by multiplying the index by the size of an element. - let offset_bytes = builder.build_int_mul(elem_index, elem_size, "mul_offset"); + // Calculate the offset at runtime by multiplying the index by the size of an element. + let offset_bytes = builder.build_int_mul(elem_index, elem_size, "mul_offset"); - // We already checked the bounds earlier. - let elem_ptr = unsafe { builder.build_in_bounds_gep(array_data_ptr, &[offset_bytes], "elem") }; + // We already checked the bounds earlier. + let elem_ptr = unsafe { builder.build_in_bounds_gep(array_data_ptr, &[offset_bytes], "elem") }; - builder.build_load(elem_ptr, "List.get") + builder.build_load(elem_ptr, "List.get") + } + _ => { + unreachable!("Invalid List layout for List.get: {:?}", list_layout); + } + } } Symbol::LIST_SET /* TODO clone first for LIST_SET! */ | Symbol::LIST_SET_IN_PLACE => { let builder = env.builder; debug_assert!(args.len() == 3); - let wrapper_struct = args[0].into_struct_value(); - let elem_index = args[1].into_int_value(); - let elem = args[2]; + let wrapper_struct = args[0].0.into_struct_value(); + let elem_index = args[1].0.into_int_value(); + let (elem, elem_layout) = args[2]; // Slot 1 in the wrapper struct is the length let _list_len = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "unwrapped_list_len").unwrap().into_int_value(); @@ -1135,7 +1143,7 @@ fn call_with_args<'a, 'ctx, 'env>( // Slot 0 in the wrapper struct is the pointer to the array data let array_data_ptr = builder.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "unwrapped_list_ptr").unwrap().into_pointer_value(); - let elem_bytes = 8; // TODO Look this size up instead of hardcoding it! + let elem_bytes = elem_layout.stack_size(env.ptr_bytes) as u64; let elem_size = env.context.i64_type().const_int(elem_bytes, false); // Calculate the offset at runtime by multiplying the index by the size of an element. @@ -1156,7 +1164,13 @@ fn call_with_args<'a, 'ctx, 'env>( .get_function(symbol.ident_string(&env.interns)) .unwrap_or_else(|| panic!("Unrecognized function: {:?}", symbol)); - let call = env.builder.build_call(fn_val, args, "tmp"); + let mut arg_vals: Vec = Vec::with_capacity_in(args.len(), env.arena); + + for (arg, _layout) in args.iter() { + arg_vals.push(*arg); + } + + let call = env.builder.build_call(fn_val, arg_vals.into_bump_slice(), "call"); call.try_as_basic_value() .left() diff --git a/compiler/gen/src/llvm/convert.rs b/compiler/gen/src/llvm/convert.rs index 1581c349c3..0d6b558b14 100644 --- a/compiler/gen/src/llvm/convert.rs +++ b/compiler/gen/src/llvm/convert.rs @@ -2,7 +2,7 @@ use bumpalo::collections::Vec; use bumpalo::Bump; use inkwell::context::Context; use inkwell::types::BasicTypeEnum::{self, *}; -use inkwell::types::{ArrayType, BasicType, FunctionType, PointerType, StructType}; +use inkwell::types::{ArrayType, BasicType, FunctionType, IntType, PointerType, StructType}; use inkwell::AddressSpace; use roc_mono::layout::Layout; @@ -38,17 +38,20 @@ pub fn basic_type_from_layout<'ctx>( arena: &Bump, context: &'ctx Context, layout: &Layout<'_>, + ptr_bytes: u32, ) -> BasicTypeEnum<'ctx> { use roc_mono::layout::Builtin::*; use roc_mono::layout::Layout::*; match layout { FunctionPointer(args, ret_layout) => { - let ret_type = basic_type_from_layout(arena, context, &ret_layout); + let ret_type = basic_type_from_layout(arena, context, &ret_layout, ptr_bytes); let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena); for arg_layout in args.iter() { - arg_basic_types.push(basic_type_from_layout(arena, context, arg_layout)); + arg_basic_types.push(basic_type_from_layout( + arena, context, arg_layout, ptr_bytes, + )); } let fn_type = get_fn_type(&ret_type, arg_basic_types.into_bump_slice()); @@ -61,7 +64,12 @@ pub fn basic_type_from_layout<'ctx>( let mut field_types = Vec::with_capacity_in(sorted_fields.len(), arena); for (_, field_layout) in sorted_fields.iter() { - field_types.push(basic_type_from_layout(arena, context, field_layout)); + field_types.push(basic_type_from_layout( + arena, + context, + field_layout, + ptr_bytes, + )); } context @@ -75,7 +83,7 @@ pub fn basic_type_from_layout<'ctx>( let mut field_types = Vec::with_capacity_in(layouts.len(), arena); for layout in layouts.iter() { - field_types.push(basic_type_from_layout(arena, context, layout)); + field_types.push(basic_type_from_layout(arena, context, layout, ptr_bytes)); } context @@ -107,17 +115,17 @@ pub fn basic_type_from_layout<'ctx>( Map(_, _) | EmptyMap => panic!("TODO layout_to_basic_type for Builtin::Map"), Set(_) | EmptySet => panic!("TODO layout_to_basic_type for Builtin::Set"), List(elem_layout) => { - let ptr_type = basic_type_from_layout(arena, context, elem_layout) + let ptr_type = basic_type_from_layout(arena, context, elem_layout, ptr_bytes) .ptr_type(AddressSpace::Generic); - collection_wrapper(context, ptr_type).into() + collection_wrapper(context, ptr_type, ptr_bytes).into() } EmptyList => { let array_type = get_array_type(&context.opaque_struct_type("empty_list_elem").into(), 0); let ptr_type = array_type.ptr_type(AddressSpace::Generic); - collection_wrapper(context, ptr_type).into() + collection_wrapper(context, ptr_type, ptr_bytes).into() } }, } @@ -127,9 +135,24 @@ pub fn basic_type_from_layout<'ctx>( pub fn collection_wrapper<'ctx>( ctx: &'ctx Context, ptr_type: PointerType<'ctx>, + ptr_bytes: u32, ) -> StructType<'ctx> { let ptr_type_enum = BasicTypeEnum::PointerType(ptr_type); - let u32_type = BasicTypeEnum::IntType(ctx.i32_type()); + let len_type = BasicTypeEnum::IntType(ptr_int(ctx, ptr_bytes)); - ctx.struct_type(&[ptr_type_enum, u32_type, u32_type], false) + ctx.struct_type(&[ptr_type_enum, len_type], false) +} + +pub fn ptr_int(ctx: &Context, ptr_bytes: u32) -> IntType<'_> { + match ptr_bytes { + 1 => ctx.i8_type(), + 2 => ctx.i16_type(), + 4 => ctx.i32_type(), + 8 => ctx.i64_type(), + 16 => ctx.i128_type(), + _ => panic!( + "Invalid target: Roc does't support compiling to {}-bit systems.", + ptr_bytes * 8 + ), + } } diff --git a/compiler/gen/tests/test_gen.rs b/compiler/gen/tests/test_gen.rs index e31b977b3d..529607d7c9 100644 --- a/compiler/gen/tests/test_gen.rs +++ b/compiler/gen/tests/test_gen.rs @@ -202,16 +202,16 @@ mod test_gen { // Compute main_fn_type before moving subs to Env let layout = Layout::from_content(&arena, content, &subs, POINTER_SIZE) .unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs)); - let main_fn_type = basic_type_from_layout(&arena, &context, &layout) - .fn_type(&[], false); - let main_fn_name = "$Test.main"; - let execution_engine = module .create_jit_execution_engine(OptimizationLevel::None) .expect("Error creating JIT execution engine for test"); - let pointer_bytes = execution_engine.get_target_data().get_pointer_byte_size(None); + let ptr_bytes = execution_engine.get_target_data().get_pointer_byte_size(None); + + let main_fn_type = basic_type_from_layout(&arena, &context, &layout, ptr_bytes) + .fn_type(&[], false); + let main_fn_name = "$Test.main"; // Compile and add all the Procs before adding main let mut env = roc_gen::llvm::build::Env { @@ -220,7 +220,7 @@ mod test_gen { context: &context, interns, module: arena.alloc(module), - pointer_bytes + ptr_bytes }; let mut procs = Procs::default(); let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap(); @@ -337,16 +337,16 @@ mod test_gen { // Compute main_fn_type before moving subs to Env let layout = Layout::from_content(&arena, content, &subs, POINTER_SIZE) .unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs)); - let main_fn_type = basic_type_from_layout(&arena, &context, &layout) - .fn_type(&[], false); - let main_fn_name = "$Test.main"; let execution_engine = module .create_jit_execution_engine(OptimizationLevel::None) .expect("Error creating JIT execution engine for test"); - let pointer_bytes = execution_engine.get_target_data().get_pointer_byte_size(None); + let ptr_bytes = execution_engine.get_target_data().get_pointer_byte_size(None); + let main_fn_type = basic_type_from_layout(&arena, &context, &layout, ptr_bytes) + .fn_type(&[], false); + let main_fn_name = "$Test.main"; // Compile and add all the Procs before adding main let mut env = roc_gen::llvm::build::Env { @@ -355,7 +355,7 @@ mod test_gen { context: &context, interns, module: arena.alloc(module), - pointer_bytes + ptr_bytes }; let mut procs = Procs::default(); let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();