diff options
| -rw-r--r-- | src/codegen/c.zig | 85 | ||||
| -rw-r--r-- | test/behavior.zig | 5 | ||||
| -rw-r--r-- | test/behavior/array.zig | 32 | ||||
| -rw-r--r-- | test/behavior/array_llvm.zig | 35 |
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(); +} |
