aboutsummaryrefslogtreecommitdiff
path: root/src/codegen
diff options
context:
space:
mode:
authordrew <reserveblue@protonmail.com>2021-12-30 12:19:12 -0800
committerGitHub <noreply@github.com>2021-12-30 15:19:12 -0500
commit2f53406ad821c6fd8239551d3b812cd4100929bd (patch)
tree3ef2ae54516af3842c2c144879c5b7ac253b0913 /src/codegen
parent726ee671befd06b18fec90a01dabf2eca96b2d02 (diff)
downloadzig-2f53406ad821c6fd8239551d3b812cd4100929bd.tar.gz
zig-2f53406ad821c6fd8239551d3b812cd4100929bd.zip
CBE; implement airLoad and airStore for arrays (#10452)
Effectively a small continuation of #10152 This allows the for.zig behavior tests to pass. Unfortunately to fully test everything I had to move a lot of behavior tests from array.zig; most of them now pass (sorry @rainbowbismuth!) I'm also conflicted on how I store constants into arrays because it's kind of stupid; array's can't be re-initialized using the same syntax, so instead of initializing each element, a new array is made which is copied into the destination. This also required that renderValue can't emit string literals for byte arrays given that they need to always have an extra byte for the NULL terminator, meaning that strings are no longer grep-able in the output.
Diffstat (limited to 'src/codegen')
-rw-r--r--src/codegen/c.zig85
1 files changed, 61 insertions, 24 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index b23b937e05..191be36494 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -385,11 +385,6 @@ pub const DeclGen = struct {
// First try specific tag representations for more efficiency.
switch (val.tag()) {
.undef, .empty_struct_value, .empty_array => try writer.writeAll("{}"),
- .bytes => {
- const bytes = val.castTag(.bytes).?.data;
- // TODO: make our own C string escape instead of using std.zig.fmtEscapes
- try writer.print("\"{}\"", .{std.zig.fmtEscapes(bytes)});
- },
else => {
// Fall back to generic implementation.
var arena = std.heap.ArenaAllocator.init(dg.module.gpa);
@@ -1449,14 +1444,18 @@ fn airArg(f: *Function) CValue {
fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
const ty_op = f.air.instructions.items(.data)[inst].ty_op;
const is_volatile = f.air.typeOf(ty_op.operand).isVolatilePtr();
+
if (!is_volatile and f.liveness.isUnused(inst))
return CValue.none;
+
const inst_ty = f.air.typeOfIndex(inst);
- if (inst_ty.zigTypeTag() == .Array)
- return f.fail("TODO: C backend: implement airLoad for arrays", .{});
+ const is_array = inst_ty.zigTypeTag() == .Array;
const operand = try f.resolveInst(ty_op.operand);
const writer = f.object.writer();
- const local = try f.allocLocal(inst_ty, .Const);
+
+ // We need to separately initialize arrays with a memcpy so they must be mutable.
+ const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
+
switch (operand) {
.local_ref => |i| {
const wrapped: CValue = .{ .local = i };
@@ -1471,9 +1470,23 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
try writer.writeAll(";\n");
},
else => {
- try writer.writeAll(" = *");
- try f.writeCValue(writer, operand);
- try writer.writeAll(";\n");
+ if (is_array) {
+ // Insert a memcpy to initialize this array. The source operand is always a pointer
+ // and thus we only need to know size/type information from the local type/dest.
+ try writer.writeAll(";");
+ try f.object.indent_writer.insertNewline();
+ try writer.writeAll("memcpy(");
+ try f.writeCValue(writer, local);
+ try writer.writeAll(", ");
+ try f.writeCValue(writer, operand);
+ try writer.writeAll(", sizeof(");
+ try f.writeCValue(writer, local);
+ try writer.writeAll("));\n");
+ } else {
+ try writer.writeAll(" = *");
+ try f.writeCValue(writer, operand);
+ try writer.writeAll(";\n");
+ }
},
}
return local;
@@ -1580,7 +1593,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
return local;
}
-fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue {
+fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_child_type: Type) !CValue {
const is_debug_build = f.object.dg.module.optimizeMode() == .Debug;
if (!is_debug_build)
return CValue.none;
@@ -1604,7 +1617,7 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue {
try writer.writeAll("));\n");
},
else => {
- const indirection = if (dest_type.childType().zigTypeTag() == .Array) "" else "*";
+ const indirection = if (dest_child_type.zigTypeTag() == .Array) "" else "*";
try writer.writeAll("memset(");
try f.writeCValue(writer, dest_ptr);
@@ -1621,18 +1634,14 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
const bin_op = f.air.instructions.items(.data)[inst].bin_op;
const dest_ptr = try f.resolveInst(bin_op.lhs);
const src_val = try f.resolveInst(bin_op.rhs);
- const lhs_type = f.air.typeOf(bin_op.lhs);
+ const lhs_child_type = f.air.typeOf(bin_op.lhs).childType();
// TODO Sema should emit a different instruction when the store should
// possibly do the safety 0xaa bytes for undefined.
const src_val_is_undefined =
if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
if (src_val_is_undefined)
- return try airStoreUndefined(f, dest_ptr, lhs_type);
-
- // Don't check this for airStoreUndefined as that will work for arrays already
- if (lhs_type.childType().zigTypeTag() == .Array)
- return f.fail("TODO: C backend: implement airStore for arrays", .{});
+ return try airStoreUndefined(f, dest_ptr, lhs_child_type);
const writer = f.object.writer();
switch (dest_ptr) {
@@ -1651,11 +1660,39 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
try writer.writeAll(";\n");
},
else => {
- try writer.writeAll("*");
- try f.writeCValue(writer, dest_ptr);
- try writer.writeAll(" = ");
- try f.writeCValue(writer, src_val);
- try writer.writeAll(";\n");
+ if (lhs_child_type.zigTypeTag() == .Array) {
+ // For this memcpy to safely work we need the rhs to have the same
+ // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).
+ const rhs_type = f.air.typeOf(bin_op.rhs);
+ assert(rhs_type.eql(lhs_child_type));
+
+ // If the source is a constant, writeCValue will emit a brace initialization
+ // so work around this by initializing into new local.
+ // TODO this should be done by manually initializing elements of the dest array
+ const array_src = if (src_val == .constant) blk: {
+ const new_local = try f.allocLocal(rhs_type, .Const);
+ try writer.writeAll(" = ");
+ try f.writeCValue(writer, src_val);
+ try writer.writeAll(";");
+ try f.object.indent_writer.insertNewline();
+
+ break :blk new_local;
+ } else src_val;
+
+ try writer.writeAll("memcpy(");
+ try f.writeCValue(writer, dest_ptr);
+ try writer.writeAll(", ");
+ try f.writeCValue(writer, array_src);
+ try writer.writeAll(", sizeof(");
+ try f.writeCValue(writer, array_src);
+ try writer.writeAll("));\n");
+ } else {
+ try writer.writeAll("*");
+ try f.writeCValue(writer, dest_ptr);
+ try writer.writeAll(" = ");
+ try f.writeCValue(writer, src_val);
+ try writer.writeAll(";\n");
+ }
},
}
return CValue.none;