nullable unwrapped for the dev backend

This commit is contained in:
Folkert 2023-05-31 23:14:37 +02:00
parent 0adf075123
commit f6fafdb019
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
8 changed files with 198 additions and 10 deletions

View file

@ -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;

View file

@ -458,6 +458,11 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg, AArch64Assembler> for AArch64C
}
impl Assembler<AArch64GeneralReg, AArch64FloatReg> 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);

View file

@ -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<GeneralReg: RegTrait, FloatReg: RegTrait>: 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),
}
}

View file

@ -1139,6 +1139,11 @@ where
}
impl Assembler<X86_64GeneralReg, X86_64FloatReg> 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)]

View file

@ -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())),

View file

@ -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)

View file

@ -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 }))
}
}

View file

@ -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