Commit f1b91bb41b

Jimmi Holst Christensen <jhc@dismail.de>
2022-01-20 22:46:15
c backend: Implement aligning fields and local/global variables
There are some restrictions here. - We either need C11 or a compiler that supports the aligned attribute - We cannot provide align less than the type's natural C alignment.
1 parent 7287c74
Changed files (6)
src/codegen/c.zig
@@ -121,7 +121,13 @@ pub const Function = struct {
                 const decl_c_value = f.allocLocalValue();
                 gop.value_ptr.* = decl_c_value;
                 try writer.writeAll("static ");
-                try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .Const);
+                try f.object.dg.renderTypeAndName(
+                    writer,
+                    ty,
+                    decl_c_value,
+                    .Const,
+                    Value.initTag(.abi_align_default),
+                );
                 try writer.writeAll(" = ");
                 try f.object.dg.renderValue(writer, ty, val);
                 try writer.writeAll(";\n ");
@@ -142,8 +148,18 @@ pub const Function = struct {
     }
 
     fn allocLocal(f: *Function, ty: Type, mutability: Mutability) !CValue {
+        return f.allocAlignedLocal(ty, mutability, Value.initTag(.abi_align_default));
+    }
+
+    fn allocAlignedLocal(f: *Function, ty: Type, mutability: Mutability, alignment: Value) !CValue {
         const local_value = f.allocLocalValue();
-        try f.object.dg.renderTypeAndName(f.object.writer(), ty, local_value, mutability);
+        try f.object.dg.renderTypeAndName(
+            f.object.writer(),
+            ty,
+            local_value,
+            mutability,
+            alignment,
+        );
         return local_value;
     }
 
@@ -711,9 +727,11 @@ pub const DeclGen = struct {
             while (it.next()) |entry| {
                 const field_ty = entry.value_ptr.ty;
                 if (!field_ty.hasCodeGenBits()) continue;
+
+                const alignment = entry.value_ptr.abi_align;
                 const name: CValue = .{ .bytes = entry.key_ptr.* };
                 try buffer.append(' ');
-                try dg.renderTypeAndName(buffer.writer(), field_ty, name, .Mut);
+                try dg.renderTypeAndName(buffer.writer(), field_ty, name, .Mut, alignment);
                 try buffer.appendSlice(";\n");
             }
         }
@@ -950,6 +968,7 @@ pub const DeclGen = struct {
         ty: Type,
         name: CValue,
         mutability: Mutability,
+        alignment: Value,
     ) error{ OutOfMemory, AnalysisFail }!void {
         var suffix = std.ArrayList(u8).init(dg.gpa);
         defer suffix.deinit();
@@ -962,6 +981,8 @@ pub const DeclGen = struct {
             render_ty = render_ty.elemType();
         }
 
+        if (alignment.tag() != .abi_align_default and alignment.tag() != .null_value)
+            try w.print("ZIG_ALIGN({}) ", .{alignment.toUnsignedInt()});
         try dg.renderType(w, render_ty);
 
         const const_prefix = switch (mutability) {
@@ -1118,7 +1139,7 @@ pub fn genDecl(o: *Object) !void {
         // https://github.com/ziglang/zig/issues/7582
 
         const decl_c_value: CValue = .{ .decl = o.dg.decl };
-        try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut);
+        try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.align_val);
 
         try writer.writeAll(" = ");
         try o.dg.renderValue(writer, tv.ty, tv.val);
@@ -1460,8 +1481,16 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
         return CValue{ .bytes = literal };
     }
 
+    const target = f.object.dg.module.getTarget();
+    const alignment = inst_ty.ptrAlignment(target);
+    var payload = Value.Payload.U64{
+        .base = .{ .tag = .int_u64 },
+        .data = alignment,
+    };
+    const alignment_value = Value.initPayload(&payload.base);
+
     // First line: the variable used as data storage.
-    const local = try f.allocLocal(elem_type, mutability);
+    const local = try f.allocAlignedLocal(elem_type, mutability, alignment_value);
     try writer.writeAll(";\n");
 
     // Arrays are already pointers so they don't need to be referenced.
src/link/C/zig.h
@@ -26,6 +26,15 @@
 #define ZIG_RESTRICT
 #endif
 
+#if __STDC_VERSION__ >= 201112L
+#include <stdalign.h>
+#define ZIG_ALIGN(alignment) alignas(alignment)
+#elif defined(__GNUC__)
+#define ZIG_ALIGN(alignment) __attribute__((aligned(alignment)))
+#else
+#define ZIG_ALIGN(alignment) zig_compile_error("the C compiler being used does not support aligning variables")
+#endif
+
 #if __STDC_VERSION__ >= 199901L
 #include <stdbool.h>
 #else
test/behavior/align.zig
@@ -163,3 +163,21 @@ test "return error union with 128-bit integer" {
 fn give() anyerror!u128 {
     return 3;
 }
+
+test "page aligned array on stack" {
+    if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm or
+        builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
+
+    // Large alignment value to make it hard to accidentally pass.
+    var array align(0x1000) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
+    var number1: u8 align(16) = 42;
+    var number2: u8 align(16) = 43;
+
+    try expect(@ptrToInt(&array[0]) & 0xFFF == 0);
+    try expect(array[3] == 4);
+
+    try expect(@truncate(u4, @ptrToInt(&number1)) == 0);
+    try expect(@truncate(u4, @ptrToInt(&number2)) == 0);
+    try expect(number1 == 42);
+    try expect(number2 == 43);
+}
test/behavior/align_llvm.zig
@@ -1,19 +0,0 @@
-const std = @import("std");
-const expect = std.testing.expect;
-const builtin = @import("builtin");
-const native_arch = builtin.target.cpu.arch;
-
-test "page aligned array on stack" {
-    // Large alignment value to make it hard to accidentally pass.
-    var array align(0x1000) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
-    var number1: u8 align(16) = 42;
-    var number2: u8 align(16) = 43;
-
-    try expect(@ptrToInt(&array[0]) & 0xFFF == 0);
-    try expect(array[3] == 4);
-
-    try expect(@truncate(u4, @ptrToInt(&number1)) == 0);
-    try expect(@truncate(u4, @ptrToInt(&number2)) == 0);
-    try expect(number1 == 42);
-    try expect(number2 == 43);
-}
test/behavior/struct.zig
@@ -201,6 +201,8 @@ test "struct field init with catch" {
 }
 
 test "packed struct field alignment" {
+    if (builtin.object_format == .c) return error.SkipZigTest;
+
     const Stage1 = struct {
         var baz: packed struct {
             a: u32,
test/behavior.zig
@@ -71,7 +71,6 @@ test {
 
             if (builtin.zig_backend != .stage2_c) {
                 // Tests that pass for stage1 and the llvm backend.
-                _ = @import("behavior/align_llvm.zig");
                 _ = @import("behavior/alignof.zig");
                 _ = @import("behavior/array_llvm.zig");
                 _ = @import("behavior/atomics.zig");