authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-04-16 20:26:38+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-17 13:42:23-04:00
log8e96922f3118601dd30da23ba7db1440066784ab
treea47f640736e0be09a452e1d90bc64997f7794767
parenta4b1242f0aeaf785f6b05cb843bb2efc0e6e702d

stage1: Fix several bugs in constant generation

The codegen would sometimes change the LLVM type for some constants to an unnamed structure in order to accomodate extra padding. This is fine as long as the alignment of each field is still respected and it was not the case for structure types, leading to ill-formed constants being generated. Optional types suffer from this to a lower extent as their layout is quite lucky, the only missing piece was the tail padding. Closes #4530 Closes #4594 Closes #4295 Closes my will to live

4 files changed, 112 insertions(+), 27 deletions(-)

src/analyze.cpp+2
...@@ -858,10 +858,12 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {...@@ -858,10 +858,12 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
858 entry->data.structure.fields[slice_ptr_index]->type_entry = ptr_type;858 entry->data.structure.fields[slice_ptr_index]->type_entry = ptr_type;
859 entry->data.structure.fields[slice_ptr_index]->src_index = slice_ptr_index;859 entry->data.structure.fields[slice_ptr_index]->src_index = slice_ptr_index;
860 entry->data.structure.fields[slice_ptr_index]->gen_index = 0;860 entry->data.structure.fields[slice_ptr_index]->gen_index = 0;
861 entry->data.structure.fields[slice_ptr_index]->offset = 0;
861 entry->data.structure.fields[slice_len_index]->name = len_field_name;862 entry->data.structure.fields[slice_len_index]->name = len_field_name;
862 entry->data.structure.fields[slice_len_index]->type_entry = g->builtin_types.entry_usize;863 entry->data.structure.fields[slice_len_index]->type_entry = g->builtin_types.entry_usize;
863 entry->data.structure.fields[slice_len_index]->src_index = slice_len_index;864 entry->data.structure.fields[slice_len_index]->src_index = slice_len_index;
864 entry->data.structure.fields[slice_len_index]->gen_index = 1;865 entry->data.structure.fields[slice_len_index]->gen_index = 1;
866 entry->data.structure.fields[slice_len_index]->offset = ptr_type->abi_size;
865867
866 entry->data.structure.fields_by_name.put(ptr_field_name, entry->data.structure.fields[slice_ptr_index]);868 entry->data.structure.fields_by_name.put(ptr_field_name, entry->data.structure.fields[slice_ptr_index]);
867 entry->data.structure.fields_by_name.put(len_field_name, entry->data.structure.fields[slice_len_index]);869 entry->data.structure.fields_by_name.put(len_field_name, entry->data.structure.fields[slice_len_index]);
src/codegen.cpp+58-26
...@@ -7140,12 +7140,12 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n...@@ -7140,12 +7140,12 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n
7140 ZigType *type_entry = const_val->type;7140 ZigType *type_entry = const_val->type;
7141 assert(type_has_bits(g, type_entry));7141 assert(type_has_bits(g, type_entry));
71427142
7143check: switch (const_val->special) {7143 if (const_val->special == ConstValSpecialLazy &&
7144 (err = ir_resolve_lazy(g, nullptr, const_val)))
7145 codegen_report_errors_and_exit(g);
7146
7147 switch (const_val->special) {
7144 case ConstValSpecialLazy:7148 case ConstValSpecialLazy:
7145 if ((err = ir_resolve_lazy(g, nullptr, const_val))) {
7146 codegen_report_errors_and_exit(g);
7147 }
7148 goto check;
7149 case ConstValSpecialRuntime:7149 case ConstValSpecialRuntime:
7150 zig_unreachable();7150 zig_unreachable();
7151 case ConstValSpecialUndef:7151 case ConstValSpecialUndef:
...@@ -7222,12 +7222,26 @@ check: switch (const_val->special) {...@@ -7222,12 +7222,26 @@ check: switch (const_val->special) {
72227222
7223 make_unnamed_struct = false;7223 make_unnamed_struct = false;
7224 }7224 }
7225
7225 LLVMValueRef fields[] = {7226 LLVMValueRef fields[] = {
7226 child_val,7227 child_val,
7227 maybe_val,7228 maybe_val,
7229 nullptr,
7228 };7230 };
7229 if (make_unnamed_struct) {7231 if (make_unnamed_struct) {
7230 return LLVMConstStruct(fields, 2, false);7232 LLVMValueRef result = LLVMConstStruct(fields, 2, false);
7233 uint64_t last_field_offset = LLVMOffsetOfElement(g->target_data_ref, LLVMTypeOf(result), 1);
7234 uint64_t end_offset = last_field_offset +
7235 LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(fields[1]));
7236 uint64_t expected_sz = LLVMABISizeOfType(g->target_data_ref, get_llvm_type(g, type_entry));
7237 unsigned pad_sz = expected_sz - end_offset;
7238 if (pad_sz != 0) {
7239 fields[2] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz));
7240 result = LLVMConstStruct(fields, 3, false);
7241 }
7242 uint64_t actual_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(result));
7243 assert(actual_sz == expected_sz);
7244 return result;
7231 } else {7245 } else {
7232 return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, 2);7246 return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, 2);
7233 }7247 }
...@@ -7316,32 +7330,50 @@ check: switch (const_val->special) {...@@ -7316,32 +7330,50 @@ check: switch (const_val->special) {
7316 continue;7330 continue;
7317 }7331 }
7318 ZigValue *field_val = const_val->data.x_struct.fields[i];7332 ZigValue *field_val = const_val->data.x_struct.fields[i];
7319 assert(field_val->type != nullptr);7333 ZigType *field_type = field_val->type;
7320 if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val,7334 assert(field_type != nullptr);
7321 type_struct_field->type_entry)))7335 if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val, field_type))) {
7322 {
7323 zig_unreachable();7336 zig_unreachable();
7324 }7337 }
73257338
7326 LLVMValueRef val = gen_const_val(g, field_val, "");7339 LLVMValueRef val = gen_const_val(g, field_val, "");
7327 fields[type_struct_field->gen_index] = val;7340 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, field_type, val);
7328 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, field_val->type, val);7341
73297342 // Find the next runtime field
7330 size_t end_pad_gen_index = (i + 1 < src_field_count) ?7343 size_t next_rt_gen_index = type_entry->data.structure.gen_field_count;
7331 type_entry->data.structure.fields[i + 1]->gen_index :7344 size_t next_offset = type_entry->abi_size;
7332 type_entry->data.structure.gen_field_count;7345 for (size_t j = i + 1; j < src_field_count; j++) {
7333 size_t next_offset = (i + 1 < src_field_count) ?7346 const size_t index = type_entry->data.structure.fields[j]->gen_index;
7334 type_entry->data.structure.fields[i + 1]->offset : type_entry->abi_size;7347 const size_t offset = type_entry->data.structure.fields[j]->offset;
7335 if (end_pad_gen_index != SIZE_MAX) {7348
7336 for (size_t gen_i = type_struct_field->gen_index + 1; gen_i < end_pad_gen_index;7349 if (index != SIZE_MAX) {
7337 gen_i += 1)7350 next_rt_gen_index = index;
7338 {7351 next_offset = offset;
7339 size_t pad_bytes = next_offset -7352 break;
7340 (type_struct_field->offset + type_struct_field->type_entry->abi_size);
7341 LLVMTypeRef llvm_array_type = LLVMArrayType(LLVMInt8Type(), pad_bytes);
7342 fields[gen_i] = LLVMGetUndef(llvm_array_type);
7343 }7353 }
7344 }7354 }
7355
7356 // How much padding is needed to reach the next field
7357 const size_t pad_bytes = next_offset -
7358 (type_struct_field->offset + LLVMABISizeOfType(g->target_data_ref, LLVMTypeOf(val)));
7359 // Catch underflow
7360 assert((ssize_t)pad_bytes >= 0);
7361
7362 if (type_struct_field->gen_index + 1 != next_rt_gen_index) {
7363 // If there's a hole between this field and the next
7364 // we have an alignment gap to fill
7365 fields[type_struct_field->gen_index] = val;
7366 fields[type_struct_field->gen_index + 1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_bytes));
7367 } else if (pad_bytes != 0) {
7368 LLVMValueRef padded_val[] = {
7369 val,
7370 LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_bytes)),
7371 };
7372 fields[type_struct_field->gen_index] = LLVMConstStruct(padded_val, 2, true);
7373 make_unnamed_struct = true;
7374 } else {
7375 fields[type_struct_field->gen_index] = val;
7376 }
7345 }7377 }
7346 }7378 }
7347 if (make_unnamed_struct) {7379 if (make_unnamed_struct) {
test/stage1/behavior/optional.zig+36-1
...@@ -1,4 +1,7 @@...@@ -1,4 +1,7 @@
1const expect = @import("std").testing.expect;1const std = @import("std");
2const testing = std.testing;
3const expect = testing.expect;
4const expectEqual = testing.expectEqual;
25
3pub const EmptyStruct = struct {};6pub const EmptyStruct = struct {};
47
...@@ -198,3 +201,35 @@ test "0-bit child type coerced to optional" {...@@ -198,3 +201,35 @@ test "0-bit child type coerced to optional" {
198 S.doTheTest();201 S.doTheTest();
199 comptime S.doTheTest();202 comptime S.doTheTest();
200}203}
204
205test "array of optional unaligned types" {
206 const Enum = enum { one, two, three };
207
208 const SomeUnion = union(enum) {
209 Num: Enum,
210 Other: u32,
211 };
212
213 const values = [_]?SomeUnion{
214 SomeUnion{ .Num = .one },
215 SomeUnion{ .Num = .two },
216 SomeUnion{ .Num = .three },
217 SomeUnion{ .Num = .one },
218 SomeUnion{ .Num = .two },
219 SomeUnion{ .Num = .three },
220 };
221
222 // The index must be a runtime value
223 var i: usize = 0;
224 expectEqual(Enum.one, values[i].?.Num);
225 i += 1;
226 expectEqual(Enum.two, values[i].?.Num);
227 i += 1;
228 expectEqual(Enum.three, values[i].?.Num);
229 i += 1;
230 expectEqual(Enum.one, values[i].?.Num);
231 i += 1;
232 expectEqual(Enum.two, values[i].?.Num);
233 i += 1;
234 expectEqual(Enum.three, values[i].?.Num);
235}
test/stage1/behavior/struct.zig+16
...@@ -825,3 +825,19 @@ test "self-referencing struct via array member" {...@@ -825,3 +825,19 @@ test "self-referencing struct via array member" {
825 x = T{ .children = .{&x} };825 x = T{ .children = .{&x} };
826 expect(x.children[0] == &x);826 expect(x.children[0] == &x);
827}827}
828
829test "struct with union field" {
830 const Value = struct {
831 ref: u32 = 2,
832 kind: union(enum) {
833 None: usize,
834 Bool: bool,
835 },
836 };
837
838 var True = Value{
839 .kind = .{ .Bool = true },
840 };
841 expectEqual(@as(u32, 2), True.ref);
842 expectEqual(true, True.kind.Bool);
843}