diff --git a/Cargo.lock b/Cargo.lock index fbdb3ba093..22a6dcae25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3918,9 +3918,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.10.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d" +checksum = "d9bffcddbc2458fa3e6058414599e3c838a022abae82e5c67b4f7f80298d5bff" [[package]] name = "tempfile" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 8288d26d6f..4d8c6bc4b4 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -67,7 +67,7 @@ libc = "0.2" libloading = "0.6" inkwell = { path = "../vendor/inkwell", optional = true } -target-lexicon = "0.10" +target-lexicon = "0.12.2" tempfile = "3.1.0" [dev-dependencies] diff --git a/cli/src/repl/eval.rs b/cli/src/repl/eval.rs index d97d8087c6..c3792a1153 100644 --- a/cli/src/repl/eval.rs +++ b/cli/src/repl/eval.rs @@ -222,7 +222,8 @@ fn jit_to_ast_help<'a>( let tags_map: roc_collections::all::MutMap<_, _> = tags_vec.iter().cloned().collect(); - let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs); + let union_variant = + union_sorted_tags_help(env.arena, tags_vec, None, env.subs, env.ptr_bytes); let size = layout.stack_size(env.ptr_bytes); use roc_mono::layout::WrappedVariant::*; @@ -886,7 +887,8 @@ 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); + let union_variant = + union_sorted_tags_help(env.arena, tags_vec, None, env.subs, env.ptr_bytes); match union_variant { UnionVariant::ByteUnion(tagnames) => { diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 0fb00a9e48..c201bc009e 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -223,13 +223,13 @@ mod cli_run { // expected_ending: "", // use_valgrind: true, // }, - // cli:"cli" => Example { - // filename: "Echo.roc", - // executable_filename: "echo", - // stdin: &["Giovanni\n", "Giorgio\n"], - // expected_ending: "Giovanni Giorgio!\n", - // use_valgrind: true, - // }, + cli:"cli" => Example { + filename: "Echo.roc", + executable_filename: "echo", + stdin: &["Giovanni\n", "Giorgio\n"], + expected_ending: "Hi, Giovanni Giorgio!\n", + use_valgrind: true, + }, // custom_malloc:"custom-malloc" => Example { // filename: "Main.roc", // executable_filename: "custom-malloc-example", diff --git a/compiler/build/Cargo.toml b/compiler/build/Cargo.toml index dcddc88461..96e91511c3 100644 --- a/compiler/build/Cargo.toml +++ b/compiler/build/Cargo.toml @@ -30,7 +30,7 @@ libloading = "0.6" tempfile = "3.1.0" serde_json = "1.0" inkwell = { path = "../../vendor/inkwell", optional = true } -target-lexicon = "0.10" +target-lexicon = "0.12.2" [dev-dependencies] pretty_assertions = "0.5.1" diff --git a/compiler/gen_dev/Cargo.toml b/compiler/gen_dev/Cargo.toml index 40f35682f7..07b85fd205 100644 --- a/compiler/gen_dev/Cargo.toml +++ b/compiler/gen_dev/Cargo.toml @@ -21,7 +21,7 @@ roc_mono = { path = "../mono" } im = "14" # im and im-rc should always have the same version! im-rc = "14" # im and im-rc should always have the same version! bumpalo = { version = "3.6.1", features = ["collections"] } -target-lexicon = "0.10" +target-lexicon = "0.12.2" libloading = "0.6" object = { version = "0.24", features = ["write"] } diff --git a/compiler/gen_llvm/Cargo.toml b/compiler/gen_llvm/Cargo.toml index 2706ee35eb..eaec8982bd 100644 --- a/compiler/gen_llvm/Cargo.toml +++ b/compiler/gen_llvm/Cargo.toml @@ -22,7 +22,7 @@ im = "14" # im and im-rc should always have the same version! im-rc = "14" # im and im-rc should always have the same version! bumpalo = { version = "3.6.1", features = ["collections"] } inkwell = { path = "../../vendor/inkwell" } -target-lexicon = "0.10" +target-lexicon = "0.12.2" [dev-dependencies] roc_can = { path = "../can" } diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 9515e43130..a416cbb8a4 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -3935,6 +3935,8 @@ pub fn get_call_conventions(cc: target_lexicon::CallingConvention) -> u32 { SystemV => C_CALL_CONV, WasmBasicCAbi => C_CALL_CONV, WindowsFastcall => C_CALL_CONV, + AppleAarch64 => C_CALL_CONV, + _ => C_CALL_CONV, } } @@ -5174,89 +5176,171 @@ fn run_low_level<'a, 'ctx, 'env>( } } -fn build_foreign_symbol_return_result<'a, 'ctx, 'env>( +/// A type that is valid according to the C ABI +/// +/// As an example, structs that fit inside an integer type should +/// (this does not currently happen here) be coerced to that integer type. +fn to_cc_type<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - scope: &mut Scope<'a, 'ctx>, - foreign: &roc_module::ident::ForeignSymbol, - arguments: &[Symbol], - return_type: BasicTypeEnum<'ctx>, -) -> (FunctionValue<'ctx>, &'a [BasicValueEnum<'ctx>]) { - let mut arg_vals: Vec = Vec::with_capacity_in(arguments.len(), env.arena); - let mut arg_types = Vec::with_capacity_in(arguments.len() + 1, env.arena); - - for arg in arguments.iter() { - let (value, layout) = load_symbol_and_layout(scope, arg); - arg_vals.push(value); - let arg_type = basic_type_from_layout(env, layout); - debug_assert_eq!(arg_type, value.get_type()); - arg_types.push(arg_type); + layout: &Layout<'a>, +) -> BasicTypeEnum<'ctx> { + match layout { + Layout::Builtin(builtin) => to_cc_type_builtin(env, builtin), + _ => { + // TODO this is almost certainly incorrect for bigger structs + basic_type_from_layout(env, layout) + } } - - let function_type = return_type.fn_type(&arg_types, false); - let function = get_foreign_symbol(env, foreign.clone(), function_type); - - (function, arg_vals.into_bump_slice()) } -fn build_foreign_symbol_write_result_into_ptr<'a, 'ctx, 'env>( +fn to_cc_type_builtin<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - scope: &mut Scope<'a, 'ctx>, - foreign: &roc_module::ident::ForeignSymbol, - arguments: &[Symbol], - return_pointer: PointerValue<'ctx>, -) -> (FunctionValue<'ctx>, &'a [BasicValueEnum<'ctx>]) { - let mut arg_vals: Vec = Vec::with_capacity_in(arguments.len(), env.arena); - let mut arg_types = Vec::with_capacity_in(arguments.len() + 1, env.arena); - - arg_vals.push(return_pointer.into()); - arg_types.push(return_pointer.get_type().into()); - - for arg in arguments.iter() { - let (value, layout) = load_symbol_and_layout(scope, arg); - arg_vals.push(value); - let arg_type = basic_type_from_layout(env, layout); - debug_assert_eq!(arg_type, value.get_type()); - arg_types.push(arg_type); + builtin: &Builtin<'a>, +) -> BasicTypeEnum<'ctx> { + match builtin { + Builtin::Int128 + | Builtin::Int64 + | Builtin::Int32 + | Builtin::Int16 + | Builtin::Int8 + | Builtin::Int1 + | Builtin::Usize + | Builtin::Decimal + | Builtin::Float128 + | Builtin::Float64 + | Builtin::Float32 + | Builtin::Float16 => basic_type_from_builtin(env, builtin), + Builtin::Str | Builtin::EmptyStr | Builtin::List(_) | Builtin::EmptyList => { + env.str_list_c_abi().into() + } + Builtin::Dict(_, _) | Builtin::Set(_) | Builtin::EmptyDict | Builtin::EmptySet => { + // TODO verify this is what actually happens + basic_type_from_builtin(env, builtin) + } + } +} + +enum CCReturn { + /// Return as normal + Return, + /// require an extra argument, a pointer + /// where the result is written into + /// returns void + ByPointer, + /// The return type is zero-sized + Void, +} + +/// 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; + + if return_size == 0 { + CCReturn::Void + } else if pass_result_by_pointer { + CCReturn::ByPointer + } else { + CCReturn::Return } - - let function_type = env.context.void_type().fn_type(&arg_types, false); - let function = get_foreign_symbol(env, foreign.clone(), function_type); - - (function, arg_vals.into_bump_slice()) } -#[allow(clippy::too_many_arguments)] fn build_foreign_symbol<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, scope: &mut Scope<'a, 'ctx>, foreign: &roc_module::ident::ForeignSymbol, - arguments: &[Symbol], + argument_symbols: &[Symbol], ret_layout: &Layout<'a>, ) -> BasicValueEnum<'ctx> { - let ret_type = basic_type_from_layout(env, ret_layout); - let return_pointer = env.builder.build_alloca(ret_type, "return_value"); + let builder = env.builder; + let context = env.context; - // crude approximation of the C calling convention - let pass_result_by_pointer = ret_layout.stack_size(env.ptr_bytes) > 2 * env.ptr_bytes; + // Here we build two functions: + // + // - an C_CALL_CONV extern that will be provided by the host, e.g. `roc_fx_putLine` + // This is just a type signature that we make available to the linker, + // and can use in the wrapper + // - a FAST_CALL_CONV wrapper that we make here, e.g. `roc_fx_putLine_fastcc_wrapper` - let (function, arguments) = if pass_result_by_pointer { - build_foreign_symbol_write_result_into_ptr(env, scope, foreign, arguments, return_pointer) - } else { - build_foreign_symbol_return_result(env, scope, foreign, arguments, ret_type) + let return_type = basic_type_from_layout(env, ret_layout); + let cc_return = to_cc_return(env, ret_layout); + + let mut cc_argument_types = Vec::with_capacity_in(argument_symbols.len() + 1, env.arena); + let mut fastcc_argument_types = Vec::with_capacity_in(argument_symbols.len(), env.arena); + let mut arguments = Vec::with_capacity_in(argument_symbols.len(), env.arena); + + for symbol in argument_symbols { + let (value, layout) = load_symbol_and_layout(scope, symbol); + + cc_argument_types.push(to_cc_type(env, layout)); + + let basic_type = basic_type_from_layout(env, layout); + fastcc_argument_types.push(basic_type); + + arguments.push(value); + } + + let cc_type = match cc_return { + CCReturn::Void => env.context.void_type().fn_type(&cc_argument_types, false), + CCReturn::ByPointer => { + cc_argument_types.push(return_type.ptr_type(AddressSpace::Generic).into()); + env.context.void_type().fn_type(&cc_argument_types, false) + } + CCReturn::Return => return_type.fn_type(&cc_argument_types, false), }; - let call = env.builder.build_call(function, arguments, "tmp"); + let cc_function = get_foreign_symbol(env, foreign.clone(), cc_type); - // this is a foreign function, use c calling convention - call.set_call_convention(C_CALL_CONV); + let fastcc_type = return_type.fn_type(&fastcc_argument_types, false); - call.try_as_basic_value(); + let fastcc_function = add_func( + env.module, + &format!("{}_fastcc_wrapper", foreign.as_str()), + fastcc_type, + Linkage::Private, + FAST_CALL_CONV, + ); - if pass_result_by_pointer { - env.builder.build_load(return_pointer, "read_result") - } else { - call.try_as_basic_value().left().unwrap() + let old = builder.get_insert_block().unwrap(); + + let entry = context.append_basic_block(fastcc_function, "entry"); + { + builder.position_at_end(entry); + let return_pointer = env.builder.build_alloca(return_type, "return_value"); + + let fastcc_parameters = fastcc_function.get_params(); + let mut cc_arguments = Vec::with_capacity_in(fastcc_parameters.len() + 1, env.arena); + + for (param, cc_type) in fastcc_parameters.into_iter().zip(cc_argument_types.iter()) { + if param.get_type() == *cc_type { + cc_arguments.push(param); + } else { + let as_cc_type = complex_bitcast(env.builder, param, *cc_type, "to_cc_type"); + cc_arguments.push(as_cc_type); + } + } + + if let CCReturn::ByPointer = cc_return { + cc_arguments.push(return_pointer.into()); + } + + let call = env.builder.build_call(cc_function, &cc_arguments, "tmp"); + call.set_call_convention(C_CALL_CONV); + + let return_value = match cc_return { + CCReturn::Return => call.try_as_basic_value().left().unwrap(), + + CCReturn::ByPointer => env.builder.build_load(return_pointer, "read_result"), + CCReturn::Void => return_type.const_zero(), + }; + + builder.build_return(Some(&return_value)); } + + builder.position_at_end(old); + let call = env.builder.build_call(fastcc_function, &arguments, "tmp"); + call.set_call_convention(FAST_CALL_CONV); + return call.try_as_basic_value().left().unwrap(); } fn throw_on_overflow<'a, 'ctx, 'env>( diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 639f3a3cd6..6b4867fe09 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -832,6 +832,7 @@ struct State<'a> { pub exposed_types: SubsByModule, pub output_path: Option<&'a str>, pub platform_path: PlatformPath<'a>, + pub ptr_bytes: u32, pub headers_parsed: MutSet, @@ -1467,6 +1468,7 @@ where let mut state = State { root_id, + ptr_bytes, platform_data: None, goal_phase, stdlib, @@ -1978,7 +1980,10 @@ fn update<'a>( ); if state.goal_phase > Phase::SolveTypes { - let layout_cache = state.layout_caches.pop().unwrap_or_default(); + let layout_cache = state + .layout_caches + .pop() + .unwrap_or_else(|| LayoutCache::new(state.ptr_bytes)); let typechecked = TypeCheckedModule { module_id, diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 6f032c46ff..daade748e6 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -3124,7 +3124,8 @@ pub fn with_hole<'a>( mut fields, .. } => { - let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs); + let sorted_fields = + crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes); let mut field_symbols = Vec::with_capacity_in(fields.len(), env.arena); let mut can_fields = Vec::with_capacity_in(fields.len(), env.arena); @@ -3459,7 +3460,8 @@ pub fn with_hole<'a>( loc_expr, .. } => { - let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs); + let sorted_fields = + crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes); let mut index = None; let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena); @@ -3601,7 +3603,8 @@ pub fn with_hole<'a>( // This has the benefit that we don't need to do anything special for reference // counting - let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs); + let sorted_fields = + crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes); let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena); @@ -4205,7 +4208,8 @@ fn convert_tag_union<'a>( arena: &'a Bump, ) -> Stmt<'a> { use crate::layout::UnionVariant::*; - let res_variant = crate::layout::union_sorted_tags(env.arena, variant_var, env.subs); + let res_variant = + crate::layout::union_sorted_tags(env.arena, variant_var, env.subs, env.ptr_bytes); let variant = match res_variant { Ok(cached) => cached, Err(LayoutProblem::UnresolvedTypeVar(_)) => { @@ -4541,7 +4545,7 @@ fn sorted_field_symbols<'a>( } }; - let alignment = layout.alignment_bytes(8); + let alignment = layout.alignment_bytes(env.ptr_bytes); let symbol = possible_reuse_symbol(env, procs, &arg.value); field_symbols_temp.push((alignment, symbol, ((var, arg), &*env.arena.alloc(symbol)))); @@ -6978,7 +6982,8 @@ fn from_can_pattern_help<'a>( use crate::exhaustive::Union; use crate::layout::UnionVariant::*; - let res_variant = crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs); + let res_variant = + crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.ptr_bytes); let variant = match res_variant { Ok(cached) => cached, @@ -7397,7 +7402,8 @@ 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); + let sorted_fields = + crate::layout::sort_record_fields(env.arena, *whole_var, env.subs, env.ptr_bytes); // sorted fields based on the destruct let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena); diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index e0a96e6a32..141bd9877b 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -138,7 +138,8 @@ impl<'a> RawFunctionLayout<'a> { let fn_args = fn_args.into_bump_slice(); let ret = arena.alloc(ret); - let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var)?; + let lambda_set = + LambdaSet::from_var(env.arena, env.subs, closure_var, env.ptr_bytes)?; Ok(Self::Function(fn_args, lambda_set, ret)) } @@ -516,6 +517,7 @@ impl<'a> LambdaSet<'a> { arena: &'a Bump, subs: &Subs, closure_var: Variable, + ptr_bytes: u32, ) -> Result { let mut tags = std::vec::Vec::new(); match roc_types::pretty_print::chase_ext_tag_union(subs, closure_var, &mut tags) { @@ -529,6 +531,7 @@ impl<'a> LambdaSet<'a> { arena, subs, seen: Vec::new_in(arena), + ptr_bytes, }; for (tag_name, variables) in tags.iter() { @@ -545,7 +548,8 @@ impl<'a> LambdaSet<'a> { } } - let representation = arena.alloc(Self::make_representation(arena, subs, tags)); + let representation = + arena.alloc(Self::make_representation(arena, subs, tags, ptr_bytes)); Ok(LambdaSet { set: set.into_bump_slice(), @@ -568,9 +572,10 @@ impl<'a> LambdaSet<'a> { arena: &'a Bump, subs: &Subs, tags: std::vec::Vec<(TagName, std::vec::Vec)>, + ptr_bytes: u32, ) -> Layout<'a> { // otherwise, this is a closure with a payload - let variant = union_sorted_tags_help(arena, tags, None, subs); + let variant = union_sorted_tags_help(arena, tags, None, subs, ptr_bytes); use UnionVariant::*; match variant { @@ -648,6 +653,7 @@ pub enum Builtin<'a> { } pub struct Env<'a, 'b> { + ptr_bytes: u32, arena: &'a Bump, seen: Vec<'a, Variable>, subs: &'b Subs, @@ -972,8 +978,9 @@ impl<'a> Layout<'a> { /// e.g. `identity : a -> a` could be specialized to `Bool -> Bool` or `Str -> Str`. /// Therefore in general it's invalid to store a map from variables to layouts /// But if we're careful when to invalidate certain keys, we still get some benefit -#[derive(Default, Debug)] +#[derive(Debug)] pub struct LayoutCache<'a> { + ptr_bytes: u32, _marker: std::marker::PhantomData<&'a u8>, } @@ -985,6 +992,13 @@ pub enum CachedLayout<'a> { } impl<'a> LayoutCache<'a> { + pub fn new(ptr_bytes: u32) -> Self { + Self { + ptr_bytes, + _marker: Default::default(), + } + } + pub fn from_var( &mut self, arena: &'a Bump, @@ -998,6 +1012,7 @@ impl<'a> LayoutCache<'a> { arena, subs, seen: Vec::new_in(arena), + ptr_bytes: self.ptr_bytes, }; Layout::from_var(&mut env, var) @@ -1016,6 +1031,7 @@ impl<'a> LayoutCache<'a> { arena, subs, seen: Vec::new_in(arena), + ptr_bytes: self.ptr_bytes, }; RawFunctionLayout::from_var(&mut env, var) @@ -1182,6 +1198,7 @@ fn layout_from_flat_type<'a>( let arena = env.arena; let subs = env.subs; + let ptr_bytes = env.ptr_bytes; match flat_type { Apply(symbol, args) => { @@ -1273,7 +1290,7 @@ fn layout_from_flat_type<'a>( } } Func(_, closure_var, _) => { - let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var)?; + let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var, env.ptr_bytes)?; Ok(lambda_set.runtime_representation()) } @@ -1299,8 +1316,6 @@ fn layout_from_flat_type<'a>( let mut pairs = Vec::from_iter_in(pairs_it, arena); pairs.sort_by(|(label1, layout1), (label2, layout2)| { - let ptr_bytes = 8; - let size1 = layout1.alignment_bytes(ptr_bytes); let size2 = layout2.alignment_bytes(ptr_bytes); @@ -1320,14 +1335,14 @@ fn layout_from_flat_type<'a>( TagUnion(tags, ext_var) => { debug_assert!(ext_var_is_empty_tag_union(subs, ext_var)); - Ok(layout_from_tag_union(arena, tags, subs)) + Ok(layout_from_tag_union(arena, tags, subs, env.ptr_bytes)) } FunctionOrTagUnion(tag_name, _, ext_var) => { debug_assert!(ext_var_is_empty_tag_union(subs, ext_var)); let tags = UnionTags::from_tag_name_index(tag_name); - Ok(layout_from_tag_union(arena, tags, subs)) + Ok(layout_from_tag_union(arena, tags, subs, env.ptr_bytes)) } RecursiveTagUnion(rec_var, tags, ext_var) => { debug_assert!(ext_var_is_empty_tag_union(subs, ext_var)); @@ -1377,8 +1392,6 @@ fn layout_from_flat_type<'a>( } tag_layout.sort_by(|layout1, layout2| { - let ptr_bytes = 8; - let size1 = layout1.alignment_bytes(ptr_bytes); let size2 = layout2.alignment_bytes(ptr_bytes); @@ -1425,11 +1438,13 @@ pub fn sort_record_fields<'a>( arena: &'a Bump, var: Variable, subs: &Subs, + ptr_bytes: u32, ) -> Vec<'a, (Lowercase, Variable, Result, Layout<'a>>)> { let mut env = Env { arena, subs, seen: Vec::new_in(arena), + ptr_bytes, }; let (it, _) = gather_fields_unsorted_iter(subs, RecordFields::empty(), var); @@ -1445,6 +1460,8 @@ fn sort_record_fields_help<'a>( env: &mut Env<'a, '_>, fields_map: impl Iterator)>, ) -> Vec<'a, (Lowercase, Variable, Result, Layout<'a>>)> { + let ptr_bytes = env.ptr_bytes; + // Sort the fields by label let mut sorted_fields = Vec::with_capacity_in(fields_map.size_hint().0, env.arena); @@ -1468,8 +1485,6 @@ 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 ptr_bytes = 8; - let size1 = layout1.alignment_bytes(ptr_bytes); let size2 = layout2.alignment_bytes(ptr_bytes); @@ -1605,6 +1620,7 @@ pub fn union_sorted_tags<'a>( arena: &'a Bump, var: Variable, subs: &Subs, + ptr_bytes: u32, ) -> Result, LayoutProblem> { let var = if let Content::RecursionVar { structure, .. } = subs.get_content_without_compacting(var) { @@ -1617,7 +1633,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) + union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, ptr_bytes) } Err((_, Content::Error)) => return Err(LayoutProblem::Erroneous), Err(other) => panic!("invalid content in tag union variable: {:?}", other), @@ -1651,6 +1667,7 @@ fn union_sorted_tags_help_new<'a>( mut tags_vec: Vec<(&'_ TagName, VariableSubsSlice)>, opt_rec_var: Option, subs: &Subs, + ptr_bytes: u32, ) -> UnionVariant<'a> { // sort up front; make sure the ordering stays intact! tags_vec.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); @@ -1659,6 +1676,7 @@ fn union_sorted_tags_help_new<'a>( arena, subs, seen: Vec::new_in(arena), + ptr_bytes, }; match tags_vec.len() { @@ -1708,8 +1726,6 @@ fn union_sorted_tags_help_new<'a>( } layouts.sort_by(|layout1, layout2| { - let ptr_bytes = 8; - let size1 = layout1.alignment_bytes(ptr_bytes); let size2 = layout2.alignment_bytes(ptr_bytes); @@ -1793,8 +1809,6 @@ fn union_sorted_tags_help_new<'a>( } arg_layouts.sort_by(|layout1, layout2| { - let ptr_bytes = 8; - let size1 = layout1.alignment_bytes(ptr_bytes); let size2 = layout2.alignment_bytes(ptr_bytes); @@ -1867,6 +1881,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, ) -> UnionVariant<'a> { // sort up front; make sure the ordering stays intact! tags_vec.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); @@ -1875,6 +1890,7 @@ pub fn union_sorted_tags_help<'a>( arena, subs, seen: Vec::new_in(arena), + ptr_bytes, }; match tags_vec.len() { @@ -1921,8 +1937,6 @@ pub fn union_sorted_tags_help<'a>( } layouts.sort_by(|layout1, layout2| { - let ptr_bytes = 8; - let size1 = layout1.alignment_bytes(ptr_bytes); let size2 = layout2.alignment_bytes(ptr_bytes); @@ -2005,8 +2019,6 @@ pub fn union_sorted_tags_help<'a>( } arg_layouts.sort_by(|layout1, layout2| { - let ptr_bytes = 8; - let size1 = layout1.alignment_bytes(ptr_bytes); let size2 = layout2.alignment_bytes(ptr_bytes); @@ -2091,7 +2103,12 @@ fn cheap_sort_tags<'a, 'b>( tags_vec } -fn layout_from_newtype<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Layout<'a> { +fn layout_from_newtype<'a>( + arena: &'a Bump, + tags: UnionTags, + subs: &Subs, + ptr_bytes: u32, +) -> Layout<'a> { debug_assert!(tags.is_newtype_wrapper(subs)); let slice_index = tags.variables().into_iter().next().unwrap(); @@ -2109,6 +2126,7 @@ fn layout_from_newtype<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Lay arena, subs, seen: Vec::new_in(arena), + ptr_bytes, }; match Layout::from_var(&mut env, var) { @@ -2128,11 +2146,16 @@ fn layout_from_newtype<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Lay } } -fn layout_from_tag_union<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Layout<'a> { +fn layout_from_tag_union<'a>( + arena: &'a Bump, + tags: UnionTags, + subs: &Subs, + ptr_bytes: u32, +) -> Layout<'a> { use UnionVariant::*; if tags.is_newtype_wrapper(subs) { - return layout_from_newtype(arena, tags, subs); + return layout_from_newtype(arena, tags, subs, ptr_bytes); } let tags_vec = cheap_sort_tags(arena, tags, subs); @@ -2148,7 +2171,7 @@ fn layout_from_tag_union<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> L } _ => { let opt_rec_var = None; - let variant = union_sorted_tags_help_new(arena, tags_vec, opt_rec_var, subs); + let variant = union_sorted_tags_help_new(arena, tags_vec, opt_rec_var, subs, ptr_bytes); match variant { Never => Layout::Union(UnionLayout::NonRecursive(&[])), diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index af5a4c3d36..5575e0a20f 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -92,14 +92,15 @@ mod test_reporting { let mut ident_ids = interns.all_ident_ids.remove(&home).unwrap(); // Populate Procs and Subs, and get the low-level Expr from the canonical Expr - let mut layout_cache = LayoutCache::default(); + let ptr_bytes = 8; + let mut layout_cache = LayoutCache::new(ptr_bytes); let mut mono_env = roc_mono::ir::Env { arena: &arena, subs: &mut subs, problems: &mut mono_problems, home, ident_ids: &mut ident_ids, - ptr_bytes: 8, + ptr_bytes, update_mode_counter: 0, // call_specialization_counter=0 is reserved call_specialization_counter: 1, diff --git a/compiler/test_gen/Cargo.toml b/compiler/test_gen/Cargo.toml index 30f3e1a646..06a457e089 100644 --- a/compiler/test_gen/Cargo.toml +++ b/compiler/test_gen/Cargo.toml @@ -30,7 +30,7 @@ either = "1.6.1" indoc = "0.3.3" libc = "0.2" inkwell = { path = "../../vendor/inkwell" } -target-lexicon = "0.10" +target-lexicon = "0.12.2" libloading = "0.6" [dev-dependencies] diff --git a/compiler/test_mono/Cargo.toml b/compiler/test_mono/Cargo.toml index 7929679da6..3167bbc789 100644 --- a/compiler/test_mono/Cargo.toml +++ b/compiler/test_mono/Cargo.toml @@ -28,7 +28,7 @@ bumpalo = { version = "3.6.1", features = ["collections"] } either = "1.6.1" indoc = "0.3.3" libc = "0.2" -target-lexicon = "0.10" +target-lexicon = "0.12.2" libloading = "0.6" [dev-dependencies] diff --git a/examples/.gitignore b/examples/.gitignore index a7b3b64e43..874bbd86e5 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -4,4 +4,3 @@ app libhost.a roc_app.ll roc_app.bc -effect-example diff --git a/examples/benchmarks/platform/host.zig b/examples/benchmarks/platform/host.zig index 945c4d515e..7d6bba7632 100644 --- a/examples/benchmarks/platform/host.zig +++ b/examples/benchmarks/platform/host.zig @@ -29,23 +29,33 @@ extern fn roc__mainForHost_1_Fx_caller(*const u8, [*]u8, [*]u8) void; extern fn roc__mainForHost_1_Fx_size() i64; extern fn roc__mainForHost_1_Fx_result_size() i64; -extern fn malloc(size: usize) callconv(.C) ?*c_void; -extern fn realloc(c_ptr: [*]align(@alignOf(u128)) u8, size: usize) callconv(.C) ?*c_void; -extern fn free(c_ptr: [*]align(@alignOf(u128)) u8) callconv(.C) void; +const Align = extern struct { a: usize, b: usize }; +extern fn malloc(size: usize) callconv(.C) ?*align(@alignOf(Align)) c_void; +extern fn realloc(c_ptr: [*]align(@alignOf(Align)) u8, size: usize) callconv(.C) ?*c_void; +extern fn free(c_ptr: [*]align(@alignOf(Align)) u8) callconv(.C) void; export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void { + _ = alignment; + return malloc(size); } export fn roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*c_void { - return realloc(@alignCast(16, @ptrCast([*]u8, c_ptr)), new_size); + _ = old_size; + _ = alignment; + + return realloc(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)), new_size); } export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void { - free(@alignCast(16, @ptrCast([*]u8, c_ptr))); + _ = alignment; + + free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr))); } export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void { + _ = tag_id; + const stderr = std.io.getStdErr().writer(); const msg = @ptrCast([*:0]const u8, c_ptr); stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable; @@ -54,12 +64,9 @@ export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void { const Unit = extern struct {}; -pub export fn main() u8 { - const stdout = std.io.getStdOut().writer(); - const stderr = std.io.getStdErr().writer(); - +pub fn main() u8 { const size = @intCast(usize, roc__mainForHost_size()); - const raw_output = std.heap.c_allocator.alloc(u8, size) catch unreachable; + const raw_output = std.heap.c_allocator.allocAdvanced(u8, @alignOf(u64), @intCast(usize, size), .at_least) catch unreachable; var output = @ptrCast([*]u8, raw_output); defer { @@ -71,17 +78,17 @@ pub export fn main() u8 { roc__mainForHost_1_exposed(output); - const elements = @ptrCast([*]u64, @alignCast(8, output)); - - var flag = elements[0]; + const flag = @ptrCast(*u64, @alignCast(@alignOf(u64), output)).*; if (flag == 0) { // all is well - const closure_data_pointer = @ptrCast([*]u8, output[8..size]); + const closure_data_pointer = @ptrCast([*]u8, output[@sizeOf(u64)..size]); call_the_closure(closure_data_pointer); } else { - const msg = @intToPtr([*:0]const u8, elements[1]); + const ptr = @ptrCast(*u32, output + @sizeOf(u64)); + const msg = @intToPtr([*:0]const u8, ptr.*); + const stderr = std.io.getStdErr().writer(); stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable; return 0; @@ -92,6 +99,7 @@ pub export fn main() u8 { const delta = to_seconds(ts2) - to_seconds(ts1); + const stderr = std.io.getStdErr().writer(); stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable; return 0; @@ -103,7 +111,7 @@ fn to_seconds(tms: std.os.timespec) f64 { fn call_the_closure(closure_data_pointer: [*]u8) void { const size = roc__mainForHost_1_Fx_result_size(); - const raw_output = std.heap.c_allocator.alloc(u8, @intCast(usize, size)) catch unreachable; + const raw_output = std.heap.c_allocator.allocAdvanced(u8, @alignOf(u64), @intCast(usize, size), .at_least) catch unreachable; var output = @ptrCast([*]u8, raw_output); defer { @@ -135,7 +143,7 @@ pub export fn roc_fx_putInt(int: i64) i64 { return 0; } -pub export fn roc_fx_putLine(rocPath: str.RocStr) i64 { +export fn roc_fx_putLine(rocPath: str.RocStr) callconv(.C) void { const stdout = std.io.getStdOut().writer(); for (rocPath.asSlice()) |char| { @@ -143,8 +151,6 @@ pub export fn roc_fx_putLine(rocPath: str.RocStr) i64 { } stdout.print("\n", .{}) catch unreachable; - - return 0; } const GetInt = extern struct { diff --git a/examples/cli/.gitignore b/examples/cli/.gitignore new file mode 100644 index 0000000000..fa11a6a9c5 --- /dev/null +++ b/examples/cli/.gitignore @@ -0,0 +1 @@ +echo diff --git a/examples/cli/Echo.roc b/examples/cli/Echo.roc new file mode 100644 index 0000000000..1dbb742a07 --- /dev/null +++ b/examples/cli/Echo.roc @@ -0,0 +1,18 @@ +#!/usr/bin/env roc + +app "echo" + packages { base: "platform" } + imports [ base.Task.{ Task, await }, base.Stdout, base.Stdin ] + provides [ main ] to base + +main : Task {} * +main = + {} <- await (Stdout.line "What's your first name?") + + firstName <- await Stdin.line + + {} <- await (Stdout.line "What's your last name?") + + lastName <- await Stdin.line + + Stdout.line "Hi, \(firstName) \(lastName)!" diff --git a/examples/cli/cli-example b/examples/cli/cli-example new file mode 100755 index 0000000000..28e07a40f7 Binary files /dev/null and b/examples/cli/cli-example differ diff --git a/examples/cli/hello-world b/examples/cli/hello-world new file mode 100755 index 0000000000..c3248ebe2c Binary files /dev/null and b/examples/cli/hello-world differ diff --git a/examples/cli/platform/Cargo.lock b/examples/cli/platform/Cargo.lock new file mode 100644 index 0000000000..cfd1e1e09d --- /dev/null +++ b/examples/cli/platform/Cargo.lock @@ -0,0 +1,21 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "host" +version = "0.1.0" +dependencies = [ + "libc", + "roc_std", +] + +[[package]] +name = "libc" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" + +[[package]] +name = "roc_std" +version = "0.1.0" diff --git a/examples/cli/platform/Cargo.toml b/examples/cli/platform/Cargo.toml new file mode 100644 index 0000000000..ad2bc7c449 --- /dev/null +++ b/examples/cli/platform/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "host" +version = "0.1.0" +authors = ["The Roc Contributors"] +license = "UPL-1.0" +edition = "2018" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +roc_std = { path = "../../../roc_std" } +libc = "0.2" + +[workspace] diff --git a/examples/cli/platform/Package-Config.roc b/examples/cli/platform/Package-Config.roc new file mode 100644 index 0000000000..1efb7e95fa --- /dev/null +++ b/examples/cli/platform/Package-Config.roc @@ -0,0 +1,14 @@ +platform examples/cli + requires {}{ main : Task {} [] } # TODO FIXME + exposes [] + packages {} + imports [ Task.{ Task } ] + provides [ mainForHost ] + effects fx.Effect + { + putLine : Str -> Effect {}, + getLine : Effect Str + } + +mainForHost : Task {} [] as Fx +mainForHost = main diff --git a/examples/cli/platform/Stdin.roc b/examples/cli/platform/Stdin.roc new file mode 100644 index 0000000000..b46406c6de --- /dev/null +++ b/examples/cli/platform/Stdin.roc @@ -0,0 +1,6 @@ +interface Stdin + exposes [ line ] + imports [ fx.Effect, Task ] + +line : Task.Task Str * +line = Effect.after Effect.getLine Task.succeed # TODO FIXME Effect.getLine should suffice diff --git a/examples/cli/platform/Stdout.roc b/examples/cli/platform/Stdout.roc new file mode 100644 index 0000000000..dbde579ffc --- /dev/null +++ b/examples/cli/platform/Stdout.roc @@ -0,0 +1,9 @@ +interface Stdout + exposes [ line ] + imports [ fx.Effect, Task.{ Task } ] + +# line : Str -> Task.Task {} * +# line = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {}) + +line : Str -> Task {} * +line = \str -> Effect.map (Effect.putLine str) (\_ -> Ok {}) diff --git a/examples/cli/platform/Task.roc b/examples/cli/platform/Task.roc new file mode 100644 index 0000000000..d3f996bc8a --- /dev/null +++ b/examples/cli/platform/Task.roc @@ -0,0 +1,44 @@ +interface Task + exposes [ Task, succeed, fail, await, map, onFail, attempt ] + imports [ fx.Effect ] + + +Task ok err : Effect.Effect (Result ok err) + + +succeed : val -> Task val * +succeed = \val -> + Effect.always (Ok val) + + +fail : err -> Task * err +fail = \val -> + Effect.always (Err val) + +attempt : Task a b, (Result a b -> Task c d) -> Task c d +attempt = \effect, transform -> + Effect.after effect \result -> + when result is + Ok ok -> transform (Ok ok) + Err err -> transform (Err err) + +await : Task a err, (a -> Task b err) -> Task b err +await = \effect, transform -> + Effect.after effect \result -> + when result is + Ok a -> transform a + Err err -> Task.fail err + +onFail : Task ok a, (a -> Task ok b) -> Task ok b +onFail = \effect, transform -> + Effect.after effect \result -> + when result is + Ok a -> Task.succeed a + Err err -> transform err + +map : Task a err, (a -> b) -> Task b err +map = \effect, transform -> + Effect.after effect \result -> + when result is + Ok a -> Task.succeed (transform a) + Err err -> Task.fail err diff --git a/examples/cli/platform/host.c b/examples/cli/platform/host.c new file mode 100644 index 0000000000..0378c69589 --- /dev/null +++ b/examples/cli/platform/host.c @@ -0,0 +1,7 @@ +#include + +extern int rust_main(); + +int main() { + return rust_main(); +} diff --git a/examples/cli/platform/src/lib.rs b/examples/cli/platform/src/lib.rs new file mode 100644 index 0000000000..2a98179f8e --- /dev/null +++ b/examples/cli/platform/src/lib.rs @@ -0,0 +1,139 @@ +#![allow(non_snake_case)] + +use core::alloc::Layout; +use core::ffi::c_void; +use core::mem::MaybeUninit; +use libc; +use roc_std::{RocCallResult, RocStr}; +use std::ffi::CStr; +use std::os::raw::c_char; + +extern "C" { + #[link_name = "roc__mainForHost_1_exposed"] + fn roc_main(output: *mut u8) -> (); + + #[link_name = "roc__mainForHost_size"] + fn roc_main_size() -> i64; + + #[link_name = "roc__mainForHost_1_Fx_caller"] + fn call_Fx(flags: *const u8, closure_data: *const u8, output: *mut u8) -> (); + + #[allow(dead_code)] + #[link_name = "roc__mainForHost_1_Fx_size"] + fn size_Fx() -> i64; + + #[link_name = "roc__mainForHost_1_Fx_result_size"] + fn size_Fx_result() -> i64; +} + +#[no_mangle] +pub unsafe fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void { + libc::malloc(size) +} + +#[no_mangle] +pub unsafe fn roc_realloc( + c_ptr: *mut c_void, + new_size: usize, + _old_size: usize, + _alignment: u32, +) -> *mut c_void { + libc::realloc(c_ptr, new_size) +} + +#[no_mangle] +pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) { + libc::free(c_ptr) +} + +#[no_mangle] +pub unsafe fn roc_panic(c_ptr: *mut c_void, tag_id: u32) { + match tag_id { + 0 => { + let slice = CStr::from_ptr(c_ptr as *const c_char); + let string = slice.to_str().unwrap(); + eprintln!("Roc hit a panic: {}", string); + std::process::exit(1); + } + _ => todo!(), + } +} + +#[no_mangle] +pub fn rust_main() -> isize { + let size = unsafe { roc_main_size() } as usize; + let layout = Layout::array::(size).unwrap(); + + unsafe { + // TODO allocate on the stack if it's under a certain size + let buffer = std::alloc::alloc(layout); + + roc_main(buffer); + + let output = buffer as *mut RocCallResult<()>; + + match (&*output).into() { + Ok(()) => { + let closure_data_ptr = buffer.offset(8); + let result = call_the_closure(closure_data_ptr as *const u8); + + std::alloc::dealloc(buffer, layout); + + result + } + Err(msg) => { + std::alloc::dealloc(buffer, layout); + + panic!("Roc failed with message: {}", msg); + } + } + }; + + // Exit code + 0 +} + +unsafe fn call_the_closure(closure_data_ptr: *const u8) -> i64 { + let size = size_Fx_result() as usize; + let layout = Layout::array::(size).unwrap(); + let buffer = std::alloc::alloc(layout) as *mut u8; + + call_Fx( + // This flags pointer will never get dereferenced + MaybeUninit::uninit().as_ptr(), + closure_data_ptr as *const u8, + buffer as *mut u8, + ); + + let output = &*(buffer as *mut RocCallResult<()>); + + match output.into() { + Ok(_) => { + std::alloc::dealloc(buffer, layout); + 0 + } + Err(e) => panic!("failed with {}", e), + } +} + +#[no_mangle] +pub fn roc_fx_getLine() -> RocStr { + use std::io::{self, BufRead}; + + let stdin = io::stdin(); + let line1 = stdin.lock().lines().next().unwrap().unwrap(); + + RocStr::from_slice(line1.as_bytes()) +} + +#[no_mangle] +pub fn roc_fx_putLine(line: RocStr) -> () { + let bytes = line.as_slice(); + let string = unsafe { std::str::from_utf8_unchecked(bytes) }; + println!("{}", string); + + // don't mess with the refcount! + core::mem::forget(line); + + () +} diff --git a/examples/hello-zig/platform/host.zig b/examples/hello-zig/platform/host.zig index 02b4d939a0..07a6a1d52f 100644 --- a/examples/hello-zig/platform/host.zig +++ b/examples/hello-zig/platform/host.zig @@ -19,23 +19,32 @@ comptime { } } -extern fn malloc(size: usize) callconv(.C) ?*c_void; -extern fn realloc(c_ptr: [*]align(@alignOf(u128)) u8, size: usize) callconv(.C) ?*c_void; -extern fn free(c_ptr: [*]align(@alignOf(u128)) u8) callconv(.C) void; +const Align = extern struct { a: usize, b: usize }; +extern fn malloc(size: usize) callconv(.C) ?*align(@alignOf(Align)) c_void; +extern fn realloc(c_ptr: [*]align(@alignOf(Align)) u8, size: usize) callconv(.C) ?*c_void; +extern fn free(c_ptr: [*]align(@alignOf(Align)) u8) callconv(.C) void; export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void { + _ = alignment; + return malloc(size); } -export fn roc_realloc(c_ptr: *c_void, old_size: usize, new_size: usize, alignment: u32) callconv(.C) ?*c_void { - return realloc(@alignCast(16, @ptrCast([*]u8, c_ptr)), new_size); +export fn roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*c_void { + _ = old_size; + _ = alignment; + + return realloc(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)), new_size); } export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void { - free(@alignCast(16, @ptrCast([*]u8, c_ptr))); + _ = alignment; + + free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr))); } export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void { + _ = tag_id; const stderr = std.io.getStdErr().writer(); const msg = @ptrCast([*:0]const u8, c_ptr); stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable; @@ -47,11 +56,11 @@ const Allocator = mem.Allocator; extern fn roc__mainForHost_1_exposed(*RocCallResult) void; -const RocCallResult = extern struct { flag: usize, content: RocStr }; +const RocCallResult = extern struct { flag: u64, content: RocStr }; const Unit = extern struct {}; -pub export fn main() i32 { +pub fn main() u8 { const stdout = std.io.getStdOut().writer(); const stderr = std.io.getStdErr().writer(); diff --git a/roc_std/src/lib.rs b/roc_std/src/lib.rs index f2880ed4ef..c5b1f3e87a 100644 --- a/roc_std/src/lib.rs +++ b/roc_std/src/lib.rs @@ -584,6 +584,10 @@ impl RocStr { let raw_ptr = Self::get_element_ptr(raw_ptr as *mut u8); + // write the refcount + let refcount_ptr = raw_ptr as *mut isize; + *(refcount_ptr.offset(-1)) = isize::MIN; + { // NOTE: using a memcpy here causes weird issues let target_ptr = raw_ptr as *mut u8;