authorgravatar for reserveblue@protonmail.comdrew <reserveblue@protonmail.com> 2021-12-30 12:19:12-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-12-30 15:19:12-05:00
log2f53406ad821c6fd8239551d3b812cd4100929bd
tree3ef2ae54516af3842c2c144879c5b7ac253b0913
parent726ee671befd06b18fec90a01dabf2eca96b2d02
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

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.

4 files changed, 99 insertions(+), 58 deletions(-)

src/codegen/c.zig+61-24
......@@ -385,11 +385,6 @@ pub const DeclGen = struct {
385385 // First try specific tag representations for more efficiency.
386386 switch (val.tag()) {
387387 .undef, .empty_struct_value, .empty_array => try writer.writeAll("{}"),
388 .bytes => {
389 const bytes = val.castTag(.bytes).?.data;
390 // TODO: make our own C string escape instead of using std.zig.fmtEscapes
391 try writer.print("\"{}\"", .{std.zig.fmtEscapes(bytes)});
392 },
393388 else => {
394389 // Fall back to generic implementation.
395390 var arena = std.heap.ArenaAllocator.init(dg.module.gpa);
......@@ -1449,14 +1444,18 @@ fn airArg(f: *Function) CValue {
14491444fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
14501445 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
14511446 const is_volatile = f.air.typeOf(ty_op.operand).isVolatilePtr();
1447
14521448 if (!is_volatile and f.liveness.isUnused(inst))
14531449 return CValue.none;
1450
14541451 const inst_ty = f.air.typeOfIndex(inst);
1455 if (inst_ty.zigTypeTag() == .Array)
1456 return f.fail("TODO: C backend: implement airLoad for arrays", .{});
1452 const is_array = inst_ty.zigTypeTag() == .Array;
14571453 const operand = try f.resolveInst(ty_op.operand);
14581454 const writer = f.object.writer();
1459 const local = try f.allocLocal(inst_ty, .Const);
1455
1456 // We need to separately initialize arrays with a memcpy so they must be mutable.
1457 const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
1458
14601459 switch (operand) {
14611460 .local_ref => |i| {
14621461 const wrapped: CValue = .{ .local = i };
......@@ -1471,9 +1470,23 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
14711470 try writer.writeAll(";\n");
14721471 },
14731472 else => {
1474 try writer.writeAll(" = *");
1475 try f.writeCValue(writer, operand);
1476 try writer.writeAll(";\n");
1473 if (is_array) {
1474 // Insert a memcpy to initialize this array. The source operand is always a pointer
1475 // and thus we only need to know size/type information from the local type/dest.
1476 try writer.writeAll(";");
1477 try f.object.indent_writer.insertNewline();
1478 try writer.writeAll("memcpy(");
1479 try f.writeCValue(writer, local);
1480 try writer.writeAll(", ");
1481 try f.writeCValue(writer, operand);
1482 try writer.writeAll(", sizeof(");
1483 try f.writeCValue(writer, local);
1484 try writer.writeAll("));\n");
1485 } else {
1486 try writer.writeAll(" = *");
1487 try f.writeCValue(writer, operand);
1488 try writer.writeAll(";\n");
1489 }
14771490 },
14781491 }
14791492 return local;
......@@ -1580,7 +1593,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
15801593 return local;
15811594}
15821595
1583fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue {
1596fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_child_type: Type) !CValue {
15841597 const is_debug_build = f.object.dg.module.optimizeMode() == .Debug;
15851598 if (!is_debug_build)
15861599 return CValue.none;
......@@ -1604,7 +1617,7 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue {
16041617 try writer.writeAll("));\n");
16051618 },
16061619 else => {
1607 const indirection = if (dest_type.childType().zigTypeTag() == .Array) "" else "*";
1620 const indirection = if (dest_child_type.zigTypeTag() == .Array) "" else "*";
16081621
16091622 try writer.writeAll("memset(");
16101623 try f.writeCValue(writer, dest_ptr);
......@@ -1621,18 +1634,14 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
16211634 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
16221635 const dest_ptr = try f.resolveInst(bin_op.lhs);
16231636 const src_val = try f.resolveInst(bin_op.rhs);
1624 const lhs_type = f.air.typeOf(bin_op.lhs);
1637 const lhs_child_type = f.air.typeOf(bin_op.lhs).childType();
16251638
16261639 // TODO Sema should emit a different instruction when the store should
16271640 // possibly do the safety 0xaa bytes for undefined.
16281641 const src_val_is_undefined =
16291642 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
16301643 if (src_val_is_undefined)
1631 return try airStoreUndefined(f, dest_ptr, lhs_type);
1632
1633 // Don't check this for airStoreUndefined as that will work for arrays already
1634 if (lhs_type.childType().zigTypeTag() == .Array)
1635 return f.fail("TODO: C backend: implement airStore for arrays", .{});
1644 return try airStoreUndefined(f, dest_ptr, lhs_child_type);
16361645
16371646 const writer = f.object.writer();
16381647 switch (dest_ptr) {
......@@ -1651,11 +1660,39 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
16511660 try writer.writeAll(";\n");
16521661 },
16531662 else => {
1654 try writer.writeAll("*");
1655 try f.writeCValue(writer, dest_ptr);
1656 try writer.writeAll(" = ");
1657 try f.writeCValue(writer, src_val);
1658 try writer.writeAll(";\n");
1663 if (lhs_child_type.zigTypeTag() == .Array) {
1664 // For this memcpy to safely work we need the rhs to have the same
1665 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).
1666 const rhs_type = f.air.typeOf(bin_op.rhs);
1667 assert(rhs_type.eql(lhs_child_type));
1668
1669 // If the source is a constant, writeCValue will emit a brace initialization
1670 // so work around this by initializing into new local.
1671 // TODO this should be done by manually initializing elements of the dest array
1672 const array_src = if (src_val == .constant) blk: {
1673 const new_local = try f.allocLocal(rhs_type, .Const);
1674 try writer.writeAll(" = ");
1675 try f.writeCValue(writer, src_val);
1676 try writer.writeAll(";");
1677 try f.object.indent_writer.insertNewline();
1678
1679 break :blk new_local;
1680 } else src_val;
1681
1682 try writer.writeAll("memcpy(");
1683 try f.writeCValue(writer, dest_ptr);
1684 try writer.writeAll(", ");
1685 try f.writeCValue(writer, array_src);
1686 try writer.writeAll(", sizeof(");
1687 try f.writeCValue(writer, array_src);
1688 try writer.writeAll("));\n");
1689 } else {
1690 try writer.writeAll("*");
1691 try f.writeCValue(writer, dest_ptr);
1692 try writer.writeAll(" = ");
1693 try f.writeCValue(writer, src_val);
1694 try writer.writeAll(";\n");
1695 }
16591696 },
16601697 }
16611698 return CValue.none;
test/behavior.zig+3-2
......@@ -25,6 +25,7 @@ test {
2525 // Tests that pass for stage1, stage2 and the C backend, but not for the wasm backend
2626 if (!builtin.zig_is_stage2 or builtin.stage2_arch != .wasm32) {
2727 _ = @import("behavior/align.zig");
28 _ = @import("behavior/array.zig");
2829 _ = @import("behavior/bool.zig");
2930 _ = @import("behavior/bugs/704.zig");
3031 _ = @import("behavior/bugs/2692.zig");
......@@ -41,6 +42,7 @@ test {
4142 _ = @import("behavior/defer.zig");
4243 _ = @import("behavior/error.zig");
4344 _ = @import("behavior/fn_in_struct_in_comptime.zig");
45 _ = @import("behavior/for.zig");
4446 _ = @import("behavior/generics.zig");
4547 _ = @import("behavior/if.zig");
4648 _ = @import("behavior/incomplete_struct_param_tld.zig");
......@@ -63,7 +65,7 @@ test {
6365 // Tests that pass for stage1 and stage2 but not the C backend and wasm backend.
6466 _ = @import("behavior/align_llvm.zig");
6567 _ = @import("behavior/alignof.zig");
66 _ = @import("behavior/array.zig");
68 _ = @import("behavior/array_llvm.zig");
6769 _ = @import("behavior/atomics.zig");
6870 _ = @import("behavior/basic_llvm.zig");
6971 _ = @import("behavior/bugs/394.zig");
......@@ -85,7 +87,6 @@ test {
8587 _ = @import("behavior/eval.zig");
8688 _ = @import("behavior/floatop.zig");
8789 _ = @import("behavior/fn.zig");
88 _ = @import("behavior/for.zig");
8990 _ = @import("behavior/generics_llvm.zig");
9091 _ = @import("behavior/math.zig");
9192 _ = @import("behavior/maximum_minimum.zig");
test/behavior/array.zig-32
......@@ -124,21 +124,6 @@ test "nested arrays" {
124124 }
125125}
126126
127var s_array: [8]Sub = undefined;
128const Sub = struct { b: u8 };
129const Str = struct { a: []Sub };
130test "set global var array via slice embedded in struct" {
131 var s = Str{ .a = s_array[0..] };
132
133 s.a[0].b = 1;
134 s.a[1].b = 2;
135 s.a[2].b = 3;
136
137 try expect(s_array[0].b == 1);
138 try expect(s_array[1].b == 2);
139 try expect(s_array[2].b == 3);
140}
141
142127test "implicit comptime in array type size" {
143128 var arr: [plusOne(10)]bool = undefined;
144129 try expect(arr.len == 11);
......@@ -148,23 +133,6 @@ fn plusOne(x: u32) u32 {
148133 return x + 1;
149134}
150135
151test "read/write through global variable array of struct fields initialized via array mult" {
152 const S = struct {
153 fn doTheTest() !void {
154 try expect(storage[0].term == 1);
155 storage[0] = MyStruct{ .term = 123 };
156 try expect(storage[0].term == 123);
157 }
158
159 pub const MyStruct = struct {
160 term: usize,
161 };
162
163 var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1;
164 };
165 try S.doTheTest();
166}
167
168136test "single-item pointer to array indexing and slicing" {
169137 try testSingleItemPtrArrayIndexSlice();
170138 comptime try testSingleItemPtrArrayIndexSlice();
test/behavior/array_llvm.zig created+35
......@@ -0,0 +1,35 @@
1const std = @import("std");
2const testing = std.testing;
3const expect = testing.expect;
4
5var s_array: [8]Sub = undefined;
6const Sub = struct { b: u8 };
7const Str = struct { a: []Sub };
8test "set global var array via slice embedded in struct" {
9 var s = Str{ .a = s_array[0..] };
10
11 s.a[0].b = 1;
12 s.a[1].b = 2;
13 s.a[2].b = 3;
14
15 try expect(s_array[0].b == 1);
16 try expect(s_array[1].b == 2);
17 try expect(s_array[2].b == 3);
18}
19
20test "read/write through global variable array of struct fields initialized via array mult" {
21 const S = struct {
22 fn doTheTest() !void {
23 try expect(storage[0].term == 1);
24 storage[0] = MyStruct{ .term = 123 };
25 try expect(storage[0].term == 123);
26 }
27
28 pub const MyStruct = struct {
29 term: usize,
30 };
31
32 var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1;
33 };
34 try S.doTheTest();
35}