From 2d23adb2a119482326faef72d1f70e4263814c67 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jan 2022 13:55:06 +0100 Subject: [PATCH 01/36] add roc_target crate --- .gitignore | 2 +- Cargo.lock | 7 +++++++ Cargo.toml | 1 + compiler/target/Cargo.toml | 9 +++++++++ compiler/target/src/lib.rs | 15 +++++++++++++++ 5 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 compiler/target/Cargo.toml create mode 100644 compiler/target/src/lib.rs diff --git a/.gitignore b/.gitignore index 87635559c0..d5960bf0cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -target +target/ generated-docs zig-cache .direnv diff --git a/Cargo.lock b/Cargo.lock index f98de87446..7e01948455 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3717,6 +3717,13 @@ dependencies = [ "quickcheck_macros", ] +[[package]] +name = "roc_target" +version = "0.1.0" +dependencies = [ + "target-lexicon", +] + [[package]] name = "roc_test_utils" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 465e29e8a3..28a4f6193e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ members = [ "compiler/build", "compiler/arena_pool", "compiler/test_gen", + "compiler/target", "vendor/ena", "vendor/inkwell", "vendor/pathfinding", diff --git a/compiler/target/Cargo.toml b/compiler/target/Cargo.toml new file mode 100644 index 0000000000..28211dbdc9 --- /dev/null +++ b/compiler/target/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "roc_target" +version = "0.1.0" +authors = ["The Roc Contributors"] +license = "UPL-1.0" +edition = "2018" + +[dependencies] +target-lexicon = "0.12.2" diff --git a/compiler/target/src/lib.rs b/compiler/target/src/lib.rs new file mode 100644 index 0000000000..75e3f98111 --- /dev/null +++ b/compiler/target/src/lib.rs @@ -0,0 +1,15 @@ +#![warn(clippy::dbg_macro)] +// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +#![allow(clippy::large_enum_variant)] + +pub struct TargetInfo { + architecture: Architecture, +} + +pub enum Architecture { + X86_64, + X86_32, + Aarch64, + Arm, + Wasm32, +} From 456404ccfecb943b710318e2d61b548d20485766 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jan 2022 14:01:53 +0100 Subject: [PATCH 02/36] add some helpers --- compiler/target/src/lib.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/compiler/target/src/lib.rs b/compiler/target/src/lib.rs index 75e3f98111..933cc2f6c6 100644 --- a/compiler/target/src/lib.rs +++ b/compiler/target/src/lib.rs @@ -13,3 +13,27 @@ pub enum Architecture { Arm, Wasm32, } + +impl Architecture { + pub const fn ptr_width(&self) -> u32 { + use Architecture::*; + + match self { + X86_64 | Aarch64 | Arm => 8, + X86_32 | Wasm32 => 4, + } + } +} + +impl From for Architecture { + fn from(target: target_lexicon::Architecture) -> Self { + match target { + target_lexicon::Architecture::X86_64 => Architecture::X86_64, + target_lexicon::Architecture::X86_32(_) => Architecture::X86_32, + target_lexicon::Architecture::Aarch64(_) => Architecture::Aarch64, + target_lexicon::Architecture::Arm(_) => Architecture::Arm, + target_lexicon::Architecture::Wasm32 => Architecture::Wasm32, + _ => unreachable!("unsupported architecture"), + } + } +} From 7e9081233249196758b53ec53b14a3424a31747b Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jan 2022 14:28:26 +0100 Subject: [PATCH 03/36] ptr_bytes -> target info, step 1 --- Cargo.lock | 3 + compiler/builtins/Cargo.toml | 1 + compiler/gen_llvm/Cargo.toml | 1 + compiler/gen_llvm/src/llvm/build.rs | 109 +++++----- compiler/mono/Cargo.toml | 1 + compiler/mono/src/layout.rs | 302 ++++++++++++++-------------- compiler/target/src/lib.rs | 27 ++- 7 files changed, 240 insertions(+), 204 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e01948455..4bf5787f06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3297,6 +3297,7 @@ dependencies = [ "roc_collections", "roc_module", "roc_region", + "roc_target", "roc_types", ] @@ -3526,6 +3527,7 @@ dependencies = [ "roc_module", "roc_mono", "roc_std", + "roc_target", "target-lexicon", ] @@ -3621,6 +3623,7 @@ dependencies = [ "roc_region", "roc_solve", "roc_std", + "roc_target", "roc_types", "roc_unify", "static_assertions", diff --git a/compiler/builtins/Cargo.toml b/compiler/builtins/Cargo.toml index 9321839c93..4b73584449 100644 --- a/compiler/builtins/Cargo.toml +++ b/compiler/builtins/Cargo.toml @@ -10,3 +10,4 @@ roc_collections = { path = "../collections" } roc_region = { path = "../region" } roc_module = { path = "../module" } roc_types = { path = "../types" } +roc_target = { path = "../target" } diff --git a/compiler/gen_llvm/Cargo.toml b/compiler/gen_llvm/Cargo.toml index 32a79c4767..591f4ff92c 100644 --- a/compiler/gen_llvm/Cargo.toml +++ b/compiler/gen_llvm/Cargo.toml @@ -12,6 +12,7 @@ roc_module = { path = "../module" } roc_builtins = { path = "../builtins" } roc_error_macros = { path = "../../error_macros" } roc_mono = { path = "../mono" } +roc_target = { path = "../target" } roc_std = { path = "../../roc_std" } morphic_lib = { path = "../../vendor/morphic_lib" } bumpalo = { version = "3.8.0", features = ["collections"] } diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index d1a08a4bd8..94ffe74b89 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -63,6 +63,7 @@ use roc_mono::ir::{ ModifyRc, OptLevel, ProcLayout, }; use roc_mono::layout::{Builtin, LambdaSet, Layout, LayoutIds, TagIdIntType, UnionLayout}; +use roc_target::TargetInfo; use target_lexicon::{Architecture, OperatingSystem, Triple}; /// This is for Inkwell's FunctionValue::verify - we want to know the verification @@ -166,7 +167,7 @@ pub struct Env<'a, 'ctx, 'env> { pub compile_unit: &'env DICompileUnit<'ctx>, pub module: &'ctx Module<'ctx>, pub interns: Interns, - pub ptr_bytes: u32, + pub target_info: TargetInfo, pub is_gen_test: bool, pub exposed_to_host: MutSet, } @@ -195,14 +196,14 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { pub fn ptr_int(&self) -> IntType<'ctx> { let ctx = self.context; - match self.ptr_bytes { + match self.target_info { 1 => ctx.i8_type(), 2 => ctx.i16_type(), 4 => ctx.i32_type(), 8 => ctx.i64_type(), _ => panic!( "Invalid target: Roc does't support compiling to {}-bit systems.", - self.ptr_bytes * 8 + self.target_info * 8 ), } } @@ -212,11 +213,11 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { /// on 64-bit systems, this is i128 /// on 32-bit systems, this is i64 pub fn str_list_c_abi(&self) -> IntType<'ctx> { - crate::llvm::convert::str_list_int(self.context, self.ptr_bytes) + crate::llvm::convert::str_list_int(self.context, self.target_info) } pub fn small_str_bytes(&self) -> u32 { - self.ptr_bytes * 2 + self.target_info * 2 } pub fn build_intrinsic_call( @@ -269,7 +270,7 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { } pub fn alignment_intvalue(&self, element_layout: &Layout<'a>) -> BasicValueEnum<'ctx> { - let alignment = element_layout.alignment_bytes(self.ptr_bytes); + let alignment = element_layout.alignment_bytes(self.target_info); let alignment_iv = self.alignment_const(alignment); alignment_iv.into() @@ -317,7 +318,7 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { ) -> CallSiteValue<'ctx> { let false_val = self.context.bool_type().const_int(0, false); - let intrinsic_name = match self.ptr_bytes { + let intrinsic_name = match self.target_info { 8 => LLVM_MEMSET_I64, 4 => LLVM_MEMSET_I32, other => { @@ -1438,7 +1439,7 @@ fn build_wrapped_tag<'a, 'ctx, 'env>( let raw_data_ptr = allocate_tag(env, parent, reuse_allocation, union_layout, tags); let struct_type = env.context.struct_type(&field_types, false); - if union_layout.stores_tag_id_as_data(env.ptr_bytes) { + if union_layout.stores_tag_id_as_data(env.target_info) { let tag_id_ptr = builder .build_struct_gep(raw_data_ptr, TAG_ID_INDEX, "tag_id_index") .unwrap(); @@ -1524,7 +1525,7 @@ pub fn build_tag<'a, 'ctx, 'env>( UnionLayout::NonRecursive(tags) => { debug_assert!(union_size > 1); - let internal_type = block_of_memory_slices(env.context, tags, env.ptr_bytes); + let internal_type = block_of_memory_slices(env.context, tags, env.target_info); let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type(); let wrapper_type = env @@ -1711,7 +1712,7 @@ pub fn build_tag<'a, 'ctx, 'env>( other_fields, } => { let tag_struct_type = - block_of_memory_slices(env.context, &[other_fields], env.ptr_bytes); + block_of_memory_slices(env.context, &[other_fields], env.target_info); if tag_id == *nullable_id as _ { let output_type = tag_struct_type.ptr_type(AddressSpace::Generic); @@ -1788,7 +1789,7 @@ fn tag_pointer_set_tag_id<'a, 'ctx, 'env>( pointer: PointerValue<'ctx>, ) -> PointerValue<'ctx> { // we only have 3 bits, so can encode only 0..7 (or on 32-bit targets, 2 bits to encode 0..3) - debug_assert!((tag_id as u32) < env.ptr_bytes); + debug_assert!((tag_id as u32) < env.target_info); let ptr_int = env.ptr_int(); @@ -1813,7 +1814,7 @@ pub fn tag_pointer_read_tag_id<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, pointer: PointerValue<'ctx>, ) -> IntValue<'ctx> { - let (_, mask) = tag_pointer_tag_id_bits_and_mask(env.ptr_bytes); + let (_, mask) = tag_pointer_tag_id_bits_and_mask(env.target_info); let ptr_int = env.ptr_int(); let as_int = env.builder.build_ptr_to_int(pointer, ptr_int, "to_int"); @@ -1831,7 +1832,7 @@ pub fn tag_pointer_clear_tag_id<'a, 'ctx, 'env>( ) -> PointerValue<'ctx> { let ptr_int = env.ptr_int(); - let (tag_id_bits_mask, _) = tag_pointer_tag_id_bits_and_mask(env.ptr_bytes); + let (tag_id_bits_mask, _) = tag_pointer_tag_id_bits_and_mask(env.target_info); let as_int = env.builder.build_ptr_to_int(pointer, ptr_int, "to_int"); @@ -1918,7 +1919,7 @@ pub fn get_tag_id<'a, 'ctx, 'env>( UnionLayout::Recursive(_) => { let argument_ptr = argument.into_pointer_value(); - if union_layout.stores_tag_id_as_data(env.ptr_bytes) { + if union_layout.stores_tag_id_as_data(env.target_info) { get_tag_id_wrapped(env, argument_ptr) } else { tag_pointer_read_tag_id(env, argument_ptr) @@ -1949,7 +1950,7 @@ pub fn get_tag_id<'a, 'ctx, 'env>( { env.builder.position_at_end(else_block); - let tag_id = if union_layout.stores_tag_id_as_data(env.ptr_bytes) { + let tag_id = if union_layout.stores_tag_id_as_data(env.target_info) { get_tag_id_wrapped(env, argument_ptr) } else { tag_pointer_read_tag_id(env, argument_ptr) @@ -2057,12 +2058,12 @@ fn lookup_at_index_ptr2<'a, 'ctx, 'env>( let result = if field_layout.is_passed_by_reference() { let field_type = basic_type_from_layout(env, &field_layout); - let align_bytes = field_layout.alignment_bytes(env.ptr_bytes); + let align_bytes = field_layout.alignment_bytes(env.target_info); let alloca = tag_alloca(env, field_type, "copied_tag"); if align_bytes > 0 { let size = env .ptr_int() - .const_int(field_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(field_layout.stack_size(env.target_info) as u64, false); env.builder .build_memcpy(alloca, align_bytes, elem_ptr, align_bytes, size) @@ -2095,8 +2096,8 @@ pub fn reserve_with_refcount<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>, ) -> PointerValue<'ctx> { - let stack_size = layout.stack_size(env.ptr_bytes); - let alignment_bytes = layout.alignment_bytes(env.ptr_bytes); + let stack_size = layout.stack_size(env.target_info); + let alignment_bytes = layout.alignment_bytes(env.target_info); let basic_type = basic_type_from_layout(env, layout); @@ -2108,9 +2109,9 @@ fn reserve_with_refcount_union_as_block_of_memory<'a, 'ctx, 'env>( union_layout: UnionLayout<'a>, fields: &[&[Layout<'a>]], ) -> PointerValue<'ctx> { - let ptr_bytes = env.ptr_bytes; + let ptr_bytes = env.target_info; - let block_type = block_of_memory_slices(env.context, fields, env.ptr_bytes); + let block_type = block_of_memory_slices(env.context, fields, env.target_info); let basic_type = if union_layout.stores_tag_id_as_data(ptr_bytes) { let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout()); @@ -2124,17 +2125,17 @@ fn reserve_with_refcount_union_as_block_of_memory<'a, 'ctx, 'env>( let mut stack_size = fields .iter() - .map(|tag| tag.iter().map(|l| l.stack_size(env.ptr_bytes)).sum()) + .map(|tag| tag.iter().map(|l| l.stack_size(env.target_info)).sum()) .max() .unwrap_or_default(); if union_layout.stores_tag_id_as_data(ptr_bytes) { - stack_size += union_layout.tag_id_layout().stack_size(env.ptr_bytes); + stack_size += union_layout.tag_id_layout().stack_size(env.target_info); } let alignment_bytes = fields .iter() - .map(|tag| tag.iter().map(|l| l.alignment_bytes(env.ptr_bytes))) + .map(|tag| tag.iter().map(|l| l.alignment_bytes(env.target_info))) .flatten() .max() .unwrap_or(0); @@ -2154,7 +2155,7 @@ fn reserve_with_refcount_help<'a, 'ctx, 'env>( let value_bytes_intvalue = len_type.const_int(stack_size as u64, false); - let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes); + let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.target_info); allocate_with_refcount_help(env, basic_type, alignment_bytes, value_bytes_intvalue, rc1) } @@ -2183,7 +2184,7 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>( let len_type = env.ptr_int(); - let extra_bytes = alignment_bytes.max(env.ptr_bytes); + let extra_bytes = alignment_bytes.max(env.target_info); let ptr = { // number of bytes we will allocated @@ -2208,8 +2209,8 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>( .into_pointer_value(); let index = match extra_bytes { - n if n == env.ptr_bytes => 1, - n if n == 2 * env.ptr_bytes => 2, + n if n == env.target_info => 1, + n if n == 2 * env.target_info => 2, _ => unreachable!("invalid extra_bytes, {}", extra_bytes), }; @@ -2228,11 +2229,11 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>( }; let refcount_ptr = match extra_bytes { - n if n == env.ptr_bytes => { + n if n == env.target_info => { // the allocated pointer is the same as the refcounted pointer unsafe { PointerToRefcount::from_ptr(env, ptr) } } - n if n == 2 * env.ptr_bytes => { + n if n == 2 * env.target_info => { // the refcount is stored just before the start of the actual data // but in this case (because of alignment) not at the start of the allocated buffer PointerToRefcount::from_ptr_to_data(env, data_ptr) @@ -2283,14 +2284,14 @@ fn list_literal<'a, 'ctx, 'env>( // if element_type.is_int_type() { if false { let element_type = element_type.into_int_type(); - let element_width = element_layout.stack_size(env.ptr_bytes); + let element_width = element_layout.stack_size(env.target_info); let size = list_length * element_width as usize; let alignment = element_layout - .alignment_bytes(env.ptr_bytes) - .max(env.ptr_bytes); + .alignment_bytes(env.target_info) + .max(env.target_info); let mut is_all_constant = true; - let zero_elements = (env.ptr_bytes as f64 / element_width as f64).ceil() as usize; + let zero_elements = (env.target_info as f64 / element_width as f64).ceil() as usize; // runtime-evaluated elements let mut runtime_evaluated_elements = Vec::with_capacity_in(list_length, env.arena); @@ -2471,12 +2472,12 @@ pub fn store_roc_value<'a, 'ctx, 'env>( value: BasicValueEnum<'ctx>, ) { if layout.is_passed_by_reference() { - let align_bytes = layout.alignment_bytes(env.ptr_bytes); + let align_bytes = layout.alignment_bytes(env.target_info); if align_bytes > 0 { let size = env .ptr_int() - .const_int(layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(layout.stack_size(env.target_info) as u64, false); env.builder .build_memcpy( @@ -2572,7 +2573,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( let destination = out_parameter.into_pointer_value(); if layout.is_passed_by_reference() { - let align_bytes = layout.alignment_bytes(env.ptr_bytes); + let align_bytes = layout.alignment_bytes(env.target_info); if align_bytes > 0 { let value_ptr = value.into_pointer_value(); @@ -2585,7 +2586,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( } else { let size = env .ptr_int() - .const_int(layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(layout.stack_size(env.target_info) as u64, false); env.builder .build_memcpy( @@ -2776,21 +2777,21 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( match layout { Layout::Builtin(Builtin::List(element_layout)) => { debug_assert!(value.is_struct_value()); - let alignment = element_layout.alignment_bytes(env.ptr_bytes); + let alignment = element_layout.alignment_bytes(env.target_info); build_list::decref(env, value.into_struct_value(), alignment); } Layout::Builtin(Builtin::Dict(key_layout, value_layout)) => { debug_assert!(value.is_struct_value()); let alignment = key_layout - .alignment_bytes(env.ptr_bytes) - .max(value_layout.alignment_bytes(env.ptr_bytes)); + .alignment_bytes(env.target_info) + .max(value_layout.alignment_bytes(env.target_info)); build_dict::decref(env, value.into_struct_value(), alignment); } Layout::Builtin(Builtin::Set(key_layout)) => { debug_assert!(value.is_struct_value()); - let alignment = key_layout.alignment_bytes(env.ptr_bytes); + let alignment = key_layout.alignment_bytes(env.target_info); build_dict::decref(env, value.into_struct_value(), alignment); } @@ -3408,7 +3409,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx, 'env>( builder.position_at_end(entry); - let wrapped_layout = roc_result_layout(env.arena, return_layout, env.ptr_bytes); + let wrapped_layout = roc_result_layout(env.arena, return_layout, env.target_info); call_roc_function(env, roc_function, &wrapped_layout, arguments_for_call) } else { call_roc_function(env, roc_function, &return_layout, arguments_for_call) @@ -3726,7 +3727,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>( } pub fn get_sjlj_buffer<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValue<'ctx> { - let type_ = env.context.i8_type().array_type(5 * env.ptr_bytes); + let type_ = env.context.i8_type().array_type(5 * env.target_info); let global = match env.module.get_global("roc_sjlj_buffer") { Some(global) => global, @@ -4329,12 +4330,12 @@ pub fn build_closure_caller<'a, 'ctx, 'env>( let call_result = call_roc_function(env, evaluator, return_layout, &evaluator_arguments); if return_layout.is_passed_by_reference() { - let align_bytes = return_layout.alignment_bytes(env.ptr_bytes); + let align_bytes = return_layout.alignment_bytes(env.target_info); if align_bytes > 0 { let size = env .ptr_int() - .const_int(return_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(return_layout.stack_size(env.target_info) as u64, false); env.builder .build_memcpy( @@ -5020,7 +5021,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>( Layout::Builtin(Builtin::List(element_layout)), Layout::Builtin(Builtin::List(result_layout)), ) => { - let argument_layouts = &[Layout::usize(env.ptr_bytes), **element_layout]; + let argument_layouts = &[Layout::usize(env.target_info), **element_layout]; let roc_function_call = roc_function_call( env, @@ -6075,7 +6076,7 @@ fn run_low_level<'a, 'ctx, 'env>( { bd.position_at_end(throw_block); - match env.ptr_bytes { + match env.target_info { 8 => { let fn_ptr_type = context .void_type() @@ -6194,8 +6195,8 @@ enum 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 { - let return_size = layout.stack_size(env.ptr_bytes); - let pass_result_by_pointer = return_size > 2 * env.ptr_bytes; + let return_size = layout.stack_size(env.target_info); + let pass_result_by_pointer = return_size > 2 * env.target_info; if return_size == 0 { CCReturn::Void @@ -7129,7 +7130,7 @@ fn define_global_str_literal_ptr<'a, 'ctx, 'env>( let ptr = unsafe { env.builder.build_in_bounds_gep( ptr, - &[env.ptr_int().const_int(env.ptr_bytes as u64, false)], + &[env.ptr_int().const_int(env.target_info as u64, false)], "get_rc_ptr", ) }; @@ -7159,11 +7160,11 @@ fn define_global_str_literal<'a, 'ctx, 'env>( Some(current) => current, None => { - let size = message.bytes().len() + env.ptr_bytes as usize; + let size = message.bytes().len() + env.target_info as usize; let mut bytes = Vec::with_capacity_in(size, env.arena); // insert NULL bytes for the refcount - for _ in 0..env.ptr_bytes { + for _ in 0..env.target_info { bytes.push(env.context.i8_type().const_zero()); } @@ -7182,7 +7183,7 @@ fn define_global_str_literal<'a, 'ctx, 'env>( // strings are NULL-terminated, which means we can't store the refcount (which is 8 // NULL bytes) global.set_constant(true); - global.set_alignment(env.ptr_bytes); + global.set_alignment(env.target_info); global.set_unnamed_addr(true); global.set_linkage(inkwell::module::Linkage::Private); diff --git a/compiler/mono/Cargo.toml b/compiler/mono/Cargo.toml index 6cd3129ed3..88b33a8d82 100644 --- a/compiler/mono/Cargo.toml +++ b/compiler/mono/Cargo.toml @@ -16,6 +16,7 @@ roc_solve = { path = "../solve" } roc_std = { path = "../../roc_std" } roc_problem = { path = "../problem" } roc_builtins = { path = "../builtins" } +roc_target = { path = "../target" } ven_pretty = { path = "../../vendor/pretty" } morphic_lib = { path = "../../vendor/morphic_lib" } bumpalo = { version = "3.8.0", features = ["collections"] } diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 3d19699399..c56a10cb4d 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -6,6 +6,7 @@ use roc_collections::all::{default_hasher, MutMap}; use roc_module::ident::{Lowercase, TagName}; use roc_module::symbol::{Interns, Symbol}; use roc_problem::can::RuntimeError; +use roc_target::TargetInfo; use roc_types::subs::{ Content, FlatType, RecordFields, Subs, UnionTags, UnsortedUnionTags, Variable, }; @@ -123,7 +124,7 @@ impl<'a> RawFunctionLayout<'a> { // Nat Alias(Symbol::NUM_NAT, args, _) => { debug_assert!(args.is_empty()); - Ok(Self::ZeroArgumentThunk(Layout::usize(env.ptr_bytes))) + Ok(Self::ZeroArgumentThunk(Layout::usize(env.target_info))) } Alias(symbol, _, _) if symbol.is_builtin() => Ok(Self::ZeroArgumentThunk( @@ -158,7 +159,7 @@ impl<'a> RawFunctionLayout<'a> { let ret = arena.alloc(ret); let lambda_set = - LambdaSet::from_var(env.arena, env.subs, closure_var, env.ptr_bytes)?; + LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?; Ok(Self::Function(fn_args, lambda_set, ret)) } @@ -360,29 +361,29 @@ impl<'a> UnionLayout<'a> { Layout::Builtin(self.tag_id_builtin()) } - fn stores_tag_id_in_pointer_bits(tags: &[&[Layout<'a>]], ptr_bytes: u32) -> bool { - tags.len() < ptr_bytes as usize + fn stores_tag_id_in_pointer_bits(tags: &[&[Layout<'a>]], target_info: TargetInfo) -> bool { + tags.len() < target_info.ptr_width() as usize } // i.e. it is not implicit and not stored in the pointer bits - pub fn stores_tag_id_as_data(&self, ptr_bytes: u32) -> bool { + pub fn stores_tag_id_as_data(&self, target_info: TargetInfo) -> bool { match self { UnionLayout::NonRecursive(_) => true, UnionLayout::Recursive(tags) | UnionLayout::NullableWrapped { other_tags: tags, .. - } => !Self::stores_tag_id_in_pointer_bits(tags, ptr_bytes), + } => !Self::stores_tag_id_in_pointer_bits(tags, target_info), UnionLayout::NonNullableUnwrapped(_) | UnionLayout::NullableUnwrapped { .. } => false, } } - pub fn stores_tag_id_in_pointer(&self, ptr_bytes: u32) -> bool { + pub fn stores_tag_id_in_pointer(&self, target_info: TargetInfo) -> bool { match self { UnionLayout::NonRecursive(_) => false, UnionLayout::Recursive(tags) | UnionLayout::NullableWrapped { other_tags: tags, .. - } => Self::stores_tag_id_in_pointer_bits(tags, ptr_bytes), + } => Self::stores_tag_id_in_pointer_bits(tags, target_info), UnionLayout::NonNullableUnwrapped(_) | UnionLayout::NullableUnwrapped { .. } => false, } } @@ -406,76 +407,73 @@ impl<'a> UnionLayout<'a> { } } - fn tags_alignment_bytes(tags: &[&[Layout]], pointer_size: u32) -> u32 { + fn tags_alignment_bytes(tags: &[&[Layout]], target_info: TargetInfo) -> u32 { tags.iter() - .map(|fields| Layout::Struct(fields).alignment_bytes(pointer_size)) + .map(|fields| Layout::Struct(fields).alignment_bytes(target_info)) .max() .unwrap_or(0) } - pub fn allocation_alignment_bytes(&self, pointer_size: u32) -> u32 { + pub fn allocation_alignment_bytes(&self, target_info: TargetInfo) -> u32 { let allocation = match self { UnionLayout::NonRecursive(_) => unreachable!("not heap-allocated"), - UnionLayout::Recursive(tags) => Self::tags_alignment_bytes(tags, pointer_size), + UnionLayout::Recursive(tags) => Self::tags_alignment_bytes(tags, target_info), UnionLayout::NonNullableUnwrapped(fields) => { - Layout::Struct(fields).alignment_bytes(pointer_size) + Layout::Struct(fields).alignment_bytes(target_info) } UnionLayout::NullableWrapped { other_tags, .. } => { - Self::tags_alignment_bytes(other_tags, pointer_size) + Self::tags_alignment_bytes(other_tags, target_info) } UnionLayout::NullableUnwrapped { other_fields, .. } => { - Layout::Struct(other_fields).alignment_bytes(pointer_size) + Layout::Struct(other_fields).alignment_bytes(target_info) } }; // because we store a refcount, the alignment must be at least the size of a pointer - allocation.max(pointer_size) + allocation.max(target_info.ptr_width() as u32) } /// Size of the data in memory, whether it's stack or heap (for non-null tag ids) - pub fn data_size_and_alignment(&self, pointer_size: u32) -> (u32, u32) { - let id_data_layout = if self.stores_tag_id_as_data(pointer_size) { + pub fn data_size_and_alignment(&self, target_info: TargetInfo) -> (u32, u32) { + let id_data_layout = if self.stores_tag_id_as_data(target_info) { Some(self.tag_id_layout()) } else { None }; - self.data_size_and_alignment_help_match(id_data_layout, pointer_size) + self.data_size_and_alignment_help_match(id_data_layout, target_info) } /// Size of the data before the tag_id, if it exists. /// Returns None if the tag_id is not stored as data in the layout. - pub fn data_size_without_tag_id(&self, pointer_size: u32) -> Option { - if !self.stores_tag_id_as_data(pointer_size) { + pub fn data_size_without_tag_id(&self, target_info: TargetInfo) -> Option { + if !self.stores_tag_id_as_data(target_info) { return None; }; - Some( - self.data_size_and_alignment_help_match(None, pointer_size) - .0, - ) + Some(self.data_size_and_alignment_help_match(None, target_info).0) } fn data_size_and_alignment_help_match( &self, id_data_layout: Option, - pointer_size: u32, + target_info: TargetInfo, ) -> (u32, u32) { match self { Self::NonRecursive(tags) => { - Self::data_size_and_alignment_help(tags, id_data_layout, pointer_size) + Self::data_size_and_alignment_help(tags, id_data_layout, target_info) } Self::Recursive(tags) => { - Self::data_size_and_alignment_help(tags, id_data_layout, pointer_size) + Self::data_size_and_alignment_help(tags, id_data_layout, target_info) } Self::NonNullableUnwrapped(fields) => { - Self::data_size_and_alignment_help(&[fields], id_data_layout, pointer_size) + Self::data_size_and_alignment_help(&[fields], id_data_layout, target_info) } Self::NullableWrapped { other_tags, .. } => { - Self::data_size_and_alignment_help(other_tags, id_data_layout, pointer_size) + Self::data_size_and_alignment_help(other_tags, id_data_layout, target_info) } Self::NullableUnwrapped { other_fields, .. } => { - Self::data_size_and_alignment_help(&[other_fields], id_data_layout, pointer_size) + Self::data_size_and_alignment_help(&[other_fields], id_data_layout, target_info) } } } @@ -483,7 +481,7 @@ impl<'a> UnionLayout<'a> { fn data_size_and_alignment_help( variant_field_layouts: &[&[Layout]], id_data_layout: Option, - pointer_size: u32, + target_info: TargetInfo, ) -> (u32, u32) { let mut size = 0; let mut alignment_bytes = 0; @@ -497,7 +495,7 @@ impl<'a> UnionLayout<'a> { data = Layout::Struct(&fields_and_id); } - let (variant_size, variant_alignment) = data.stack_size_and_alignment(pointer_size); + let (variant_size, variant_alignment) = data.stack_size_and_alignment(target_info); alignment_bytes = alignment_bytes.max(variant_alignment); size = size.max(variant_size); } @@ -692,7 +690,7 @@ impl<'a> LambdaSet<'a> { arena: &'a Bump, subs: &Subs, closure_var: Variable, - ptr_bytes: u32, + target_info: TargetInfo, ) -> Result { let mut tags = std::vec::Vec::new(); match roc_types::pretty_print::chase_ext_tag_union(subs, closure_var, &mut tags) { @@ -706,7 +704,7 @@ impl<'a> LambdaSet<'a> { arena, subs, seen: Vec::new_in(arena), - ptr_bytes, + target_info, }; for (tag_name, variables) in tags.iter() { @@ -724,7 +722,7 @@ impl<'a> LambdaSet<'a> { } let representation = - arena.alloc(Self::make_representation(arena, subs, tags, ptr_bytes)); + arena.alloc(Self::make_representation(arena, subs, tags, target_info)); Ok(LambdaSet { set: set.into_bump_slice(), @@ -747,10 +745,10 @@ impl<'a> LambdaSet<'a> { arena: &'a Bump, subs: &Subs, tags: std::vec::Vec<(TagName, std::vec::Vec)>, - ptr_bytes: u32, + target_info: TargetInfo, ) -> Layout<'a> { // otherwise, this is a closure with a payload - let variant = union_sorted_tags_help(arena, tags, None, subs, ptr_bytes); + let variant = union_sorted_tags_help(arena, tags, None, subs, target_info); use UnionVariant::*; match variant { @@ -790,8 +788,8 @@ impl<'a> LambdaSet<'a> { } } - pub fn stack_size(&self, pointer_size: u32) -> u32 { - self.representation.stack_size(pointer_size) + pub fn stack_size(&self, target_info: TargetInfo) -> u32 { + self.representation.stack_size(target_info) } pub fn contains_refcounted(&self) -> bool { self.representation.contains_refcounted() @@ -800,8 +798,8 @@ impl<'a> LambdaSet<'a> { self.representation.safe_to_memcpy() } - pub fn alignment_bytes(&self, pointer_size: u32) -> u32 { - self.representation.alignment_bytes(pointer_size) + pub fn alignment_bytes(&self, target_info: TargetInfo) -> u32 { + self.representation.alignment_bytes(target_info) } } @@ -818,7 +816,7 @@ pub enum Builtin<'a> { } pub struct Env<'a, 'b> { - ptr_bytes: u32, + target_info: TargetInfo, arena: &'a Bump, seen: Vec<'a, Variable>, subs: &'b Subs, @@ -890,7 +888,7 @@ impl<'a> Layout<'a> { } Symbol::NUM_NAT | Symbol::NUM_NATURAL | Symbol::NUM_AT_NATURAL => { - return Ok(Layout::usize(env.ptr_bytes)) + return Ok(Layout::usize(env.target_info)) } _ => Self::from_var(env, actual_var), @@ -962,31 +960,31 @@ impl<'a> Layout<'a> { } } - pub fn stack_size(&self, pointer_size: u32) -> u32 { - let width = self.stack_size_without_alignment(pointer_size); - let alignment = self.alignment_bytes(pointer_size); + pub fn stack_size(&self, target_info: TargetInfo) -> u32 { + let width = self.stack_size_without_alignment(target_info); + let alignment = self.alignment_bytes(target_info); round_up_to_alignment(width, alignment) } - pub fn stack_size_and_alignment(&self, pointer_size: u32) -> (u32, u32) { - let width = self.stack_size_without_alignment(pointer_size); - let alignment = self.alignment_bytes(pointer_size); + pub fn stack_size_and_alignment(&self, target_info: TargetInfo) -> (u32, u32) { + let width = self.stack_size_without_alignment(target_info); + let alignment = self.alignment_bytes(target_info); let size = round_up_to_alignment(width, alignment); (size, alignment) } - fn stack_size_without_alignment(&self, pointer_size: u32) -> u32 { + fn stack_size_without_alignment(&self, target_info: TargetInfo) -> u32 { use Layout::*; match self { - Builtin(builtin) => builtin.stack_size(pointer_size), + Builtin(builtin) => builtin.stack_size(target_info), Struct(fields) => { let mut sum = 0; for field_layout in *fields { - sum += field_layout.stack_size(pointer_size); + sum += field_layout.stack_size(target_info); } sum @@ -995,26 +993,26 @@ impl<'a> Layout<'a> { use UnionLayout::*; match variant { - NonRecursive(_) => variant.data_size_and_alignment(pointer_size).0, + NonRecursive(_) => variant.data_size_and_alignment(target_info).0, Recursive(_) | NullableWrapped { .. } | NullableUnwrapped { .. } - | NonNullableUnwrapped(_) => pointer_size, + | NonNullableUnwrapped(_) => target_info.ptr_width() as u32, } } LambdaSet(lambda_set) => lambda_set .runtime_representation() - .stack_size_without_alignment(pointer_size), - RecursivePointer => pointer_size, + .stack_size_without_alignment(target_info), + RecursivePointer => target_info.ptr_width() as u32, } } - pub fn alignment_bytes(&self, pointer_size: u32) -> u32 { + pub fn alignment_bytes(&self, target_info: TargetInfo) -> u32 { match self { Layout::Struct(fields) => fields .iter() - .map(|x| x.alignment_bytes(pointer_size)) + .map(|x| x.alignment_bytes(target_info)) .max() .unwrap_or(0), @@ -1028,44 +1026,44 @@ impl<'a> Layout<'a> { .flat_map(|layouts| { layouts .iter() - .map(|layout| layout.alignment_bytes(pointer_size)) + .map(|layout| layout.alignment_bytes(target_info)) }) .max(); let tag_id_builtin = variant.tag_id_builtin(); match max_alignment { Some(align) => round_up_to_alignment( - align.max(tag_id_builtin.alignment_bytes(pointer_size)), - tag_id_builtin.alignment_bytes(pointer_size), + align.max(tag_id_builtin.alignment_bytes(target_info)), + tag_id_builtin.alignment_bytes(target_info), ), None => { // none of the tags had any payload, but the tag id still contains information - tag_id_builtin.alignment_bytes(pointer_size) + tag_id_builtin.alignment_bytes(target_info) } } } Recursive(_) | NullableWrapped { .. } | NullableUnwrapped { .. } - | NonNullableUnwrapped(_) => pointer_size, + | NonNullableUnwrapped(_) => target_info.ptr_width() as u32, } } Layout::LambdaSet(lambda_set) => lambda_set .runtime_representation() - .alignment_bytes(pointer_size), - Layout::Builtin(builtin) => builtin.alignment_bytes(pointer_size), - Layout::RecursivePointer => pointer_size, + .alignment_bytes(target_info), + Layout::Builtin(builtin) => builtin.alignment_bytes(target_info), + Layout::RecursivePointer => target_info.ptr_width() as u32, } } - pub fn allocation_alignment_bytes(&self, pointer_size: u32) -> u32 { + pub fn allocation_alignment_bytes(&self, target_info: TargetInfo) -> u32 { match self { - Layout::Builtin(builtin) => builtin.allocation_alignment_bytes(pointer_size), + Layout::Builtin(builtin) => builtin.allocation_alignment_bytes(target_info), Layout::Struct(_) => unreachable!("not heap-allocated"), - Layout::Union(union_layout) => union_layout.allocation_alignment_bytes(pointer_size), + Layout::Union(union_layout) => union_layout.allocation_alignment_bytes(target_info), Layout::LambdaSet(lambda_set) => lambda_set .runtime_representation() - .allocation_alignment_bytes(pointer_size), + .allocation_alignment_bytes(target_info), Layout::RecursivePointer => unreachable!("should be looked up to get an actual layout"), } } @@ -1149,7 +1147,7 @@ impl<'a> Layout<'a> { /// But if we're careful when to invalidate certain keys, we still get some benefit #[derive(Debug)] pub struct LayoutCache<'a> { - ptr_bytes: u32, + target_info: TargetInfo, _marker: std::marker::PhantomData<&'a u8>, } @@ -1161,9 +1159,9 @@ pub enum CachedLayout<'a> { } impl<'a> LayoutCache<'a> { - pub fn new(ptr_bytes: u32) -> Self { + pub fn new(target_info: TargetInfo) -> Self { Self { - ptr_bytes, + target_info, _marker: Default::default(), } } @@ -1181,7 +1179,7 @@ impl<'a> LayoutCache<'a> { arena, subs, seen: Vec::new_in(arena), - ptr_bytes: self.ptr_bytes, + target_info: self.target_info, }; Layout::from_var(&mut env, var) @@ -1200,7 +1198,7 @@ impl<'a> LayoutCache<'a> { arena, subs, seen: Vec::new_in(arena), - ptr_bytes: self.ptr_bytes, + target_info: self.target_info, }; RawFunctionLayout::from_var(&mut env, var) } @@ -1232,11 +1230,10 @@ impl<'a> Layout<'a> { Layout::Builtin(Builtin::Float(FloatWidth::F32)) } - pub fn usize(ptr_bytes: u32) -> Layout<'a> { - match ptr_bytes { - 4 => Self::u32(), - 8 => Self::u64(), - _ => panic!("width of usize {} not supported", ptr_bytes), + pub fn usize(target_info: TargetInfo) -> Layout<'a> { + match target_info.ptr_width() { + roc_target::PtrWidth::Bytes4 => Self::u32(), + roc_target::PtrWidth::Bytes8 => Self::u64(), } } @@ -1310,25 +1307,29 @@ impl<'a> Builtin<'a> { pub const WRAPPER_PTR: u32 = 0; pub const WRAPPER_LEN: u32 = 1; - pub fn stack_size(&self, pointer_size: u32) -> u32 { + pub fn stack_size(&self, target_info: TargetInfo) -> u32 { use Builtin::*; + let ptr_width = target_info.ptr_width() as u32; + match self { Int(int) => int.stack_size(), Float(float) => float.stack_size(), Bool => Builtin::I1_SIZE, Decimal => Builtin::DECIMAL_SIZE, - Str => Builtin::STR_WORDS * pointer_size, - Dict(_, _) => Builtin::DICT_WORDS * pointer_size, - Set(_) => Builtin::SET_WORDS * pointer_size, - List(_) => Builtin::LIST_WORDS * pointer_size, + Str => Builtin::STR_WORDS * ptr_width, + Dict(_, _) => Builtin::DICT_WORDS * ptr_width, + Set(_) => Builtin::SET_WORDS * ptr_width, + List(_) => Builtin::LIST_WORDS * ptr_width, } } - pub fn alignment_bytes(&self, pointer_size: u32) -> u32 { + pub fn alignment_bytes(&self, target_info: TargetInfo) -> u32 { use std::mem::align_of; use Builtin::*; + let ptr_width = target_info.ptr_width() as u32; + // for our data structures, what counts is the alignment of the `( ptr, len )` tuple, and // since both of those are one pointer size, the alignment of that structure is a pointer // size @@ -1336,16 +1337,16 @@ impl<'a> Builtin<'a> { Int(int_width) => int_width.alignment_bytes(), Float(float_width) => float_width.alignment_bytes(), Bool => align_of::() as u32, - Decimal => align_of::() as u32, - Dict(_, _) => pointer_size, - Set(_) => pointer_size, + Decimal => IntWidth::I128.alignment_bytes(), + Dict(_, _) => ptr_width, + Set(_) => ptr_width, // we often treat these as i128 (64-bit systems) // or i64 (32-bit systems). // // In webassembly, For that to be safe // they must be aligned to allow such access - List(_) => pointer_size, - Str => pointer_size, + List(_) => ptr_width, + Str => ptr_width, } } @@ -1425,21 +1426,23 @@ impl<'a> Builtin<'a> { } } - pub fn allocation_alignment_bytes(&self, pointer_size: u32) -> u32 { + pub fn allocation_alignment_bytes(&self, target_info: TargetInfo) -> u32 { + let ptr_width = target_info.ptr_width() as u32; + let allocation = match self { Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal => { unreachable!("not heap-allocated") } - Builtin::Str => pointer_size, + Builtin::Str => ptr_width, Builtin::Dict(k, v) => k - .alignment_bytes(pointer_size) - .max(v.alignment_bytes(pointer_size)) - .max(pointer_size), - Builtin::Set(k) => k.alignment_bytes(pointer_size).max(pointer_size), - Builtin::List(e) => e.alignment_bytes(pointer_size).max(pointer_size), + .alignment_bytes(target_info) + .max(v.alignment_bytes(target_info)) + .max(ptr_width), + Builtin::Set(k) => k.alignment_bytes(target_info).max(ptr_width), + Builtin::List(e) => e.alignment_bytes(target_info).max(ptr_width), }; - allocation.max(pointer_size) + allocation.max(ptr_width) } } @@ -1451,7 +1454,7 @@ fn layout_from_flat_type<'a>( let arena = env.arena; let subs = env.subs; - let ptr_bytes = env.ptr_bytes; + let target_info = env.target_info; match flat_type { Apply(symbol, args) => { @@ -1461,7 +1464,7 @@ fn layout_from_flat_type<'a>( // Ints Symbol::NUM_NAT => { debug_assert_eq!(args.len(), 0); - Ok(Layout::usize(env.ptr_bytes)) + Ok(Layout::usize(env.target_info)) } Symbol::NUM_I128 => { @@ -1527,7 +1530,7 @@ fn layout_from_flat_type<'a>( let var = args[0]; let content = subs.get_content_without_compacting(var); - layout_from_num_content(content) + layout_from_num_content(content, target_info) } Symbol::STR_STR => Ok(Layout::Builtin(Builtin::Str)), @@ -1543,7 +1546,8 @@ fn layout_from_flat_type<'a>( } } Func(_, closure_var, _) => { - let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var, env.ptr_bytes)?; + let lambda_set = + LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?; Ok(Layout::LambdaSet(lambda_set)) } @@ -1563,8 +1567,8 @@ fn layout_from_flat_type<'a>( } pairs.sort_by(|(label1, layout1), (label2, layout2)| { - let size1 = layout1.alignment_bytes(ptr_bytes); - let size2 = layout2.alignment_bytes(ptr_bytes); + let size1 = layout1.alignment_bytes(target_info); + let size2 = layout2.alignment_bytes(target_info); size2.cmp(&size1).then(label1.cmp(label2)) }); @@ -1584,7 +1588,7 @@ fn layout_from_flat_type<'a>( debug_assert!(ext_var_is_empty_tag_union(subs, ext_var)); - Ok(layout_from_tag_union(arena, &tags, subs, env.ptr_bytes)) + Ok(layout_from_tag_union(arena, &tags, subs, env.target_info)) } FunctionOrTagUnion(tag_name, _, ext_var) => { debug_assert!( @@ -1595,7 +1599,7 @@ fn layout_from_flat_type<'a>( let union_tags = UnionTags::from_tag_name_index(tag_name); let (tags, _) = union_tags.unsorted_tags_and_ext(subs, ext_var); - Ok(layout_from_tag_union(arena, &tags, subs, env.ptr_bytes)) + Ok(layout_from_tag_union(arena, &tags, subs, env.target_info)) } RecursiveTagUnion(rec_var, tags, ext_var) => { let (tags, ext_var) = tags.unsorted_tags_and_ext(subs, ext_var); @@ -1645,8 +1649,8 @@ fn layout_from_flat_type<'a>( } tag_layout.sort_by(|layout1, layout2| { - let size1 = layout1.alignment_bytes(ptr_bytes); - let size2 = layout2.alignment_bytes(ptr_bytes); + let size1 = layout1.alignment_bytes(target_info); + let size2 = layout2.alignment_bytes(target_info); size2.cmp(&size1) }); @@ -1691,13 +1695,13 @@ pub fn sort_record_fields<'a>( arena: &'a Bump, var: Variable, subs: &Subs, - ptr_bytes: u32, + target_info: TargetInfo, ) -> Result>, LayoutProblem> { let mut env = Env { arena, subs, seen: Vec::new_in(arena), - ptr_bytes, + target_info, }; let (it, _) = match gather_fields_unsorted_iter(subs, RecordFields::empty(), var) { @@ -1716,7 +1720,7 @@ fn sort_record_fields_help<'a>( env: &mut Env<'a, '_>, fields_map: impl Iterator)>, ) -> Result>, LayoutProblem> { - let ptr_bytes = env.ptr_bytes; + let target_info = env.target_info; // Sort the fields by label let mut sorted_fields = Vec::with_capacity_in(fields_map.size_hint().0, env.arena); @@ -1738,8 +1742,8 @@ fn sort_record_fields_help<'a>( |(label1, _, res_layout1), (label2, _, res_layout2)| match res_layout1 { Ok(layout1) | Err(layout1) => match res_layout2 { Ok(layout2) | Err(layout2) => { - let size1 = layout1.alignment_bytes(ptr_bytes); - let size2 = layout2.alignment_bytes(ptr_bytes); + let size1 = layout1.alignment_bytes(target_info); + let size2 = layout2.alignment_bytes(target_info); size2.cmp(&size1).then(label1.cmp(label2)) } @@ -1873,7 +1877,7 @@ pub fn union_sorted_tags<'a>( arena: &'a Bump, var: Variable, subs: &Subs, - ptr_bytes: u32, + target_info: TargetInfo, ) -> Result, LayoutProblem> { let var = if let Content::RecursionVar { structure, .. } = subs.get_content_without_compacting(var) { @@ -1886,7 +1890,7 @@ pub fn union_sorted_tags<'a>( let result = match roc_types::pretty_print::chase_ext_tag_union(subs, var, &mut tags_vec) { Ok(()) | Err((_, Content::FlexVar(_))) | Err((_, Content::RecursionVar { .. })) => { let opt_rec_var = get_recursion_var(subs, var); - union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, ptr_bytes) + union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, target_info) } Err((_, Content::Error)) => return Err(LayoutProblem::Erroneous), Err(other) => panic!("invalid content in tag union variable: {:?}", other), @@ -1920,7 +1924,7 @@ fn union_sorted_tags_help_new<'a>( tags_list: &[(&'_ TagName, &[Variable])], opt_rec_var: Option, subs: &Subs, - ptr_bytes: u32, + target_info: TargetInfo, ) -> UnionVariant<'a> { // sort up front; make sure the ordering stays intact! let mut tags_list = Vec::from_iter_in(tags_list.iter(), arena); @@ -1930,7 +1934,7 @@ fn union_sorted_tags_help_new<'a>( arena, subs, seen: Vec::new_in(arena), - ptr_bytes, + target_info, }; match tags_list.len() { @@ -1949,7 +1953,8 @@ fn union_sorted_tags_help_new<'a>( match tag_name { TagName::Private(Symbol::NUM_AT_NUM) => { let var = arguments[0]; - layouts.push(unwrap_num_tag(subs, var, ptr_bytes).expect("invalid num layout")); + layouts + .push(unwrap_num_tag(subs, var, target_info).expect("invalid num layout")); } _ => { for &var in arguments { @@ -1973,8 +1978,8 @@ fn union_sorted_tags_help_new<'a>( } layouts.sort_by(|layout1, layout2| { - let size1 = layout1.alignment_bytes(ptr_bytes); - let size2 = layout2.alignment_bytes(ptr_bytes); + let size1 = layout1.alignment_bytes(target_info); + let size2 = layout2.alignment_bytes(target_info); size2.cmp(&size1) }); @@ -2051,8 +2056,8 @@ fn union_sorted_tags_help_new<'a>( } arg_layouts.sort_by(|layout1, layout2| { - let size1 = layout1.alignment_bytes(ptr_bytes); - let size2 = layout2.alignment_bytes(ptr_bytes); + let size1 = layout1.alignment_bytes(target_info); + let size2 = layout2.alignment_bytes(target_info); size2.cmp(&size1) }); @@ -2123,7 +2128,7 @@ pub fn union_sorted_tags_help<'a>( mut tags_vec: std::vec::Vec<(TagName, std::vec::Vec)>, opt_rec_var: Option, subs: &Subs, - ptr_bytes: u32, + target_info: TargetInfo, ) -> UnionVariant<'a> { // sort up front; make sure the ordering stays intact! tags_vec.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); @@ -2132,7 +2137,7 @@ pub fn union_sorted_tags_help<'a>( arena, subs, seen: Vec::new_in(arena), - ptr_bytes, + target_info, }; match tags_vec.len() { @@ -2151,7 +2156,8 @@ pub fn union_sorted_tags_help<'a>( match tag_name { TagName::Private(Symbol::NUM_AT_NUM) => { layouts.push( - unwrap_num_tag(subs, arguments[0], ptr_bytes).expect("invalid num layout"), + unwrap_num_tag(subs, arguments[0], target_info) + .expect("invalid num layout"), ); } _ => { @@ -2181,8 +2187,8 @@ pub fn union_sorted_tags_help<'a>( } layouts.sort_by(|layout1, layout2| { - let size1 = layout1.alignment_bytes(ptr_bytes); - let size2 = layout2.alignment_bytes(ptr_bytes); + let size1 = layout1.alignment_bytes(target_info); + let size2 = layout2.alignment_bytes(target_info); size2.cmp(&size1) }); @@ -2264,8 +2270,8 @@ pub fn union_sorted_tags_help<'a>( } arg_layouts.sort_by(|layout1, layout2| { - let size1 = layout1.alignment_bytes(ptr_bytes); - let size2 = layout2.alignment_bytes(ptr_bytes); + let size1 = layout1.alignment_bytes(target_info); + let size2 = layout2.alignment_bytes(target_info); size2.cmp(&size1) }); @@ -2335,20 +2341,20 @@ fn layout_from_newtype<'a>( arena: &'a Bump, tags: &UnsortedUnionTags, subs: &Subs, - ptr_bytes: u32, + target_info: TargetInfo, ) -> Layout<'a> { debug_assert!(tags.is_newtype_wrapper(subs)); let (tag_name, var) = tags.get_newtype(subs); if tag_name == &TagName::Private(Symbol::NUM_AT_NUM) { - unwrap_num_tag(subs, var, ptr_bytes).expect("invalid Num argument") + unwrap_num_tag(subs, var, target_info).expect("invalid Num argument") } else { let mut env = Env { arena, subs, seen: Vec::new_in(arena), - ptr_bytes, + target_info, }; match Layout::from_var(&mut env, var) { @@ -2372,12 +2378,12 @@ fn layout_from_tag_union<'a>( arena: &'a Bump, tags: &UnsortedUnionTags, subs: &Subs, - ptr_bytes: u32, + target_info: TargetInfo, ) -> Layout<'a> { use UnionVariant::*; if tags.is_newtype_wrapper(subs) { - return layout_from_newtype(arena, tags, subs, ptr_bytes); + return layout_from_newtype(arena, tags, subs, target_info); } let tags_vec = &tags.tags; @@ -2388,11 +2394,12 @@ fn layout_from_tag_union<'a>( let &var = arguments.iter().next().unwrap(); - unwrap_num_tag(subs, var, ptr_bytes).expect("invalid Num argument") + unwrap_num_tag(subs, var, target_info).expect("invalid Num argument") } _ => { let opt_rec_var = None; - let variant = union_sorted_tags_help_new(arena, tags_vec, opt_rec_var, subs, ptr_bytes); + let variant = + union_sorted_tags_help_new(arena, tags_vec, opt_rec_var, subs, target_info); match variant { Never => Layout::VOID, @@ -2490,12 +2497,13 @@ pub fn ext_var_is_empty_tag_union(_: &Subs, _: Variable) -> bool { unreachable!(); } -fn layout_from_num_content<'a>(content: &Content) -> Result, LayoutProblem> { +fn layout_from_num_content<'a>( + content: &Content, + target_info: TargetInfo, +) -> Result, LayoutProblem> { use roc_types::subs::Content::*; use roc_types::subs::FlatType::*; - let ptr_bytes = 8; - match content { RecursionVar { .. } => panic!("recursion var in num"), FlexVar(_) | RigidVar(_) => { @@ -2507,7 +2515,7 @@ fn layout_from_num_content<'a>(content: &Content) -> Result, LayoutPr } Structure(Apply(symbol, args)) => match *symbol { // Ints - Symbol::NUM_NAT => Ok(Layout::usize(ptr_bytes)), + Symbol::NUM_NAT => Ok(Layout::usize(target_info)), Symbol::NUM_INTEGER => Ok(Layout::i64()), Symbol::NUM_I128 => Ok(Layout::i128()), @@ -2550,7 +2558,7 @@ fn layout_from_num_content<'a>(content: &Content) -> Result, LayoutPr fn unwrap_num_tag<'a>( subs: &Subs, var: Variable, - ptr_bytes: u32, + target_info: TargetInfo, ) -> Result, LayoutProblem> { match subs.get_content_without_compacting(var) { Content::Alias(Symbol::NUM_INTEGER, args, _) => { @@ -2575,7 +2583,7 @@ fn unwrap_num_tag<'a>( Symbol::NUM_UNSIGNED32 => Layout::u32(), Symbol::NUM_UNSIGNED16 => Layout::u16(), Symbol::NUM_UNSIGNED8 => Layout::u8(), - Symbol::NUM_NATURAL => Layout::usize(ptr_bytes), + Symbol::NUM_NATURAL => Layout::usize(target_info), _ => unreachable!("not a valid int variant: {:?} {:?}", symbol, args), }; @@ -2821,8 +2829,8 @@ mod test { let layout = Layout::Union(UnionLayout::NonRecursive(&tt)); - let ptr_width = 8; - assert_eq!(layout.stack_size(ptr_width), 1); - assert_eq!(layout.alignment_bytes(ptr_width), 1); + let target_info = TargetInfo::default_x86_64(); + assert_eq!(layout.stack_size(target_info), 1); + assert_eq!(layout.alignment_bytes(target_info), 1); } } diff --git a/compiler/target/src/lib.rs b/compiler/target/src/lib.rs index 933cc2f6c6..237ca81f29 100644 --- a/compiler/target/src/lib.rs +++ b/compiler/target/src/lib.rs @@ -2,10 +2,31 @@ // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] +#[derive(Debug, Clone, Copy)] pub struct TargetInfo { architecture: Architecture, } +impl TargetInfo { + pub const fn ptr_width(&self) -> PtrWidth { + self.architecture.ptr_width() + } + + pub const fn default_x86_64() -> Self { + TargetInfo { + architecture: Architecture::X86_64, + } + } +} + +#[repr(u8)] +#[derive(Debug, Clone, Copy)] +pub enum PtrWidth { + Bytes4 = 4, + Bytes8 = 8, +} + +#[derive(Debug, Clone, Copy)] pub enum Architecture { X86_64, X86_32, @@ -15,12 +36,12 @@ pub enum Architecture { } impl Architecture { - pub const fn ptr_width(&self) -> u32 { + pub const fn ptr_width(&self) -> PtrWidth { use Architecture::*; match self { - X86_64 | Aarch64 | Arm => 8, - X86_32 | Wasm32 => 4, + X86_64 | Aarch64 | Arm => PtrWidth::Bytes8, + X86_32 | Wasm32 => PtrWidth::Bytes4, } } } From 74932a4cab728eb7919e4eabec1c74d21d58da51 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jan 2022 14:30:37 +0100 Subject: [PATCH 04/36] phase 2 --- cli/src/repl/gen.rs | 2 +- compiler/build/src/program.rs | 2 +- compiler/gen_llvm/src/llvm/build_dict.rs | 62 +++++++++++------------ compiler/gen_llvm/src/llvm/build_hash.rs | 10 ++-- compiler/gen_llvm/src/llvm/build_list.rs | 8 +-- compiler/gen_llvm/src/llvm/build_str.rs | 8 +-- compiler/gen_llvm/src/llvm/convert.rs | 22 ++++---- compiler/gen_llvm/src/llvm/externs.rs | 2 +- compiler/gen_llvm/src/llvm/refcounting.rs | 8 +-- compiler/load/src/file.rs | 4 +- compiler/mono/src/ir.rs | 52 +++++++++---------- compiler/test_gen/src/helpers/llvm.rs | 2 +- reporting/tests/test_reporting.rs | 2 +- 13 files changed, 93 insertions(+), 91 deletions(-) diff --git a/cli/src/repl/gen.rs b/cli/src/repl/gen.rs index 5a52f74420..1510a4c448 100644 --- a/cli/src/repl/gen.rs +++ b/cli/src/repl/gen.rs @@ -181,7 +181,7 @@ pub fn gen_and_eval<'a>( context: &context, interns, module, - ptr_bytes, + target_info: ptr_bytes, is_gen_test: true, // so roc_panic is generated // important! we don't want any procedures to get the C calling convention exposed_to_host: MutSet::default(), diff --git a/compiler/build/src/program.rs b/compiler/build/src/program.rs index c3ee7cc397..1ba657e615 100644 --- a/compiler/build/src/program.rs +++ b/compiler/build/src/program.rs @@ -286,7 +286,7 @@ pub fn gen_from_mono_module_llvm( context: &context, interns: loaded.interns, module, - ptr_bytes, + target_info: ptr_bytes, // in gen_tests, the compiler provides roc_panic // and sets up the setjump/longjump exception handling is_gen_test: false, diff --git a/compiler/gen_llvm/src/llvm/build_dict.rs b/compiler/gen_llvm/src/llvm/build_dict.rs index 4feb77aa2d..47f8595d70 100644 --- a/compiler/gen_llvm/src/llvm/build_dict.rs +++ b/compiler/gen_llvm/src/llvm/build_dict.rs @@ -105,15 +105,15 @@ pub fn dict_insert<'a, 'ctx, 'env>( let key_width = env .ptr_int() - .const_int(key_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(key_layout.stack_size(env.target_info) as u64, false); let value_width = env .ptr_int() - .const_int(value_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(value_layout.stack_size(env.target_info) as u64, false); let result_ptr = builder.build_alloca(zig_dict_type(env), "result_ptr"); - let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes); + let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info); let alignment_iv = alignment.as_int_value(env.context); let hash_fn = build_hash_wrapper(env, layout_ids, key_layout); @@ -162,15 +162,15 @@ pub fn dict_remove<'a, 'ctx, 'env>( let key_width = env .ptr_int() - .const_int(key_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(key_layout.stack_size(env.target_info) as u64, false); let value_width = env .ptr_int() - .const_int(value_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(value_layout.stack_size(env.target_info) as u64, false); let result_ptr = builder.build_alloca(zig_dict_type(env), "result_ptr"); - let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes); + let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info); let alignment_iv = alignment.as_int_value(env.context); let hash_fn = build_hash_wrapper(env, layout_ids, key_layout); @@ -218,13 +218,13 @@ pub fn dict_contains<'a, 'ctx, 'env>( let key_width = env .ptr_int() - .const_int(key_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(key_layout.stack_size(env.target_info) as u64, false); let value_width = env .ptr_int() - .const_int(value_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(value_layout.stack_size(env.target_info) as u64, false); - let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes); + let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info); let alignment_iv = alignment.as_int_value(env.context); let hash_fn = build_hash_wrapper(env, layout_ids, key_layout); @@ -264,13 +264,13 @@ pub fn dict_get<'a, 'ctx, 'env>( let key_width = env .ptr_int() - .const_int(key_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(key_layout.stack_size(env.target_info) as u64, false); let value_width = env .ptr_int() - .const_int(value_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(value_layout.stack_size(env.target_info) as u64, false); - let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes); + let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info); let alignment_iv = alignment.as_int_value(env.context); let hash_fn = build_hash_wrapper(env, layout_ids, key_layout); @@ -366,13 +366,13 @@ pub fn dict_elements_rc<'a, 'ctx, 'env>( ) { let key_width = env .ptr_int() - .const_int(key_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(key_layout.stack_size(env.target_info) as u64, false); let value_width = env .ptr_int() - .const_int(value_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(value_layout.stack_size(env.target_info) as u64, false); - let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes); + let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info); let alignment_iv = alignment.as_int_value(env.context); let (key_fn, value_fn) = match rc_operation { @@ -412,13 +412,13 @@ pub fn dict_keys<'a, 'ctx, 'env>( let key_width = env .ptr_int() - .const_int(key_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(key_layout.stack_size(env.target_info) as u64, false); let value_width = env .ptr_int() - .const_int(value_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(value_layout.stack_size(env.target_info) as u64, false); - let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes); + let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info); let alignment_iv = alignment.as_int_value(env.context); let inc_key_fn = build_inc_wrapper(env, layout_ids, key_layout); @@ -454,7 +454,7 @@ fn pass_dict_c_abi<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, dict: BasicValueEnum<'ctx>, ) -> BasicValueEnum<'ctx> { - match env.ptr_bytes { + match env.target_info { 4 => { let target_type = env.context.custom_width_int_type(96).into(); @@ -483,13 +483,13 @@ pub fn dict_union<'a, 'ctx, 'env>( let key_width = env .ptr_int() - .const_int(key_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(key_layout.stack_size(env.target_info) as u64, false); let value_width = env .ptr_int() - .const_int(value_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(value_layout.stack_size(env.target_info) as u64, false); - let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes); + let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info); let alignment_iv = alignment.as_int_value(env.context); let hash_fn = build_hash_wrapper(env, layout_ids, key_layout); @@ -576,13 +576,13 @@ fn dict_intersect_or_difference<'a, 'ctx, 'env>( let key_width = env .ptr_int() - .const_int(key_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(key_layout.stack_size(env.target_info) as u64, false); let value_width = env .ptr_int() - .const_int(value_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(value_layout.stack_size(env.target_info) as u64, false); - let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes); + let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info); let alignment_iv = alignment.as_int_value(env.context); let hash_fn = build_hash_wrapper(env, layout_ids, key_layout); @@ -631,7 +631,7 @@ pub fn dict_walk<'a, 'ctx, 'env>( let accum_ptr = builder.build_alloca(accum_bt, "accum_ptr"); env.builder.build_store(accum_ptr, accum); - let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes); + let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info); let alignment_iv = alignment.as_int_value(env.context); let output_ptr = builder.build_alloca(accum_bt, "output_ptr"); @@ -671,13 +671,13 @@ pub fn dict_values<'a, 'ctx, 'env>( let key_width = env .ptr_int() - .const_int(key_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(key_layout.stack_size(env.target_info) as u64, false); let value_width = env .ptr_int() - .const_int(value_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(value_layout.stack_size(env.target_info) as u64, false); - let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes); + let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info); let alignment_iv = alignment.as_int_value(env.context); let inc_value_fn = build_inc_wrapper(env, layout_ids, value_layout); @@ -729,14 +729,14 @@ pub fn set_from_list<'a, 'ctx, 'env>( let key_width = env .ptr_int() - .const_int(key_layout.stack_size(env.ptr_bytes) as u64, false); + .const_int(key_layout.stack_size(env.target_info) as u64, false); let value_width = env.ptr_int().const_zero(); let result_alloca = builder.build_alloca(zig_dict_type(env), "result_alloca"); let alignment = - Alignment::from_key_value_layout(key_layout, &Layout::Struct(&[]), env.ptr_bytes); + Alignment::from_key_value_layout(key_layout, &Layout::Struct(&[]), env.target_info); let alignment_iv = alignment.as_int_value(env.context); let hash_fn = build_hash_wrapper(env, layout_ids, key_layout); diff --git a/compiler/gen_llvm/src/llvm/build_hash.rs b/compiler/gen_llvm/src/llvm/build_hash.rs index 8036871a74..0db5348d1d 100644 --- a/compiler/gen_llvm/src/llvm/build_hash.rs +++ b/compiler/gen_llvm/src/llvm/build_hash.rs @@ -120,7 +120,7 @@ fn hash_builtin<'a, 'ctx, 'env>( builtin: &Builtin<'a>, when_recursive: WhenRecursive<'a>, ) -> IntValue<'ctx> { - let ptr_bytes = env.ptr_bytes; + let ptr_bytes = env.target_info; match builtin { Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal => { @@ -246,7 +246,7 @@ fn hash_struct<'a, 'ctx, 'env>( when_recursive: WhenRecursive<'a>, field_layouts: &[Layout<'a>], ) -> IntValue<'ctx> { - let ptr_bytes = env.ptr_bytes; + let ptr_bytes = env.target_info; let layout = Layout::Struct(field_layouts); @@ -423,7 +423,7 @@ fn hash_tag<'a, 'ctx, 'env>( env, seed, hash_bytes, - tag_id_layout.stack_size(env.ptr_bytes), + tag_id_layout.stack_size(env.target_info), ); // hash the tag data @@ -474,7 +474,7 @@ fn hash_tag<'a, 'ctx, 'env>( env, seed, hash_bytes, - tag_id_layout.stack_size(env.ptr_bytes), + tag_id_layout.stack_size(env.target_info), ); // hash the tag data @@ -574,7 +574,7 @@ fn hash_tag<'a, 'ctx, 'env>( env, seed, hash_bytes, - tag_id_layout.stack_size(env.ptr_bytes), + tag_id_layout.stack_size(env.target_info), ); // hash tag data diff --git a/compiler/gen_llvm/src/llvm/build_list.rs b/compiler/gen_llvm/src/llvm/build_list.rs index f2c5351d1b..169db029c1 100644 --- a/compiler/gen_llvm/src/llvm/build_list.rs +++ b/compiler/gen_llvm/src/llvm/build_list.rs @@ -87,7 +87,7 @@ pub fn layout_width<'a, 'ctx, 'env>( layout: &Layout<'a>, ) -> BasicValueEnum<'ctx> { env.ptr_int() - .const_int(layout.stack_size(env.ptr_bytes) as u64, false) + .const_int(layout.stack_size(env.target_info) as u64, false) .into() } @@ -1254,17 +1254,17 @@ pub fn allocate_list<'a, 'ctx, 'env>( let ctx = env.context; let len_type = env.ptr_int(); - let elem_bytes = elem_layout.stack_size(env.ptr_bytes) as u64; + let elem_bytes = elem_layout.stack_size(env.target_info) as u64; let bytes_per_element = len_type.const_int(elem_bytes, false); let number_of_data_bytes = builder.build_int_mul(bytes_per_element, number_of_elements, "data_length"); // the refcount of a new list is initially 1 // we assume that the list is indeed used (dead variables are eliminated) - let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes); + let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.target_info); let basic_type = basic_type_from_layout(env, elem_layout); - let alignment_bytes = elem_layout.alignment_bytes(env.ptr_bytes); + let alignment_bytes = elem_layout.alignment_bytes(env.target_info); allocate_with_refcount_help(env, basic_type, alignment_bytes, number_of_data_bytes, rc1) } diff --git a/compiler/gen_llvm/src/llvm/build_str.rs b/compiler/gen_llvm/src/llvm/build_str.rs index 3c92e39cdb..a7337cd07d 100644 --- a/compiler/gen_llvm/src/llvm/build_str.rs +++ b/compiler/gen_llvm/src/llvm/build_str.rs @@ -79,7 +79,7 @@ fn str_symbol_to_c_abi<'a, 'ctx, 'env>( ) -> IntValue<'ctx> { let string = load_symbol(scope, &symbol); - let target_type = match env.ptr_bytes { + let target_type = match env.target_info { 8 => env.context.i128_type().into(), 4 => env.context.i64_type().into(), _ => unreachable!(), @@ -96,7 +96,7 @@ pub fn str_to_c_abi<'a, 'ctx, 'env>( env.builder.build_store(cell, value); - let target_type = match env.ptr_bytes { + let target_type = match env.target_info { 8 => env.context.i128_type(), 4 => env.context.i64_type(), _ => unreachable!(), @@ -310,7 +310,7 @@ fn decode_from_utf8_result<'a, 'ctx, 'env>( let builder = env.builder; let ctx = env.context; - let fields = match env.ptr_bytes { + let fields = match env.target_info { 8 | 4 => [ env.ptr_int().into(), super::convert::zig_str_type(env).into(), @@ -322,7 +322,7 @@ fn decode_from_utf8_result<'a, 'ctx, 'env>( let record_type = env.context.struct_type(&fields, false); - match env.ptr_bytes { + match env.target_info { 8 | 4 => { let result_ptr_cast = env .builder diff --git a/compiler/gen_llvm/src/llvm/convert.rs b/compiler/gen_llvm/src/llvm/convert.rs index 1fc8179134..3bebd44fa1 100644 --- a/compiler/gen_llvm/src/llvm/convert.rs +++ b/compiler/gen_llvm/src/llvm/convert.rs @@ -36,7 +36,7 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>( match union_layout { NonRecursive(tags) => { - let data = block_of_memory_slices(env.context, tags, env.ptr_bytes); + let data = block_of_memory_slices(env.context, tags, env.target_info); env.context.struct_type(&[data, tag_id_type], false).into() } @@ -44,9 +44,9 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>( | NullableWrapped { other_tags: tags, .. } => { - let data = block_of_memory_slices(env.context, tags, env.ptr_bytes); + let data = block_of_memory_slices(env.context, tags, env.target_info); - if union_layout.stores_tag_id_as_data(env.ptr_bytes) { + if union_layout.stores_tag_id_as_data(env.target_info) { env.context .struct_type(&[data, tag_id_type], false) .ptr_type(AddressSpace::Generic) @@ -56,11 +56,12 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>( } } NullableUnwrapped { other_fields, .. } => { - let block = block_of_memory_slices(env.context, &[other_fields], env.ptr_bytes); + let block = + block_of_memory_slices(env.context, &[other_fields], env.target_info); block.ptr_type(AddressSpace::Generic).into() } NonNullableUnwrapped(fields) => { - let block = block_of_memory_slices(env.context, &[fields], env.ptr_bytes); + let block = block_of_memory_slices(env.context, &[fields], env.target_info); block.ptr_type(AddressSpace::Generic).into() } } @@ -95,7 +96,7 @@ pub fn basic_type_from_layout_1<'a, 'ctx, 'env>( match union_layout { NonRecursive(tags) => { - let data = block_of_memory_slices(env.context, tags, env.ptr_bytes); + let data = block_of_memory_slices(env.context, tags, env.target_info); let struct_type = env.context.struct_type(&[data, tag_id_type], false); struct_type.ptr_type(AddressSpace::Generic).into() @@ -104,9 +105,9 @@ pub fn basic_type_from_layout_1<'a, 'ctx, 'env>( | NullableWrapped { other_tags: tags, .. } => { - let data = block_of_memory_slices(env.context, tags, env.ptr_bytes); + let data = block_of_memory_slices(env.context, tags, env.target_info); - if union_layout.stores_tag_id_as_data(env.ptr_bytes) { + if union_layout.stores_tag_id_as_data(env.target_info) { env.context .struct_type(&[data, tag_id_type], false) .ptr_type(AddressSpace::Generic) @@ -116,11 +117,12 @@ pub fn basic_type_from_layout_1<'a, 'ctx, 'env>( } } NullableUnwrapped { other_fields, .. } => { - let block = block_of_memory_slices(env.context, &[other_fields], env.ptr_bytes); + let block = + block_of_memory_slices(env.context, &[other_fields], env.target_info); block.ptr_type(AddressSpace::Generic).into() } NonNullableUnwrapped(fields) => { - let block = block_of_memory_slices(env.context, &[fields], env.ptr_bytes); + let block = block_of_memory_slices(env.context, &[fields], env.target_info); block.ptr_type(AddressSpace::Generic).into() } } diff --git a/compiler/gen_llvm/src/llvm/externs.rs b/compiler/gen_llvm/src/llvm/externs.rs index 75bc506cc2..69b251cdcf 100644 --- a/compiler/gen_llvm/src/llvm/externs.rs +++ b/compiler/gen_llvm/src/llvm/externs.rs @@ -175,7 +175,7 @@ pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) { let buffer = crate::llvm::build::get_sjlj_buffer(env); // write our error message pointer - let index = env.ptr_int().const_int(3 * env.ptr_bytes as u64, false); + let index = env.ptr_int().const_int(3 * env.target_info as u64, false); let message_buffer_raw = unsafe { builder.build_gep(buffer, &[index], "raw_msg_buffer_ptr") }; let message_buffer = builder.build_bitcast( diff --git a/compiler/gen_llvm/src/llvm/refcounting.rs b/compiler/gen_llvm/src/llvm/refcounting.rs index b04acb06a9..229f3c4b86 100644 --- a/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/compiler/gen_llvm/src/llvm/refcounting.rs @@ -98,7 +98,7 @@ impl<'ctx> PointerToRefcount<'ctx> { pub fn is_1<'a, 'env>(&self, env: &Env<'a, 'ctx, 'env>) -> IntValue<'ctx> { let current = self.get_refcount(env); - let one = refcount_1(env.context, env.ptr_bytes); + let one = refcount_1(env.context, env.target_info); env.builder .build_int_compare(IntPredicate::EQ, current, one, "is_one") @@ -163,8 +163,8 @@ impl<'ctx> PointerToRefcount<'ctx> { pub fn decrement<'a, 'env>(&self, env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>) { let alignment = layout - .allocation_alignment_bytes(env.ptr_bytes) - .max(env.ptr_bytes); + .allocation_alignment_bytes(env.target_info) + .max(env.target_info); let context = env.context; let block = env.builder.get_insert_block().expect("to be in a function"); @@ -1192,7 +1192,7 @@ fn build_rec_union_help<'a, 'ctx, 'env>( debug_assert!(arg_val.is_pointer_value()); let current_tag_id = get_tag_id(env, fn_val, &union_layout, arg_val); - let value_ptr = if union_layout.stores_tag_id_in_pointer(env.ptr_bytes) { + let value_ptr = if union_layout.stores_tag_id_in_pointer(env.target_info) { tag_pointer_clear_tag_id(env, arg_val.into_pointer_value()) } else { arg_val.into_pointer_value() diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index ce3fb7ee11..f0b4753863 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -3824,7 +3824,7 @@ fn make_specializations<'a>( subs: &mut subs, home, ident_ids: &mut ident_ids, - ptr_bytes, + target_info: ptr_bytes, update_mode_ids: &mut update_mode_ids, // call_specialization_counter=0 is reserved call_specialization_counter: 1, @@ -3920,7 +3920,7 @@ fn build_pending_specializations<'a>( subs: &mut subs, home, ident_ids: &mut ident_ids, - ptr_bytes, + target_info: ptr_bytes, update_mode_ids: &mut update_mode_ids, // call_specialization_counter=0 is reserved call_specialization_counter: 1, diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 9b83b2b388..a2517b05f5 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -1071,7 +1071,7 @@ pub struct Env<'a, 'i> { pub problems: &'i mut std::vec::Vec, pub home: ModuleId, pub ident_ids: &'i mut IdentIds, - pub ptr_bytes: u32, + pub target_info: u32, pub update_mode_ids: &'i mut UpdateModeIds, pub call_specialization_counter: u32, } @@ -2471,7 +2471,7 @@ fn specialize_external<'a>( env.arena, ); - let ptr_bytes = env.ptr_bytes; + let ptr_bytes = env.target_info; combined.sort_by(|(_, layout1), (_, layout2)| { let size1 = layout1.alignment_bytes(ptr_bytes); @@ -2504,7 +2504,7 @@ fn specialize_external<'a>( env.arena, ); - let ptr_bytes = env.ptr_bytes; + let ptr_bytes = env.target_info; combined.sort_by(|(_, layout1), (_, layout2)| { let size1 = layout1.alignment_bytes(ptr_bytes); @@ -3009,14 +3009,14 @@ fn try_make_literal<'a>( match can_expr { Int(_, precision, _, int) => { - match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *precision, false) { + match num_argument_to_int_or_float(env.subs, env.target_info, *precision, false) { IntOrFloat::Int(_) => Some(Literal::Int(*int)), _ => unreachable!("unexpected float precision for integer"), } } Float(_, precision, float_str, float) => { - match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *precision, true) { + match num_argument_to_int_or_float(env.subs, env.target_info, *precision, true) { IntOrFloat::Float(_) => Some(Literal::Float(*float)), IntOrFloat::DecimalFloatType => { let dec = match RocDec::from_str(float_str) { @@ -3037,7 +3037,7 @@ fn try_make_literal<'a>( // Str(string) => Some(Literal::Str(env.arena.alloc(string))), Num(var, num_str, num) => { // first figure out what kind of number this is - match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, false) { + match num_argument_to_int_or_float(env.subs, env.target_info, *var, false) { IntOrFloat::Int(_) => Some(Literal::Int((*num).into())), IntOrFloat::Float(_) => Some(Literal::Float(*num as f64)), IntOrFloat::DecimalFloatType => { @@ -3072,7 +3072,7 @@ pub fn with_hole<'a>( match can_expr { Int(_, precision, _, int) => { - match num_argument_to_int_or_float(env.subs, env.ptr_bytes, precision, false) { + match num_argument_to_int_or_float(env.subs, env.target_info, precision, false) { IntOrFloat::Int(precision) => Stmt::Let( assigned, Expr::Literal(Literal::Int(int)), @@ -3084,7 +3084,7 @@ pub fn with_hole<'a>( } Float(_, precision, float_str, float) => { - match num_argument_to_int_or_float(env.subs, env.ptr_bytes, precision, true) { + match num_argument_to_int_or_float(env.subs, env.target_info, precision, true) { IntOrFloat::Float(precision) => Stmt::Let( assigned, Expr::Literal(Literal::Float(float)), @@ -3116,7 +3116,7 @@ pub fn with_hole<'a>( Num(var, num_str, num) => { // first figure out what kind of number this is - match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) { + match num_argument_to_int_or_float(env.subs, env.target_info, var, false) { IntOrFloat::Int(precision) => Stmt::Let( assigned, Expr::Literal(Literal::Int(num.into())), @@ -3393,7 +3393,7 @@ pub fn with_hole<'a>( env.arena, record_var, env.subs, - env.ptr_bytes, + env.target_info, ) { Ok(fields) => fields, Err(_) => return Stmt::RuntimeError("Can't create record with improper layout"), @@ -3754,7 +3754,7 @@ pub fn with_hole<'a>( env.arena, record_var, env.subs, - env.ptr_bytes, + env.target_info, ) { Ok(fields) => fields, Err(_) => return Stmt::RuntimeError("Can't access record with improper layout"), @@ -3911,7 +3911,7 @@ pub fn with_hole<'a>( env.arena, record_var, env.subs, - env.ptr_bytes, + env.target_info, ) { Ok(fields) => fields, Err(_) => return Stmt::RuntimeError("Can't update record with improper layout"), @@ -4586,7 +4586,7 @@ fn construct_closure_data<'a>( env.arena, ); - let ptr_bytes = env.ptr_bytes; + let ptr_bytes = env.target_info; combined.sort_by(|(_, layout1), (_, layout2)| { let size1 = layout1.alignment_bytes(ptr_bytes); @@ -4617,7 +4617,7 @@ fn construct_closure_data<'a>( env.arena, ); - let ptr_bytes = env.ptr_bytes; + let ptr_bytes = env.target_info; combined.sort_by(|(_, layout1), (_, layout2)| { let size1 = layout1.alignment_bytes(ptr_bytes); @@ -4692,7 +4692,7 @@ fn convert_tag_union<'a>( ) -> Stmt<'a> { use crate::layout::UnionVariant::*; let res_variant = - crate::layout::union_sorted_tags(env.arena, variant_var, env.subs, env.ptr_bytes); + crate::layout::union_sorted_tags(env.arena, variant_var, env.subs, env.target_info); let variant = match res_variant { Ok(cached) => cached, Err(LayoutProblem::UnresolvedTypeVar(_)) => { @@ -5035,7 +5035,7 @@ fn sorted_field_symbols<'a>( } }; - let alignment = layout.alignment_bytes(env.ptr_bytes); + let alignment = layout.alignment_bytes(env.target_info); let symbol = possible_reuse_symbol(env, procs, &arg.value); field_symbols_temp.push((alignment, symbol, ((var, arg), &*env.arena.alloc(symbol)))); @@ -5120,7 +5120,7 @@ fn register_capturing_closure<'a>( let captured_symbols = match *env.subs.get_content_without_compacting(function_type) { Content::Structure(FlatType::Func(_, closure_var, _)) => { - match LambdaSet::from_var(env.arena, env.subs, closure_var, env.ptr_bytes) { + match LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info) { Ok(lambda_set) => { if let Layout::Struct(&[]) = lambda_set.runtime_representation() { CapturedSymbols::None @@ -7621,7 +7621,7 @@ fn from_can_pattern_help<'a>( Underscore => Ok(Pattern::Underscore), Identifier(symbol) => Ok(Pattern::Identifier(*symbol)), IntLiteral(var, _, int) => { - match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, false) { + match num_argument_to_int_or_float(env.subs, env.target_info, *var, false) { IntOrFloat::Int(precision) => Ok(Pattern::IntLiteral(*int as i128, precision)), other => { panic!( @@ -7633,7 +7633,7 @@ fn from_can_pattern_help<'a>( } FloatLiteral(var, float_str, float) => { // TODO: Can I reuse num_argument_to_int_or_float here if I pass in true? - match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, true) { + match num_argument_to_int_or_float(env.subs, env.target_info, *var, true) { IntOrFloat::Int(_) => { panic!("Invalid precision for float pattern {:?}", var) } @@ -7663,7 +7663,7 @@ fn from_can_pattern_help<'a>( Err(RuntimeError::UnsupportedPattern(*region)) } NumLiteral(var, num_str, num) => { - match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, false) { + match num_argument_to_int_or_float(env.subs, env.target_info, *var, false) { IntOrFloat::Int(precision) => Ok(Pattern::IntLiteral(*num as i128, precision)), IntOrFloat::Float(precision) => Ok(Pattern::FloatLiteral(*num as u64, precision)), IntOrFloat::DecimalFloatType => { @@ -7686,7 +7686,7 @@ fn from_can_pattern_help<'a>( use crate::layout::UnionVariant::*; let res_variant = - crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.ptr_bytes) + crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.target_info) .map_err(Into::into); let variant = match res_variant { @@ -7768,12 +7768,12 @@ fn from_can_pattern_help<'a>( arguments.sort_by(|arg1, arg2| { let size1 = layout_cache .from_var(env.arena, arg1.0, env.subs) - .map(|x| x.alignment_bytes(env.ptr_bytes)) + .map(|x| x.alignment_bytes(env.target_info)) .unwrap_or(0); let size2 = layout_cache .from_var(env.arena, arg2.0, env.subs) - .map(|x| x.alignment_bytes(env.ptr_bytes)) + .map(|x| x.alignment_bytes(env.target_info)) .unwrap_or(0); size2.cmp(&size1) @@ -7806,8 +7806,8 @@ fn from_can_pattern_help<'a>( let layout2 = layout_cache.from_var(env.arena, arg2.0, env.subs).unwrap(); - let size1 = layout1.alignment_bytes(env.ptr_bytes); - let size2 = layout2.alignment_bytes(env.ptr_bytes); + let size1 = layout1.alignment_bytes(env.target_info); + let size2 = layout2.alignment_bytes(env.target_info); size2.cmp(&size1) }); @@ -8107,7 +8107,7 @@ fn from_can_pattern_help<'a>( } => { // sorted fields based on the type let sorted_fields = - crate::layout::sort_record_fields(env.arena, *whole_var, env.subs, env.ptr_bytes) + crate::layout::sort_record_fields(env.arena, *whole_var, env.subs, env.target_info) .map_err(RuntimeError::from)?; // sorted fields based on the destruct diff --git a/compiler/test_gen/src/helpers/llvm.rs b/compiler/test_gen/src/helpers/llvm.rs index e1bec306b1..6dc6c2b7f4 100644 --- a/compiler/test_gen/src/helpers/llvm.rs +++ b/compiler/test_gen/src/helpers/llvm.rs @@ -213,7 +213,7 @@ fn create_llvm_module<'a>( context, interns, module, - ptr_bytes, + target_info: ptr_bytes, is_gen_test, // important! we don't want any procedures to get the C calling convention exposed_to_host: MutSet::default(), diff --git a/reporting/tests/test_reporting.rs b/reporting/tests/test_reporting.rs index 0cb8640770..d0c6946def 100644 --- a/reporting/tests/test_reporting.rs +++ b/reporting/tests/test_reporting.rs @@ -105,7 +105,7 @@ mod test_reporting { home, ident_ids: &mut ident_ids, update_mode_ids: &mut update_mode_ids, - ptr_bytes, + target_info: ptr_bytes, // call_specialization_counter=0 is reserved call_specialization_counter: 1, }; From 0ed259a80dc367a014d4ef0733e699f8108abefe Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jan 2022 14:37:32 +0100 Subject: [PATCH 05/36] phase 3 --- compiler/mono/src/code_gen_help/equality.rs | 4 +++- compiler/mono/src/code_gen_help/mod.rs | 10 +++++----- compiler/mono/src/code_gen_help/refcount.rs | 20 +++++++++++--------- compiler/mono/src/ir.rs | 16 ++++++++-------- compiler/mono/src/layout.rs | 7 +++++++ 5 files changed, 34 insertions(+), 23 deletions(-) diff --git a/compiler/mono/src/code_gen_help/equality.rs b/compiler/mono/src/code_gen_help/equality.rs index db14a1e3b9..d58d095274 100644 --- a/compiler/mono/src/code_gen_help/equality.rs +++ b/compiler/mono/src/code_gen_help/equality.rs @@ -590,7 +590,9 @@ fn eq_list<'a>( // let size = literal int let size = root.create_symbol(ident_ids, "size"); - let size_expr = Expr::Literal(Literal::Int(elem_layout.stack_size(root.ptr_size) as i128)); + let size_expr = Expr::Literal(Literal::Int( + elem_layout.stack_size(root.target_info) as i128 + )); let size_stmt = |next| Stmt::Let(size, size_expr, layout_isize, next); // let list_size = len_1 * size diff --git a/compiler/mono/src/code_gen_help/mod.rs b/compiler/mono/src/code_gen_help/mod.rs index 61c4c32c9e..e74e4058ed 100644 --- a/compiler/mono/src/code_gen_help/mod.rs +++ b/compiler/mono/src/code_gen_help/mod.rs @@ -1,9 +1,9 @@ use bumpalo::collections::vec::Vec; use bumpalo::Bump; -use roc_builtins::bitcode::IntWidth; use roc_module::ident::Ident; use roc_module::low_level::LowLevel; use roc_module::symbol::{IdentIds, ModuleId, Symbol}; +use roc_target::TargetInfo; use crate::ir::{ Call, CallSpecId, CallType, Expr, HostExposedLayouts, JoinPointId, ModifyRc, Proc, ProcLayout, @@ -74,19 +74,19 @@ pub struct Context<'a> { pub struct CodeGenHelp<'a> { arena: &'a Bump, home: ModuleId, - ptr_size: u32, + target_info: TargetInfo, layout_isize: Layout<'a>, specializations: Vec<'a, Specialization<'a>>, debug_recursion_depth: usize, } impl<'a> CodeGenHelp<'a> { - pub fn new(arena: &'a Bump, intwidth_isize: IntWidth, home: ModuleId) -> Self { + pub fn new(arena: &'a Bump, target_info: TargetInfo, home: ModuleId) -> Self { CodeGenHelp { arena, home, - ptr_size: intwidth_isize.stack_size(), - layout_isize: Layout::Builtin(Builtin::Int(intwidth_isize)), + target_info, + layout_isize: Layout::usize(target_info), specializations: Vec::with_capacity_in(16, arena), debug_recursion_depth: 0, } diff --git a/compiler/mono/src/code_gen_help/refcount.rs b/compiler/mono/src/code_gen_help/refcount.rs index 2bc4aeff92..272e503d18 100644 --- a/compiler/mono/src/code_gen_help/refcount.rs +++ b/compiler/mono/src/code_gen_help/refcount.rs @@ -207,7 +207,7 @@ pub fn rc_ptr_from_data_ptr<'a>( // Mask for lower bits (for tag union id) let mask_sym = root.create_symbol(ident_ids, "mask"); - let mask_expr = Expr::Literal(Literal::Int(-(root.ptr_size as i128))); + let mask_expr = Expr::Literal(Literal::Int(-(root.target_info.ptr_width() as i128))); let mask_stmt = |next| Stmt::Let(mask_sym, mask_expr, root.layout_isize, next); let masked_sym = root.create_symbol(ident_ids, "masked"); @@ -222,7 +222,7 @@ pub fn rc_ptr_from_data_ptr<'a>( // Pointer size constant let ptr_size_sym = root.create_symbol(ident_ids, "ptr_size"); - let ptr_size_expr = Expr::Literal(Literal::Int(root.ptr_size as i128)); + let ptr_size_expr = Expr::Literal(Literal::Int(root.target_info.ptr_width() as i128)); let ptr_size_stmt = |next| Stmt::Let(ptr_size_sym, ptr_size_expr, root.layout_isize, next); // Refcount address @@ -382,7 +382,7 @@ fn refcount_str<'a>( // A pointer to the refcount value itself let rc_ptr = root.create_symbol(ident_ids, "rc_ptr"); - let alignment = root.ptr_size; + let alignment = root.target_info.ptr_width() as u32; let ret_unit_stmt = rc_return_stmt(root, ident_ids, ctx); let mod_rc_stmt = modify_refcount( @@ -487,7 +487,7 @@ fn refcount_list<'a>( // let rc_ptr = root.create_symbol(ident_ids, "rc_ptr"); - let alignment = layout.alignment_bytes(root.ptr_size); + let alignment = layout.alignment_bytes(root.target_info); let ret_stmt = rc_return_stmt(root, ident_ids, ctx); let modify_list = modify_refcount( @@ -584,7 +584,9 @@ fn refcount_list_elems<'a>( // let size = literal int let elem_size = root.create_symbol(ident_ids, "elem_size"); - let elem_size_expr = Expr::Literal(Literal::Int(elem_layout.stack_size(root.ptr_size) as i128)); + let elem_size_expr = Expr::Literal(Literal::Int( + elem_layout.stack_size(root.target_info) as i128 + )); let elem_size_stmt = |next| Stmt::Let(elem_size, elem_size_expr, layout_isize, next); // let list_size = len * size @@ -972,7 +974,7 @@ fn refcount_union_rec<'a>( let rc_structure_stmt = { let rc_ptr = root.create_symbol(ident_ids, "rc_ptr"); - let alignment = Layout::Union(union_layout).alignment_bytes(root.ptr_size); + let alignment = Layout::Union(union_layout).alignment_bytes(root.target_info); let ret_stmt = rc_return_stmt(root, ident_ids, ctx); let modify_structure_stmt = modify_refcount( root, @@ -988,7 +990,7 @@ fn refcount_union_rec<'a>( ident_ids, structure, rc_ptr, - union_layout.stores_tag_id_in_pointer(root.ptr_size), + union_layout.stores_tag_id_in_pointer(root.target_info), root.arena.alloc(modify_structure_stmt), ) }; @@ -1080,7 +1082,7 @@ fn refcount_union_tailrec<'a>( ) }; - let alignment = layout.alignment_bytes(root.ptr_size); + let alignment = layout.alignment_bytes(root.target_info); let modify_structure_stmt = modify_refcount( root, ident_ids, @@ -1095,7 +1097,7 @@ fn refcount_union_tailrec<'a>( ident_ids, current, rc_ptr, - union_layout.stores_tag_id_in_pointer(root.ptr_size), + union_layout.stores_tag_id_in_pointer(root.target_info), root.arena.alloc(modify_structure_stmt), ) }; diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index a2517b05f5..d048a0f4c2 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -16,6 +16,7 @@ use roc_module::symbol::{IdentIds, ModuleId, Symbol}; use roc_problem::can::RuntimeError; use roc_region::all::{Loc, Region}; use roc_std::RocDec; +use roc_target::TargetInfo; use roc_types::subs::{Content, FlatType, StorageSubs, Subs, Variable, VariableSubsSlice}; use std::collections::HashMap; use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder}; @@ -1071,7 +1072,7 @@ pub struct Env<'a, 'i> { pub problems: &'i mut std::vec::Vec, pub home: ModuleId, pub ident_ids: &'i mut IdentIds, - pub target_info: u32, + pub target_info: TargetInfo, pub update_mode_ids: &'i mut UpdateModeIds, pub call_specialization_counter: u32, } @@ -8259,7 +8260,7 @@ pub enum IntOrFloat { /// Given the `a` in `Num a`, determines whether it's an int or a float pub fn num_argument_to_int_or_float( subs: &Subs, - ptr_bytes: u32, + target_info: TargetInfo, var: Variable, known_to_be_float: bool, ) -> IntOrFloat { @@ -8274,7 +8275,7 @@ pub fn num_argument_to_int_or_float( // Recurse on the second argument let var = subs[args.variables().into_iter().next().unwrap()]; - num_argument_to_int_or_float(subs, ptr_bytes, var, false) + num_argument_to_int_or_float(subs, target_info, var, false) } other @ Content::Alias(symbol, args, _) => { @@ -8292,16 +8293,15 @@ pub fn num_argument_to_int_or_float( // Recurse on the second argument let var = subs[args.variables().into_iter().next().unwrap()]; - num_argument_to_int_or_float(subs, ptr_bytes, var, true) + num_argument_to_int_or_float(subs, target_info, var, true) } Symbol::NUM_DECIMAL | Symbol::NUM_AT_DECIMAL => IntOrFloat::DecimalFloatType, Symbol::NUM_NAT | Symbol::NUM_NATURAL | Symbol::NUM_AT_NATURAL => { - let int_width = match ptr_bytes { - 4 => IntWidth::U32, - 8 => IntWidth::U64, - _ => panic!("unsupported word size"), + let int_width = match target_info.ptr_width() { + roc_target::PtrWidth::Bytes4 => IntWidth::U32, + roc_target::PtrWidth::Bytes8 => IntWidth::U64, }; IntOrFloat::Int(int_width) diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index c56a10cb4d..12d759f8b8 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -1237,6 +1237,13 @@ impl<'a> Layout<'a> { } } + pub fn isize(target_info: TargetInfo) -> Layout<'a> { + match target_info.ptr_width() { + roc_target::PtrWidth::Bytes4 => Self::i32(), + roc_target::PtrWidth::Bytes8 => Self::i64(), + } + } + pub fn bool() -> Layout<'a> { Layout::Builtin(Builtin::Bool) } From c663a35e161feb395a3fa3882742036b111e4f03 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jan 2022 15:44:24 +0100 Subject: [PATCH 06/36] final phase --- Cargo.lock | 7 ++ ast/Cargo.toml | 1 + ast/src/module.rs | 3 +- cli/Cargo.toml | 1 + cli/src/build.rs | 9 +-- cli/src/repl/eval.rs | 73 +++++++++++--------- cli/src/repl/gen.rs | 10 +-- compiler/build/Cargo.toml | 1 + compiler/build/src/program.rs | 4 +- compiler/gen_dev/Cargo.toml | 1 + compiler/gen_dev/src/generic64/mod.rs | 17 ++--- compiler/gen_dev/src/generic64/x86_64.rs | 6 +- compiler/gen_llvm/src/llvm/build.rs | 82 +++++++++++------------ compiler/gen_llvm/src/llvm/build_dict.rs | 14 ++-- compiler/gen_llvm/src/llvm/build_str.rs | 25 +++---- compiler/gen_llvm/src/llvm/convert.rs | 25 +++---- compiler/gen_llvm/src/llvm/externs.rs | 4 +- compiler/gen_llvm/src/llvm/refcounting.rs | 17 ++--- compiler/gen_wasm/Cargo.toml | 1 + compiler/gen_wasm/src/backend.rs | 28 ++++---- compiler/gen_wasm/src/layout.rs | 6 +- compiler/gen_wasm/src/lib.rs | 15 ++++- compiler/load/Cargo.toml | 1 + compiler/load/src/file.rs | 37 +++++----- compiler/target/src/lib.rs | 14 ++++ docs/Cargo.toml | 1 + docs/src/lib.rs | 2 +- 27 files changed, 222 insertions(+), 183 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4bf5787f06..8ff94289d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3254,6 +3254,7 @@ dependencies = [ "roc_parse", "roc_problem", "roc_region", + "roc_target", "roc_types", "roc_unify", "snafu", @@ -3283,6 +3284,7 @@ dependencies = [ "roc_reporting", "roc_solve", "roc_std", + "roc_target", "roc_types", "roc_unify", "serde_json", @@ -3351,6 +3353,7 @@ dependencies = [ "roc_region", "roc_reporting", "roc_solve", + "roc_target", "roc_test_utils", "roc_types", "roc_unify", @@ -3417,6 +3420,7 @@ dependencies = [ "roc_module", "roc_parse", "roc_region", + "roc_target", "roc_types", "snafu", "tempfile", @@ -3509,6 +3513,7 @@ dependencies = [ "roc_region", "roc_solve", "roc_std", + "roc_target", "roc_types", "roc_unify", "target-lexicon", @@ -3542,6 +3547,7 @@ dependencies = [ "roc_module", "roc_mono", "roc_std", + "roc_target", ] [[package]] @@ -3589,6 +3595,7 @@ dependencies = [ "roc_region", "roc_reporting", "roc_solve", + "roc_target", "roc_types", "roc_unify", "tempfile", diff --git a/ast/Cargo.toml b/ast/Cargo.toml index 66407ee82b..f0edcc1fab 100644 --- a/ast/Cargo.toml +++ b/ast/Cargo.toml @@ -17,6 +17,7 @@ roc_problem = { path = "../compiler/problem" } roc_types = { path = "../compiler/types" } roc_unify = { path = "../compiler/unify"} roc_load = { path = "../compiler/load" } +roc_target = { path = "../compiler/target" } roc_error_macros = { path = "../error_macros" } arrayvec = "0.7.2" bumpalo = { version = "3.8.0", features = ["collections"] } diff --git a/ast/src/module.rs b/ast/src/module.rs index e744760c42..f13028e8d1 100644 --- a/ast/src/module.rs +++ b/ast/src/module.rs @@ -3,6 +3,7 @@ use std::path::Path; use bumpalo::Bump; use roc_collections::all::MutMap; use roc_load::file::LoadedModule; +use roc_target::TargetInfo; pub fn load_module(src_file: &Path) -> LoadedModule { let subs_by_module = MutMap::default(); @@ -19,7 +20,7 @@ pub fn load_module(src_file: &Path) -> LoadedModule { ) }), subs_by_module, - 8, + TargetInfo::default_x86_64(), roc_can::builtins::builtin_defs_map, ); diff --git a/cli/Cargo.toml b/cli/Cargo.toml index bba74100cc..f889f4b16b 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -61,6 +61,7 @@ roc_load = { path = "../compiler/load" } roc_gen_llvm = { path = "../compiler/gen_llvm", optional = true } roc_build = { path = "../compiler/build", default-features = false } roc_fmt = { path = "../compiler/fmt" } +roc_target = { path = "../compiler/target" } roc_reporting = { path = "../reporting" } roc_error_macros = { path = "../error_macros" } roc_editor = { path = "../editor", optional = true } diff --git a/cli/src/build.rs b/cli/src/build.rs index 89c6d1d601..4b0daee242 100644 --- a/cli/src/build.rs +++ b/cli/src/build.rs @@ -8,6 +8,7 @@ use roc_can::builtins::builtin_defs_map; use roc_collections::all::MutMap; use roc_load::file::LoadingProblem; use roc_mono::ir::OptLevel; +use roc_target::TargetInfo; use std::path::PathBuf; use std::time::{Duration, SystemTime}; use target_lexicon::Triple; @@ -58,7 +59,7 @@ pub fn build_file<'a>( target_valgrind: bool, ) -> Result> { let compilation_start = SystemTime::now(); - let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; + let target_info = TargetInfo::from(target); // Step 1: compile the app and generate the .o file let subs_by_module = MutMap::default(); @@ -72,7 +73,7 @@ pub fn build_file<'a>( stdlib, src_dir.as_path(), subs_by_module, - ptr_bytes, + target_info, builtin_defs_map, )?; @@ -356,7 +357,7 @@ pub fn check_file( // only used for generating errors. We don't do code generation, so hardcoding should be fine // we need monomorphization for when exhaustiveness checking - let ptr_bytes = 8; + let target_info = TargetInfo::default_x86_64(); // Step 1: compile the app and generate the .o file let subs_by_module = MutMap::default(); @@ -370,7 +371,7 @@ pub fn check_file( stdlib, src_dir.as_path(), subs_by_module, - ptr_bytes, + target_info, builtin_defs_map, )?; diff --git a/cli/src/repl/eval.rs b/cli/src/repl/eval.rs index bbd0e93a38..8b6ddd49b9 100644 --- a/cli/src/repl/eval.rs +++ b/cli/src/repl/eval.rs @@ -14,13 +14,14 @@ use roc_mono::layout::{ }; use roc_parse::ast::{AssignedField, Collection, Expr, StrLiteral}; use roc_region::all::{Loc, Region}; +use roc_target::TargetInfo; use roc_types::subs::{Content, FlatType, GetSubsSlice, RecordFields, Subs, UnionTags, Variable}; use std::cmp::{max_by_key, min_by_key}; struct Env<'a, 'env> { arena: &'a Bump, subs: &'env Subs, - ptr_bytes: u32, + target_info: TargetInfo, interns: &'env Interns, home: ModuleId, } @@ -47,12 +48,12 @@ pub unsafe fn jit_to_ast<'a>( interns: &'a Interns, home: ModuleId, subs: &'a Subs, - ptr_bytes: u32, + target_info: TargetInfo, ) -> Result, ToAstProblem> { let env = Env { arena, subs, - ptr_bytes, + target_info, interns, home, }; @@ -172,7 +173,7 @@ fn get_tags_vars_and_variant<'a>( let vars_of_tag: MutMap<_, _> = tags_vec.iter().cloned().collect(); let union_variant = - union_sorted_tags_help(env.arena, tags_vec, opt_rec_var, env.subs, env.ptr_bytes); + union_sorted_tags_help(env.arena, tags_vec, opt_rec_var, env.subs, env.target_info); (vars_of_tag, union_variant) } @@ -200,8 +201,12 @@ fn expr_of_tag<'a>( /// Gets the tag ID of a union variant, assuming that the tag ID is stored alongside (after) the /// tag data. The caller is expected to check that the tag ID is indeed stored this way. -fn tag_id_from_data(union_layout: UnionLayout, data_ptr: *const u8, ptr_bytes: u32) -> i64 { - let offset = union_layout.data_size_without_tag_id(ptr_bytes).unwrap(); +fn tag_id_from_data( + union_layout: UnionLayout, + data_ptr: *const u8, + target_info: TargetInfo, +) -> i64 { + let offset = union_layout.data_size_without_tag_id(target_info).unwrap(); unsafe { match union_layout.tag_id_builtin() { @@ -218,13 +223,12 @@ fn tag_id_from_data(union_layout: UnionLayout, data_ptr: *const u8, ptr_bytes: u } } -fn deref_ptr_of_ptr(ptr_of_ptr: *const u8, ptr_bytes: u32) -> *const u8 { +fn deref_ptr_of_ptr(ptr_of_ptr: *const u8, target_info: TargetInfo) -> *const u8 { unsafe { - match ptr_bytes { + match target_info.ptr_width() { // Our LLVM codegen represents pointers as i32/i64s. - 4 => *(ptr_of_ptr as *const i32) as *const u8, - 8 => *(ptr_of_ptr as *const i64) as *const u8, - _ => unreachable!(), + roc_target::PtrWidth::Bytes4 => *(ptr_of_ptr as *const i32) as *const u8, + roc_target::PtrWidth::Bytes8 => *(ptr_of_ptr as *const i64) as *const u8, } } } @@ -236,20 +240,20 @@ fn deref_ptr_of_ptr(ptr_of_ptr: *const u8, ptr_bytes: u32) -> *const u8 { fn tag_id_from_recursive_ptr( union_layout: UnionLayout, rec_ptr: *const u8, - ptr_bytes: u32, + target_info: TargetInfo, ) -> (i64, *const u8) { - let tag_in_ptr = union_layout.stores_tag_id_in_pointer(ptr_bytes); + let tag_in_ptr = union_layout.stores_tag_id_in_pointer(target_info); if tag_in_ptr { - let masked_ptr_to_data = deref_ptr_of_ptr(rec_ptr, ptr_bytes) as i64; - let (tag_id_bits, tag_id_mask) = tag_pointer_tag_id_bits_and_mask(ptr_bytes); + let masked_ptr_to_data = deref_ptr_of_ptr(rec_ptr, target_info) as i64; + let (tag_id_bits, tag_id_mask) = tag_pointer_tag_id_bits_and_mask(target_info); let tag_id = masked_ptr_to_data & (tag_id_mask as i64); // Clear the tag ID data from the pointer let ptr_to_data = ((masked_ptr_to_data >> tag_id_bits) << tag_id_bits) as *const u8; (tag_id as i64, ptr_to_data) } else { - let ptr_to_data = deref_ptr_of_ptr(rec_ptr, ptr_bytes); - let tag_id = tag_id_from_data(union_layout, ptr_to_data, ptr_bytes); + let ptr_to_data = deref_ptr_of_ptr(rec_ptr, target_info); + let tag_id = tag_id_from_data(union_layout, ptr_to_data, target_info); (tag_id, ptr_to_data) } } @@ -388,7 +392,7 @@ fn jit_to_ast_help<'a>( let fields = [Layout::u64(), *layout]; let layout = Layout::Struct(&fields); - let result_stack_size = layout.stack_size(env.ptr_bytes); + let result_stack_size = layout.stack_size(env.target_info); run_jit_function_dynamic_type!( lib, @@ -398,7 +402,7 @@ fn jit_to_ast_help<'a>( ) } Layout::Union(UnionLayout::NonRecursive(_)) => { - let size = layout.stack_size(env.ptr_bytes); + let size = layout.stack_size(env.target_info); Ok(run_jit_function_dynamic_type!( lib, main_fn_name, @@ -412,7 +416,7 @@ fn jit_to_ast_help<'a>( | Layout::Union(UnionLayout::NonNullableUnwrapped(_)) | Layout::Union(UnionLayout::NullableUnwrapped { .. }) | Layout::Union(UnionLayout::NullableWrapped { .. }) => { - let size = layout.stack_size(env.ptr_bytes); + let size = layout.stack_size(env.target_info); Ok(run_jit_function_dynamic_type!( lib, main_fn_name, @@ -506,7 +510,7 @@ fn ptr_to_ast<'a>( } (_, Layout::Builtin(Builtin::List(elem_layout))) => { // Turn the (ptr, len) wrapper struct into actual ptr and len values. - let len = unsafe { *(ptr.offset(env.ptr_bytes as isize) as *const usize) }; + let len = unsafe { *(ptr.offset(env.target_info.ptr_width() as isize) as *const usize) }; let ptr = unsafe { *(ptr as *const *const u8) }; list_to_ast(env, ptr, len, elem_layout, content) @@ -573,7 +577,7 @@ fn ptr_to_ast<'a>( // Because this is a `NonRecursive`, the tag ID is definitely after the data. let tag_id = - tag_id_from_data(union_layout, ptr, env.ptr_bytes); + tag_id_from_data(union_layout, ptr, env.target_info); // use the tag ID as an index, to get its name and layout of any arguments let (tag_name, arg_layouts) = @@ -605,7 +609,7 @@ fn ptr_to_ast<'a>( _ => unreachable!("any other variant would have a different layout"), }; - let (tag_id, ptr_to_data) = tag_id_from_recursive_ptr(*union_layout, ptr, env.ptr_bytes); + let (tag_id, ptr_to_data) = tag_id_from_recursive_ptr(*union_layout, ptr, env.target_info); let (tag_name, arg_layouts) = &tags_and_layouts[tag_id as usize]; expr_of_tag( @@ -633,7 +637,7 @@ fn ptr_to_ast<'a>( _ => unreachable!("any other variant would have a different layout"), }; - let ptr_to_data = deref_ptr_of_ptr(ptr, env.ptr_bytes); + let ptr_to_data = deref_ptr_of_ptr(ptr, env.target_info); expr_of_tag( env, @@ -663,7 +667,7 @@ fn ptr_to_ast<'a>( _ => unreachable!("any other variant would have a different layout"), }; - let ptr_to_data = deref_ptr_of_ptr(ptr, env.ptr_bytes); + let ptr_to_data = deref_ptr_of_ptr(ptr, env.target_info); if ptr_to_data.is_null() { tag_name_to_expr(env, &nullable_name) } else { @@ -694,11 +698,11 @@ fn ptr_to_ast<'a>( _ => unreachable!("any other variant would have a different layout"), }; - let ptr_to_data = deref_ptr_of_ptr(ptr, env.ptr_bytes); + let ptr_to_data = deref_ptr_of_ptr(ptr, env.target_info); if ptr_to_data.is_null() { tag_name_to_expr(env, &nullable_name) } else { - let (tag_id, ptr_to_data) = tag_id_from_recursive_ptr(*union_layout, ptr, env.ptr_bytes); + let (tag_id, ptr_to_data) = tag_id_from_recursive_ptr(*union_layout, ptr, env.target_info); let tag_id = if tag_id > nullable_id.into() { tag_id - 1 } else { tag_id }; @@ -749,7 +753,7 @@ fn list_to_ast<'a>( let arena = env.arena; let mut output = Vec::with_capacity_in(len, arena); - let elem_size = elem_layout.stack_size(env.ptr_bytes) as usize; + let elem_size = elem_layout.stack_size(env.target_info) as usize; for index in 0..len { let offset_bytes = index * elem_size; @@ -823,7 +827,7 @@ where output.push(&*arena.alloc(loc_expr)); // Advance the field pointer to the next field. - field_ptr = unsafe { field_ptr.offset(layout.stack_size(env.ptr_bytes) as isize) }; + field_ptr = unsafe { field_ptr.offset(layout.stack_size(env.target_info) as isize) }; } output @@ -908,7 +912,7 @@ fn struct_to_ast<'a>( // Advance the field pointer to the next field. field_ptr = - unsafe { field_ptr.offset(field_layout.stack_size(env.ptr_bytes) as isize) }; + unsafe { field_ptr.offset(field_layout.stack_size(env.target_info) as isize) }; } let output = output.into_bump_slice(); @@ -1083,8 +1087,13 @@ fn byte_to_ast<'a>(env: &Env<'a, '_>, value: u8, content: &Content) -> Expr<'a> .map(|(a, b)| (a.clone(), b.to_vec())) .collect(); - let union_variant = - union_sorted_tags_help(env.arena, tags_vec, None, env.subs, env.ptr_bytes); + let union_variant = union_sorted_tags_help( + env.arena, + tags_vec, + None, + env.subs, + env.target_info, + ); match union_variant { UnionVariant::ByteUnion(tagnames) => { diff --git a/cli/src/repl/gen.rs b/cli/src/repl/gen.rs index 1510a4c448..695fdd320b 100644 --- a/cli/src/repl/gen.rs +++ b/cli/src/repl/gen.rs @@ -13,6 +13,7 @@ use roc_load::file::LoadingProblem; use roc_mono::ir::OptLevel; use roc_parse::parser::SyntaxError; use roc_region::all::LineInfo; +use roc_target::TargetInfo; use roc_types::pretty_print::{content_to_string, name_all_type_vars}; use std::path::{Path, PathBuf}; use std::str::from_utf8_unchecked; @@ -43,7 +44,7 @@ pub fn gen_and_eval<'a>( let module_src = promote_expr_to_module(src_str); - let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; + let target_info = TargetInfo::from(&target); let exposed_types = MutMap::default(); let loaded = roc_load::file::load_and_monomorphize_from_str( @@ -53,7 +54,7 @@ pub fn gen_and_eval<'a>( &stdlib, src_dir, exposed_types, - ptr_bytes, + target_info, builtin_defs_map, ); @@ -133,7 +134,6 @@ pub fn gen_and_eval<'a>( } else { let context = Context::create(); let builder = context.create_builder(); - let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; let module = arena.alloc(roc_gen_llvm::llvm::build::module_from_builtins( &target, &context, "", )); @@ -181,7 +181,7 @@ pub fn gen_and_eval<'a>( context: &context, interns, module, - target_info: ptr_bytes, + target_info, is_gen_test: true, // so roc_panic is generated // important! we don't want any procedures to get the C calling convention exposed_to_host: MutSet::default(), @@ -237,7 +237,7 @@ pub fn gen_and_eval<'a>( &env.interns, home, &subs, - ptr_bytes, + target_info, ) }; let mut expr = roc_fmt::Buf::new_in(&arena); diff --git a/compiler/build/Cargo.toml b/compiler/build/Cargo.toml index 80aaa3dd00..4ef876bbf6 100644 --- a/compiler/build/Cargo.toml +++ b/compiler/build/Cargo.toml @@ -19,6 +19,7 @@ roc_unify = { path = "../unify" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } roc_load = { path = "../load" } +roc_target = { path = "../target" } roc_gen_llvm = { path = "../gen_llvm", optional = true } roc_gen_wasm = { path = "../gen_wasm", optional = true } roc_gen_dev = { path = "../gen_dev", default-features = false } diff --git a/compiler/build/src/program.rs b/compiler/build/src/program.rs index 1ba657e615..24b2cb9f0e 100644 --- a/compiler/build/src/program.rs +++ b/compiler/build/src/program.rs @@ -235,7 +235,7 @@ pub fn gen_from_mono_module_llvm( let code_gen_start = SystemTime::now(); // Generate the binary - let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; + let target_info = roc_target::TargetInfo::from(target); let context = Context::create(); let module = arena.alloc(module_from_builtins(target, &context, "app")); @@ -286,7 +286,7 @@ pub fn gen_from_mono_module_llvm( context: &context, interns: loaded.interns, module, - target_info: ptr_bytes, + target_info, // in gen_tests, the compiler provides roc_panic // and sets up the setjump/longjump exception handling is_gen_test: false, diff --git a/compiler/gen_dev/Cargo.toml b/compiler/gen_dev/Cargo.toml index 8efcaca241..d454e3ffbb 100644 --- a/compiler/gen_dev/Cargo.toml +++ b/compiler/gen_dev/Cargo.toml @@ -16,6 +16,7 @@ roc_builtins = { path = "../builtins" } roc_unify = { path = "../unify" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } +roc_target = { path = "../target" } roc_error_macros = { path = "../../error_macros" } bumpalo = { version = "3.8.0", features = ["collections"] } target-lexicon = "0.12.2" diff --git a/compiler/gen_dev/src/generic64/mod.rs b/compiler/gen_dev/src/generic64/mod.rs index 26f336ab40..f30e98edc3 100644 --- a/compiler/gen_dev/src/generic64/mod.rs +++ b/compiler/gen_dev/src/generic64/mod.rs @@ -7,12 +7,13 @@ use roc_module::symbol::{Interns, Symbol}; use roc_mono::code_gen_help::CodeGenHelp; use roc_mono::ir::{BranchInfo, JoinPointId, Literal, Param, ProcLayout, SelfRecursive, Stmt}; use roc_mono::layout::{Builtin, Layout}; +use roc_target::TargetInfo; use std::marker::PhantomData; pub mod aarch64; pub mod x86_64; -const PTR_SIZE: u32 = 8; +const TARGET_INFO: TargetInfo = TargetInfo::default_x86_64(); pub trait CallConv { const BASE_PTR_REG: GeneralReg; @@ -308,7 +309,7 @@ pub fn new_backend_64bit< phantom_cc: PhantomData, env, interns, - helper_proc_gen: CodeGenHelp::new(env.arena, IntWidth::I64, env.module_id), + helper_proc_gen: CodeGenHelp::new(env.arena, TARGET_INFO, env.module_id), helper_proc_symbols: bumpalo::vec![in env.arena], proc_name: None, is_self_recursive: None, @@ -974,7 +975,7 @@ impl< } fn create_struct(&mut self, sym: &Symbol, layout: &Layout<'a>, fields: &'a [Symbol]) { - let struct_size = layout.stack_size(PTR_SIZE); + let struct_size = layout.stack_size(TARGET_INFO); if let Layout::Struct(field_layouts) = layout { if struct_size > 0 { @@ -991,7 +992,7 @@ impl< let mut current_offset = offset; for (field, field_layout) in fields.iter().zip(field_layouts.iter()) { self.copy_symbol_to_stack_offset(current_offset, field, field_layout); - let field_size = field_layout.stack_size(PTR_SIZE); + let field_size = field_layout.stack_size(TARGET_INFO); current_offset += field_size as i32; } } else { @@ -1029,14 +1030,14 @@ impl< if let Some(SymbolStorage::Base { offset, .. }) = self.symbol_storage_map.get(structure) { let mut data_offset = *offset; for i in 0..index { - let field_size = field_layouts[i as usize].stack_size(PTR_SIZE); + let field_size = field_layouts[i as usize].stack_size(TARGET_INFO); data_offset += field_size as i32; } self.symbol_storage_map.insert( *sym, SymbolStorage::Base { offset: data_offset, - size: field_layouts[index as usize].stack_size(PTR_SIZE), + size: field_layouts[index as usize].stack_size(TARGET_INFO), owned: false, }, ); @@ -1569,10 +1570,10 @@ impl< { debug_assert_eq!( *size, - layout.stack_size(PTR_SIZE), + layout.stack_size(TARGET_INFO), "expected struct to have same size as data being stored in it" ); - for i in 0..layout.stack_size(PTR_SIZE) as i32 { + for i in 0..layout.stack_size(TARGET_INFO) as i32 { ASM::mov_reg64_base32(&mut self.buf, tmp_reg, from_offset + i); ASM::mov_base32_reg64(&mut self.buf, to_offset + i, tmp_reg); } diff --git a/compiler/gen_dev/src/generic64/x86_64.rs b/compiler/gen_dev/src/generic64/x86_64.rs index b6cba72e95..359dd4670f 100644 --- a/compiler/gen_dev/src/generic64/x86_64.rs +++ b/compiler/gen_dev/src/generic64/x86_64.rs @@ -1,4 +1,4 @@ -use crate::generic64::{Assembler, CallConv, RegTrait, SymbolStorage, PTR_SIZE}; +use crate::generic64::{Assembler, CallConv, RegTrait, SymbolStorage, TARGET_INFO}; use crate::{ single_register_builtins, single_register_floats, single_register_integers, Relocation, }; @@ -451,7 +451,7 @@ impl CallConv for X86_64SystemV { fn returns_via_arg_pointer(ret_layout: &Layout) -> bool { // TODO: This may need to be more complex/extended to fully support the calling convention. // details here: https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf - ret_layout.stack_size(PTR_SIZE) > 16 + ret_layout.stack_size(TARGET_INFO) > 16 } } @@ -775,7 +775,7 @@ impl CallConv for X86_64WindowsFastcall { fn returns_via_arg_pointer(ret_layout: &Layout) -> bool { // TODO: This is not fully correct there are some exceptions for "vector" types. // details here: https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-160#return-values - ret_layout.stack_size(PTR_SIZE) > 8 + ret_layout.stack_size(TARGET_INFO) > 8 } } diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 94ffe74b89..a1e016f937 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -196,15 +196,9 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { pub fn ptr_int(&self) -> IntType<'ctx> { let ctx = self.context; - match self.target_info { - 1 => ctx.i8_type(), - 2 => ctx.i16_type(), - 4 => ctx.i32_type(), - 8 => ctx.i64_type(), - _ => panic!( - "Invalid target: Roc does't support compiling to {}-bit systems.", - self.target_info * 8 - ), + match self.target_info.ptr_width() { + roc_target::PtrWidth::Bytes4 => ctx.i32_type(), + roc_target::PtrWidth::Bytes8 => ctx.i64_type(), } } @@ -217,7 +211,7 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { } pub fn small_str_bytes(&self) -> u32 { - self.target_info * 2 + self.target_info.ptr_width() as u32 * 2 } pub fn build_intrinsic_call( @@ -318,12 +312,9 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { ) -> CallSiteValue<'ctx> { let false_val = self.context.bool_type().const_int(0, false); - let intrinsic_name = match self.target_info { - 8 => LLVM_MEMSET_I64, - 4 => LLVM_MEMSET_I32, - other => { - unreachable!("Unsupported number of ptr_bytes {:?}", other); - } + let intrinsic_name = match self.target_info.ptr_width() { + roc_target::PtrWidth::Bytes8 => LLVM_MEMSET_I64, + roc_target::PtrWidth::Bytes4 => LLVM_MEMSET_I32, }; self.build_intrinsic_call( @@ -1789,7 +1780,7 @@ fn tag_pointer_set_tag_id<'a, 'ctx, 'env>( pointer: PointerValue<'ctx>, ) -> PointerValue<'ctx> { // we only have 3 bits, so can encode only 0..7 (or on 32-bit targets, 2 bits to encode 0..3) - debug_assert!((tag_id as u32) < env.target_info); + debug_assert!((tag_id as u32) < env.target_info.ptr_width() as u32); let ptr_int = env.ptr_int(); @@ -1802,11 +1793,10 @@ fn tag_pointer_set_tag_id<'a, 'ctx, 'env>( .build_int_to_ptr(combined, pointer.get_type(), "to_ptr") } -pub fn tag_pointer_tag_id_bits_and_mask(ptr_bytes: u32) -> (u64, u64) { - match ptr_bytes { - 8 => (3, 0b0000_0111), - 4 => (2, 0b0000_0011), - _ => unreachable!(), +pub fn tag_pointer_tag_id_bits_and_mask(target_info: TargetInfo) -> (u64, u64) { + match target_info.ptr_width() { + roc_target::PtrWidth::Bytes8 => (3, 0b0000_0111), + roc_target::PtrWidth::Bytes4 => (2, 0b0000_0011), } } @@ -2183,8 +2173,9 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>( let builder = env.builder; let len_type = env.ptr_int(); + let ptr_width_u32 = env.target_info.ptr_width() as u32; - let extra_bytes = alignment_bytes.max(env.target_info); + let extra_bytes = alignment_bytes.max(ptr_width_u32); let ptr = { // number of bytes we will allocated @@ -2209,8 +2200,8 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>( .into_pointer_value(); let index = match extra_bytes { - n if n == env.target_info => 1, - n if n == 2 * env.target_info => 2, + n if n == ptr_width_u32 => 1, + n if n == 2 * ptr_width_u32 => 2, _ => unreachable!("invalid extra_bytes, {}", extra_bytes), }; @@ -2229,11 +2220,11 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>( }; let refcount_ptr = match extra_bytes { - n if n == env.target_info => { + n if n == ptr_width_u32 => { // the allocated pointer is the same as the refcounted pointer unsafe { PointerToRefcount::from_ptr(env, ptr) } } - n if n == 2 * env.target_info => { + n if n == 2 * ptr_width_u32 => { // the refcount is stored just before the start of the actual data // but in this case (because of alignment) not at the start of the allocated buffer PointerToRefcount::from_ptr_to_data(env, data_ptr) @@ -2288,10 +2279,11 @@ fn list_literal<'a, 'ctx, 'env>( let size = list_length * element_width as usize; let alignment = element_layout .alignment_bytes(env.target_info) - .max(env.target_info); + .max(env.target_info.ptr_width() as u32); let mut is_all_constant = true; - let zero_elements = (env.target_info as f64 / element_width as f64).ceil() as usize; + let zero_elements = + (env.target_info.ptr_width() as u8 as f64 / element_width as f64).ceil() as usize; // runtime-evaluated elements let mut runtime_evaluated_elements = Vec::with_capacity_in(list_length, env.arena); @@ -3727,7 +3719,10 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>( } pub fn get_sjlj_buffer<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValue<'ctx> { - let type_ = env.context.i8_type().array_type(5 * env.target_info); + let type_ = env + .context + .i8_type() + .array_type(5 * env.target_info.ptr_width() as u32); let global = match env.module.get_global("roc_sjlj_buffer") { Some(global) => global, @@ -3895,8 +3890,12 @@ fn make_exception_catcher<'a, 'ctx, 'env>( function_value } -fn roc_result_layout<'a>(arena: &'a Bump, return_layout: Layout<'a>, ptr_bytes: u32) -> Layout<'a> { - let elements = [Layout::u64(), Layout::usize(ptr_bytes), return_layout]; +fn roc_result_layout<'a>( + arena: &'a Bump, + return_layout: Layout<'a>, + target_info: TargetInfo, +) -> Layout<'a> { + let elements = [Layout::u64(), Layout::usize(target_info), return_layout]; Layout::Struct(arena.alloc(elements)) } @@ -6076,8 +6075,8 @@ fn run_low_level<'a, 'ctx, 'env>( { bd.position_at_end(throw_block); - match env.target_info { - 8 => { + match env.target_info.ptr_width() { + roc_target::PtrWidth::Bytes8 => { let fn_ptr_type = context .void_type() .fn_type(&[], false) @@ -6096,11 +6095,10 @@ fn run_low_level<'a, 'ctx, 'env>( bd.build_unconditional_branch(then_block); } - 4 => { + roc_target::PtrWidth::Bytes4 => { // temporary WASM implementation throw_exception(env, "An expectation failed!"); } - _ => unreachable!(), } } @@ -6196,7 +6194,7 @@ enum 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 { let return_size = layout.stack_size(env.target_info); - let pass_result_by_pointer = return_size > 2 * env.target_info; + let pass_result_by_pointer = return_size > 2 * env.target_info.ptr_width() as u32; if return_size == 0 { CCReturn::Void @@ -7130,7 +7128,9 @@ fn define_global_str_literal_ptr<'a, 'ctx, 'env>( let ptr = unsafe { env.builder.build_in_bounds_gep( ptr, - &[env.ptr_int().const_int(env.target_info as u64, false)], + &[env + .ptr_int() + .const_int(env.target_info.ptr_width() as u64, false)], "get_rc_ptr", ) }; @@ -7160,11 +7160,11 @@ fn define_global_str_literal<'a, 'ctx, 'env>( Some(current) => current, None => { - let size = message.bytes().len() + env.target_info as usize; + let size = message.bytes().len() + env.target_info.ptr_width() as usize; let mut bytes = Vec::with_capacity_in(size, env.arena); // insert NULL bytes for the refcount - for _ in 0..env.target_info { + for _ in 0..env.target_info.ptr_width() as usize { bytes.push(env.context.i8_type().const_zero()); } @@ -7183,7 +7183,7 @@ fn define_global_str_literal<'a, 'ctx, 'env>( // strings are NULL-terminated, which means we can't store the refcount (which is 8 // NULL bytes) global.set_constant(true); - global.set_alignment(env.target_info); + global.set_alignment(env.target_info.ptr_width() as u32); global.set_unnamed_addr(true); global.set_linkage(inkwell::module::Linkage::Private); diff --git a/compiler/gen_llvm/src/llvm/build_dict.rs b/compiler/gen_llvm/src/llvm/build_dict.rs index 47f8595d70..37b81e9129 100644 --- a/compiler/gen_llvm/src/llvm/build_dict.rs +++ b/compiler/gen_llvm/src/llvm/build_dict.rs @@ -17,14 +17,15 @@ use inkwell::AddressSpace; use roc_builtins::bitcode; use roc_module::symbol::Symbol; use roc_mono::layout::{Builtin, Layout, LayoutIds}; +use roc_target::TargetInfo; #[repr(transparent)] struct Alignment(u8); impl Alignment { - fn from_key_value_layout(key: &Layout, value: &Layout, ptr_bytes: u32) -> Alignment { - let key_align = key.alignment_bytes(ptr_bytes); - let value_align = value.alignment_bytes(ptr_bytes); + fn from_key_value_layout(key: &Layout, value: &Layout, target_info: TargetInfo) -> Alignment { + let key_align = key.alignment_bytes(target_info); + let value_align = value.alignment_bytes(target_info); let mut bits = key_align.max(value_align) as u8; @@ -454,19 +455,18 @@ fn pass_dict_c_abi<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, dict: BasicValueEnum<'ctx>, ) -> BasicValueEnum<'ctx> { - match env.target_info { - 4 => { + match env.target_info.ptr_width() { + roc_target::PtrWidth::Bytes4 => { let target_type = env.context.custom_width_int_type(96).into(); complex_bitcast(env.builder, dict, target_type, "to_i96") } - 8 => { + roc_target::PtrWidth::Bytes8 => { let dict_ptr = env.builder.build_alloca(zig_dict_type(env), "dict_ptr"); env.builder.build_store(dict_ptr, dict); dict_ptr.into() } - _ => unreachable!(), } } diff --git a/compiler/gen_llvm/src/llvm/build_str.rs b/compiler/gen_llvm/src/llvm/build_str.rs index a7337cd07d..4ce155aac6 100644 --- a/compiler/gen_llvm/src/llvm/build_str.rs +++ b/compiler/gen_llvm/src/llvm/build_str.rs @@ -10,6 +10,7 @@ use morphic_lib::UpdateMode; use roc_builtins::bitcode::{self, IntWidth}; use roc_module::symbol::Symbol; use roc_mono::layout::{Builtin, Layout}; +use roc_target::PtrWidth; use super::build::load_symbol; @@ -79,10 +80,9 @@ fn str_symbol_to_c_abi<'a, 'ctx, 'env>( ) -> IntValue<'ctx> { let string = load_symbol(scope, &symbol); - let target_type = match env.target_info { - 8 => env.context.i128_type().into(), - 4 => env.context.i64_type().into(), - _ => unreachable!(), + let target_type = match env.target_info.ptr_width() { + PtrWidth::Bytes8 => env.context.i128_type().into(), + PtrWidth::Bytes4 => env.context.i64_type().into(), }; complex_bitcast(env.builder, string, target_type, "str_to_c_abi").into_int_value() @@ -96,10 +96,9 @@ pub fn str_to_c_abi<'a, 'ctx, 'env>( env.builder.build_store(cell, value); - let target_type = match env.target_info { - 8 => env.context.i128_type(), - 4 => env.context.i64_type(), - _ => unreachable!(), + let target_type = match env.target_info.ptr_width() { + PtrWidth::Bytes8 => env.context.i128_type(), + PtrWidth::Bytes4 => env.context.i64_type(), }; let target_type_ptr = env @@ -310,20 +309,19 @@ fn decode_from_utf8_result<'a, 'ctx, 'env>( let builder = env.builder; let ctx = env.context; - let fields = match env.target_info { - 8 | 4 => [ + let fields = match env.target_info.ptr_width() { + PtrWidth::Bytes4 | PtrWidth::Bytes8 => [ env.ptr_int().into(), super::convert::zig_str_type(env).into(), env.context.bool_type().into(), ctx.i8_type().into(), ], - _ => unreachable!(), }; let record_type = env.context.struct_type(&fields, false); - match env.target_info { - 8 | 4 => { + match env.target_info.ptr_width() { + PtrWidth::Bytes4 | PtrWidth::Bytes8 => { let result_ptr_cast = env .builder .build_bitcast( @@ -337,7 +335,6 @@ fn decode_from_utf8_result<'a, 'ctx, 'env>( .build_load(result_ptr_cast, "load_utf8_validate_bytes_result") .into_struct_value() } - _ => unreachable!(), } } diff --git a/compiler/gen_llvm/src/llvm/convert.rs b/compiler/gen_llvm/src/llvm/convert.rs index 3bebd44fa1..00d98ff5e3 100644 --- a/compiler/gen_llvm/src/llvm/convert.rs +++ b/compiler/gen_llvm/src/llvm/convert.rs @@ -4,6 +4,7 @@ use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType}; use inkwell::AddressSpace; use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_mono::layout::{Builtin, Layout, UnionLayout}; +use roc_target::TargetInfo; fn basic_type_from_record<'a, 'ctx, 'env>( env: &crate::llvm::build::Env<'a, 'ctx, 'env>, @@ -190,13 +191,13 @@ pub fn float_type_from_float_width<'a, 'ctx, 'env>( pub fn block_of_memory_slices<'ctx>( context: &'ctx Context, layouts: &[&[Layout<'_>]], - ptr_bytes: u32, + target_info: TargetInfo, ) -> BasicTypeEnum<'ctx> { let mut union_size = 0; for tag in layouts { let mut total = 0; for layout in tag.iter() { - total += layout.stack_size(ptr_bytes as u32); + total += layout.stack_size(target_info); } union_size = union_size.max(total); @@ -208,13 +209,13 @@ pub fn block_of_memory_slices<'ctx>( pub fn block_of_memory<'ctx>( context: &'ctx Context, layout: &Layout<'_>, - ptr_bytes: u32, + target_info: TargetInfo, ) -> BasicTypeEnum<'ctx> { // TODO make this dynamic - let mut union_size = layout.stack_size(ptr_bytes as u32); + let mut union_size = layout.stack_size(target_info); if let Layout::Union(UnionLayout::NonRecursive { .. }) = layout { - union_size -= ptr_bytes; + union_size -= target_info.ptr_width() as u32; } block_of_memory_help(context, union_size) @@ -253,16 +254,10 @@ fn block_of_memory_help(context: &Context, union_size: u32) -> BasicTypeEnum<'_> } /// The int type that the C ABI turns our RocList/RocStr into -pub fn str_list_int(ctx: &Context, ptr_bytes: u32) -> IntType<'_> { - match ptr_bytes { - 1 => ctx.i16_type(), - 2 => ctx.i32_type(), - 4 => ctx.i64_type(), - 8 => ctx.i128_type(), - _ => panic!( - "Invalid target: Roc does't support compiling to {}-bit systems.", - ptr_bytes * 8 - ), +pub fn str_list_int(ctx: &Context, target_info: TargetInfo) -> IntType<'_> { + match target_info.ptr_width() { + roc_target::PtrWidth::Bytes4 => ctx.i32_type(), + roc_target::PtrWidth::Bytes8 => ctx.i64_type(), } } diff --git a/compiler/gen_llvm/src/llvm/externs.rs b/compiler/gen_llvm/src/llvm/externs.rs index 69b251cdcf..91af05faf8 100644 --- a/compiler/gen_llvm/src/llvm/externs.rs +++ b/compiler/gen_llvm/src/llvm/externs.rs @@ -175,7 +175,9 @@ pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) { let buffer = crate::llvm::build::get_sjlj_buffer(env); // write our error message pointer - let index = env.ptr_int().const_int(3 * env.target_info as u64, false); + let index = env + .ptr_int() + .const_int(3 * env.target_info.ptr_width() as u64, false); let message_buffer_raw = unsafe { builder.build_gep(buffer, &[index], "raw_msg_buffer_ptr") }; let message_buffer = builder.build_bitcast( diff --git a/compiler/gen_llvm/src/llvm/refcounting.rs b/compiler/gen_llvm/src/llvm/refcounting.rs index 229f3c4b86..2962361c29 100644 --- a/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/compiler/gen_llvm/src/llvm/refcounting.rs @@ -18,21 +18,16 @@ use inkwell::{AddressSpace, IntPredicate}; use roc_module::symbol::Interns; use roc_module::symbol::Symbol; use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout}; +use roc_target::TargetInfo; /// "Infinite" reference count, for static values /// Ref counts are encoded as negative numbers where isize::MIN represents 1 pub const REFCOUNT_MAX: usize = 0_usize; -pub fn refcount_1(ctx: &Context, ptr_bytes: u32) -> IntValue<'_> { - match ptr_bytes { - 1 => ctx.i8_type().const_int(i8::MIN as u64, false), - 2 => ctx.i16_type().const_int(i16::MIN as u64, false), - 4 => ctx.i32_type().const_int(i32::MIN as u64, false), - 8 => ctx.i64_type().const_int(i64::MIN as u64, false), - _ => panic!( - "Invalid target: Roc does't support compiling to {}-bit systems.", - ptr_bytes * 8 - ), +pub fn refcount_1(ctx: &Context, target_info: TargetInfo) -> IntValue<'_> { + match target_info.ptr_width() { + roc_target::PtrWidth::Bytes4 => ctx.i32_type().const_int(i32::MIN as u64, false), + roc_target::PtrWidth::Bytes8 => ctx.i64_type().const_int(i64::MIN as u64, false), } } @@ -164,7 +159,7 @@ impl<'ctx> PointerToRefcount<'ctx> { pub fn decrement<'a, 'env>(&self, env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>) { let alignment = layout .allocation_alignment_bytes(env.target_info) - .max(env.target_info); + .max(env.target_info.ptr_width() as u32); let context = env.context; let block = env.builder.get_insert_block().expect("to be in a function"); diff --git a/compiler/gen_wasm/Cargo.toml b/compiler/gen_wasm/Cargo.toml index 90578803f7..a40dd13703 100644 --- a/compiler/gen_wasm/Cargo.toml +++ b/compiler/gen_wasm/Cargo.toml @@ -11,5 +11,6 @@ roc_builtins = { path = "../builtins" } roc_collections = { path = "../collections" } roc_module = { path = "../module" } roc_mono = { path = "../mono" } +roc_target = { path = "../target" } roc_std = { path = "../../roc_std" } roc_error_macros = { path = "../../error_macros" } diff --git a/compiler/gen_wasm/src/backend.rs b/compiler/gen_wasm/src/backend.rs index 1200a80e12..716758fc56 100644 --- a/compiler/gen_wasm/src/backend.rs +++ b/compiler/gen_wasm/src/backend.rs @@ -26,7 +26,7 @@ use crate::wasm_module::{ }; use crate::{ copy_memory, round_up_to_alignment, CopyMemoryConfig, Env, DEBUG_LOG_SETTINGS, MEMORY_NAME, - PTR_SIZE, PTR_TYPE, STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME, + PTR_SIZE, PTR_TYPE, STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME, TARGET_INFO, }; /// The memory address where the constants data will be loaded during module instantiation. @@ -943,7 +943,7 @@ impl<'a> WasmBackend<'a> { } }; for field in field_layouts.iter().take(index as usize) { - offset += field.stack_size(PTR_SIZE); + offset += field.stack_size(TARGET_INFO); } self.storage .copy_value_from_memory(&mut self.code_builder, sym, local_id, offset); @@ -1010,11 +1010,11 @@ impl<'a> WasmBackend<'a> { elems: &'a [ListLiteralElement<'a>], ) { if let StoredValue::StackMemory { location, .. } = storage { - let size = elem_layout.stack_size(PTR_SIZE) * (elems.len() as u32); + let size = elem_layout.stack_size(TARGET_INFO) * (elems.len() as u32); // Allocate heap space and store its address in a local variable let heap_local_id = self.storage.create_anonymous_local(PTR_TYPE); - let heap_alignment = elem_layout.alignment_bytes(PTR_SIZE); + let heap_alignment = elem_layout.alignment_bytes(TARGET_INFO); self.allocate_with_refcount(Some(size), heap_alignment, 1); self.code_builder.set_local(heap_local_id); @@ -1099,9 +1099,9 @@ impl<'a> WasmBackend<'a> { return; } - let stores_tag_id_as_data = union_layout.stores_tag_id_as_data(PTR_SIZE); - let stores_tag_id_in_pointer = union_layout.stores_tag_id_in_pointer(PTR_SIZE); - let (data_size, data_alignment) = union_layout.data_size_and_alignment(PTR_SIZE); + let stores_tag_id_as_data = union_layout.stores_tag_id_as_data(TARGET_INFO); + let stores_tag_id_in_pointer = union_layout.stores_tag_id_in_pointer(TARGET_INFO); + let (data_size, data_alignment) = union_layout.data_size_and_alignment(TARGET_INFO); // We're going to use the pointer many times, so put it in a local variable let stored_with_local = @@ -1138,7 +1138,7 @@ impl<'a> WasmBackend<'a> { if stores_tag_id_as_data { let id_offset = data_offset + data_size - data_alignment; - let id_align = union_layout.tag_id_builtin().alignment_bytes(PTR_SIZE); + let id_align = union_layout.tag_id_builtin().alignment_bytes(TARGET_INFO); let id_align = Align::from(id_align); self.code_builder.get_local(local_id); @@ -1218,11 +1218,11 @@ impl<'a> WasmBackend<'a> { } }; - if union_layout.stores_tag_id_as_data(PTR_SIZE) { - let (data_size, data_alignment) = union_layout.data_size_and_alignment(PTR_SIZE); + if union_layout.stores_tag_id_as_data(TARGET_INFO) { + let (data_size, data_alignment) = union_layout.data_size_and_alignment(TARGET_INFO); let id_offset = data_size - data_alignment; - let id_align = union_layout.tag_id_builtin().alignment_bytes(PTR_SIZE); + let id_align = union_layout.tag_id_builtin().alignment_bytes(TARGET_INFO); let id_align = Align::from(id_align); self.storage @@ -1237,7 +1237,7 @@ impl<'a> WasmBackend<'a> { Builtin::Int(IntWidth::U64) => self.code_builder.i64_load(id_align, id_offset), x => internal_error!("Unexpected layout for tag union id {:?}", x), } - } else if union_layout.stores_tag_id_in_pointer(PTR_SIZE) { + } else if union_layout.stores_tag_id_in_pointer(TARGET_INFO) { self.storage .load_symbols(&mut self.code_builder, &[structure]); self.code_builder.i32_const(3); @@ -1284,7 +1284,7 @@ impl<'a> WasmBackend<'a> { let field_offset: u32 = field_layouts .iter() .take(index as usize) - .map(|field_layout| field_layout.stack_size(PTR_SIZE)) + .map(|field_layout| field_layout.stack_size(TARGET_INFO)) .sum(); // Get pointer and offset to the tag's data @@ -1304,7 +1304,7 @@ impl<'a> WasmBackend<'a> { } }; - let stores_tag_id_in_pointer = union_layout.stores_tag_id_in_pointer(PTR_SIZE); + let stores_tag_id_in_pointer = union_layout.stores_tag_id_in_pointer(TARGET_INFO); let from_ptr = if stores_tag_id_in_pointer { let ptr = self.storage.create_anonymous_local(ValueType::I32); diff --git a/compiler/gen_wasm/src/layout.rs b/compiler/gen_wasm/src/layout.rs index 330de5261b..b7ad18b2ef 100644 --- a/compiler/gen_wasm/src/layout.rs +++ b/compiler/gen_wasm/src/layout.rs @@ -2,7 +2,7 @@ use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_mono::layout::{Layout, UnionLayout}; use crate::wasm_module::ValueType; -use crate::{PTR_SIZE, PTR_TYPE}; +use crate::{PTR_SIZE, PTR_TYPE, TARGET_INFO}; /// Manually keep up to date with the Zig version we are using for builtins pub const BUILTINS_ZIG_VERSION: ZigVersion = ZigVersion::Zig8; @@ -47,8 +47,8 @@ impl WasmLayout { use UnionLayout::*; use ValueType::*; - let size = layout.stack_size(PTR_SIZE); - let alignment_bytes = layout.alignment_bytes(PTR_SIZE); + let size = layout.stack_size(TARGET_INFO); + let alignment_bytes = layout.alignment_bytes(TARGET_INFO); match layout { Layout::Builtin(Int(int_width)) => { diff --git a/compiler/gen_wasm/src/lib.rs b/compiler/gen_wasm/src/lib.rs index b033c66583..9163e592e5 100644 --- a/compiler/gen_wasm/src/lib.rs +++ b/compiler/gen_wasm/src/lib.rs @@ -6,20 +6,29 @@ pub mod wasm_module; use bumpalo::{self, collections::Vec, Bump}; -use roc_builtins::bitcode::IntWidth; use roc_collections::all::{MutMap, MutSet}; use roc_module::low_level::LowLevelWrapperType; use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_mono::code_gen_help::CodeGenHelp; use roc_mono::ir::{Proc, ProcLayout}; use roc_mono::layout::LayoutIds; +use roc_target::TargetInfo; use crate::backend::WasmBackend; use crate::wasm_module::{ Align, CodeBuilder, Export, ExportType, LocalId, SymInfo, ValueType, WasmModule, }; -const PTR_SIZE: u32 = 4; +const TARGET_INFO: TargetInfo = TargetInfo::default_wasm32(); +const PTR_SIZE: u32 = { + let value = TARGET_INFO.ptr_width() as u32; + + // const assert that our pointer width is actually 4 + // the code relies on the pointer width being exactly 4 + assert!(value == 4); + + value +}; const PTR_TYPE: ValueType = ValueType::I32; pub const STACK_POINTER_GLOBAL_ID: u32 = 0; @@ -111,7 +120,7 @@ pub fn build_module_without_test_wrapper<'a>( proc_symbols, initial_module, fn_index_offset, - CodeGenHelp::new(env.arena, IntWidth::I32, env.module_id), + CodeGenHelp::new(env.arena, TargetInfo::default_wasm32(), env.module_id), ); if DEBUG_LOG_SETTINGS.user_procs_ir { diff --git a/compiler/load/Cargo.toml b/compiler/load/Cargo.toml index ac6505b016..27da9138af 100644 --- a/compiler/load/Cargo.toml +++ b/compiler/load/Cargo.toml @@ -18,6 +18,7 @@ roc_unify = { path = "../unify" } roc_parse = { path = "../parse" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } +roc_target = { path = "../target" } roc_reporting = { path = "../../reporting" } morphic_lib = { path = "../../vendor/morphic_lib" } ven_pretty = { path = "../../vendor/pretty" } diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index f0b4753863..fed406405e 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -32,6 +32,7 @@ use roc_parse::parser::{FileError, Parser, SyntaxError}; use roc_region::all::{LineInfo, Loc, Region}; use roc_solve::module::SolvedModule; use roc_solve::solve; +use roc_target::TargetInfo; use roc_types::solved_types::Solved; use roc_types::subs::{Subs, VarStore, Variable}; use roc_types::types::{Alias, Type}; @@ -878,7 +879,7 @@ struct State<'a> { pub exposed_types: SubsByModule, pub output_path: Option<&'a str>, pub platform_path: PlatformPath<'a>, - pub ptr_bytes: u32, + pub target_info: TargetInfo, pub module_cache: ModuleCache<'a>, pub dependencies: Dependencies<'a>, @@ -1083,7 +1084,7 @@ pub fn load_and_typecheck<'a, F>( stdlib: &'a StdLib, src_dir: &Path, exposed_types: SubsByModule, - ptr_bytes: u32, + target_info: TargetInfo, look_up_builtin: F, ) -> Result> where @@ -1100,7 +1101,7 @@ where src_dir, exposed_types, Phase::SolveTypes, - ptr_bytes, + target_info, look_up_builtin, )? { Monomorphized(_) => unreachable!(""), @@ -1115,7 +1116,7 @@ pub fn load_and_monomorphize<'a, F>( stdlib: &'a StdLib, src_dir: &Path, exposed_types: SubsByModule, - ptr_bytes: u32, + target_info: TargetInfo, look_up_builtin: F, ) -> Result, LoadingProblem<'a>> where @@ -1132,7 +1133,7 @@ where src_dir, exposed_types, Phase::MakeSpecializations, - ptr_bytes, + target_info, look_up_builtin, )? { Monomorphized(module) => Ok(module), @@ -1148,7 +1149,7 @@ pub fn load_and_monomorphize_from_str<'a, F>( stdlib: &'a StdLib, src_dir: &Path, exposed_types: SubsByModule, - ptr_bytes: u32, + target_info: TargetInfo, look_up_builtin: F, ) -> Result, LoadingProblem<'a>> where @@ -1165,7 +1166,7 @@ where src_dir, exposed_types, Phase::MakeSpecializations, - ptr_bytes, + target_info, look_up_builtin, )? { Monomorphized(module) => Ok(module), @@ -1317,7 +1318,7 @@ fn load<'a, F>( src_dir: &Path, exposed_types: SubsByModule, goal_phase: Phase, - ptr_bytes: u32, + target_info: TargetInfo, look_up_builtins: F, ) -> Result, LoadingProblem<'a>> where @@ -1433,7 +1434,7 @@ where worker_arena, src_dir, msg_tx.clone(), - ptr_bytes, + target_info, look_up_builtins, ); @@ -1474,7 +1475,7 @@ where let mut state = State { root_id, - ptr_bytes, + target_info, platform_data: None, goal_phase, stdlib, @@ -1999,7 +2000,7 @@ fn update<'a>( let layout_cache = state .layout_caches .pop() - .unwrap_or_else(|| LayoutCache::new(state.ptr_bytes)); + .unwrap_or_else(|| LayoutCache::new(state.target_info)); let typechecked = TypeCheckedModule { module_id, @@ -3812,7 +3813,7 @@ fn make_specializations<'a>( mut layout_cache: LayoutCache<'a>, specializations_we_must_make: Vec, mut module_timing: ModuleTiming, - ptr_bytes: u32, + target_info: TargetInfo, ) -> Msg<'a> { let make_specializations_start = SystemTime::now(); let mut mono_problems = Vec::new(); @@ -3824,7 +3825,7 @@ fn make_specializations<'a>( subs: &mut subs, home, ident_ids: &mut ident_ids, - target_info: ptr_bytes, + target_info, update_mode_ids: &mut update_mode_ids, // call_specialization_counter=0 is reserved call_specialization_counter: 1, @@ -3895,7 +3896,7 @@ fn build_pending_specializations<'a>( decls: Vec, mut module_timing: ModuleTiming, mut layout_cache: LayoutCache<'a>, - ptr_bytes: u32, + target_info: TargetInfo, // TODO remove exposed_to_host: ExposedToHost, ) -> Msg<'a> { @@ -3920,7 +3921,7 @@ fn build_pending_specializations<'a>( subs: &mut subs, home, ident_ids: &mut ident_ids, - target_info: ptr_bytes, + target_info, update_mode_ids: &mut update_mode_ids, // call_specialization_counter=0 is reserved call_specialization_counter: 1, @@ -4128,7 +4129,7 @@ fn run_task<'a, F>( arena: &'a Bump, src_dir: &Path, msg_tx: MsgSender<'a>, - ptr_bytes: u32, + target_info: TargetInfo, look_up_builtins: F, ) -> Result<(), LoadingProblem<'a>> where @@ -4206,7 +4207,7 @@ where decls, module_timing, layout_cache, - ptr_bytes, + target_info, exposed_to_host, )), MakeSpecializations { @@ -4226,7 +4227,7 @@ where layout_cache, specializations_we_must_make, module_timing, - ptr_bytes, + target_info, )), }?; diff --git a/compiler/target/src/lib.rs b/compiler/target/src/lib.rs index 237ca81f29..e62cefb710 100644 --- a/compiler/target/src/lib.rs +++ b/compiler/target/src/lib.rs @@ -17,6 +17,20 @@ impl TargetInfo { architecture: Architecture::X86_64, } } + + pub const fn default_wasm32() -> Self { + TargetInfo { + architecture: Architecture::Wasm32, + } + } +} + +impl From<&target_lexicon::Triple> for TargetInfo { + fn from(triple: &target_lexicon::Triple) -> Self { + let architecture = Architecture::from(triple.architecture); + + Self { architecture } + } } #[repr(u8)] diff --git a/docs/Cargo.toml b/docs/Cargo.toml index 8a1e2637cb..77b1bf7972 100644 --- a/docs/Cargo.toml +++ b/docs/Cargo.toml @@ -18,6 +18,7 @@ roc_module = { path = "../compiler/module" } roc_region = { path = "../compiler/region" } roc_types = { path = "../compiler/types" } roc_parse = { path = "../compiler/parse" } +roc_target = { path = "../compiler/target" } roc_collections = { path = "../compiler/collections" } bumpalo = { version = "3.8.0", features = ["collections"] } snafu = { version = "0.6.10", features = ["backtraces"] } diff --git a/docs/src/lib.rs b/docs/src/lib.rs index 35eaa93a13..8c791b72ee 100644 --- a/docs/src/lib.rs +++ b/docs/src/lib.rs @@ -428,7 +428,7 @@ pub fn load_modules_for_files(filenames: Vec, std_lib: StdLib) -> Vec() as u32, // This is just type-checking for docs, so "target" doesn't matter + roc_target::TargetInfo::default_x86_64(), // This is just type-checking for docs, so "target" doesn't matter builtin_defs_map, ) { Ok(loaded) => modules.push(loaded), From b9c318e9fb65dc3d7f57f956a0a50f217fa9c571 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jan 2022 15:59:21 +0100 Subject: [PATCH 07/36] update the tests --- Cargo.lock | 4 ++++ compiler/load/tests/test_load.rs | 8 +++++--- compiler/solve/Cargo.toml | 1 + compiler/solve/tests/solve_expr.rs | 2 +- compiler/test_gen/Cargo.toml | 1 + compiler/test_gen/src/gen_tags.rs | 6 +++--- compiler/test_gen/src/helpers/dev.rs | 2 +- compiler/test_gen/src/helpers/llvm.rs | 8 ++++---- compiler/test_gen/src/helpers/wasm.rs | 3 +-- compiler/test_mono/Cargo.toml | 1 + compiler/test_mono/src/tests.rs | 4 +++- reporting/Cargo.toml | 1 + reporting/tests/test_reporting.rs | 6 +++--- 13 files changed, 29 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ff94289d0..53fc08cafd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3690,6 +3690,7 @@ dependencies = [ "roc_problem", "roc_region", "roc_solve", + "roc_target", "roc_test_utils", "roc_types", "ven_pretty", @@ -3712,6 +3713,7 @@ dependencies = [ "roc_problem", "roc_region", "roc_solve", + "roc_target", "roc_types", "roc_unify", "tempfile", @@ -4280,6 +4282,7 @@ dependencies = [ "roc_reporting", "roc_solve", "roc_std", + "roc_target", "roc_types", "roc_unify", "target-lexicon", @@ -4301,6 +4304,7 @@ dependencies = [ "roc_load", "roc_module", "roc_mono", + "roc_target", "test_mono_macros", ] diff --git a/compiler/load/tests/test_load.rs b/compiler/load/tests/test_load.rs index 7a72b7b98b..b0196c44e0 100644 --- a/compiler/load/tests/test_load.rs +++ b/compiler/load/tests/test_load.rs @@ -28,6 +28,8 @@ mod test_load { use roc_types::subs::Subs; use std::collections::HashMap; + const TARGET_INFO: roc_target::TargetInfo = roc_target::TargetInfo::default_x86_64(); + // HELPERS fn multiple_modules(files: Vec<(&str, &str)>) -> Result { @@ -110,7 +112,7 @@ mod test_load { arena.alloc(stdlib), dir.path(), exposed_types, - 8, + TARGET_INFO, builtin_defs_map, ) }; @@ -134,7 +136,7 @@ mod test_load { arena.alloc(roc_builtins::std::standard_stdlib()), src_dir.as_path(), subs_by_module, - 8, + TARGET_INFO, builtin_defs_map, ); let mut loaded_module = match loaded { @@ -305,7 +307,7 @@ mod test_load { arena.alloc(roc_builtins::std::standard_stdlib()), src_dir.as_path(), subs_by_module, - 8, + TARGET_INFO, builtin_defs_map, ); diff --git a/compiler/solve/Cargo.toml b/compiler/solve/Cargo.toml index 89da067e95..7b776821a3 100644 --- a/compiler/solve/Cargo.toml +++ b/compiler/solve/Cargo.toml @@ -21,6 +21,7 @@ roc_builtins = { path = "../builtins" } roc_problem = { path = "../problem" } roc_parse = { path = "../parse" } roc_solve = { path = "../solve" } +roc_target = { path = "../target" } pretty_assertions = "1.0.0" indoc = "1.0.3" tempfile = "3.2.0" diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index df5d36580f..b86caf3c81 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -63,7 +63,7 @@ mod solve_expr { &stdlib, dir.path(), exposed_types, - 8, + roc_target::TargetInfo::default_x86_64(), builtin_defs_map, ); diff --git a/compiler/test_gen/Cargo.toml b/compiler/test_gen/Cargo.toml index 162b058fd8..07fd0c91e7 100644 --- a/compiler/test_gen/Cargo.toml +++ b/compiler/test_gen/Cargo.toml @@ -31,6 +31,7 @@ roc_load = { path = "../load" } roc_can = { path = "../can" } roc_parse = { path = "../parse" } roc_build = { path = "../build" } +roc_target = { path = "../target" } roc_std = { path = "../../roc_std" } bumpalo = { version = "3.8.0", features = ["collections"] } either = "1.6.1" diff --git a/compiler/test_gen/src/gen_tags.rs b/compiler/test_gen/src/gen_tags.rs index bbbc9fa490..b4abaf711f 100644 --- a/compiler/test_gen/src/gen_tags.rs +++ b/compiler/test_gen/src/gen_tags.rs @@ -23,9 +23,9 @@ fn width_and_alignment_u8_u8() { let layout = Layout::Union(UnionLayout::NonRecursive(&tt)); - let ptr_width = 8; - assert_eq!(layout.alignment_bytes(ptr_width), 1); - assert_eq!(layout.stack_size(ptr_width), 2); + let target_info = roc_target::TargetInfo::default_x86_64(); + assert_eq!(layout.alignment_bytes(target_info), 1); + assert_eq!(layout.stack_size(target_info), 2); } #[test] diff --git a/compiler/test_gen/src/helpers/dev.rs b/compiler/test_gen/src/helpers/dev.rs index 31a87d9c57..3a562cbf8c 100644 --- a/compiler/test_gen/src/helpers/dev.rs +++ b/compiler/test_gen/src/helpers/dev.rs @@ -57,7 +57,7 @@ pub fn helper( &stdlib, src_dir, exposed_types, - 8, + roc_target::TargetInfo::default_x86_64(), builtin_defs_map, ); diff --git a/compiler/test_gen/src/helpers/llvm.rs b/compiler/test_gen/src/helpers/llvm.rs index 6dc6c2b7f4..1392b1ab13 100644 --- a/compiler/test_gen/src/helpers/llvm.rs +++ b/compiler/test_gen/src/helpers/llvm.rs @@ -42,6 +42,8 @@ fn create_llvm_module<'a>( ) -> (&'static str, String, &'a Module<'a>) { use std::path::{Path, PathBuf}; + let target_info = roc_target::TargetInfo::from(target); + let filename = PathBuf::from("Test.roc"); let src_dir = Path::new("fake/test/path"); @@ -56,8 +58,6 @@ fn create_llvm_module<'a>( module_src = &temp; } - let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; - let exposed_types = MutMap::default(); let loaded = roc_load::file::load_and_monomorphize_from_str( arena, @@ -66,7 +66,7 @@ fn create_llvm_module<'a>( stdlib, src_dir, exposed_types, - ptr_bytes, + target_info, test_builtin_defs, ); @@ -213,7 +213,7 @@ fn create_llvm_module<'a>( context, interns, module, - target_info: ptr_bytes, + target_info, is_gen_test, // important! we don't want any procedures to get the C calling convention exposed_to_host: MutSet::default(), diff --git a/compiler/test_gen/src/helpers/wasm.rs b/compiler/test_gen/src/helpers/wasm.rs index f68f6b5da8..5d756fe921 100644 --- a/compiler/test_gen/src/helpers/wasm.rs +++ b/compiler/test_gen/src/helpers/wasm.rs @@ -94,7 +94,6 @@ fn compile_roc_to_wasm_bytes<'a, T: Wasm32TestResult>( } let exposed_types = MutMap::default(); - let ptr_bytes = 4; let loaded = roc_load::file::load_and_monomorphize_from_str( arena, filename, @@ -102,7 +101,7 @@ fn compile_roc_to_wasm_bytes<'a, T: Wasm32TestResult>( stdlib, src_dir, exposed_types, - ptr_bytes, + roc_target::TargetInfo::default_wasm32(), builtin_defs_map, ); diff --git a/compiler/test_mono/Cargo.toml b/compiler/test_mono/Cargo.toml index d599b1b64c..a2225349cb 100644 --- a/compiler/test_mono/Cargo.toml +++ b/compiler/test_mono/Cargo.toml @@ -16,6 +16,7 @@ roc_builtins = { path = "../builtins" } roc_load = { path = "../load" } roc_can = { path = "../can" } roc_mono = { path = "../mono" } +roc_target = { path = "../target" } test_mono_macros = { path = "../test_mono_macros" } pretty_assertions = "1.0.0" bumpalo = { version = "3.8.0", features = ["collections"] } diff --git a/compiler/test_mono/src/tests.rs b/compiler/test_mono/src/tests.rs index 4a398e6d38..2cf783baa2 100644 --- a/compiler/test_mono/src/tests.rs +++ b/compiler/test_mono/src/tests.rs @@ -25,6 +25,8 @@ use roc_mono::ir::Proc; use roc_mono::ir::ProcLayout; +const TARGET_INFO: roc_target::TargetInfo = roc_target::TargetInfo::default_x86_64(); + /// Without this, some tests pass in `cargo test --release` but fail without /// the --release flag because they run out of stack space. This increases /// stack size for debug builds only, while leaving the stack space at the default @@ -104,7 +106,7 @@ fn compiles_to_ir(test_name: &str, src: &str) { &stdlib, src_dir, exposed_types, - 8, + TARGET_INFO, builtin_defs_map, ); diff --git a/reporting/Cargo.toml b/reporting/Cargo.toml index 195406c968..be96603493 100644 --- a/reporting/Cargo.toml +++ b/reporting/Cargo.toml @@ -24,6 +24,7 @@ roc_constrain = { path = "../compiler/constrain" } roc_builtins = { path = "../compiler/builtins" } roc_problem = { path = "../compiler/problem" } roc_parse = { path = "../compiler/parse" } +roc_target = { path = "../compiler/target" } roc_test_utils = { path = "../test_utils" } pretty_assertions = "1.0.0" indoc = "1.0.3" diff --git a/reporting/tests/test_reporting.rs b/reporting/tests/test_reporting.rs index d0c6946def..45215dd437 100644 --- a/reporting/tests/test_reporting.rs +++ b/reporting/tests/test_reporting.rs @@ -96,8 +96,8 @@ mod test_reporting { let mut update_mode_ids = UpdateModeIds::new(); // Populate Procs and Subs, and get the low-level Expr from the canonical Expr - let ptr_bytes = 8; - let mut layout_cache = LayoutCache::new(ptr_bytes); + let target_info = roc_target::TargetInfo::default_x86_64(); + let mut layout_cache = LayoutCache::new(target_info); let mut mono_env = roc_mono::ir::Env { arena: &arena, subs: &mut subs, @@ -105,7 +105,7 @@ mod test_reporting { home, ident_ids: &mut ident_ids, update_mode_ids: &mut update_mode_ids, - target_info: ptr_bytes, + target_info, // call_specialization_counter=0 is reserved call_specialization_counter: 1, }; From 0298013346f8f9866b96f44f03b8e192d26d1aa4 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jan 2022 17:03:49 +0100 Subject: [PATCH 08/36] fix logical error --- compiler/can/src/module.rs | 2 +- compiler/gen_llvm/src/llvm/convert.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/can/src/module.rs b/compiler/can/src/module.rs index 7a292807bb..2cdd6e8e4e 100644 --- a/compiler/can/src/module.rs +++ b/compiler/can/src/module.rs @@ -124,7 +124,7 @@ where // This is a type alias // the symbol should already be added to the scope when this module is canonicalized - debug_assert!(scope.contains_alias(symbol)); + debug_assert!(scope.contains_alias(symbol), "{:?}", symbol); // but now we know this symbol by a different identifier, so we still need to add it to // the scope diff --git a/compiler/gen_llvm/src/llvm/convert.rs b/compiler/gen_llvm/src/llvm/convert.rs index 00d98ff5e3..c7d3142d40 100644 --- a/compiler/gen_llvm/src/llvm/convert.rs +++ b/compiler/gen_llvm/src/llvm/convert.rs @@ -256,8 +256,8 @@ fn block_of_memory_help(context: &Context, union_size: u32) -> BasicTypeEnum<'_> /// The int type that the C ABI turns our RocList/RocStr into pub fn str_list_int(ctx: &Context, target_info: TargetInfo) -> IntType<'_> { match target_info.ptr_width() { - roc_target::PtrWidth::Bytes4 => ctx.i32_type(), - roc_target::PtrWidth::Bytes8 => ctx.i64_type(), + roc_target::PtrWidth::Bytes4 => ctx.i64_type(), + roc_target::PtrWidth::Bytes8 => ctx.i128_type(), } } From fbd26c598e4d9e1844f6f719e7a837106ea84ac9 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jan 2022 17:19:53 +0100 Subject: [PATCH 09/36] provide target info to number alignment function --- compiler/builtins/src/bitcode.rs | 6 +++--- compiler/mono/src/layout.rs | 6 +++--- compiler/mono/src/layout_soa.rs | 36 +++++++++++++++++++++++--------- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/compiler/builtins/src/bitcode.rs b/compiler/builtins/src/bitcode.rs index e1a2a9f8f4..9321d08637 100644 --- a/compiler/builtins/src/bitcode.rs +++ b/compiler/builtins/src/bitcode.rs @@ -1,4 +1,5 @@ use roc_module::symbol::Symbol; +use roc_target::TargetInfo; use std::ops::Index; pub const BUILTINS_HOST_OBJ_PATH: &str = env!( @@ -46,7 +47,7 @@ impl FloatWidth { } } - pub const fn alignment_bytes(&self) -> u32 { + pub const fn alignment_bytes(&self, target_info: TargetInfo) -> u32 { use std::mem::align_of; use FloatWidth::*; @@ -106,11 +107,10 @@ impl IntWidth { } } - pub const fn alignment_bytes(&self) -> u32 { + pub const fn alignment_bytes(&self, target_info: TargetInfo) -> u32 { use std::mem::align_of; use IntWidth::*; - // TODO actually alignment is architecture-specific match self { U8 | I8 => align_of::() as u32, U16 | I16 => align_of::() as u32, diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 12d759f8b8..2055b03fd2 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -1341,10 +1341,10 @@ impl<'a> Builtin<'a> { // since both of those are one pointer size, the alignment of that structure is a pointer // size match self { - Int(int_width) => int_width.alignment_bytes(), - Float(float_width) => float_width.alignment_bytes(), + Int(int_width) => int_width.alignment_bytes(target_info), + Float(float_width) => float_width.alignment_bytes(target_info), Bool => align_of::() as u32, - Decimal => IntWidth::I128.alignment_bytes(), + Decimal => IntWidth::I128.alignment_bytes(target_info), Dict(_, _) => ptr_width, Set(_) => ptr_width, // we often treat these as i128 (64-bit systems) diff --git a/compiler/mono/src/layout_soa.rs b/compiler/mono/src/layout_soa.rs index 1481b38723..fe37cc385d 100644 --- a/compiler/mono/src/layout_soa.rs +++ b/compiler/mono/src/layout_soa.rs @@ -3,6 +3,7 @@ use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_collections::all::MutMap; use roc_module::ident::TagName; use roc_module::symbol::Symbol; +use roc_target::TargetInfo; use roc_types::subs::{Content, FlatType, Subs, Variable}; use roc_types::types::RecordField; use std::collections::hash_map::Entry; @@ -99,7 +100,7 @@ pub struct Layouts { lambda_sets: Vec, symbols: Vec, recursion_variable_to_structure_variable_map: MutMap>, - usize_int_width: IntWidth, + target_info: TargetInfo, } pub struct FunctionLayout { @@ -402,7 +403,7 @@ impl Layouts { const VOID_TUPLE: Index<(Layout, Layout)> = Index::new(0); const UNIT_INDEX: Index = Index::new(2); - pub fn new(usize_int_width: IntWidth) -> Self { + pub fn new(target_info: TargetInfo) -> Self { let mut layouts = Vec::with_capacity(64); layouts.push(Layout::VOID); @@ -420,7 +421,7 @@ impl Layouts { lambda_sets: Vec::default(), symbols: Vec::default(), recursion_variable_to_structure_variable_map: MutMap::default(), - usize_int_width, + target_info, } } @@ -443,7 +444,12 @@ impl Layouts { } fn usize(&self) -> Layout { - Layout::Int(self.usize_int_width) + let usize_int_width = match self.target_info.ptr_width() { + roc_target::PtrWidth::Bytes4 => IntWidth::U32, + roc_target::PtrWidth::Bytes8 => IntWidth::U64, + }; + + Layout::Int(usize_int_width) } fn align_of_layout_index(&self, index: Index) -> u16 { @@ -453,18 +459,23 @@ impl Layouts { } fn align_of_layout(&self, layout: Layout) -> u16 { - let ptr_alignment = self.usize_int_width.alignment_bytes() as u16; + let usize_int_width = match self.target_info.ptr_width() { + roc_target::PtrWidth::Bytes4 => IntWidth::U32, + roc_target::PtrWidth::Bytes8 => IntWidth::U64, + }; + + let ptr_alignment = usize_int_width.alignment_bytes(self.target_info) as u16; match layout { Layout::Reserved => unreachable!(), - Layout::Int(int_width) => int_width.alignment_bytes() as u16, - Layout::Float(float_width) => float_width.alignment_bytes() as u16, - Layout::Decimal => IntWidth::U128.alignment_bytes() as u16, + Layout::Int(int_width) => int_width.alignment_bytes(self.target_info) as u16, + Layout::Float(float_width) => float_width.alignment_bytes(self.target_info) as u16, + Layout::Decimal => IntWidth::U128.alignment_bytes(self.target_info) as u16, Layout::Str | Layout::Dict(_) | Layout::Set(_) | Layout::List(_) => ptr_alignment, Layout::Struct(slice) => self.align_of_layout_slice(slice), Layout::Boxed(_) | Layout::UnionRecursive(_) => ptr_alignment, Layout::UnionNonRecursive(slices) => { - let tag_id_align = IntWidth::I64.alignment_bytes() as u16; + let tag_id_align = IntWidth::I64.alignment_bytes(self.target_info) as u16; self.align_of_layout_slices(slices).max(tag_id_align) } @@ -518,7 +529,12 @@ impl Layouts { } pub fn size_of_layout(&self, layout: Layout) -> u16 { - let ptr_width = self.usize_int_width.stack_size() as u16; + let usize_int_width = match self.target_info.ptr_width() { + roc_target::PtrWidth::Bytes4 => IntWidth::U32, + roc_target::PtrWidth::Bytes8 => IntWidth::U64, + }; + + let ptr_width = usize_int_width.stack_size() as u16; match layout { Layout::Reserved => unreachable!(), From 7e383093644b819e7951268ae2e88dc588346413 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jan 2022 17:24:52 +0100 Subject: [PATCH 10/36] make alignment target-specific --- compiler/builtins/src/bitcode.rs | 18 ++++++++++++++++-- compiler/target/src/lib.rs | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/compiler/builtins/src/bitcode.rs b/compiler/builtins/src/bitcode.rs index 9321d08637..0284e9e774 100644 --- a/compiler/builtins/src/bitcode.rs +++ b/compiler/builtins/src/bitcode.rs @@ -48,13 +48,20 @@ impl FloatWidth { } pub const fn alignment_bytes(&self, target_info: TargetInfo) -> u32 { + use roc_target::Architecture; use std::mem::align_of; use FloatWidth::*; // TODO actually alignment is architecture-specific match self { F32 => align_of::() as u32, - F64 => align_of::() as u32, + F64 => match target_info.architecture { + Architecture::X86_64 + | Architecture::Aarch64 + | Architecture::Arm + | Architecture::Wasm32 => 8, + Architecture::X86_32 => 4, + }, F128 => align_of::() as u32, } } @@ -108,6 +115,7 @@ impl IntWidth { } pub const fn alignment_bytes(&self, target_info: TargetInfo) -> u32 { + use roc_target::Architecture; use std::mem::align_of; use IntWidth::*; @@ -115,7 +123,13 @@ impl IntWidth { U8 | I8 => align_of::() as u32, U16 | I16 => align_of::() as u32, U32 | I32 => align_of::() as u32, - U64 | I64 => align_of::() as u32, + U64 | I64 => match target_info.architecture { + Architecture::X86_64 + | Architecture::Aarch64 + | Architecture::Arm + | Architecture::Wasm32 => 8, + Architecture::X86_32 => 4, + }, U128 | I128 => align_of::() as u32, } } diff --git a/compiler/target/src/lib.rs b/compiler/target/src/lib.rs index e62cefb710..68269a3849 100644 --- a/compiler/target/src/lib.rs +++ b/compiler/target/src/lib.rs @@ -4,7 +4,7 @@ #[derive(Debug, Clone, Copy)] pub struct TargetInfo { - architecture: Architecture, + pub architecture: Architecture, } impl TargetInfo { From 490bbf381202aa7ef38313ade290c23c97e9a150 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 25 Jan 2022 22:09:12 +0100 Subject: [PATCH 11/36] ignore 'normal' functions when running i386 tests --- cli/tests/cli_run.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index bd7de64fed..149a632d73 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -421,9 +421,11 @@ mod cli_run { macro_rules! benchmarks { ($($test_name:ident => $benchmark:expr,)+) => { + $( #[test] #[cfg_attr(not(debug_assertions), serial(benchmark))] + #[cfg(all(not(feature = "wasm32-cli-run"), not(feature = "i386-cli-run")))] fn $test_name() { let benchmark = $benchmark; let file_name = examples_dir("benchmarks").join(benchmark.filename); From 4ab9a3302b26b3eb5d7e46952a30b346c2745cb0 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 21 Jan 2022 20:01:45 +0100 Subject: [PATCH 12/36] use Task.loop in Deriv --- examples/benchmarks/Deriv.roc | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/examples/benchmarks/Deriv.roc b/examples/benchmarks/Deriv.roc index 5e5a81f8b2..2cad781765 100644 --- a/examples/benchmarks/Deriv.roc +++ b/examples/benchmarks/Deriv.roc @@ -21,6 +21,22 @@ main = |> Task.map (\_ -> {}) +nest : (I64, Expr -> IO Expr), I64, Expr -> IO Expr +nest = \f, n, e -> Task.loop { s: n, f, m: n, x: e } nestHelp + +State : { s : I64, f : I64, Expr -> IO Expr, m : I64, x : Expr } + +nestHelp : State -> IO [ Step State, Done Expr ] +nestHelp = \{ s, f, m, x } -> + when m is + 0 -> + Task.succeed (Done x) + + _ -> + w <- Task.after (f (s - m) x) + + Task.succeed (Step { s, f, m: (m - 1), x: w }) + Expr : [ Val I64, Var Str, Add Expr Expr, Mul Expr Expr, Pow Expr Expr, Ln Expr ] divmod : I64, I64 -> Result { div : I64, mod : I64 } [ DivByZero ]* @@ -180,18 +196,6 @@ count = \expr -> Ln f -> count f -nest : (I64, Expr -> IO Expr), I64, Expr -> IO Expr -nest = \f, n, e -> nestHelp n f n e - -nestHelp : I64, (I64, Expr -> IO Expr), I64, Expr -> IO Expr -nestHelp = \s, f, m, x -> - when m is - 0 -> - Task.succeed x - - _ -> - f (s - m) x |> Task.after \w -> nestHelp s f (m - 1) w - deriv : I64, Expr -> IO Expr deriv = \i, f -> fprime = d "x" f From ddf2a09cbfdf56f1faec7db3dc00b5194568f70f Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 21 Jan 2022 20:57:03 +0100 Subject: [PATCH 13/36] use Task.loop in False --- examples/false-interpreter/False.roc | 42 +++++++++++--------- examples/false-interpreter/platform/Task.roc | 20 +++++++++- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/examples/false-interpreter/False.roc b/examples/false-interpreter/False.roc index 57fc3c14a9..d7e0ac0c4f 100644 --- a/examples/false-interpreter/False.roc +++ b/examples/false-interpreter/False.roc @@ -89,6 +89,10 @@ isWhitespace = \char -> == 0x9# tab interpretCtx : Context -> Task Context InterpreterErrors interpretCtx = \ctx -> + Task.loop ctx interpretCtxLoop + +interpretCtxLoop : Context -> Task [ Step Context, Done Context ] InterpreterErrors +interpretCtxLoop = \ctx -> when ctx.state is Executing if Context.inWhileScope ctx -> # Deal with the current while loop potentially looping. @@ -104,11 +108,11 @@ interpretCtx = \ctx -> if n == 0 then newScope = { scope & whileInfo: None } - interpretCtx { popCtx & scopes: List.set ctx.scopes last newScope } + Task.succeed (Step { popCtx & scopes: List.set ctx.scopes last newScope }) else newScope = { scope & whileInfo: Some { state: InBody, body, cond } } - interpretCtx { popCtx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: body, index: 0, whileInfo: None } } + Task.succeed (Step { popCtx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: body, index: 0, whileInfo: None } }) Err e -> Task.fail e @@ -117,7 +121,7 @@ interpretCtx = \ctx -> # Just rand the body. Run the condition again. newScope = { scope & whileInfo: Some { state: InCond, body, cond } } - interpretCtx { ctx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: cond, index: 0, whileInfo: None } } + Task.succeed (Step { ctx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: cond, index: 0, whileInfo: None } }) None -> Task.fail NoScope @@ -131,7 +135,7 @@ interpretCtx = \ctx -> when result is Ok (T val newCtx) -> execCtx <- Task.await (stepExecCtx newCtx val) - interpretCtx execCtx + Task.succeed (Step execCtx) Err NoScope -> Task.fail NoScope @@ -143,9 +147,9 @@ interpretCtx = \ctx -> # If no scopes left, all execution complete. if List.isEmpty dropCtx.scopes then - Task.succeed dropCtx + Task.succeed (Done dropCtx) else - interpretCtx dropCtx + Task.succeed (Step dropCtx) InComment -> result <- Task.attempt (Context.getChar ctx) @@ -153,9 +157,9 @@ interpretCtx = \ctx -> Ok (T val newCtx) -> if val == 0x7D then # `}` end of comment - interpretCtx { newCtx & state: Executing } + Task.succeed (Step { newCtx & state: Executing }) else - interpretCtx { newCtx & state: InComment } + Task.succeed (Step { newCtx & state: InComment }) Err NoScope -> Task.fail NoScope @@ -174,13 +178,13 @@ interpretCtx = \ctx -> # so this is make i64 mul by 10 then convert back to i32. nextAccum = (10 * Num.intCast accum) + Num.intCast (val - 0x30) - interpretCtx { newCtx & state: InNumber (Num.intCast nextAccum) } + Task.succeed (Step { newCtx & state: InNumber (Num.intCast nextAccum) }) else # outside of number now, this needs to be executed. pushCtx = Context.pushStack newCtx (Number accum) execCtx <- Task.await (stepExecCtx { pushCtx & state: Executing } val) - interpretCtx execCtx + Task.succeed (Step execCtx) Err NoScope -> Task.fail NoScope @@ -197,12 +201,12 @@ interpretCtx = \ctx -> when Str.fromUtf8 bytes is Ok str -> { } <- Task.await (Stdout.raw str) - interpretCtx { newCtx & state: Executing } + Task.succeed (Step { newCtx & state: Executing }) Err _ -> Task.fail BadUtf8 else - interpretCtx { newCtx & state: InString (List.append bytes val) } + Task.succeed (Step { newCtx & state: InString (List.append bytes val) }) Err NoScope -> Task.fail NoScope @@ -216,17 +220,17 @@ interpretCtx = \ctx -> Ok (T val newCtx) -> if val == 0x5B then # start of a nested lambda `[` - interpretCtx { newCtx & state: InLambda (depth + 1) (List.append bytes val) } + Task.succeed (Step { newCtx & state: InLambda (depth + 1) (List.append bytes val) }) else if val == 0x5D then # `]` end of current lambda if depth == 0 then # end of all lambdas - interpretCtx (Context.pushStack { newCtx & state: Executing } (Lambda bytes)) + Task.succeed (Step (Context.pushStack { newCtx & state: Executing } (Lambda bytes))) else # end of nested lambda - interpretCtx { newCtx & state: InLambda (depth - 1) (List.append bytes val) } + Task.succeed (Step { newCtx & state: InLambda (depth - 1) (List.append bytes val) }) else - interpretCtx { newCtx & state: InLambda depth (List.append bytes val) } + Task.succeed (Step { newCtx & state: InLambda depth (List.append bytes val) }) Err NoScope -> Task.fail NoScope @@ -252,14 +256,14 @@ interpretCtx = \ctx -> when result2 is Ok a -> - interpretCtx a + Task.succeed (Step a) Err e -> Task.fail e Ok (T 0x9F newCtx) -> # This is supposed to flush io buffers. We don't buffer, so it does nothing - interpretCtx newCtx + Task.succeed (Step newCtx) Ok (T x _) -> data = Num.toStr (Num.intCast x) @@ -276,7 +280,7 @@ interpretCtx = \ctx -> result <- Task.attempt (Context.getChar { ctx & state: Executing }) when result is Ok (T x newCtx) -> - interpretCtx (Context.pushStack newCtx (Number (Num.intCast x))) + Task.succeed (Step (Context.pushStack newCtx (Number (Num.intCast x)))) Err NoScope -> Task.fail NoScope diff --git a/examples/false-interpreter/platform/Task.roc b/examples/false-interpreter/platform/Task.roc index 520eba4976..0ad7c223b2 100644 --- a/examples/false-interpreter/platform/Task.roc +++ b/examples/false-interpreter/platform/Task.roc @@ -1,9 +1,27 @@ interface Task - exposes [ Task, succeed, fail, await, map, onFail, attempt, fromResult ] + exposes [ Task, succeed, fail, await, map, onFail, attempt, fromResult, loop ] imports [ fx.Effect ] Task ok err : Effect.Effect (Result ok err) +loop : state, (state -> Task [ Step state, Done done ] err) -> Task done err +loop = \state, step -> + looper = \current -> + step current + |> Effect.map + \res -> + when res is + Ok (Step newState) -> + Step newState + + Ok (Done result) -> + Done (Ok result) + + Err e -> + Done (Err e) + + Effect.loop state looper + succeed : val -> Task val * succeed = \val -> Effect.always (Ok val) From b3d605cade2eac8391fa1d629de69e7b46881f68 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jan 2022 20:22:18 +0100 Subject: [PATCH 14/36] fix failing debug_assert --- compiler/can/src/module.rs | 6 +++++- examples/benchmarks/platform/Package-Config.roc | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/can/src/module.rs b/compiler/can/src/module.rs index 2cdd6e8e4e..e164fb8680 100644 --- a/compiler/can/src/module.rs +++ b/compiler/can/src/module.rs @@ -124,7 +124,11 @@ where // This is a type alias // the symbol should already be added to the scope when this module is canonicalized - debug_assert!(scope.contains_alias(symbol), "{:?}", symbol); + debug_assert!( + scope.contains_alias(symbol), + "apparently, {:?} is not actually a type alias", + symbol + ); // but now we know this symbol by a different identifier, so we still need to add it to // the scope diff --git a/examples/benchmarks/platform/Package-Config.roc b/examples/benchmarks/platform/Package-Config.roc index d6f3a4452e..e2fa48be9b 100644 --- a/examples/benchmarks/platform/Package-Config.roc +++ b/examples/benchmarks/platform/Package-Config.roc @@ -1,5 +1,5 @@ platform "folkertdev/foo" - requires { Model, Msg } { main : Effect {} } + requires { } { main : Effect {} } exposes [] packages {} imports [ Task.{ Task } ] From 27d960f7200bc21f0ee6eb61918cb7b7b74ca310 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jan 2022 20:25:31 +0100 Subject: [PATCH 15/36] formatting --- examples/benchmarks/platform/Package-Config.roc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/benchmarks/platform/Package-Config.roc b/examples/benchmarks/platform/Package-Config.roc index e2fa48be9b..619534f070 100644 --- a/examples/benchmarks/platform/Package-Config.roc +++ b/examples/benchmarks/platform/Package-Config.roc @@ -1,5 +1,5 @@ platform "folkertdev/foo" - requires { } { main : Effect {} } + requires {} { main : Effect {} } exposes [] packages {} imports [ Task.{ Task } ] From afd11e1cb15db226ce1204b1018fcd27786f8ca0 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jan 2022 23:33:02 +0100 Subject: [PATCH 16/36] move target -> roc_target --- Cargo.toml | 2 +- ast/Cargo.toml | 2 +- cli/Cargo.toml | 2 +- compiler/build/Cargo.toml | 2 +- compiler/builtins/Cargo.toml | 2 +- compiler/gen_dev/Cargo.toml | 2 +- compiler/gen_llvm/Cargo.toml | 2 +- compiler/gen_wasm/Cargo.toml | 2 +- compiler/load/Cargo.toml | 2 +- compiler/mono/Cargo.toml | 2 +- compiler/{target => roc_target}/Cargo.toml | 0 compiler/{target => roc_target}/src/lib.rs | 0 compiler/solve/Cargo.toml | 2 +- compiler/test_gen/Cargo.toml | 2 +- compiler/test_mono/Cargo.toml | 2 +- docs/Cargo.toml | 2 +- reporting/Cargo.toml | 2 +- 17 files changed, 15 insertions(+), 15 deletions(-) rename compiler/{target => roc_target}/Cargo.toml (100%) rename compiler/{target => roc_target}/src/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 28a4f6193e..93974ca757 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ members = [ "compiler/build", "compiler/arena_pool", "compiler/test_gen", - "compiler/target", + "compiler/roc_target", "vendor/ena", "vendor/inkwell", "vendor/pathfinding", diff --git a/ast/Cargo.toml b/ast/Cargo.toml index f0edcc1fab..f0b82d7ca4 100644 --- a/ast/Cargo.toml +++ b/ast/Cargo.toml @@ -17,7 +17,7 @@ roc_problem = { path = "../compiler/problem" } roc_types = { path = "../compiler/types" } roc_unify = { path = "../compiler/unify"} roc_load = { path = "../compiler/load" } -roc_target = { path = "../compiler/target" } +roc_target = { path = "../compiler/roc_target" } roc_error_macros = { path = "../error_macros" } arrayvec = "0.7.2" bumpalo = { version = "3.8.0", features = ["collections"] } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index f889f4b16b..d680a24888 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -61,7 +61,7 @@ roc_load = { path = "../compiler/load" } roc_gen_llvm = { path = "../compiler/gen_llvm", optional = true } roc_build = { path = "../compiler/build", default-features = false } roc_fmt = { path = "../compiler/fmt" } -roc_target = { path = "../compiler/target" } +roc_target = { path = "../compiler/roc_target" } roc_reporting = { path = "../reporting" } roc_error_macros = { path = "../error_macros" } roc_editor = { path = "../editor", optional = true } diff --git a/compiler/build/Cargo.toml b/compiler/build/Cargo.toml index 4ef876bbf6..ab61cf2637 100644 --- a/compiler/build/Cargo.toml +++ b/compiler/build/Cargo.toml @@ -19,7 +19,7 @@ roc_unify = { path = "../unify" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } roc_load = { path = "../load" } -roc_target = { path = "../target" } +roc_target = { path = "../roc_target" } roc_gen_llvm = { path = "../gen_llvm", optional = true } roc_gen_wasm = { path = "../gen_wasm", optional = true } roc_gen_dev = { path = "../gen_dev", default-features = false } diff --git a/compiler/builtins/Cargo.toml b/compiler/builtins/Cargo.toml index 4b73584449..91879456d7 100644 --- a/compiler/builtins/Cargo.toml +++ b/compiler/builtins/Cargo.toml @@ -10,4 +10,4 @@ roc_collections = { path = "../collections" } roc_region = { path = "../region" } roc_module = { path = "../module" } roc_types = { path = "../types" } -roc_target = { path = "../target" } +roc_target = { path = "../roc_target" } diff --git a/compiler/gen_dev/Cargo.toml b/compiler/gen_dev/Cargo.toml index d454e3ffbb..ca81c78e67 100644 --- a/compiler/gen_dev/Cargo.toml +++ b/compiler/gen_dev/Cargo.toml @@ -16,7 +16,7 @@ roc_builtins = { path = "../builtins" } roc_unify = { path = "../unify" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } -roc_target = { path = "../target" } +roc_target = { path = "../roc_target" } roc_error_macros = { path = "../../error_macros" } bumpalo = { version = "3.8.0", features = ["collections"] } target-lexicon = "0.12.2" diff --git a/compiler/gen_llvm/Cargo.toml b/compiler/gen_llvm/Cargo.toml index 591f4ff92c..e87ed1c434 100644 --- a/compiler/gen_llvm/Cargo.toml +++ b/compiler/gen_llvm/Cargo.toml @@ -12,7 +12,7 @@ roc_module = { path = "../module" } roc_builtins = { path = "../builtins" } roc_error_macros = { path = "../../error_macros" } roc_mono = { path = "../mono" } -roc_target = { path = "../target" } +roc_target = { path = "../roc_target" } roc_std = { path = "../../roc_std" } morphic_lib = { path = "../../vendor/morphic_lib" } bumpalo = { version = "3.8.0", features = ["collections"] } diff --git a/compiler/gen_wasm/Cargo.toml b/compiler/gen_wasm/Cargo.toml index a40dd13703..b2223ee48a 100644 --- a/compiler/gen_wasm/Cargo.toml +++ b/compiler/gen_wasm/Cargo.toml @@ -11,6 +11,6 @@ roc_builtins = { path = "../builtins" } roc_collections = { path = "../collections" } roc_module = { path = "../module" } roc_mono = { path = "../mono" } -roc_target = { path = "../target" } +roc_target = { path = "../roc_target" } roc_std = { path = "../../roc_std" } roc_error_macros = { path = "../../error_macros" } diff --git a/compiler/load/Cargo.toml b/compiler/load/Cargo.toml index 27da9138af..329a3f5386 100644 --- a/compiler/load/Cargo.toml +++ b/compiler/load/Cargo.toml @@ -18,7 +18,7 @@ roc_unify = { path = "../unify" } roc_parse = { path = "../parse" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } -roc_target = { path = "../target" } +roc_target = { path = "../roc_target" } roc_reporting = { path = "../../reporting" } morphic_lib = { path = "../../vendor/morphic_lib" } ven_pretty = { path = "../../vendor/pretty" } diff --git a/compiler/mono/Cargo.toml b/compiler/mono/Cargo.toml index 88b33a8d82..969bd4e256 100644 --- a/compiler/mono/Cargo.toml +++ b/compiler/mono/Cargo.toml @@ -16,7 +16,7 @@ roc_solve = { path = "../solve" } roc_std = { path = "../../roc_std" } roc_problem = { path = "../problem" } roc_builtins = { path = "../builtins" } -roc_target = { path = "../target" } +roc_target = { path = "../roc_target" } ven_pretty = { path = "../../vendor/pretty" } morphic_lib = { path = "../../vendor/morphic_lib" } bumpalo = { version = "3.8.0", features = ["collections"] } diff --git a/compiler/target/Cargo.toml b/compiler/roc_target/Cargo.toml similarity index 100% rename from compiler/target/Cargo.toml rename to compiler/roc_target/Cargo.toml diff --git a/compiler/target/src/lib.rs b/compiler/roc_target/src/lib.rs similarity index 100% rename from compiler/target/src/lib.rs rename to compiler/roc_target/src/lib.rs diff --git a/compiler/solve/Cargo.toml b/compiler/solve/Cargo.toml index 7b776821a3..ebb9e4a615 100644 --- a/compiler/solve/Cargo.toml +++ b/compiler/solve/Cargo.toml @@ -21,7 +21,7 @@ roc_builtins = { path = "../builtins" } roc_problem = { path = "../problem" } roc_parse = { path = "../parse" } roc_solve = { path = "../solve" } -roc_target = { path = "../target" } +roc_target = { path = "../roc_target" } pretty_assertions = "1.0.0" indoc = "1.0.3" tempfile = "3.2.0" diff --git a/compiler/test_gen/Cargo.toml b/compiler/test_gen/Cargo.toml index 07fd0c91e7..607b14b7ff 100644 --- a/compiler/test_gen/Cargo.toml +++ b/compiler/test_gen/Cargo.toml @@ -31,7 +31,7 @@ roc_load = { path = "../load" } roc_can = { path = "../can" } roc_parse = { path = "../parse" } roc_build = { path = "../build" } -roc_target = { path = "../target" } +roc_target = { path = "../roc_target" } roc_std = { path = "../../roc_std" } bumpalo = { version = "3.8.0", features = ["collections"] } either = "1.6.1" diff --git a/compiler/test_mono/Cargo.toml b/compiler/test_mono/Cargo.toml index a2225349cb..2f56668cb1 100644 --- a/compiler/test_mono/Cargo.toml +++ b/compiler/test_mono/Cargo.toml @@ -16,7 +16,7 @@ roc_builtins = { path = "../builtins" } roc_load = { path = "../load" } roc_can = { path = "../can" } roc_mono = { path = "../mono" } -roc_target = { path = "../target" } +roc_target = { path = "../roc_target" } test_mono_macros = { path = "../test_mono_macros" } pretty_assertions = "1.0.0" bumpalo = { version = "3.8.0", features = ["collections"] } diff --git a/docs/Cargo.toml b/docs/Cargo.toml index 77b1bf7972..0f92636b9a 100644 --- a/docs/Cargo.toml +++ b/docs/Cargo.toml @@ -18,7 +18,7 @@ roc_module = { path = "../compiler/module" } roc_region = { path = "../compiler/region" } roc_types = { path = "../compiler/types" } roc_parse = { path = "../compiler/parse" } -roc_target = { path = "../compiler/target" } +roc_target = { path = "../compiler/roc_target" } roc_collections = { path = "../compiler/collections" } bumpalo = { version = "3.8.0", features = ["collections"] } snafu = { version = "0.6.10", features = ["backtraces"] } diff --git a/reporting/Cargo.toml b/reporting/Cargo.toml index be96603493..2add55b827 100644 --- a/reporting/Cargo.toml +++ b/reporting/Cargo.toml @@ -24,7 +24,7 @@ roc_constrain = { path = "../compiler/constrain" } roc_builtins = { path = "../compiler/builtins" } roc_problem = { path = "../compiler/problem" } roc_parse = { path = "../compiler/parse" } -roc_target = { path = "../compiler/target" } +roc_target = { path = "../compiler/roc_target" } roc_test_utils = { path = "../test_utils" } pretty_assertions = "1.0.0" indoc = "1.0.3" From 0f30704839d4c1b3215f5adf90bb167bc449730d Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 26 Jan 2022 23:34:17 +0100 Subject: [PATCH 17/36] revert gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d5960bf0cb..87635559c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -target/ +target generated-docs zig-cache .direnv From 0079f8f45ca5b99894465b791f48d3b987cff521 Mon Sep 17 00:00:00 2001 From: Jan Van Bruggen Date: Sat, 22 Jan 2022 00:41:39 -0700 Subject: [PATCH 18/36] Rename name example app to "form" --- examples/cli/.gitignore | 1 + examples/cli/{Echo.roc => form.roc} | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) rename examples/cli/{Echo.roc => form.roc} (97%) diff --git a/examples/cli/.gitignore b/examples/cli/.gitignore index fa11a6a9c5..5773a9c4eb 100644 --- a/examples/cli/.gitignore +++ b/examples/cli/.gitignore @@ -1 +1,2 @@ echo +form diff --git a/examples/cli/Echo.roc b/examples/cli/form.roc similarity index 97% rename from examples/cli/Echo.roc rename to examples/cli/form.roc index fcb9cf5acf..d3dda31965 100644 --- a/examples/cli/Echo.roc +++ b/examples/cli/form.roc @@ -1,4 +1,4 @@ -app "echo" +app "form" packages { pf: "platform" } imports [ pf.Task.{ Task, await }, pf.Stdout, pf.Stdin ] provides [ main ] to pf From 97149d453070352b3cb267154d8a76c6e7a6293f Mon Sep 17 00:00:00 2001 From: Jan Van Bruggen Date: Sat, 22 Jan 2022 01:00:51 -0700 Subject: [PATCH 19/36] Add countdown example --- examples/cli/.gitignore | 1 + examples/cli/countdown.roc | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 examples/cli/countdown.roc diff --git a/examples/cli/.gitignore b/examples/cli/.gitignore index 5773a9c4eb..a87a68d9e2 100644 --- a/examples/cli/.gitignore +++ b/examples/cli/.gitignore @@ -1,2 +1,3 @@ +countdown echo form diff --git a/examples/cli/countdown.roc b/examples/cli/countdown.roc new file mode 100644 index 0000000000..79f9decb7c --- /dev/null +++ b/examples/cli/countdown.roc @@ -0,0 +1,18 @@ +app "countdown" + packages { pf: "platform" } + imports [ pf.Stdin, pf.Stdout, pf.Task.{ await, loop, succeed } ] + provides [ main ] to pf + +main = + _ <- await (Stdout.line "\nLet's count down from 10 together - all you have to do is press .") + _ <- await Stdin.line + loop 10 tick + +tick = \n -> + if n == 0 then + _ <- await (Stdout.line "🎉 SURPRISE! Happy Birthday! 🎂") + succeed (Done {}) + else + _ <- await (n |> Num.toStr |> \s -> "\(s)..." |> Stdout.line) + _ <- await Stdin.line + succeed (Step (n - 1)) From 3866723e3662bb55a4a112055ec0733326eb5357 Mon Sep 17 00:00:00 2001 From: Jan Van Bruggen Date: Sat, 22 Jan 2022 01:08:41 -0700 Subject: [PATCH 20/36] Add echo example --- examples/cli/echo.roc | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 examples/cli/echo.roc diff --git a/examples/cli/echo.roc b/examples/cli/echo.roc new file mode 100644 index 0000000000..f50456060f --- /dev/null +++ b/examples/cli/echo.roc @@ -0,0 +1,13 @@ +app "echo" + packages { pf: "platform" } + imports [ pf.Stdin, pf.Stdout, pf.Task ] + provides [ main ] to pf + +main : Task.Task {} * +main = + _ <- Task.await (Stdout.line "Shout into this cave and hear the echo!") + Task.forever tick + +tick = + shout <- Task.await Stdin.line + Stdout.line shout From accee83a3fd0cde50cb73532e9557c887a392d5b Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 22 Jan 2022 12:12:25 +0100 Subject: [PATCH 21/36] cli platform: use generic version of mainForHost --- examples/cli/platform/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cli/platform/src/lib.rs b/examples/cli/platform/src/lib.rs index e072cf5d21..d584592716 100644 --- a/examples/cli/platform/src/lib.rs +++ b/examples/cli/platform/src/lib.rs @@ -9,7 +9,7 @@ use std::ffi::CStr; use std::os::raw::c_char; extern "C" { - #[link_name = "roc__mainForHost_1_exposed"] + #[link_name = "roc__mainForHost_1_exposed_generic"] fn roc_main(output: *mut u8) -> (); #[link_name = "roc__mainForHost_size"] From de84698890750cacdd2231f261962dec94175d57 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 22 Jan 2022 15:05:56 +0100 Subject: [PATCH 22/36] use generic mainForHost in false platform --- examples/false-interpreter/platform/src/lib.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/false-interpreter/platform/src/lib.rs b/examples/false-interpreter/platform/src/lib.rs index 18584164d6..4f97dfb72d 100644 --- a/examples/false-interpreter/platform/src/lib.rs +++ b/examples/false-interpreter/platform/src/lib.rs @@ -12,7 +12,7 @@ use std::io::{BufRead, BufReader, Read, Write}; use std::os::raw::c_char; extern "C" { - #[link_name = "roc__mainForHost_1_exposed"] + #[link_name = "roc__mainForHost_1_exposed_generic"] fn roc_main(args: RocStr, output: *mut u8) -> (); #[link_name = "roc__mainForHost_size"] @@ -188,10 +188,16 @@ pub extern "C" fn roc_fx_closeFile(br_ptr: *mut BufReader) { #[no_mangle] pub extern "C" fn roc_fx_openFile(name: ManuallyDrop) -> *mut BufReader { - let f = File::open(name.as_str()).expect("Unable to open file"); - let br = BufReader::new(f); + match File::open(name.as_str()) { + Ok(f) => { + let br = BufReader::new(f); - Box::into_raw(Box::new(br)) + Box::into_raw(Box::new(br)) + } + Err(_) => { + panic!("unable to open file {:?}", name) + } + } } #[no_mangle] From 5b5b9e5eb844a2c344f2a51cb4bbcfbd1b8404a3 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 22 Jan 2022 15:06:49 +0100 Subject: [PATCH 23/36] fix cli cli_run test (echo file got moved) --- cli/tests/cli_run.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index bd7de64fed..bb07877da1 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -376,8 +376,8 @@ mod cli_run { // use_valgrind: true, // }, cli:"cli" => Example { - filename: "Echo.roc", - executable_filename: "echo", + filename: "form.roc", + executable_filename: "form", stdin: &["Giovanni\n", "Giorgio\n"], input_file: None, expected_ending: "Hi, Giovanni Giorgio!\n", From 7e28d557ecc640592e40e82f6ec7e7547650b77c Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 22 Jan 2022 15:08:02 +0100 Subject: [PATCH 24/36] improve how we generate the mainForHost function --- compiler/gen_llvm/src/llvm/build.rs | 266 ++++++++++++++-------------- 1 file changed, 134 insertions(+), 132 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index d1a08a4bd8..354437298d 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -3554,6 +3554,93 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx, 'env>( 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>( env: &Env<'a, 'ctx, 'env>, ident_string: &str, @@ -3582,122 +3669,14 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>( &format!("{}_generic", c_function_name), ); - let wrapper_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 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, + let c_function = expose_function_to_host_help_c_abi_xx( + env, + roc_function, + arguments, + return_layout, 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 let size_function_type = env.context.i64_type().fn_type(&[], false); 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); 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); + 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(); - builder.build_return(Some(&size)); + env.builder.build_return(Some(&size)); c_function } @@ -6181,6 +6167,7 @@ impl RocReturn { } } +#[derive(Debug)] enum CCReturn { /// Return as normal Return, @@ -6192,6 +6179,36 @@ enum CCReturn { 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? 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); @@ -6268,22 +6285,7 @@ fn build_foreign_symbol<'a, 'ctx, 'env>( arguments.push(value); } - let cc_type = match cc_return { - 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_type = cc_return.to_signature(env, return_type, cc_argument_types.as_slice()); let cc_function = get_foreign_symbol(env, foreign.clone(), cc_type); let fastcc_type = match roc_return { From d3b51ea8ea3b5ceba48cc3ab2f25452de252ca8f Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 22 Jan 2022 15:09:28 +0100 Subject: [PATCH 25/36] make echo example work (with loop, not yet with forever) --- examples/cli/echo.roc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/cli/echo.roc b/examples/cli/echo.roc index f50456060f..d49e49a462 100644 --- a/examples/cli/echo.roc +++ b/examples/cli/echo.roc @@ -3,11 +3,13 @@ app "echo" imports [ pf.Stdin, pf.Stdout, pf.Task ] provides [ main ] to pf -main : Task.Task {} * +main : Task.Task {} [] main = _ <- Task.await (Stdout.line "Shout into this cave and hear the echo!") - Task.forever tick + Task.loop {} (\{} -> Task.map tick Step) + # Task.forever tick # still does not work; loops for a while, then stack overflows for me +tick : Task.Task {} [] tick = shout <- Task.await Stdin.line Stdout.line shout From ea10c1f665df936eb4a8e09608b27f744ab53c0f Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 22 Jan 2022 16:11:31 +0100 Subject: [PATCH 26/36] formatting --- examples/cli/echo.roc | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/cli/echo.roc b/examples/cli/echo.roc index d49e49a462..a7d58cc847 100644 --- a/examples/cli/echo.roc +++ b/examples/cli/echo.roc @@ -7,7 +7,6 @@ main : Task.Task {} [] main = _ <- Task.await (Stdout.line "Shout into this cave and hear the echo!") Task.loop {} (\{} -> Task.map tick Step) - # Task.forever tick # still does not work; loops for a while, then stack overflows for me tick : Task.Task {} [] tick = From 1ccf7f6b344c62ef14da0221834b79d688517fde Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 23 Jan 2022 01:21:25 +0100 Subject: [PATCH 27/36] more formatting --- examples/cli/echo.roc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/cli/echo.roc b/examples/cli/echo.roc index a7d58cc847..e0d0e4fdd2 100644 --- a/examples/cli/echo.roc +++ b/examples/cli/echo.roc @@ -4,9 +4,9 @@ app "echo" provides [ main ] to pf main : Task.Task {} [] -main = +main = _ <- Task.await (Stdout.line "Shout into this cave and hear the echo!") - Task.loop {} (\{} -> Task.map tick Step) + Task.loop {} (\{ } -> Task.map tick Step) tick : Task.Task {} [] tick = From 8e0c2408c7330b460bf110bc4b3eb4df47cd465c Mon Sep 17 00:00:00 2001 From: Jan Van Bruggen Date: Sun, 23 Jan 2022 00:28:19 -0700 Subject: [PATCH 28/36] Make echo example more fun --- examples/cli/echo.roc | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/examples/cli/echo.roc b/examples/cli/echo.roc index e0d0e4fdd2..8418521540 100644 --- a/examples/cli/echo.roc +++ b/examples/cli/echo.roc @@ -6,9 +6,24 @@ app "echo" main : Task.Task {} [] main = _ <- Task.await (Stdout.line "Shout into this cave and hear the echo!") - Task.loop {} (\{ } -> Task.map tick Step) + Task.loop {} (\_ -> Task.map tick Step) tick : Task.Task {} [] tick = shout <- Task.await Stdin.line - Stdout.line shout + Stdout.line (echo shout) + +echo : Str -> Str +echo = \shout -> + silence = \length -> + spaceInUtf8 = 32 + List.repeat length spaceInUtf8 + shout + |> Str.toUtf8 + |> List.mapWithIndex (\i, _ -> + length = (List.len (Str.toUtf8 shout) - i) + phrase = (List.split (Str.toUtf8 shout) length).before + List.concat (silence (if i == 0 then 2 * length else length)) phrase) + |> List.join + |> Str.fromUtf8 + |> Result.withDefault "" From e1f52057cc78cca28469f513b49245752de88cfb Mon Sep 17 00:00:00 2001 From: Jan Van Bruggen Date: Sun, 23 Jan 2022 00:35:05 -0700 Subject: [PATCH 29/36] Reformat form example for simplicity --- examples/cli/form.roc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/examples/cli/form.roc b/examples/cli/form.roc index d3dda31965..4cf731761a 100644 --- a/examples/cli/form.roc +++ b/examples/cli/form.roc @@ -1,16 +1,12 @@ app "form" packages { pf: "platform" } - imports [ pf.Task.{ Task, await }, pf.Stdout, pf.Stdin ] + imports [ pf.Stdin, pf.Stdout, pf.Task.{ await, Task } ] provides [ main ] to pf main : Task {} * main = - { } <- await (Stdout.line "What's your first name?") - + _ <- await (Stdout.line "What's your first name?") firstName <- await Stdin.line - - { } <- await (Stdout.line "What's your last name?") - + _ <- await (Stdout.line "What's your last name?") lastName <- await Stdin.line - Stdout.line "Hi, \(firstName) \(lastName)!" From 78be94291873a169a40078bafaddfbe96934f45e Mon Sep 17 00:00:00 2001 From: Jan Van Bruggen Date: Wed, 26 Jan 2022 19:08:11 -0700 Subject: [PATCH 30/36] Add emojis to echo example --- examples/cli/echo.roc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cli/echo.roc b/examples/cli/echo.roc index 8418521540..9f3914c164 100644 --- a/examples/cli/echo.roc +++ b/examples/cli/echo.roc @@ -5,7 +5,7 @@ app "echo" main : Task.Task {} [] main = - _ <- Task.await (Stdout.line "Shout into this cave and hear the echo!") + _ <- Task.await (Stdout.line "🗣 Shout into this cave and hear the echo! 👂👂👂") Task.loop {} (\_ -> Task.map tick Step) tick : Task.Task {} [] From e36b94fc53d6f81ef2a8ff6bb0e65bcd370364a4 Mon Sep 17 00:00:00 2001 From: Jan Van Bruggen Date: Wed, 26 Jan 2022 19:08:26 -0700 Subject: [PATCH 31/36] Add emoji to form example --- cli/tests/cli_run.rs | 2 +- examples/cli/form.roc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index bb07877da1..98d8c0f147 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -380,7 +380,7 @@ mod cli_run { executable_filename: "form", stdin: &["Giovanni\n", "Giorgio\n"], input_file: None, - expected_ending: "Hi, Giovanni Giorgio!\n", + expected_ending: "Hi, Giovanni Giorgio! 👋\n", use_valgrind: true, }, tui:"tui" => Example { diff --git a/examples/cli/form.roc b/examples/cli/form.roc index 4cf731761a..735c752527 100644 --- a/examples/cli/form.roc +++ b/examples/cli/form.roc @@ -9,4 +9,4 @@ main = firstName <- await Stdin.line _ <- await (Stdout.line "What's your last name?") lastName <- await Stdin.line - Stdout.line "Hi, \(firstName) \(lastName)!" + Stdout.line "Hi, \(firstName) \(lastName)! 👋" From 33e5ecf8ba77c0c1ce6dac03b13c3a254a35af56 Mon Sep 17 00:00:00 2001 From: Jan Van Bruggen Date: Thu, 27 Jan 2022 06:26:32 -0700 Subject: [PATCH 32/36] Manually format echo example to pass tests --- examples/cli/echo.roc | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/examples/cli/echo.roc b/examples/cli/echo.roc index 9f3914c164..9f5401ab3c 100644 --- a/examples/cli/echo.roc +++ b/examples/cli/echo.roc @@ -17,13 +17,17 @@ echo : Str -> Str echo = \shout -> silence = \length -> spaceInUtf8 = 32 + List.repeat length spaceInUtf8 + shout - |> Str.toUtf8 - |> List.mapWithIndex (\i, _ -> - length = (List.len (Str.toUtf8 shout) - i) - phrase = (List.split (Str.toUtf8 shout) length).before - List.concat (silence (if i == 0 then 2 * length else length)) phrase) - |> List.join - |> Str.fromUtf8 - |> Result.withDefault "" + |> Str.toUtf8 + |> List.mapWithIndex + (\i, _ -> + length = (List.len (Str.toUtf8 shout) - i) + phrase = (List.split (Str.toUtf8 shout) length).before + + List.concat (silence (if i == 0 then 2 * length else length)) phrase) + |> List.join + |> Str.fromUtf8 + |> Result.withDefault "" From 04c2b5e88cd026ce683dc8a047cc47a82ef091e3 Mon Sep 17 00:00:00 2001 From: Jan Van Bruggen Date: Thu, 27 Jan 2022 06:48:27 -0700 Subject: [PATCH 33/36] Ignore tui executable --- examples/tui/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 examples/tui/.gitignore diff --git a/examples/tui/.gitignore b/examples/tui/.gitignore new file mode 100644 index 0000000000..ba0da0793e --- /dev/null +++ b/examples/tui/.gitignore @@ -0,0 +1 @@ +tui From e31532360b94fcb626e8cac6fedb5d1c5127a3db Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 27 Jan 2022 21:20:46 +0100 Subject: [PATCH 34/36] don't remove argument that is not pushed --- compiler/gen_llvm/src/llvm/build.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index ec8bade1e7..1947e4fd11 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -6324,7 +6324,6 @@ fn build_foreign_symbol<'a, 'ctx, 'env>( if let CCReturn::ByPointer = cc_return { cc_arguments.push(return_pointer.into()); - cc_argument_types.remove(0); } let it = fastcc_parameters.into_iter().zip(cc_argument_types.iter()); From ee151c8f943fc44f30a54d8aab77aeb5333bbd15 Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 27 Jan 2022 22:26:44 +0100 Subject: [PATCH 35/36] fix function name --- compiler/gen_llvm/src/llvm/build.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 1947e4fd11..64432ddd7f 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -3547,7 +3547,7 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx, 'env>( c_function } -fn expose_function_to_host_help_c_abi_xx<'a, 'ctx, 'env>( +fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, roc_function: FunctionValue<'ctx>, arguments: &[Layout<'a>], @@ -3662,7 +3662,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>( &format!("{}_generic", c_function_name), ); - let c_function = expose_function_to_host_help_c_abi_xx( + let c_function = expose_function_to_host_help_c_abi( env, roc_function, arguments, From 684b873453c77ddfc4e0eb1e5bae478e00ee80a1 Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 27 Jan 2022 23:20:35 +0100 Subject: [PATCH 36/36] change name back ?! --- compiler/gen_llvm/src/llvm/build.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 64432ddd7f..031ce7b1db 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -3547,7 +3547,7 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx, 'env>( c_function } -fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>( +fn expose_function_to_host_help_c_abi_v2<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, roc_function: FunctionValue<'ctx>, arguments: &[Layout<'a>], @@ -3662,7 +3662,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>( &format!("{}_generic", c_function_name), ); - let c_function = expose_function_to_host_help_c_abi( + let c_function = expose_function_to_host_help_c_abi_v2( env, roc_function, arguments,