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.
This commit is contained in:
Jimmi Holst Christensen 2022-01-20 22:46:15 +01:00 committed by Andrew Kelley
parent 7287c7482a
commit f1b91bb41b
6 changed files with 63 additions and 25 deletions

View File

@ -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.

View File

@ -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

View File

@ -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");

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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,