diff --git a/crates/compiler/builtins/bitcode/src/utils.zig b/crates/compiler/builtins/bitcode/src/utils.zig index aa2a9605e3..58849e6391 100644 --- a/crates/compiler/builtins/bitcode/src/utils.zig +++ b/crates/compiler/builtins/bitcode/src/utils.zig @@ -305,6 +305,10 @@ pub fn isUnique( const isizes: [*]isize = @intToPtr([*]isize, masked_ptr); + if (DEBUG_INCDEC and builtin.target.cpu.arch != .wasm32) { + std.debug.print("| is unique {*}\n", .{&bytes[0]}); + } + const refcount = (isizes - 1)[0]; return refcount == REFCOUNT_ONE_ISIZE; diff --git a/crates/compiler/gen_dev/src/generic64/aarch64.rs b/crates/compiler/gen_dev/src/generic64/aarch64.rs index cf08ab036e..7877b3ee4d 100644 --- a/crates/compiler/gen_dev/src/generic64/aarch64.rs +++ b/crates/compiler/gen_dev/src/generic64/aarch64.rs @@ -458,6 +458,11 @@ impl CallConv for AArch64C } impl Assembler for AArch64Assembler { + #[inline(always)] + fn base_pointer() -> AArch64GeneralReg { + AArch64GeneralReg::FP + } + #[inline(always)] fn abs_reg64_reg64(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, src: AArch64GeneralReg) { cmp_reg64_imm12(buf, src, 0); diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index 0dfa1d9c1b..2eccb3b04a 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -150,6 +150,8 @@ pub enum CompareOperation { /// Generally, I prefer explicit sources, as opposed to dst being one of the sources. Ex: `x = x + y` would be `add x, x, y` instead of `add x, y`. /// dst should always come before sources. pub trait Assembler: Sized + Copy { + fn base_pointer() -> GeneralReg; + fn abs_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg); fn abs_freg64_freg64( buf: &mut Vec<'_, u8>, @@ -2629,6 +2631,49 @@ impl< ); } + UnionLayout::NullableWrapped { + nullable_id, + other_tags, + } => { + debug_assert_ne!(tag_id, *nullable_id as TagIdIntType); + + let other_fields = if tag_id < *nullable_id { + other_tags[tag_id as usize] + } else { + other_tags[tag_id as usize - 1] + }; + + let element_layout = other_fields[index as usize]; + + let ptr_reg = self + .storage_manager + .load_to_general_reg(&mut self.buf, structure); + + let mask_symbol = self.debug_symbol("tag_id_mask"); + let mask_reg = self + .storage_manager + .claim_general_reg(&mut self.buf, &mask_symbol); + ASM::mov_reg64_imm64(&mut self.buf, mask_reg, (!0b111) as _); + + // mask out the tag id bits + ASM::and_reg64_reg64_reg64(&mut self.buf, ptr_reg, ptr_reg, mask_reg); + + let mut offset = 0; + for field in &other_fields[..index as usize] { + offset += self.layout_interner.stack_size(*field); + } + + Self::ptr_read( + &mut self.buf, + &mut self.storage_manager, + self.layout_interner, + ptr_reg, + offset as i32, + element_layout, + *sym, + ); + } + _ => { let union_in_layout = self .layout_interner @@ -2780,6 +2825,72 @@ impl< self.free_symbol(&tmp); } + UnionLayout::NullableWrapped { + nullable_id, + other_tags, + } => { + let number_of_tags = other_tags.len() + 1; + + let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, sym); + + // build a table to index into with the value that we find + let nullable_id = *nullable_id as usize; + let it = std::iter::once(nullable_id) + .chain(0..nullable_id) + .chain(nullable_id + 1..number_of_tags); + + let table = self.debug_symbol("tag_id_table"); + let table_offset = self + .storage_manager + .claim_stack_area(&table, (number_of_tags * 2) as _); + + let mut offset = table_offset; + for i in it { + ASM::mov_reg64_imm64(&mut self.buf, dst_reg, i as i64); + ASM::mov_base32_reg16(&mut self.buf, offset, dst_reg); + + offset += 2; + } + + self.free_symbol(&table); + + // mask the 3 lowest bits + let tmp = Symbol::DEV_TMP5; + let reg = self.storage_manager.claim_general_reg(&mut self.buf, &tmp); + ASM::mov_reg64_imm64(&mut self.buf, reg, 0b111); + + let src1_reg = reg; + let src2_reg = self + .storage_manager + .load_to_general_reg(&mut self.buf, structure); + + ASM::and_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); + + // we're indexing into an array of u16, so double this index + // also the stack grows down, so negate the index + ASM::mov_reg64_imm64(&mut self.buf, reg, 2); + ASM::umul_reg64_reg64_reg64( + &mut self.buf, + &mut self.storage_manager, + dst_reg, + dst_reg, + reg, + ); + + // index into the table + let base_pointer = ASM::base_pointer(); + ASM::add_reg64_reg64_reg64(&mut self.buf, dst_reg, dst_reg, base_pointer); + + // load the 16-bit value at the pointer + ASM::mov_reg16_mem16_offset32(&mut self.buf, dst_reg, dst_reg, table_offset); + + // keep only the lowest 16 bits + ASM::mov_reg64_imm64(&mut self.buf, reg, 0xFFFF); + ASM::and_reg64_reg64_reg64(&mut self.buf, dst_reg, dst_reg, reg); + + self.free_symbol(&tmp); + } + x => todo!("getting tag id of union with layout ({:?})", x), }; } @@ -2864,6 +2975,68 @@ impl< self.free_symbol(&temp_sym); } } + UnionLayout::NullableWrapped { + nullable_id, + other_tags, + } => { + let nullable_id = *nullable_id; + + if tag_id == nullable_id as TagIdIntType { + // it's just a null pointer + self.load_literal_i64(sym, 0); + } else { + let other_fields = if tag_id < nullable_id { + other_tags[tag_id as usize] + } else { + other_tags[tag_id as usize - 1] + }; + + // construct the payload as a struct on the stack + let temp_sym = Symbol::DEV_TMP5; + let layout = self + .layout_interner + .insert_no_semantic(LayoutRepr::Struct(other_fields)); + + self.load_literal_symbols(fields); + self.storage_manager.create_struct( + self.layout_interner, + &mut self.buf, + &temp_sym, + &layout, + fields, + ); + + // now effectively box this struct + let untagged_pointer_symbol = self.debug_symbol("untagged_pointer"); + self.expr_box(untagged_pointer_symbol, temp_sym, layout, reuse); + + self.free_symbol(&temp_sym); + + let tag_id_symbol = self.debug_symbol("tag_id"); + + // index zero is taken up by the nullable tag, so any tags before it in the + // ordering need to be incremented by one + let pointer_tag = if tag_id < nullable_id { + tag_id + 1 + } else { + tag_id + }; + + // finally, we need to tag the pointer + debug_assert!(tag_id < 8); + self.load_literal_i64(&tag_id_symbol, pointer_tag as _); + + self.build_int_bitwise_or( + sym, + &untagged_pointer_symbol, + &tag_id_symbol, + IntWidth::U64, + ); + + self.free_symbol(&untagged_pointer_symbol); + self.free_symbol(&tag_id_symbol); + } + } x => todo!("creating unions with layout: {:?}", x), } } diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index 17b2bc2d51..0b3ff8d075 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -1139,6 +1139,11 @@ where } impl Assembler for X86_64Assembler { + #[inline(always)] + fn base_pointer() -> X86_64GeneralReg { + X86_64GeneralReg::RBP + } + // These functions should map to the raw assembly functions below. // In some cases, that means you can just directly call one of the direct assembly functions. #[inline(always)] diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 1482a88277..379ec31b64 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -4134,7 +4134,9 @@ where Unit => env .cache .put_in(Layout::new(LayoutRepr::UNIT, compute_semantic())), - BoolUnion { .. } => Layout::BOOL, + BoolUnion { .. } => env + .cache + .put_in(Layout::new(LayoutRepr::BOOL, compute_semantic())), ByteUnion(_) => env .cache .put_in(Layout::new(LayoutRepr::U8, compute_semantic())), diff --git a/crates/compiler/mono/src/layout/intern.rs b/crates/compiler/mono/src/layout/intern.rs index 0250df1c2b..6a0b4f678f 100644 --- a/crates/compiler/mono/src/layout/intern.rs +++ b/crates/compiler/mono/src/layout/intern.rs @@ -59,7 +59,7 @@ macro_rules! nosema { cache_interned_layouts! { 0, VOID, pub, Layout::VOID_NAKED 1, UNIT, pub, Layout::UNIT_NAKED - 2, BOOL, pub, Layout { repr: LayoutRepr::BOOL, semantic: SemanticRepr::BOOL } + 2, BOOL, pub, nosema!(LayoutRepr::BOOL) 3, U8, pub, nosema!(LayoutRepr::U8) 4, U16, pub, nosema!(LayoutRepr::U16) 5, U32, pub, nosema!(LayoutRepr::U32) diff --git a/crates/compiler/mono/src/layout/semantic.rs b/crates/compiler/mono/src/layout/semantic.rs index 824bf8c453..75e9a06d67 100644 --- a/crates/compiler/mono/src/layout/semantic.rs +++ b/crates/compiler/mono/src/layout/semantic.rs @@ -27,21 +27,20 @@ enum Inner<'a> { impl<'a> SemanticRepr<'a> { pub(super) const NONE: Self = Self(Inner::None); pub(super) const EMPTY_RECORD: Self = Self::record(&[]); - pub(super) const BOOL: Self = Self::tag_union(&["False", "True"]); pub(super) const fn record(fields: &'a [&'a str]) -> Self { Self(Inner::Record(SemaRecord { fields })) } - pub(super) const fn tuple(size: usize) -> Self { + pub(super) fn tuple(size: usize) -> Self { Self(Inner::Tuple(SemaTuple { size })) } - pub(super) const fn tag_union(tags: &'a [&'a str]) -> Self { + pub(super) fn tag_union(tags: &'a [&'a str]) -> Self { Self(Inner::TagUnion(SemaTagUnion { tags })) } - pub(super) const fn lambdas(lambdas: &'a [Symbol]) -> Self { + pub(super) fn lambdas(lambdas: &'a [Symbol]) -> Self { Self(Inner::Lambdas(SemaLambdas { lambdas })) } } diff --git a/crates/compiler/test_gen/src/gen_tags.rs b/crates/compiler/test_gen/src/gen_tags.rs index 5eac23d524..bdfef4a93b 100644 --- a/crates/compiler/test_gen/src/gen_tags.rs +++ b/crates/compiler/test_gen/src/gen_tags.rs @@ -2063,7 +2063,7 @@ fn non_unary_union_with_lambda_set_with_imported_toplevels_issue_4733() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn nullable_wrapped_with_non_nullable_singleton_tags() { assert_evals_to!( indoc!( @@ -2094,7 +2094,7 @@ fn nullable_wrapped_with_non_nullable_singleton_tags() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn nullable_wrapped_with_nullable_not_last_index() { assert_evals_to!( indoc!( @@ -2102,9 +2102,9 @@ fn nullable_wrapped_with_nullable_not_last_index() { app "test" provides [main] to "./platform" Parser : [ - OneOrMore Parser, - Keyword Str, CharLiteral, + Keyword Str, + OneOrMore Parser, ] toIdParser : Parser -> Str