From f1b91bb41b2d810ecabf4c69cad91b24b3846b77 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Thu, 20 Jan 2022 22:46:15 +0100 Subject: [PATCH] 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. --- src/codegen/c.zig | 39 +++++++++++++++++++++++++++++++----- src/link/C/zig.h | 9 +++++++++ test/behavior.zig | 1 - test/behavior/align.zig | 18 +++++++++++++++++ test/behavior/align_llvm.zig | 19 ------------------ test/behavior/struct.zig | 2 ++ 6 files changed, 63 insertions(+), 25 deletions(-) delete mode 100644 test/behavior/align_llvm.zig diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 42778bccf8..de1636cd96 100644 --- a/src/codegen/c.zig +++ b/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. diff --git a/src/link/C/zig.h b/src/link/C/zig.h index eeda93894b..4a725ca678 100644 --- a/src/link/C/zig.h +++ b/src/link/C/zig.h @@ -26,6 +26,15 @@ #define ZIG_RESTRICT #endif +#if __STDC_VERSION__ >= 201112L +#include +#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 #else diff --git a/test/behavior.zig b/test/behavior.zig index 0c997f0f6d..9ea0df6fc2 100644 --- a/test/behavior.zig +++ b/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"); diff --git a/test/behavior/align.zig b/test/behavior/align.zig index dfa8d895c5..26a914576c 100644 --- a/test/behavior/align.zig +++ b/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); +} diff --git a/test/behavior/align_llvm.zig b/test/behavior/align_llvm.zig deleted file mode 100644 index e87c109b4d..0000000000 --- a/test/behavior/align_llvm.zig +++ /dev/null @@ -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); -} diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 40310f8add..7ff67e1c90 100644 --- a/test/behavior/struct.zig +++ b/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,