diff --git a/Cargo.lock b/Cargo.lock index f98de87446..53fc08cafd 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", @@ -3297,6 +3299,7 @@ dependencies = [ "roc_collections", "roc_module", "roc_region", + "roc_target", "roc_types", ] @@ -3350,6 +3353,7 @@ dependencies = [ "roc_region", "roc_reporting", "roc_solve", + "roc_target", "roc_test_utils", "roc_types", "roc_unify", @@ -3416,6 +3420,7 @@ dependencies = [ "roc_module", "roc_parse", "roc_region", + "roc_target", "roc_types", "snafu", "tempfile", @@ -3508,6 +3513,7 @@ dependencies = [ "roc_region", "roc_solve", "roc_std", + "roc_target", "roc_types", "roc_unify", "target-lexicon", @@ -3526,6 +3532,7 @@ dependencies = [ "roc_module", "roc_mono", "roc_std", + "roc_target", "target-lexicon", ] @@ -3540,6 +3547,7 @@ dependencies = [ "roc_module", "roc_mono", "roc_std", + "roc_target", ] [[package]] @@ -3587,6 +3595,7 @@ dependencies = [ "roc_region", "roc_reporting", "roc_solve", + "roc_target", "roc_types", "roc_unify", "tempfile", @@ -3621,6 +3630,7 @@ dependencies = [ "roc_region", "roc_solve", "roc_std", + "roc_target", "roc_types", "roc_unify", "static_assertions", @@ -3680,6 +3690,7 @@ dependencies = [ "roc_problem", "roc_region", "roc_solve", + "roc_target", "roc_test_utils", "roc_types", "ven_pretty", @@ -3702,6 +3713,7 @@ dependencies = [ "roc_problem", "roc_region", "roc_solve", + "roc_target", "roc_types", "roc_unify", "tempfile", @@ -3717,6 +3729,13 @@ dependencies = [ "quickcheck_macros", ] +[[package]] +name = "roc_target" +version = "0.1.0" +dependencies = [ + "target-lexicon", +] + [[package]] name = "roc_test_utils" version = "0.1.0" @@ -4263,6 +4282,7 @@ dependencies = [ "roc_reporting", "roc_solve", "roc_std", + "roc_target", "roc_types", "roc_unify", "target-lexicon", @@ -4284,6 +4304,7 @@ dependencies = [ "roc_load", "roc_module", "roc_mono", + "roc_target", "test_mono_macros", ] diff --git a/Cargo.toml b/Cargo.toml index 465e29e8a3..93974ca757 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ members = [ "compiler/build", "compiler/arena_pool", "compiler/test_gen", + "compiler/roc_target", "vendor/ena", "vendor/inkwell", "vendor/pathfinding", diff --git a/ast/Cargo.toml b/ast/Cargo.toml index 66407ee82b..f0b82d7ca4 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/roc_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..d680a24888 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/roc_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 5a52f74420..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, - 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/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 98d8c0f147..c9af664349 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); diff --git a/compiler/build/Cargo.toml b/compiler/build/Cargo.toml index 80aaa3dd00..ab61cf2637 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 = "../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/build/src/program.rs b/compiler/build/src/program.rs index c3ee7cc397..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, - 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/builtins/Cargo.toml b/compiler/builtins/Cargo.toml index 9321839c93..91879456d7 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 = "../roc_target" } diff --git a/compiler/builtins/src/bitcode.rs b/compiler/builtins/src/bitcode.rs index e1a2a9f8f4..0284e9e774 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,14 +47,21 @@ impl FloatWidth { } } - pub const fn alignment_bytes(&self) -> u32 { + 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, } } @@ -106,16 +114,22 @@ impl IntWidth { } } - pub const fn alignment_bytes(&self) -> u32 { + pub const fn alignment_bytes(&self, target_info: TargetInfo) -> u32 { + use roc_target::Architecture; 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, 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/can/src/module.rs b/compiler/can/src/module.rs index 7a292807bb..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)); + 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/compiler/gen_dev/Cargo.toml b/compiler/gen_dev/Cargo.toml index 8efcaca241..ca81c78e67 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 = "../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_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/Cargo.toml b/compiler/gen_llvm/Cargo.toml index 32a79c4767..e87ed1c434 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 = "../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_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 354437298d..ec8bade1e7 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,15 +196,9 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { pub fn ptr_int(&self) -> IntType<'ctx> { let ctx = self.context; - match self.ptr_bytes { - 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 - ), + match self.target_info.ptr_width() { + roc_target::PtrWidth::Bytes4 => ctx.i32_type(), + roc_target::PtrWidth::Bytes8 => ctx.i64_type(), } } @@ -212,11 +207,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.ptr_width() as u32 * 2 } pub fn build_intrinsic_call( @@ -269,7 +264,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,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.ptr_bytes { - 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( @@ -1438,7 +1430,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 +1516,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 +1703,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 +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.ptr_bytes); + debug_assert!((tag_id as u32) < env.target_info.ptr_width() as u32); let ptr_int = env.ptr_int(); @@ -1801,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), } } @@ -1813,7 +1804,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 +1822,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 +1909,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 +1940,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 +2048,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 +2086,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 +2099,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 +2115,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 +2145,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) } @@ -2182,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.ptr_bytes); + let extra_bytes = alignment_bytes.max(ptr_width_u32); let ptr = { // number of bytes we will allocated @@ -2208,8 +2200,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 == ptr_width_u32 => 1, + n if n == 2 * ptr_width_u32 => 2, _ => unreachable!("invalid extra_bytes, {}", extra_bytes), }; @@ -2228,11 +2220,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 == ptr_width_u32 => { // 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 * 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) @@ -2283,14 +2275,15 @@ 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.ptr_width() as u32); 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.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); @@ -2471,12 +2464,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 +2565,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 +2578,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 +2769,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 +3401,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) @@ -3712,7 +3705,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.ptr_bytes); + 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, @@ -3880,8 +3876,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)) } @@ -4315,12 +4315,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( @@ -5006,7 +5006,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, @@ -6061,8 +6061,8 @@ fn run_low_level<'a, 'ctx, 'env>( { bd.position_at_end(throw_block); - match env.ptr_bytes { - 8 => { + match env.target_info.ptr_width() { + roc_target::PtrWidth::Bytes8 => { let fn_ptr_type = context .void_type() .fn_type(&[], false) @@ -6081,11 +6081,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!(), } } @@ -6211,8 +6210,8 @@ impl CCReturn { /// According to the C ABI, how should we return a value with the given layout? fn to_cc_return<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>) -> CCReturn { - 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.ptr_width() as u32; if return_size == 0 { CCReturn::Void @@ -7131,7 +7130,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.ptr_bytes as u64, false)], + &[env + .ptr_int() + .const_int(env.target_info.ptr_width() as u64, false)], "get_rc_ptr", ) }; @@ -7161,11 +7162,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.ptr_width() 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.ptr_width() as usize { bytes.push(env.context.i8_type().const_zero()); } @@ -7184,7 +7185,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.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 4feb77aa2d..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; @@ -105,15 +106,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 +163,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 +219,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 +265,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 +367,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 +413,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,19 +455,18 @@ fn pass_dict_c_abi<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, dict: BasicValueEnum<'ctx>, ) -> BasicValueEnum<'ctx> { - match env.ptr_bytes { - 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!(), } } @@ -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..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.ptr_bytes { - 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.ptr_bytes { - 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.ptr_bytes { - 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.ptr_bytes { - 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 1fc8179134..c7d3142d40 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>, @@ -36,7 +37,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 +45,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 +57,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 +97,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 +106,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 +118,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() } } @@ -188,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); @@ -206,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) @@ -251,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.i64_type(), + roc_target::PtrWidth::Bytes8 => ctx.i128_type(), } } diff --git a/compiler/gen_llvm/src/llvm/externs.rs b/compiler/gen_llvm/src/llvm/externs.rs index 75bc506cc2..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.ptr_bytes 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 b04acb06a9..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), } } @@ -98,7 +93,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 +158,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.ptr_width() as u32); let context = env.context; let block = env.builder.get_insert_block().expect("to be in a function"); @@ -1192,7 +1187,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/gen_wasm/Cargo.toml b/compiler/gen_wasm/Cargo.toml index 90578803f7..b2223ee48a 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 = "../roc_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..329a3f5386 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 = "../roc_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 ce3fb7ee11..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, - 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, - 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/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/mono/Cargo.toml b/compiler/mono/Cargo.toml index 6cd3129ed3..969bd4e256 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 = "../roc_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/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 9b83b2b388..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 ptr_bytes: u32, + pub target_info: TargetInfo, pub update_mode_ids: &'i mut UpdateModeIds, pub call_specialization_counter: u32, } @@ -2471,7 +2472,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 +2505,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 +3010,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 +3038,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 +3073,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 +3085,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 +3117,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 +3394,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 +3755,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 +3912,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 +4587,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 +4618,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 +4693,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 +5036,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 +5121,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 +7622,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 +7634,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 +7664,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 +7687,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 +7769,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 +7807,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 +8108,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 @@ -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 3d19699399..2055b03fd2 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,17 @@ 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(), + } + } + + 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(), } } @@ -1310,42 +1314,46 @@ 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 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 => align_of::() as u32, - Dict(_, _) => pointer_size, - Set(_) => pointer_size, + Decimal => IntWidth::I128.alignment_bytes(target_info), + 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 +1433,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 +1461,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 +1471,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 +1537,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 +1553,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 +1574,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 +1595,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 +1606,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 +1656,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 +1702,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 +1727,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 +1749,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 +1884,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 +1897,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 +1931,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 +1941,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 +1960,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 +1985,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 +2063,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 +2135,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 +2144,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 +2163,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 +2194,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 +2277,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 +2348,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 +2385,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 +2401,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 +2504,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 +2522,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 +2565,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 +2590,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 +2836,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/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!(), diff --git a/compiler/roc_target/Cargo.toml b/compiler/roc_target/Cargo.toml new file mode 100644 index 0000000000..28211dbdc9 --- /dev/null +++ b/compiler/roc_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/roc_target/src/lib.rs b/compiler/roc_target/src/lib.rs new file mode 100644 index 0000000000..68269a3849 --- /dev/null +++ b/compiler/roc_target/src/lib.rs @@ -0,0 +1,74 @@ +#![warn(clippy::dbg_macro)] +// 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 { + pub 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, + } + } + + 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)] +#[derive(Debug, Clone, Copy)] +pub enum PtrWidth { + Bytes4 = 4, + Bytes8 = 8, +} + +#[derive(Debug, Clone, Copy)] +pub enum Architecture { + X86_64, + X86_32, + Aarch64, + Arm, + Wasm32, +} + +impl Architecture { + pub const fn ptr_width(&self) -> PtrWidth { + use Architecture::*; + + match self { + X86_64 | Aarch64 | Arm => PtrWidth::Bytes8, + X86_32 | Wasm32 => PtrWidth::Bytes4, + } + } +} + +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"), + } + } +} diff --git a/compiler/solve/Cargo.toml b/compiler/solve/Cargo.toml index 89da067e95..ebb9e4a615 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 = "../roc_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..607b14b7ff 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 = "../roc_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 e1bec306b1..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, - 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..2f56668cb1 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 = "../roc_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/docs/Cargo.toml b/docs/Cargo.toml index 8a1e2637cb..0f92636b9a 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/roc_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), 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 diff --git a/examples/benchmarks/platform/Package-Config.roc b/examples/benchmarks/platform/Package-Config.roc index d6f3a4452e..619534f070 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 } ] 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) diff --git a/reporting/Cargo.toml b/reporting/Cargo.toml index 195406c968..2add55b827 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/roc_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 0cb8640770..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, - ptr_bytes, + target_info, // call_specialization_counter=0 is reserved call_specialization_counter: 1, };