Commit 12ff91c1c9

Andrew Kelley <andrew@ziglang.org>
2019-08-13 18:44:30
alignment of structs no longer depends on LLVM
fixes async function tests in optimized builds
1 parent 8a92899
Changed files (7)
src/all_types.hpp
@@ -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 {
src/analyze.cpp
@@ -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:
src/analyze.hpp
@@ -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);
src/codegen.cpp
@@ -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))
-            continue;
-        if (instruction->base.ref_count == 0)
+        if (instruction->field_index == SIZE_MAX)
             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;
std/event/channel.zig
@@ -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);
test/stage1/behavior/align.zig
@@ -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);
+}
BRANCH_TODO
@@ -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