aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/codegen/c.zig85
-rw-r--r--test/behavior.zig5
-rw-r--r--test/behavior/array.zig32
-rw-r--r--test/behavior/array_llvm.zig35
4 files changed, 99 insertions, 58 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;
diff --git a/test/behavior.zig b/test/behavior.zig
index 74d7b1f73c..03de899bf5 100644
--- a/test/behavior.zig
+++ b/test/behavior.zig
@@ -25,6 +25,7 @@ test {
// Tests that pass for stage1, stage2 and the C backend, but not for the wasm backend
if (!builtin.zig_is_stage2 or builtin.stage2_arch != .wasm32) {
_ = @import("behavior/align.zig");
+ _ = @import("behavior/array.zig");
_ = @import("behavior/bool.zig");
_ = @import("behavior/bugs/704.zig");
_ = @import("behavior/bugs/2692.zig");
@@ -41,6 +42,7 @@ test {
_ = @import("behavior/defer.zig");
_ = @import("behavior/error.zig");
_ = @import("behavior/fn_in_struct_in_comptime.zig");
+ _ = @import("behavior/for.zig");
_ = @import("behavior/generics.zig");
_ = @import("behavior/if.zig");
_ = @import("behavior/incomplete_struct_param_tld.zig");
@@ -63,7 +65,7 @@ test {
// Tests that pass for stage1 and stage2 but not the C backend and wasm backend.
_ = @import("behavior/align_llvm.zig");
_ = @import("behavior/alignof.zig");
- _ = @import("behavior/array.zig");
+ _ = @import("behavior/array_llvm.zig");
_ = @import("behavior/atomics.zig");
_ = @import("behavior/basic_llvm.zig");
_ = @import("behavior/bugs/394.zig");
@@ -85,7 +87,6 @@ test {
_ = @import("behavior/eval.zig");
_ = @import("behavior/floatop.zig");
_ = @import("behavior/fn.zig");
- _ = @import("behavior/for.zig");
_ = @import("behavior/generics_llvm.zig");
_ = @import("behavior/math.zig");
_ = @import("behavior/maximum_minimum.zig");
diff --git a/test/behavior/array.zig b/test/behavior/array.zig
index c9c376be87..cd74640bea 100644
--- a/test/behavior/array.zig
+++ b/test/behavior/array.zig
@@ -124,21 +124,6 @@ test "nested arrays" {
}
}
-var s_array: [8]Sub = undefined;
-const Sub = struct { b: u8 };
-const Str = struct { a: []Sub };
-test "set global var array via slice embedded in struct" {
- var s = Str{ .a = s_array[0..] };
-
- s.a[0].b = 1;
- s.a[1].b = 2;
- s.a[2].b = 3;
-
- try expect(s_array[0].b == 1);
- try expect(s_array[1].b == 2);
- try expect(s_array[2].b == 3);
-}
-
test "implicit comptime in array type size" {
var arr: [plusOne(10)]bool = undefined;
try expect(arr.len == 11);
@@ -148,23 +133,6 @@ fn plusOne(x: u32) u32 {
return x + 1;
}
-test "read/write through global variable array of struct fields initialized via array mult" {
- const S = struct {
- fn doTheTest() !void {
- try expect(storage[0].term == 1);
- storage[0] = MyStruct{ .term = 123 };
- try expect(storage[0].term == 123);
- }
-
- pub const MyStruct = struct {
- term: usize,
- };
-
- var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1;
- };
- try S.doTheTest();
-}
-
test "single-item pointer to array indexing and slicing" {
try testSingleItemPtrArrayIndexSlice();
comptime try testSingleItemPtrArrayIndexSlice();
diff --git a/test/behavior/array_llvm.zig b/test/behavior/array_llvm.zig
new file mode 100644
index 0000000000..8e65045210
--- /dev/null
+++ b/test/behavior/array_llvm.zig
@@ -0,0 +1,35 @@
+const std = @import("std");
+const testing = std.testing;
+const expect = testing.expect;
+
+var s_array: [8]Sub = undefined;
+const Sub = struct { b: u8 };
+const Str = struct { a: []Sub };
+test "set global var array via slice embedded in struct" {
+ var s = Str{ .a = s_array[0..] };
+
+ s.a[0].b = 1;
+ s.a[1].b = 2;
+ s.a[2].b = 3;
+
+ try expect(s_array[0].b == 1);
+ try expect(s_array[1].b == 2);
+ try expect(s_array[2].b == 3);
+}
+
+test "read/write through global variable array of struct fields initialized via array mult" {
+ const S = struct {
+ fn doTheTest() !void {
+ try expect(storage[0].term == 1);
+ storage[0] = MyStruct{ .term = 123 };
+ try expect(storage[0].term == 123);
+ }
+
+ pub const MyStruct = struct {
+ term: usize,
+ };
+
+ var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1;
+ };
+ try S.doTheTest();
+}