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 {...@@ -385,11 +385,6 @@ pub const DeclGen = struct {
385 // First try specific tag representations for more efficiency.385 // First try specific tag representations for more efficiency.
386 switch (val.tag()) {386 switch (val.tag()) {
387 .undef, .empty_struct_value, .empty_array => try writer.writeAll("{}"),387 .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 },
393 else => {388 else => {
394 // Fall back to generic implementation.389 // Fall back to generic implementation.
395 var arena = std.heap.ArenaAllocator.init(dg.module.gpa);390 var arena = std.heap.ArenaAllocator.init(dg.module.gpa);
...@@ -1449,14 +1444,18 @@ fn airArg(f: *Function) CValue {...@@ -1449,14 +1444,18 @@ fn airArg(f: *Function) CValue {
1449fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {1444fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
1450 const ty_op = f.air.instructions.items(.data)[inst].ty_op;1445 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1451 const is_volatile = f.air.typeOf(ty_op.operand).isVolatilePtr();1446 const is_volatile = f.air.typeOf(ty_op.operand).isVolatilePtr();
1447
1452 if (!is_volatile and f.liveness.isUnused(inst))1448 if (!is_volatile and f.liveness.isUnused(inst))
1453 return CValue.none;1449 return CValue.none;
1450
1454 const inst_ty = f.air.typeOfIndex(inst);1451 const inst_ty = f.air.typeOfIndex(inst);
1455 if (inst_ty.zigTypeTag() == .Array)1452 const is_array = inst_ty.zigTypeTag() == .Array;
1456 return f.fail("TODO: C backend: implement airLoad for arrays", .{});
1457 const operand = try f.resolveInst(ty_op.operand);1453 const operand = try f.resolveInst(ty_op.operand);
1458 const writer = f.object.writer();1454 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
1460 switch (operand) {1459 switch (operand) {
1461 .local_ref => |i| {1460 .local_ref => |i| {
1462 const wrapped: CValue = .{ .local = i };1461 const wrapped: CValue = .{ .local = i };
...@@ -1471,9 +1470,23 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1471,9 +1470,23 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
1471 try writer.writeAll(";\n");1470 try writer.writeAll(";\n");
1472 },1471 },
1473 else => {1472 else => {
1474 try writer.writeAll(" = *");1473 if (is_array) {
1475 try f.writeCValue(writer, operand);1474 // Insert a memcpy to initialize this array. The source operand is always a pointer
1476 try writer.writeAll(";\n");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 }
1477 },1490 },
1478 }1491 }
1479 return local;1492 return local;
...@@ -1580,7 +1593,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1580,7 +1593,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
1580 return local;1593 return local;
1581}1594}
15821595
1583fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue {1596fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_child_type: Type) !CValue {
1584 const is_debug_build = f.object.dg.module.optimizeMode() == .Debug;1597 const is_debug_build = f.object.dg.module.optimizeMode() == .Debug;
1585 if (!is_debug_build)1598 if (!is_debug_build)
1586 return CValue.none;1599 return CValue.none;
...@@ -1604,7 +1617,7 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue {...@@ -1604,7 +1617,7 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue {
1604 try writer.writeAll("));\n");1617 try writer.writeAll("));\n");
1605 },1618 },
1606 else => {1619 else => {
1607 const indirection = if (dest_type.childType().zigTypeTag() == .Array) "" else "*";1620 const indirection = if (dest_child_type.zigTypeTag() == .Array) "" else "*";
16081621
1609 try writer.writeAll("memset(");1622 try writer.writeAll("memset(");
1610 try f.writeCValue(writer, dest_ptr);1623 try f.writeCValue(writer, dest_ptr);
...@@ -1621,18 +1634,14 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1621,18 +1634,14 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
1621 const bin_op = f.air.instructions.items(.data)[inst].bin_op;1634 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1622 const dest_ptr = try f.resolveInst(bin_op.lhs);1635 const dest_ptr = try f.resolveInst(bin_op.lhs);
1623 const src_val = try f.resolveInst(bin_op.rhs);1636 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
1626 // TODO Sema should emit a different instruction when the store should1639 // TODO Sema should emit a different instruction when the store should
1627 // possibly do the safety 0xaa bytes for undefined.1640 // possibly do the safety 0xaa bytes for undefined.
1628 const src_val_is_undefined =1641 const src_val_is_undefined =
1629 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;1642 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
1630 if (src_val_is_undefined)1643 if (src_val_is_undefined)
1631 return try airStoreUndefined(f, dest_ptr, lhs_type);1644 return try airStoreUndefined(f, dest_ptr, lhs_child_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", .{});
16361645
1637 const writer = f.object.writer();1646 const writer = f.object.writer();
1638 switch (dest_ptr) {1647 switch (dest_ptr) {
...@@ -1651,11 +1660,39 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1651,11 +1660,39 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
1651 try writer.writeAll(";\n");1660 try writer.writeAll(";\n");
1652 },1661 },
1653 else => {1662 else => {
1654 try writer.writeAll("*");1663 if (lhs_child_type.zigTypeTag() == .Array) {
1655 try f.writeCValue(writer, dest_ptr);1664 // For this memcpy to safely work we need the rhs to have the same
1656 try writer.writeAll(" = ");1665 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).
1657 try f.writeCValue(writer, src_val);1666 const rhs_type = f.air.typeOf(bin_op.rhs);
1658 try writer.writeAll(";\n");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 }
1659 },1696 },
1660 }1697 }
1661 return CValue.none;1698 return CValue.none;
test/behavior.zig+3-2
...@@ -25,6 +25,7 @@ test {...@@ -25,6 +25,7 @@ test {
25 // Tests that pass for stage1, stage2 and the C backend, but not for the wasm backend25 // Tests that pass for stage1, stage2 and the C backend, but not for the wasm backend
26 if (!builtin.zig_is_stage2 or builtin.stage2_arch != .wasm32) {26 if (!builtin.zig_is_stage2 or builtin.stage2_arch != .wasm32) {
27 _ = @import("behavior/align.zig");27 _ = @import("behavior/align.zig");
28 _ = @import("behavior/array.zig");
28 _ = @import("behavior/bool.zig");29 _ = @import("behavior/bool.zig");
29 _ = @import("behavior/bugs/704.zig");30 _ = @import("behavior/bugs/704.zig");
30 _ = @import("behavior/bugs/2692.zig");31 _ = @import("behavior/bugs/2692.zig");
...@@ -41,6 +42,7 @@ test {...@@ -41,6 +42,7 @@ test {
41 _ = @import("behavior/defer.zig");42 _ = @import("behavior/defer.zig");
42 _ = @import("behavior/error.zig");43 _ = @import("behavior/error.zig");
43 _ = @import("behavior/fn_in_struct_in_comptime.zig");44 _ = @import("behavior/fn_in_struct_in_comptime.zig");
45 _ = @import("behavior/for.zig");
44 _ = @import("behavior/generics.zig");46 _ = @import("behavior/generics.zig");
45 _ = @import("behavior/if.zig");47 _ = @import("behavior/if.zig");
46 _ = @import("behavior/incomplete_struct_param_tld.zig");48 _ = @import("behavior/incomplete_struct_param_tld.zig");
...@@ -63,7 +65,7 @@ test {...@@ -63,7 +65,7 @@ test {
63 // Tests that pass for stage1 and stage2 but not the C backend and wasm backend.65 // Tests that pass for stage1 and stage2 but not the C backend and wasm backend.
64 _ = @import("behavior/align_llvm.zig");66 _ = @import("behavior/align_llvm.zig");
65 _ = @import("behavior/alignof.zig");67 _ = @import("behavior/alignof.zig");
66 _ = @import("behavior/array.zig");68 _ = @import("behavior/array_llvm.zig");
67 _ = @import("behavior/atomics.zig");69 _ = @import("behavior/atomics.zig");
68 _ = @import("behavior/basic_llvm.zig");70 _ = @import("behavior/basic_llvm.zig");
69 _ = @import("behavior/bugs/394.zig");71 _ = @import("behavior/bugs/394.zig");
...@@ -85,7 +87,6 @@ test {...@@ -85,7 +87,6 @@ test {
85 _ = @import("behavior/eval.zig");87 _ = @import("behavior/eval.zig");
86 _ = @import("behavior/floatop.zig");88 _ = @import("behavior/floatop.zig");
87 _ = @import("behavior/fn.zig");89 _ = @import("behavior/fn.zig");
88 _ = @import("behavior/for.zig");
89 _ = @import("behavior/generics_llvm.zig");90 _ = @import("behavior/generics_llvm.zig");
90 _ = @import("behavior/math.zig");91 _ = @import("behavior/math.zig");
91 _ = @import("behavior/maximum_minimum.zig");92 _ = @import("behavior/maximum_minimum.zig");
test/behavior/array.zig-32
...@@ -124,21 +124,6 @@ test "nested arrays" {...@@ -124,21 +124,6 @@ test "nested arrays" {
124 }124 }
125}125}
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
142test "implicit comptime in array type size" {127test "implicit comptime in array type size" {
143 var arr: [plusOne(10)]bool = undefined;128 var arr: [plusOne(10)]bool = undefined;
144 try expect(arr.len == 11);129 try expect(arr.len == 11);
...@@ -148,23 +133,6 @@ fn plusOne(x: u32) u32 {...@@ -148,23 +133,6 @@ fn plusOne(x: u32) u32 {
148 return x + 1;133 return x + 1;
149}134}
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
168test "single-item pointer to array indexing and slicing" {136test "single-item pointer to array indexing and slicing" {
169 try testSingleItemPtrArrayIndexSlice();137 try testSingleItemPtrArrayIndexSlice();
170 comptime try testSingleItemPtrArrayIndexSlice();138 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}