mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-24 06:55:15 +00:00
nullable unwrapped for the dev backend
This commit is contained in:
parent
0adf075123
commit
f6fafdb019
8 changed files with 198 additions and 10 deletions
|
@ -305,6 +305,10 @@ pub fn isUnique(
|
||||||
|
|
||||||
const isizes: [*]isize = @intToPtr([*]isize, masked_ptr);
|
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];
|
const refcount = (isizes - 1)[0];
|
||||||
|
|
||||||
return refcount == REFCOUNT_ONE_ISIZE;
|
return refcount == REFCOUNT_ONE_ISIZE;
|
||||||
|
|
|
@ -458,6 +458,11 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg, AArch64Assembler> for AArch64C
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||||
|
#[inline(always)]
|
||||||
|
fn base_pointer() -> AArch64GeneralReg {
|
||||||
|
AArch64GeneralReg::FP
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn abs_reg64_reg64(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, src: AArch64GeneralReg) {
|
fn abs_reg64_reg64(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, src: AArch64GeneralReg) {
|
||||||
cmp_reg64_imm12(buf, src, 0);
|
cmp_reg64_imm12(buf, src, 0);
|
||||||
|
|
|
@ -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`.
|
/// 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.
|
/// dst should always come before sources.
|
||||||
pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
|
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_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg);
|
||||||
fn abs_freg64_freg64(
|
fn abs_freg64_freg64(
|
||||||
buf: &mut Vec<'_, u8>,
|
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
|
let union_in_layout = self
|
||||||
.layout_interner
|
.layout_interner
|
||||||
|
@ -2780,6 +2825,72 @@ impl<
|
||||||
|
|
||||||
self.free_symbol(&tmp);
|
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),
|
x => todo!("getting tag id of union with layout ({:?})", x),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2864,6 +2975,68 @@ impl<
|
||||||
self.free_symbol(&temp_sym);
|
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),
|
x => todo!("creating unions with layout: {:?}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1139,6 +1139,11 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
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.
|
// 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.
|
// In some cases, that means you can just directly call one of the direct assembly functions.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
@ -4134,7 +4134,9 @@ where
|
||||||
Unit => env
|
Unit => env
|
||||||
.cache
|
.cache
|
||||||
.put_in(Layout::new(LayoutRepr::UNIT, compute_semantic())),
|
.put_in(Layout::new(LayoutRepr::UNIT, compute_semantic())),
|
||||||
BoolUnion { .. } => Layout::BOOL,
|
BoolUnion { .. } => env
|
||||||
|
.cache
|
||||||
|
.put_in(Layout::new(LayoutRepr::BOOL, compute_semantic())),
|
||||||
ByteUnion(_) => env
|
ByteUnion(_) => env
|
||||||
.cache
|
.cache
|
||||||
.put_in(Layout::new(LayoutRepr::U8, compute_semantic())),
|
.put_in(Layout::new(LayoutRepr::U8, compute_semantic())),
|
||||||
|
|
|
@ -59,7 +59,7 @@ macro_rules! nosema {
|
||||||
cache_interned_layouts! {
|
cache_interned_layouts! {
|
||||||
0, VOID, pub, Layout::VOID_NAKED
|
0, VOID, pub, Layout::VOID_NAKED
|
||||||
1, UNIT, pub, Layout::UNIT_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)
|
3, U8, pub, nosema!(LayoutRepr::U8)
|
||||||
4, U16, pub, nosema!(LayoutRepr::U16)
|
4, U16, pub, nosema!(LayoutRepr::U16)
|
||||||
5, U32, pub, nosema!(LayoutRepr::U32)
|
5, U32, pub, nosema!(LayoutRepr::U32)
|
||||||
|
|
|
@ -27,21 +27,20 @@ enum Inner<'a> {
|
||||||
impl<'a> SemanticRepr<'a> {
|
impl<'a> SemanticRepr<'a> {
|
||||||
pub(super) const NONE: Self = Self(Inner::None);
|
pub(super) const NONE: Self = Self(Inner::None);
|
||||||
pub(super) const EMPTY_RECORD: Self = Self::record(&[]);
|
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 {
|
pub(super) const fn record(fields: &'a [&'a str]) -> Self {
|
||||||
Self(Inner::Record(SemaRecord { fields }))
|
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 }))
|
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 }))
|
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 }))
|
Self(Inner::Lambdas(SemaLambdas { lambdas }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2063,7 +2063,7 @@ fn non_unary_union_with_lambda_set_with_imported_toplevels_issue_4733() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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() {
|
fn nullable_wrapped_with_non_nullable_singleton_tags() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -2094,7 +2094,7 @@ fn nullable_wrapped_with_non_nullable_singleton_tags() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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() {
|
fn nullable_wrapped_with_nullable_not_last_index() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -2102,9 +2102,9 @@ fn nullable_wrapped_with_nullable_not_last_index() {
|
||||||
app "test" provides [main] to "./platform"
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
Parser : [
|
Parser : [
|
||||||
OneOrMore Parser,
|
|
||||||
Keyword Str,
|
|
||||||
CharLiteral,
|
CharLiteral,
|
||||||
|
Keyword Str,
|
||||||
|
OneOrMore Parser,
|
||||||
]
|
]
|
||||||
|
|
||||||
toIdParser : Parser -> Str
|
toIdParser : Parser -> Str
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue