Commit cf5108f222

Andrew Kelley <superjoe30@gmail.com>
2017-02-21 20:22:23
correct size of types for packed structs
with byte aligned but non-power-of-2 fields such as 24
1 parent 4709fe1
Changed files (4)
src/analyze.cpp
@@ -254,15 +254,21 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) {
 
 uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry) {
     assert(type_is_complete(type_entry));
-    if (type_has_bits(type_entry)) {
-        return LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref);
-    } else {
+    TypeTableEntry *canon_type = get_underlying_type(type_entry);
+
+    if (!type_has_bits(type_entry))
         return 0;
+
+    if (canon_type->id == TypeTableEntryIdStruct && canon_type->data.structure.layout == ContainerLayoutPacked) {
+        uint64_t size_in_bits = type_size_bits(g, type_entry);
+        return (size_in_bits + 7) / 8;
     }
+
+    return LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref);
 }
 
-// This has to do with packed structs
 uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry) {
+    assert(type_is_complete(type_entry));
     TypeTableEntry *canon_type = get_underlying_type(type_entry);
 
     if (!type_has_bits(type_entry))
@@ -531,6 +537,14 @@ TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t
 
     ensure_complete_type(g, child_type);
 
+    TypeTableEntry *canon_child_type = get_underlying_type(child_type);
+    if (canon_child_type->id == TypeTableEntryIdStruct &&
+        canon_child_type->data.structure.layout == ContainerLayoutPacked &&
+        type_size_bits(g, canon_child_type) != 8 * type_size(g, canon_child_type))
+    {
+        zig_panic("TODO array of packed struct with unaligned size");
+    }
+
     TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray);
     entry->zero_bits = (array_size == 0) || child_type->zero_bits;
 
src/ir.cpp
@@ -9566,10 +9566,6 @@ static TypeTableEntry *ir_analyze_instruction_set_fn_visible(IrAnalyze *ira,
     return ira->codegen->builtin_types.entry_void;
 }
 
-static bool is_power_of_2(uint64_t x) {
-    return x != 0 && ((x & (~x + 1)) == x);
-}
-
 static TypeTableEntry *ir_analyze_instruction_set_global_align(IrAnalyze *ira,
         IrInstructionSetGlobalAlign *instruction)
 {
src/util.hpp
@@ -99,6 +99,10 @@ static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str)
     return memcmp(mem, str, mem_len) == 0;
 }
 
+static inline bool is_power_of_2(uint64_t x) {
+    return x != 0 && ((x & (~x + 1)) == x);
+}
+
 uint32_t int_hash(int i);
 bool int_eq(int a, int b);
 uint32_t uint64_hash(uint64_t i);
test/cases/struct.zig
@@ -270,3 +270,51 @@ fn getB(data: &const BitField1) -> u3 {
 fn getC(data: &const BitField1) -> u2 {
     return data.c;
 }
+
+const u24 = @intType(false, 24);
+const Foo24Bits = packed struct {
+    field: u24,
+};
+const Foo96Bits = packed struct {
+    a: u24,
+    b: u24,
+    c: u24,
+    d: u24,
+};
+
+fn packedStruct24Bits() {
+    @setFnTest(this);
+
+    comptime assert(@sizeOf(Foo24Bits) == 3);
+    comptime assert(@sizeOf(Foo96Bits) == 12);
+
+    var value = Foo96Bits {
+        .a = 0,
+        .b = 0,
+        .c = 0,
+        .d = 0,
+    };
+    value.a += 1;
+    assert(value.a == 1);
+    assert(value.b == 0);
+    assert(value.c == 0);
+    assert(value.d == 0);
+
+    value.b += 1;
+    assert(value.a == 1);
+    assert(value.b == 1);
+    assert(value.c == 0);
+    assert(value.d == 0);
+
+    value.c += 1;
+    assert(value.a == 1);
+    assert(value.b == 1);
+    assert(value.c == 1);
+    assert(value.d == 0);
+
+    value.d += 1;
+    assert(value.a == 1);
+    assert(value.b == 1);
+    assert(value.c == 1);
+    assert(value.d == 1);
+}