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) {
858858 entry->data.structure.fields[slice_ptr_index]->type_entry = ptr_type;
859859 entry->data.structure.fields[slice_ptr_index]->src_index = slice_ptr_index;
860860 entry->data.structure.fields[slice_ptr_index]->gen_index = 0;
861 entry->data.structure.fields[slice_ptr_index]->offset = 0;
861862 entry->data.structure.fields[slice_len_index]->name = len_field_name;
862863 entry->data.structure.fields[slice_len_index]->type_entry = g->builtin_types.entry_usize;
863864 entry->data.structure.fields[slice_len_index]->src_index = slice_len_index;
864865 entry->data.structure.fields[slice_len_index]->gen_index = 1;
866 entry->data.structure.fields[slice_len_index]->offset = ptr_type->abi_size;
865867
866868 entry->data.structure.fields_by_name.put(ptr_field_name, entry->data.structure.fields[slice_ptr_index]);
867869 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
71407140 ZigType *type_entry = const_val->type;
71417141 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) {
71447148 case ConstValSpecialLazy:
7145 if ((err = ir_resolve_lazy(g, nullptr, const_val))) {
7146 codegen_report_errors_and_exit(g);
7147 }
7148 goto check;
71497149 case ConstValSpecialRuntime:
71507150 zig_unreachable();
71517151 case ConstValSpecialUndef:
......@@ -7222,12 +7222,26 @@ check: switch (const_val->special) {
72227222
72237223 make_unnamed_struct = false;
72247224 }
7225
72257226 LLVMValueRef fields[] = {
72267227 child_val,
72277228 maybe_val,
7229 nullptr,
72287230 };
72297231 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;
72317245 } else {
72327246 return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, 2);
72337247 }
......@@ -7316,32 +7330,50 @@ check: switch (const_val->special) {
73167330 continue;
73177331 }
73187332 ZigValue *field_val = const_val->data.x_struct.fields[i];
7319 assert(field_val->type != nullptr);
7320 if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val,
7321 type_struct_field->type_entry)))
7322 {
7333 ZigType *field_type = field_val->type;
7334 assert(field_type != nullptr);
7335 if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val, field_type))) {
73237336 zig_unreachable();
73247337 }
73257338
73267339 LLVMValueRef val = gen_const_val(g, field_val, "");
7327 fields[type_struct_field->gen_index] = val;
7328 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, field_val->type, val);
7329
7330 size_t end_pad_gen_index = (i + 1 < src_field_count) ?
7331 type_entry->data.structure.fields[i + 1]->gen_index :
7332 type_entry->data.structure.gen_field_count;
7333 size_t next_offset = (i + 1 < src_field_count) ?
7334 type_entry->data.structure.fields[i + 1]->offset : type_entry->abi_size;
7335 if (end_pad_gen_index != SIZE_MAX) {
7336 for (size_t gen_i = type_struct_field->gen_index + 1; gen_i < end_pad_gen_index;
7337 gen_i += 1)
7338 {
7339 size_t pad_bytes = next_offset -
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);
7340 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, field_type, val);
7341
7342 // Find the next runtime field
7343 size_t next_rt_gen_index = type_entry->data.structure.gen_field_count;
7344 size_t next_offset = type_entry->abi_size;
7345 for (size_t j = i + 1; j < src_field_count; j++) {
7346 const size_t index = type_entry->data.structure.fields[j]->gen_index;
7347 const size_t offset = type_entry->data.structure.fields[j]->offset;
7348
7349 if (index != SIZE_MAX) {
7350 next_rt_gen_index = index;
7351 next_offset = offset;
7352 break;
73437353 }
73447354 }
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 }
73457377 }
73467378 }
73477379 if (make_unnamed_struct) {
test/stage1/behavior/optional.zig+36-1
......@@ -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
36pub const EmptyStruct = struct {};
47
......@@ -198,3 +201,35 @@ test "0-bit child type coerced to optional" {
198201 S.doTheTest();
199202 comptime S.doTheTest();
200203}
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" {
825825 x = T{ .children = .{&x} };
826826 expect(x.children[0] == &x);
827827}
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}