alignment of structs no longer depends on LLVM

fixes async function tests in optimized builds
This commit is contained in:
Andrew Kelley 2019-08-13 12:44:30 -04:00
parent 8a9289996a
commit 12ff91c1c9
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
7 changed files with 204 additions and 59 deletions

View File

@ -1,3 +1,4 @@
* alignment of variables not being respected in async functions
* for loops need to spill the index. other payload captures probably also need to spill
* compile error (instead of crashing) for trying to get @Frame of generic function
* compile error (instead of crashing) for trying to async call and passing @Frame of wrong function

View File

@ -1148,6 +1148,8 @@ struct ZigTypeOptional {
struct ZigTypeErrorUnion {
ZigType *err_set_type;
ZigType *payload_type;
size_t pad_bytes;
LLVMTypeRef pad_llvm_type;
};
struct ZigTypeErrorSet {
@ -3564,6 +3566,7 @@ struct IrInstructionAllocaGen {
uint32_t align;
const char *name_hint;
size_t field_index;
};
struct IrInstructionEndExpr {

View File

@ -630,6 +630,7 @@ ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payloa
size_t field2_offset = next_field_offset(0, entry->abi_align, field_sizes[0], field_aligns[1]);
entry->abi_size = next_field_offset(field2_offset, entry->abi_align, field_sizes[1], entry->abi_align);
entry->size_in_bits = entry->abi_size * 8;
entry->data.error_union.pad_bytes = entry->abi_size - (field2_offset + field_sizes[1]);
}
g->type_table.put(type_id, entry);
@ -1499,7 +1500,7 @@ bool type_is_invalid(ZigType *type_entry) {
}
ZigType *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[],
static ZigType *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[],
ZigType *field_types[], size_t field_count, unsigned min_abi_align)
{
ZigType *struct_type = new_type_table_entry(ZigTypeIdStruct);
@ -1524,10 +1525,6 @@ ZigType *get_struct_type(CodeGen *g, const char *type_name, const char *field_na
if (field->type_entry->abi_align > abi_align) {
abi_align = field->type_entry->abi_align;
}
field->gen_index = struct_type->data.structure.gen_field_count;
struct_type->data.structure.gen_field_count += 1;
} else {
field->gen_index = SIZE_MAX;
}
auto prev_entry = struct_type->data.structure.fields_by_name.put_unique(field->name, field);
@ -1537,14 +1534,16 @@ ZigType *get_struct_type(CodeGen *g, const char *type_name, const char *field_na
size_t next_offset = 0;
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *field = &struct_type->data.structure.fields[i];
if (field->gen_index == SIZE_MAX)
if (!type_has_bits(field->type_entry))
continue;
field->offset = next_offset;
// find the next non-zero-byte field for offset calculations
size_t next_src_field_index = i + 1;
for (; next_src_field_index < field_count; next_src_field_index += 1) {
if (struct_type->data.structure.fields[next_src_field_index].gen_index != SIZE_MAX) {
if (type_has_bits(struct_type->data.structure.fields[next_src_field_index].type_entry))
break;
}
}
size_t next_abi_align = (next_src_field_index == field_count) ?
abi_align : struct_type->data.structure.fields[next_src_field_index].type_entry->abi_align;
@ -5304,6 +5303,7 @@ static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) {
for (size_t alloca_i = 0; alloca_i < fn->alloca_gen_list.length; alloca_i += 1) {
IrInstructionAllocaGen *instruction = fn->alloca_gen_list.at(alloca_i);
instruction->field_index = SIZE_MAX;
ZigType *ptr_type = instruction->base.value.type;
assert(ptr_type->id == ZigTypeIdPointer);
ZigType *child_type = ptr_type->data.pointer.child_type;
@ -5327,6 +5327,7 @@ static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) {
} else {
name = buf_ptr(buf_sprintf("%s.%" ZIG_PRI_usize, instruction->name_hint, alloca_i));
}
instruction->field_index = field_types.length;
field_names.append(name);
field_types.append(child_type);
}
@ -5949,6 +5950,15 @@ ZigType *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) {
entry->llvm_type = LLVMIntType(size_in_bits);
entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type);
entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, entry->llvm_type);
if (size_in_bits >= 128) {
// Override the incorrect alignment reported by LLVM. Clang does this as well.
// On x86_64 there are some instructions like CMPXCHG16B which require this.
// On all targets, integers 128 bits and above have ABI alignment of 16.
// See: https://github.com/ziglang/zig/issues/2987
assert(entry->abi_align == 8); // if this trips we can remove the workaround
entry->abi_align = 16;
}
}
const char u_or_i = is_signed ? 'i' : 'u';
@ -6810,10 +6820,9 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
}
size_t field_count = struct_type->data.structure.src_field_count;
size_t gen_field_count = struct_type->data.structure.gen_field_count;
LLVMTypeRef *element_types = allocate<LLVMTypeRef>(gen_field_count);
// Every field could potentially have a generated padding field after it.
LLVMTypeRef *element_types = allocate<LLVMTypeRef>(field_count * 2);
size_t gen_field_index = 0;
bool packed = (struct_type->data.structure.layout == ContainerLayoutPacked);
size_t packed_bits_offset = 0;
size_t first_packed_bits_offset_misalign = SIZE_MAX;
@ -6821,20 +6830,36 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
// trigger all the recursive get_llvm_type calls
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
ZigType *field_type = type_struct_field->type_entry;
TypeStructField *field = &struct_type->data.structure.fields[i];
ZigType *field_type = field->type_entry;
if (!type_has_bits(field_type))
continue;
(void)get_llvm_type(g, field_type);
if (struct_type->data.structure.resolve_status >= wanted_resolve_status) return;
}
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
ZigType *field_type = type_struct_field->type_entry;
size_t gen_field_index = 0;
// Calculate what LLVM thinks the ABI align of the struct will be. We do this to avoid
// inserting padding bytes where LLVM would do it automatically.
size_t llvm_struct_abi_align = 0;
for (size_t i = 0; i < field_count; i += 1) {
ZigType *field_type = struct_type->data.structure.fields[i].type_entry;
if (!type_has_bits(field_type))
continue;
LLVMTypeRef field_llvm_type = get_llvm_type(g, field_type);
size_t llvm_field_abi_align = LLVMABIAlignmentOfType(g->target_data_ref, field_llvm_type);
llvm_struct_abi_align = max(llvm_struct_abi_align, llvm_field_abi_align);
}
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *field = &struct_type->data.structure.fields[i];
ZigType *field_type = field->type_entry;
if (!type_has_bits(field_type)) {
field->gen_index = SIZE_MAX;
continue;
}
if (packed) {
size_t field_size_in_bits = type_size_bits(g, field_type);
@ -6871,11 +6896,44 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
llvm_type = get_llvm_type(g, field_type);
}
element_types[gen_field_index] = llvm_type;
field->gen_index = gen_field_index;
gen_field_index += 1;
// find the next non-zero-byte field for offset calculations
size_t next_src_field_index = i + 1;
for (; next_src_field_index < field_count; next_src_field_index += 1) {
if (type_has_bits(struct_type->data.structure.fields[next_src_field_index].type_entry))
break;
}
size_t next_abi_align = (next_src_field_index == field_count) ?
struct_type->abi_align :
struct_type->data.structure.fields[next_src_field_index].type_entry->abi_align;
size_t llvm_next_abi_align = (next_src_field_index == field_count) ?
llvm_struct_abi_align :
LLVMABIAlignmentOfType(g->target_data_ref,
get_llvm_type(g, struct_type->data.structure.fields[next_src_field_index].type_entry));
size_t next_offset = next_field_offset(field->offset, struct_type->abi_align,
field_type->abi_size, next_abi_align);
size_t llvm_next_offset = next_field_offset(field->offset, llvm_struct_abi_align,
LLVMABISizeOfType(g->target_data_ref, llvm_type), llvm_next_abi_align);
assert(next_offset >= llvm_next_offset);
if (next_offset > llvm_next_offset) {
size_t pad_bytes = next_offset - (field->offset + field_type->abi_size);
if (pad_bytes != 0) {
LLVMTypeRef pad_llvm_type = LLVMArrayType(LLVMInt8Type(), pad_bytes);
element_types[gen_field_index] = pad_llvm_type;
gen_field_index += 1;
}
}
}
debug_field_count += 1;
}
if (!packed) {
struct_type->data.structure.gen_field_count = gen_field_index;
}
if (first_packed_bits_offset_misalign != SIZE_MAX) {
size_t full_bit_count = packed_bits_offset - first_packed_bits_offset_misalign;
size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes);
@ -6884,19 +6942,20 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
}
if (type_has_bits(struct_type)) {
LLVMStructSetBody(struct_type->llvm_type, element_types, (unsigned)gen_field_count, packed);
LLVMStructSetBody(struct_type->llvm_type, element_types,
(unsigned)struct_type->data.structure.gen_field_count, packed);
}
ZigLLVMDIType **di_element_types = allocate<ZigLLVMDIType*>(debug_field_count);
size_t debug_field_index = 0;
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
size_t gen_field_index = type_struct_field->gen_index;
TypeStructField *field = &struct_type->data.structure.fields[i];
size_t gen_field_index = field->gen_index;
if (gen_field_index == SIZE_MAX) {
continue;
}
ZigType *field_type = type_struct_field->type_entry;
ZigType *field_type = field->type_entry;
// if the field is a function, actually the debug info should be a pointer.
ZigLLVMDIType *field_di_type;
@ -6914,13 +6973,13 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
uint64_t debug_align_in_bits;
uint64_t debug_offset_in_bits;
if (packed) {
debug_size_in_bits = type_struct_field->type_entry->size_in_bits;
debug_align_in_bits = 8 * type_struct_field->type_entry->abi_align;
debug_offset_in_bits = 8 * type_struct_field->offset + type_struct_field->bit_offset_in_host;
debug_size_in_bits = field->type_entry->size_in_bits;
debug_align_in_bits = 8 * field->type_entry->abi_align;
debug_offset_in_bits = 8 * field->offset + field->bit_offset_in_host;
} else {
debug_size_in_bits = 8 * get_store_size_bytes(field_type->size_in_bits);
debug_align_in_bits = 8 * field_type->abi_align;
debug_offset_in_bits = 8 * type_struct_field->offset;
debug_offset_in_bits = 8 * field->offset;
}
unsigned line;
if (decl_node != nullptr) {
@ -6930,7 +6989,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
line = 0;
}
di_element_types[debug_field_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
ZigLLVMTypeToScope(struct_type->llvm_di_type), buf_ptr(type_struct_field->name),
ZigLLVMTypeToScope(struct_type->llvm_di_type), buf_ptr(field->name),
di_file, line,
debug_size_in_bits,
debug_align_in_bits,
@ -7171,7 +7230,7 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta
union_type->data.unionation.resolve_status = ResolveStatusLLVMFull;
}
static void resolve_llvm_types_pointer(CodeGen *g, ZigType *type) {
static void resolve_llvm_types_pointer(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
if (type->llvm_di_type != nullptr) return;
if (!type_has_bits(type)) {
@ -7200,7 +7259,7 @@ static void resolve_llvm_types_pointer(CodeGen *g, ZigType *type) {
uint64_t debug_align_in_bits = 8*type->abi_align;
type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, elem_type->llvm_di_type,
debug_size_in_bits, debug_align_in_bits, buf_ptr(&type->name));
assertNoError(type_resolve(g, elem_type, ResolveStatusLLVMFull));
assertNoError(type_resolve(g, elem_type, wanted_resolve_status));
} else {
ZigType *host_int_type = get_int_type(g, false, type->data.pointer.host_int_bytes * 8);
LLVMTypeRef host_int_llvm_type = get_llvm_type(g, host_int_type);
@ -7326,10 +7385,17 @@ static void resolve_llvm_types_error_union(CodeGen *g, ZigType *type) {
} else {
LLVMTypeRef err_set_llvm_type = get_llvm_type(g, err_set_type);
LLVMTypeRef payload_llvm_type = get_llvm_type(g, payload_type);
LLVMTypeRef elem_types[2];
LLVMTypeRef elem_types[3];
elem_types[err_union_err_index] = err_set_llvm_type;
elem_types[err_union_payload_index] = payload_llvm_type;
type->llvm_type = LLVMStructType(elem_types, 2, false);
if (LLVMABISizeOfType(g->target_data_ref, type->llvm_type) != type->abi_size) {
// we need to do our own padding
type->data.error_union.pad_llvm_type = LLVMArrayType(LLVMInt8Type(), type->data.error_union.pad_bytes);
elem_types[2] = type->data.error_union.pad_llvm_type;
type->llvm_type = LLVMStructType(elem_types, 3, false);
}
ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
ZigLLVMDIFile *di_file = nullptr;
@ -7511,6 +7577,7 @@ static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) {
}
void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn) {
Error err;
if (fn->raw_di_type != nullptr) return;
ZigType *fn_type = fn->type_entry;
@ -7529,8 +7596,10 @@ void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn) {
ZigType *frame_type = get_coro_frame_type(g, fn);
ZigType *ptr_type = get_pointer_to_type(g, frame_type, false);
gen_param_types.append(get_llvm_type(g, ptr_type));
param_di_types.append(get_llvm_di_type(g, ptr_type));
if ((err = type_resolve(g, ptr_type, ResolveStatusLLVMFwdDecl)))
zig_unreachable();
gen_param_types.append(ptr_type->llvm_type);
param_di_types.append(ptr_type->llvm_di_type);
// this parameter is used to pass the result pointer when await completes
gen_param_types.append(get_llvm_type(g, g->builtin_types.entry_usize));
@ -7726,7 +7795,7 @@ static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_r
case ZigTypeIdUnion:
return resolve_llvm_types_union(g, type, wanted_resolve_status);
case ZigTypeIdPointer:
return resolve_llvm_types_pointer(g, type);
return resolve_llvm_types_pointer(g, type, wanted_resolve_status);
case ZigTypeIdInt:
return resolve_llvm_types_integer(g, type);
case ZigTypeIdOptional:

View File

@ -38,8 +38,6 @@ ZigType *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x);
ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payload_type);
ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry);
ZigType *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *full_name, Buf *bare_name);
ZigType *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[],
ZigType *field_types[], size_t field_count, unsigned min_abi_align);
ZigType *get_test_fn_type(CodeGen *g);
ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type);
bool handle_is_ptr(ZigType *type_entry);

View File

@ -3765,29 +3765,17 @@ static void render_async_spills(CodeGen *g) {
gen_var_debug_decl(g, var);
}
}
// label (grep this): [coro_frame_struct_layout]
if (codegen_fn_has_err_ret_tracing_stack(g, g->cur_fn, true)) {
async_var_index += 2;
}
ZigType *frame_type = g->cur_fn->frame_type->data.frame.locals_struct;
for (size_t alloca_i = 0; alloca_i < g->cur_fn->alloca_gen_list.length; alloca_i += 1) {
IrInstructionAllocaGen *instruction = g->cur_fn->alloca_gen_list.at(alloca_i);
ZigType *ptr_type = instruction->base.value.type;
assert(ptr_type->id == ZigTypeIdPointer);
ZigType *child_type = ptr_type->data.pointer.child_type;
if (!type_has_bits(child_type))
if (instruction->field_index == SIZE_MAX)
continue;
if (instruction->base.ref_count == 0)
continue;
if (instruction->base.value.special != ConstValSpecialRuntime) {
if (const_ptr_pointee(nullptr, g, &instruction->base.value, nullptr)->special !=
ConstValSpecialRuntime)
{
continue;
}
}
instruction->base.llvm_value = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, async_var_index,
size_t gen_index = frame_type->data.structure.fields[instruction->field_index].gen_index;
instruction->base.llvm_value = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, gen_index,
instruction->name_hint);
async_var_index += 1;
}
}
@ -6363,6 +6351,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
break;
}
if ((err = type_resolve(g, type_entry, ResolveStatusLLVMFull)))
zig_unreachable();
switch (type_entry->id) {
case ZigTypeIdInt:
return bigint_to_llvm_const(get_llvm_type(g, type_entry), &const_val->data.x_bigint);
@ -6434,6 +6425,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
LLVMValueRef *fields = allocate<LLVMValueRef>(type_entry->data.structure.gen_field_count);
size_t src_field_count = type_entry->data.structure.src_field_count;
bool make_unnamed_struct = false;
assert(type_entry->data.structure.resolve_status == ResolveStatusLLVMFull);
if (type_entry->data.structure.layout == ContainerLayoutPacked) {
size_t src_field_index = 0;
while (src_field_index < src_field_count) {
@ -6503,6 +6495,22 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
LLVMValueRef val = gen_const_val(g, field_val, "");
fields[type_struct_field->gen_index] = val;
make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, field_val->type, val);
size_t end_pad_gen_index = (i + 1 < src_field_count) ?
type_entry->data.structure.fields[i + 1].gen_index :
type_entry->data.structure.gen_field_count;
size_t next_offset = (i + 1 < src_field_count) ?
type_entry->data.structure.fields[i + 1].offset : type_entry->abi_size;
if (end_pad_gen_index != SIZE_MAX) {
for (size_t gen_i = type_struct_field->gen_index + 1; gen_i < end_pad_gen_index;
gen_i += 1)
{
size_t pad_bytes = next_offset -
(type_struct_field->offset + type_struct_field->type_entry->abi_size);
LLVMTypeRef llvm_array_type = LLVMArrayType(LLVMInt8Type(), pad_bytes);
fields[gen_i] = LLVMGetUndef(llvm_array_type);
}
}
}
}
if (make_unnamed_struct) {
@ -6690,13 +6698,18 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
err_payload_value = gen_const_val(g, payload_val, "");
make_unnamed_struct = is_llvm_value_unnamed_type(g, payload_val->type, err_payload_value);
}
LLVMValueRef fields[2];
LLVMValueRef fields[3];
fields[err_union_err_index] = err_tag_value;
fields[err_union_payload_index] = err_payload_value;
size_t field_count = 2;
if (type_entry->data.error_union.pad_llvm_type != nullptr) {
fields[2] = LLVMGetUndef(type_entry->data.error_union.pad_llvm_type);
field_count = 3;
}
if (make_unnamed_struct) {
return LLVMConstStruct(fields, 2, false);
return LLVMConstStruct(fields, field_count, false);
} else {
return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, 2);
return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, field_count);
}
}
}
@ -7139,6 +7152,7 @@ static void do_code_gen(CodeGen *g) {
}
if (is_async) {
(void)get_llvm_type(g, fn_table_entry->frame_type);
g->cur_resume_block_count = 0;
LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;

View File

@ -307,11 +307,9 @@ test "std.event.Channel" {
// https://github.com/ziglang/zig/issues/1908
if (builtin.single_threaded) return error.SkipZigTest;
const allocator = std.heap.direct_allocator;
var loop: Loop = undefined;
// TODO make a multi threaded test
try loop.initSingleThreaded(allocator);
try loop.initSingleThreaded(std.heap.direct_allocator);
defer loop.deinit();
const channel = try Channel(i32).create(&loop, 0);

View File

@ -228,3 +228,65 @@ test "alignment of extern() void" {
}
extern fn nothing() void {}
test "return error union with 128-bit integer" {
expect(3 == try give());
}
fn give() anyerror!u128 {
return 3;
}
test "alignment of >= 128-bit integer type" {
expect(@alignOf(u128) == 16);
expect(@alignOf(u129) == 16);
}
test "alignment of struct with 128-bit field" {
expect(@alignOf(struct {
x: u128,
}) == 16);
comptime {
expect(@alignOf(struct {
x: u128,
}) == 16);
}
}
test "size of extern struct with 128-bit field" {
expect(@sizeOf(extern struct {
x: u128,
y: u8,
}) == 32);
comptime {
expect(@sizeOf(extern struct {
x: u128,
y: u8,
}) == 32);
}
}
const DefaultAligned = struct {
nevermind: u32,
badguy: i128,
};
test "read 128-bit field from default aligned struct in stack memory" {
var default_aligned = DefaultAligned{
.nevermind = 1,
.badguy = 12,
};
expect((@ptrToInt(&default_aligned.badguy) % 16) == 0);
expect(12 == default_aligned.badguy);
}
var default_aligned_global = DefaultAligned{
.nevermind = 1,
.badguy = 12,
};
test "read 128-bit field from default aligned struct in global memory" {
expect((@ptrToInt(&default_aligned_global.badguy) % 16) == 0);
expect(12 == default_aligned_global.badguy);
}