Commit 1c8cd268be

LemonBoy <thatlemon@gmail.com>
2020-11-16 17:55:02
stage1: Fix asyncCall with non-abi-aligned arguments
Make the code used to calculate the variable slot index into the frame match what's done during the structure layout calculation. Prevents a few nasty LLVM errors when such types are passed around.
1 parent eea4cd2
Changed files (2)
src
test
stage1
behavior
src/stage1/codegen.cpp
@@ -242,14 +242,24 @@ struct CalcLLVMFieldIndex {
 static void calc_llvm_field_index_add(CodeGen *g, CalcLLVMFieldIndex *calc, ZigType *ty) {
     if (!type_has_bits(g, ty)) return;
     uint32_t ty_align = get_abi_alignment(g, ty);
+
     if (calc->offset % ty_align != 0) {
         uint32_t llvm_align = LLVMABIAlignmentOfType(g->target_data_ref, get_llvm_type(g, ty));
-        if (llvm_align >= ty_align) {
-            ty_align = llvm_align; // llvm's padding is sufficient
-        } else if (calc->offset) {
-            calc->field_index += 1; // zig will insert an extra padding field here
-        }
-        calc->offset += ty_align - (calc->offset % ty_align); // padding bytes
+
+        // Alignment according to Zig.
+        uint32_t adj_offset = calc->offset + (ty_align - (calc->offset % ty_align));
+        // Alignment according to LLVM.
+        uint32_t adj_llvm_offset = (calc->offset % llvm_align) ?
+                calc->offset + (llvm_align - (calc->offset % llvm_align)) :
+                calc->offset;
+        // Cannot under-align structure fields.
+        assert(adj_offset >= adj_llvm_offset);
+
+        // Zig will insert an extra padding field here.
+        if (adj_offset != adj_llvm_offset)
+            calc->field_index += 1;
+
+        calc->offset = adj_offset;
     }
     calc->offset += ty->abi_size;
     calc->field_index += 1;
test/stage1/behavior/async_fn.zig
@@ -1589,3 +1589,22 @@ test "@asyncCall with pass-by-value arguments" {
         F2,
     });
 }
+
+test "@asyncCall with arguments having non-standard alignment" {
+    const F0: u64 = 0xbeefbeef;
+    const F1: u64 = 0xf00df00df00df00d;
+
+    const S = struct {
+        pub fn f(_fill0: u32, s: struct { x: u64 align(16) }, _fill1: u64) callconv(.Async) void {
+            // The compiler inserts extra alignment for s, check that the
+            // generated code picks the right slot for fill1.
+            expectEqual(F0, _fill0);
+            expectEqual(F1, _fill1);
+        }
+    };
+
+    var buffer: [1024]u8 align(@alignOf(@Frame(S.f))) = undefined;
+    // The function pointer must not be comptime-known.
+    var t = S.f;
+    var frame_ptr = @asyncCall(&buffer, {}, t, .{ F0, undefined, F1 });
+}