authorgravatar for jhc@dismail.deJimmi Holst Christensen <jhc@dismail.de> 2022-01-20 22:46:15+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-23 17:23:55-05:00
logf1b91bb41b2d810ecabf4c69cad91b24b3846b77
tree59416bc00443451485832f6d1a1ce4b3d3357c58
parent7287c7482a2c694c7c7f56b9f7c1744a7ae7905f

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.

6 files changed, 63 insertions(+), 25 deletions(-)

src/codegen/c.zig+34-5
......@@ -121,7 +121,13 @@ pub const Function = struct {
121121 const decl_c_value = f.allocLocalValue();
122122 gop.value_ptr.* = decl_c_value;
123123 try writer.writeAll("static ");
124 try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .Const);
124 try f.object.dg.renderTypeAndName(
125 writer,
126 ty,
127 decl_c_value,
128 .Const,
129 Value.initTag(.abi_align_default),
130 );
125131 try writer.writeAll(" = ");
126132 try f.object.dg.renderValue(writer, ty, val);
127133 try writer.writeAll(";\n ");
......@@ -142,8 +148,18 @@ pub const Function = struct {
142148 }
143149
144150 fn allocLocal(f: *Function, ty: Type, mutability: Mutability) !CValue {
151 return f.allocAlignedLocal(ty, mutability, Value.initTag(.abi_align_default));
152 }
153
154 fn allocAlignedLocal(f: *Function, ty: Type, mutability: Mutability, alignment: Value) !CValue {
145155 const local_value = f.allocLocalValue();
146 try f.object.dg.renderTypeAndName(f.object.writer(), ty, local_value, mutability);
156 try f.object.dg.renderTypeAndName(
157 f.object.writer(),
158 ty,
159 local_value,
160 mutability,
161 alignment,
162 );
147163 return local_value;
148164 }
149165
......@@ -711,9 +727,11 @@ pub const DeclGen = struct {
711727 while (it.next()) |entry| {
712728 const field_ty = entry.value_ptr.ty;
713729 if (!field_ty.hasCodeGenBits()) continue;
730
731 const alignment = entry.value_ptr.abi_align;
714732 const name: CValue = .{ .bytes = entry.key_ptr.* };
715733 try buffer.append(' ');
716 try dg.renderTypeAndName(buffer.writer(), field_ty, name, .Mut);
734 try dg.renderTypeAndName(buffer.writer(), field_ty, name, .Mut, alignment);
717735 try buffer.appendSlice(";\n");
718736 }
719737 }
......@@ -950,6 +968,7 @@ pub const DeclGen = struct {
950968 ty: Type,
951969 name: CValue,
952970 mutability: Mutability,
971 alignment: Value,
953972 ) error{ OutOfMemory, AnalysisFail }!void {
954973 var suffix = std.ArrayList(u8).init(dg.gpa);
955974 defer suffix.deinit();
......@@ -962,6 +981,8 @@ pub const DeclGen = struct {
962981 render_ty = render_ty.elemType();
963982 }
964983
984 if (alignment.tag() != .abi_align_default and alignment.tag() != .null_value)
985 try w.print("ZIG_ALIGN({}) ", .{alignment.toUnsignedInt()});
965986 try dg.renderType(w, render_ty);
966987
967988 const const_prefix = switch (mutability) {
......@@ -1118,7 +1139,7 @@ pub fn genDecl(o: *Object) !void {
11181139 // https://github.com/ziglang/zig/issues/7582
11191140
11201141 const decl_c_value: CValue = .{ .decl = o.dg.decl };
1121 try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut);
1142 try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.align_val);
11221143
11231144 try writer.writeAll(" = ");
11241145 try o.dg.renderValue(writer, tv.ty, tv.val);
......@@ -1460,8 +1481,16 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
14601481 return CValue{ .bytes = literal };
14611482 }
14621483
1484 const target = f.object.dg.module.getTarget();
1485 const alignment = inst_ty.ptrAlignment(target);
1486 var payload = Value.Payload.U64{
1487 .base = .{ .tag = .int_u64 },
1488 .data = alignment,
1489 };
1490 const alignment_value = Value.initPayload(&payload.base);
1491
14631492 // First line: the variable used as data storage.
1464 const local = try f.allocLocal(elem_type, mutability);
1493 const local = try f.allocAlignedLocal(elem_type, mutability, alignment_value);
14651494 try writer.writeAll(";\n");
14661495
14671496 // Arrays are already pointers so they don't need to be referenced.
src/link/C/zig.h+9
......@@ -26,6 +26,15 @@
2626#define ZIG_RESTRICT
2727#endif
2828
29#if __STDC_VERSION__ >= 201112L
30#include <stdalign.h>
31#define ZIG_ALIGN(alignment) alignas(alignment)
32#elif defined(__GNUC__)
33#define ZIG_ALIGN(alignment) __attribute__((aligned(alignment)))
34#else
35#define ZIG_ALIGN(alignment) zig_compile_error("the C compiler being used does not support aligning variables")
36#endif
37
2938#if __STDC_VERSION__ >= 199901L
3039#include <stdbool.h>
3140#else
test/behavior.zig-1
......@@ -71,7 +71,6 @@ test {
7171
7272 if (builtin.zig_backend != .stage2_c) {
7373 // Tests that pass for stage1 and the llvm backend.
74 _ = @import("behavior/align_llvm.zig");
7574 _ = @import("behavior/alignof.zig");
7675 _ = @import("behavior/array_llvm.zig");
7776 _ = @import("behavior/atomics.zig");
test/behavior/align.zig+18
......@@ -163,3 +163,21 @@ test "return error union with 128-bit integer" {
163163fn give() anyerror!u128 {
164164 return 3;
165165}
166
167test "page aligned array on stack" {
168 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm or
169 builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
170
171 // Large alignment value to make it hard to accidentally pass.
172 var array align(0x1000) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
173 var number1: u8 align(16) = 42;
174 var number2: u8 align(16) = 43;
175
176 try expect(@ptrToInt(&array[0]) & 0xFFF == 0);
177 try expect(array[3] == 4);
178
179 try expect(@truncate(u4, @ptrToInt(&number1)) == 0);
180 try expect(@truncate(u4, @ptrToInt(&number2)) == 0);
181 try expect(number1 == 42);
182 try expect(number2 == 43);
183}
test/behavior/align_llvm.zig deleted-19
......@@ -1,19 +0,0 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const builtin = @import("builtin");
4const native_arch = builtin.target.cpu.arch;
5
6test "page aligned array on stack" {
7 // Large alignment value to make it hard to accidentally pass.
8 var array align(0x1000) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
9 var number1: u8 align(16) = 42;
10 var number2: u8 align(16) = 43;
11
12 try expect(@ptrToInt(&array[0]) & 0xFFF == 0);
13 try expect(array[3] == 4);
14
15 try expect(@truncate(u4, @ptrToInt(&number1)) == 0);
16 try expect(@truncate(u4, @ptrToInt(&number2)) == 0);
17 try expect(number1 == 42);
18 try expect(number2 == 43);
19}
test/behavior/struct.zig+2
......@@ -201,6 +201,8 @@ test "struct field init with catch" {
201201}
202202
203203test "packed struct field alignment" {
204 if (builtin.object_format == .c) return error.SkipZigTest;
205
204206 const Stage1 = struct {
205207 var baz: packed struct {
206208 a: u32,