mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
correctly add padding between data and tag id
This commit is contained in:
parent
fb4a56e916
commit
c0f49ce62c
3 changed files with 167 additions and 13 deletions
|
@ -326,6 +326,10 @@ impl<'ctx> RocUnion<'ctx> {
|
||||||
Self::new(context, target_info, data_align, data_width, None)
|
Self::new(context, target_info, data_align, data_width, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn data_width(&self) -> u32 {
|
||||||
|
self.data_width
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tag_alignment(&self) -> u32 {
|
pub fn tag_alignment(&self) -> u32 {
|
||||||
let tag_id_alignment = match self.tag_type {
|
let tag_id_alignment = match self.tag_type {
|
||||||
None => 0,
|
None => 0,
|
||||||
|
|
|
@ -129,6 +129,15 @@ fn write_state<'a, 'ctx, 'env>(
|
||||||
env.builder.build_store(offset_ptr, offset);
|
env.builder.build_store(offset_ptr, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn offset_add<'ctx>(
|
||||||
|
builder: &Builder<'ctx>,
|
||||||
|
current: IntValue<'ctx>,
|
||||||
|
extra: u32,
|
||||||
|
) -> IntValue<'ctx> {
|
||||||
|
let intval = current.get_type().const_int(extra as _, false);
|
||||||
|
builder.build_int_add(current, intval, "offset_add")
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn notify_parent_expect(env: &Env, shared_memory: &SharedMemoryPointer) {
|
pub(crate) fn notify_parent_expect(env: &Env, shared_memory: &SharedMemoryPointer) {
|
||||||
let func = env
|
let func = env
|
||||||
.module
|
.module
|
||||||
|
@ -564,6 +573,56 @@ fn load_tag_data<'a, 'ctx, 'env>(
|
||||||
env.builder.new_build_load(tag_type, data_ptr, "load_data")
|
env.builder.new_build_load(tag_type, data_ptr, "load_data")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clone_tag_payload_and_id<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_interner: &mut STLayoutInterner<'a>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
ptr: PointerValue<'ctx>,
|
||||||
|
cursors: Cursors<'ctx>,
|
||||||
|
roc_union: RocUnion<'ctx>,
|
||||||
|
tag_id: usize,
|
||||||
|
payload_in_layout: InLayout<'a>,
|
||||||
|
opaque_payload_ptr: PointerValue<'ctx>,
|
||||||
|
) -> IntValue<'ctx> {
|
||||||
|
let payload_type = basic_type_from_layout(env, layout_interner, payload_in_layout);
|
||||||
|
|
||||||
|
let payload_ptr = env.builder.build_pointer_cast(
|
||||||
|
opaque_payload_ptr,
|
||||||
|
payload_type.ptr_type(AddressSpace::default()),
|
||||||
|
"cast_payload_ptr",
|
||||||
|
);
|
||||||
|
|
||||||
|
let payload = env
|
||||||
|
.builder
|
||||||
|
.new_build_load(payload_type, payload_ptr, "payload");
|
||||||
|
|
||||||
|
// NOTE: `answer` includes any extra_offset that the tag payload may have needed
|
||||||
|
// (e.g. because it includes a list). That is what we want to return, but not what
|
||||||
|
// we need to write the padding and offset of this tag
|
||||||
|
let answer = build_clone(
|
||||||
|
env,
|
||||||
|
layout_interner,
|
||||||
|
layout_ids,
|
||||||
|
ptr,
|
||||||
|
cursors,
|
||||||
|
payload,
|
||||||
|
payload_in_layout,
|
||||||
|
);
|
||||||
|
|
||||||
|
// include padding between data and tag id
|
||||||
|
let tag_id_internal_offset = roc_union.data_width();
|
||||||
|
|
||||||
|
let tag_id_offset = offset_add(env.builder, cursors.offset, tag_id_internal_offset);
|
||||||
|
|
||||||
|
// write the tag id
|
||||||
|
let value = env.context.i8_type().const_int(tag_id as _, false);
|
||||||
|
build_copy(env, ptr, tag_id_offset, value.into());
|
||||||
|
|
||||||
|
// NOTE: padding after tag id (is taken care of by the cursor)
|
||||||
|
|
||||||
|
answer
|
||||||
|
}
|
||||||
|
|
||||||
fn build_clone_tag_help<'a, 'ctx, 'env>(
|
fn build_clone_tag_help<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_interner: &mut STLayoutInterner<'a>,
|
layout_interner: &mut STLayoutInterner<'a>,
|
||||||
|
@ -622,22 +681,38 @@ fn build_clone_tag_help<'a, 'ctx, 'env>(
|
||||||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||||
env.builder.position_at_end(block);
|
env.builder.position_at_end(block);
|
||||||
|
|
||||||
let layout = layout_interner.insert(Layout::struct_no_name_order(field_layouts));
|
let roc_union = RocUnion::tagged_from_slices(
|
||||||
let layout = layout_interner.insert(Layout::struct_no_name_order(
|
|
||||||
env.arena.alloc([layout, union_layout.tag_id_layout()]),
|
|
||||||
));
|
|
||||||
|
|
||||||
let basic_type = basic_type_from_layout(env, layout_interner, layout);
|
|
||||||
let data = load_tag_data(
|
|
||||||
env,
|
|
||||||
layout_interner,
|
layout_interner,
|
||||||
union_layout,
|
env.context,
|
||||||
tag_value.into_pointer_value(),
|
tags,
|
||||||
basic_type,
|
env.target_info,
|
||||||
);
|
);
|
||||||
|
|
||||||
let answer =
|
// load the tag payload (if any)
|
||||||
build_clone(env, layout_interner, layout_ids, ptr, cursors, data, layout);
|
let payload_layout = Layout::struct_no_name_order(field_layouts);
|
||||||
|
let payload_in_layout = layout_interner.insert(payload_layout);
|
||||||
|
|
||||||
|
let opaque_payload_ptr = env
|
||||||
|
.builder
|
||||||
|
.new_build_struct_gep(
|
||||||
|
roc_union.struct_type(),
|
||||||
|
tag_value.into_pointer_value(),
|
||||||
|
RocUnion::TAG_DATA_INDEX,
|
||||||
|
"data_buffer",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let answer = clone_tag_payload_and_id(
|
||||||
|
env,
|
||||||
|
layout_interner,
|
||||||
|
layout_ids,
|
||||||
|
ptr,
|
||||||
|
cursors,
|
||||||
|
roc_union,
|
||||||
|
tag_id,
|
||||||
|
payload_in_layout,
|
||||||
|
opaque_payload_ptr,
|
||||||
|
);
|
||||||
|
|
||||||
env.builder.build_return(Some(&answer));
|
env.builder.build_return(Some(&answer));
|
||||||
|
|
||||||
|
|
|
@ -1105,4 +1105,79 @@ mod test {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tag_payloads_of_different_size() {
|
||||||
|
run_expect_test(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
interface Test exposes [] imports []
|
||||||
|
|
||||||
|
actual : [Leftover (List U8), TooShort]
|
||||||
|
actual = Leftover [49, 93]
|
||||||
|
|
||||||
|
expect
|
||||||
|
expected : [Leftover (List U8), TooShort]
|
||||||
|
expected = TooShort
|
||||||
|
|
||||||
|
actual == expected
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
This expectation failed:
|
||||||
|
|
||||||
|
6│> expect
|
||||||
|
7│> expected : [Leftover (List U8), TooShort]
|
||||||
|
8│> expected = TooShort
|
||||||
|
9│>
|
||||||
|
10│> actual == expected
|
||||||
|
|
||||||
|
When it failed, these variables had these values:
|
||||||
|
|
||||||
|
expected : [
|
||||||
|
Leftover (List U8),
|
||||||
|
TooShort,
|
||||||
|
]
|
||||||
|
expected = TooShort
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn extra_offset_in_tag_union() {
|
||||||
|
run_expect_test(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
interface Test exposes [] imports []
|
||||||
|
|
||||||
|
actual : Result Str U64
|
||||||
|
actual = Err 1
|
||||||
|
|
||||||
|
expect
|
||||||
|
expected : Result Str U64
|
||||||
|
expected = Ok "foobar"
|
||||||
|
|
||||||
|
actual == expected
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
This expectation failed:
|
||||||
|
|
||||||
|
6│> expect
|
||||||
|
7│> expected : Result Str U64
|
||||||
|
8│> expected = Ok "foobar"
|
||||||
|
9│>
|
||||||
|
10│> actual == expected
|
||||||
|
|
||||||
|
When it failed, these variables had these values:
|
||||||
|
|
||||||
|
expected : Result Str U64
|
||||||
|
expected = Ok "foobar"
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue