(llvm) Generate code for tag unions less than 64 bits in size correctly

Previously, we assumed that a union layout always lived on >= 1 64-bit
boundary when generating an LLVM type for it. For small tags unions,
like `[ Ok i8, Err ]` this need not be the case; indeed, a tag union
like that is actually only 2 bits - 1 bit for the "i8" data, and one bit
of the tag kind.

This led to a discrepancy between what the layout IR and generated LLVM
code would assume about the size of tag unions. In the case above, the
layout IR would assume the tag data is 2 bits wide, and the tag id is 1
bit into the data. But the LLVM code would generate a type that was 65
bits wide, the first 64 bits being for the "i8" data and the last 1 bit
being for the tag kind.

Usually, just running the LLVM-emitted code would not present a problem.
But it does present a problem when we use the layout IR to inspect the
result of LLVM-run code, in particular when we try to look up the tag
ID, as the repl does. This patch fixes that issue.

Note that this bug did not present itself in `test_gen` previously
because the data that most tests check against is stored in the front of
the representation.

Closes #2149
This commit is contained in:
ayazhafiz 2021-12-26 11:45:02 -06:00
parent f314abfed9
commit 5e5eb6dca8
4 changed files with 54 additions and 40 deletions

View file

@ -246,14 +246,18 @@ fn block_of_memory_help(context: &Context, union_size: u32) -> BasicTypeEnum<'_>
let num_i64 = union_size / 8;
let num_i8 = union_size % 8;
let i8_array_type = context.i8_type().array_type(num_i8).as_basic_type_enum();
let i64_array_type = context.i64_type().array_type(num_i64).as_basic_type_enum();
if num_i8 == 0 {
// the object fits perfectly in some number of i64's
if num_i64 == 0 {
// The object fits perfectly in some number of i8s
context.struct_type(&[i8_array_type], false).into()
} else if num_i8 == 0 {
// The object fits perfectly in some number of i64s
// (i.e. the size is a multiple of 8 bytes)
context.struct_type(&[i64_array_type], false).into()
} else {
// there are some trailing bytes at the end
// There are some trailing bytes at the end
let i8_array_type = context.i8_type().array_type(num_i8).as_basic_type_enum();
context