From bdb1e014a085fdd872886dddc66cfe1081887e5c Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 22 Feb 2023 23:31:41 -0500 Subject: CBE: cleanup field access * Implement @fieldParentPtr on a union * Refactor field access to ensure that it is handled consistently * Remove `renderTypecast` as it is now behaves the same as `renderType` --- test/behavior/field_parent_ptr.zig | 3 --- test/behavior/packed-struct.zig | 1 - 2 files changed, 4 deletions(-) (limited to 'test/behavior') diff --git a/test/behavior/field_parent_ptr.zig b/test/behavior/field_parent_ptr.zig index b9e171db57..6bbd6ad7ef 100644 --- a/test/behavior/field_parent_ptr.zig +++ b/test/behavior/field_parent_ptr.zig @@ -48,7 +48,6 @@ fn testParentFieldPtrFirst(a: *const bool) !void { test "@fieldParentPtr untagged union" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testFieldParentPtrUnion(&bar.c); @@ -75,7 +74,6 @@ fn testFieldParentPtrUnion(c: *const i32) !void { test "@fieldParentPtr tagged union" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testFieldParentPtrTaggedUnion(&bar_tagged.c); @@ -102,7 +100,6 @@ fn testFieldParentPtrTaggedUnion(c: *const i32) !void { test "@fieldParentPtr extern union" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testFieldParentPtrExternUnion(&bar_extern.c); diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index e1237a578b..85214bd7d8 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -603,7 +603,6 @@ test "packed struct initialized in bitcast" { test "pointer to container level packed struct field" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; -- cgit v1.2.3 From 597e8011f7b2ea76755165fa5a09b2f725180268 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 22 Feb 2023 23:32:54 -0500 Subject: CType: fix lowering of generic function pointer --- src/codegen/c/type.zig | 29 +++++++++++++++++------------ test/behavior/pointers.zig | 1 - 2 files changed, 17 insertions(+), 13 deletions(-) (limited to 'test/behavior') diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index bf148a8b87..04c0ea3003 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -1296,19 +1296,21 @@ pub const CType = extern union { .Fn => { const info = ty.fnInfo(); - if (lookup.isMutable()) { - const param_kind: Kind = switch (kind) { - .forward, .forward_parameter => .forward_parameter, - .complete, .parameter, .global => .parameter, - .payload => unreachable, - }; - _ = try lookup.typeToIndex(info.return_type, param_kind); - for (info.param_types) |param_type| { - if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; - _ = try lookup.typeToIndex(param_type, param_kind); + if (!info.is_generic) { + if (lookup.isMutable()) { + const param_kind: Kind = switch (kind) { + .forward, .forward_parameter => .forward_parameter, + .complete, .parameter, .global => .parameter, + .payload => unreachable, + }; + _ = try lookup.typeToIndex(info.return_type, param_kind); + for (info.param_types) |param_type| { + if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; + _ = try lookup.typeToIndex(param_type, param_kind); + } } - } - self.init(if (info.is_var_args) .varargs_function else .function); + self.init(if (info.is_var_args) .varargs_function else .function); + } else self.init(.void); }, } } @@ -1619,6 +1621,7 @@ pub const CType = extern union { .varargs_function, => { const info = ty.fnInfo(); + assert(!info.is_generic); const param_kind: Kind = switch (kind) { .forward, .forward_parameter => .forward_parameter, .complete, .parameter, .global => .parameter, @@ -1764,6 +1767,7 @@ pub const CType = extern union { if (ty.zigTypeTag() != .Fn) return false; const info = ty.fnInfo(); + assert(!info.is_generic); const data = cty.cast(Payload.Function).?.data; const param_kind: Kind = switch (self.kind) { .forward, .forward_parameter => .forward_parameter, @@ -1878,6 +1882,7 @@ pub const CType = extern union { .varargs_function, => { const info = ty.fnInfo(); + assert(!info.is_generic); const param_kind: Kind = switch (self.kind) { .forward, .forward_parameter => .forward_parameter, .complete, .parameter, .global => .parameter, diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 2d55292916..ec4ff332cf 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -507,7 +507,6 @@ test "ptrCast comptime known slice to C pointer" { } test "ptrToInt on a generic function" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO -- cgit v1.2.3 From 57f6adf85da58c0c91a036deaa614c30df010b78 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 23 Feb 2023 00:28:49 -0500 Subject: CBE: implement c varargs Removed some backend test skip checks for things disabled in std. --- lib/zig.h | 1 + src/codegen/c.zig | 93 ++++++++++++++++++++++++++++++++++++++++++++-- test/behavior/var_args.zig | 15 ++++---- 3 files changed, 97 insertions(+), 12 deletions(-) (limited to 'test/behavior') diff --git a/lib/zig.h b/lib/zig.h index d336ecb2e2..67f64635c5 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -5,6 +5,7 @@ #endif #include #include +#include #include #include diff --git a/src/codegen/c.zig b/src/codegen/c.zig index b5269acc5c..390e8f8f4e 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -211,6 +211,15 @@ const reserved_idents = std.ComptimeStringMap(void, .{ .{ "volatile", {} }, .{ "while ", {} }, + // stdarg.h + .{ "va_start", {} }, + .{ "va_arg", {} }, + .{ "va_end", {} }, + .{ "va_copy", {} }, + + // stddef.h + .{ "offsetof", {} }, + // windows.h .{ "max", {} }, .{ "min", {} }, @@ -2952,10 +2961,10 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}), .vector_store_elem => return f.fail("TODO: C backend: implement vector_store_elem", .{}), - .c_va_arg => return f.fail("TODO implement c_va_arg", .{}), - .c_va_copy => return f.fail("TODO implement c_va_copy", .{}), - .c_va_end => return f.fail("TODO implement c_va_end", .{}), - .c_va_start => return f.fail("TODO implement c_va_start", .{}), + .c_va_start => try airCVaStart(f, inst), + .c_va_arg => try airCVaArg(f, inst), + .c_va_end => try airCVaEnd(f, inst), + .c_va_copy => try airCVaCopy(f, inst), // zig fmt: on }; if (result_value == .new_local) { @@ -6862,6 +6871,82 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { return local; } +fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) return .none; + + const inst_ty = f.air.typeOfIndex(inst); + const fn_cty = try f.typeToCType(f.object.dg.decl.?.ty, .complete); + + const param_len = fn_cty.castTag(.varargs_function).?.data.param_types.len; + if (param_len == 0) + return f.fail("CBE: C requires at least one runtime argument for varargs functions", .{}); + + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try writer.writeAll("va_start(*(va_list *)&"); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(", "); + try f.writeCValue(writer, .{ .arg = param_len - 1 }, .FunctionArgument); + try writer.writeAll(");\n"); + return local; +} + +fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue { + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return .none; + } + + const inst_ty = f.air.typeOfIndex(inst); + const va_list = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); + + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = va_arg(*(va_list *)"); + try f.writeCValue(writer, va_list, .Other); + try writer.writeAll(", "); + try f.renderType(writer, f.air.getRefType(ty_op.ty)); + try writer.writeAll(");\n"); + return local; +} + +fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue { + const un_op = f.air.instructions.items(.data)[inst].un_op; + + const va_list = try f.resolveInst(un_op); + try reap(f, inst, &.{un_op}); + + const writer = f.object.writer(); + try writer.writeAll("va_end(*(va_list *)"); + try f.writeCValue(writer, va_list, .Other); + try writer.writeAll(");\n"); + return .none; +} + +fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue { + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return .none; + } + + const inst_ty = f.air.typeOfIndex(inst); + const va_list = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); + + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try writer.writeAll("va_copy(*(va_list *)&"); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(", *(va_list *)"); + try f.writeCValue(writer, va_list, .Other); + try writer.writeAll(");\n"); + return local; +} + fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { return switch (order) { // Note: unordered is actually even less atomic than relaxed diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index 97f90b559d..6431ca9470 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -96,10 +96,9 @@ fn doNothingWithFirstArg(args: anytype) void { test "simple variadic function" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos and builtin.zig_backend == .stage2_llvm) { + if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } @@ -124,8 +123,10 @@ test "simple variadic function" { } }; - try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0))); - try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024))); + if (builtin.zig_backend != .stage2_c) { // C doesn't support varargs without a preceding runtime arg. + try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0))); + try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024))); + } try std.testing.expectEqual(@as(c_int, 0), S.add(0)); try std.testing.expectEqual(@as(c_int, 1), S.add(1, @as(c_int, 1))); try std.testing.expectEqual(@as(c_int, 3), S.add(2, @as(c_int, 1), @as(c_int, 2))); @@ -134,10 +135,9 @@ test "simple variadic function" { test "variadic functions" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos and builtin.zig_backend == .stage2_llvm) { + if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } @@ -178,10 +178,9 @@ test "variadic functions" { test "copy VaList" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos and builtin.zig_backend == .stage2_llvm) { + if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } -- cgit v1.2.3 From f8aecef6705a75a4c35754bcac32c27602b84711 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 23 Feb 2023 21:18:26 -0500 Subject: CBE: implement the future Turns out f(...) will be supported one day. --- src/codegen/c.zig | 9 ++++----- test/behavior/var_args.zig | 11 ++++++++++- 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'test/behavior') diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 79e0bff237..1af14cb372 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -6876,17 +6876,16 @@ fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); const fn_cty = try f.typeToCType(f.object.dg.decl.?.ty, .complete); - const param_len = fn_cty.castTag(.varargs_function).?.data.param_types.len; - if (param_len == 0) - return f.fail("CBE: C requires at least one runtime argument for varargs functions", .{}); const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); try writer.writeAll("va_start(*(va_list *)&"); try f.writeCValue(writer, local, .Other); - try writer.writeAll(", "); - try f.writeCValue(writer, .{ .arg = param_len - 1 }, .FunctionArgument); + if (param_len > 0) { + try writer.writeAll(", "); + try f.writeCValue(writer, .{ .arg = param_len - 1 }, .FunctionArgument); + } try writer.writeAll(");\n"); return local; } diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index 6431ca9470..cdfbcc9188 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -111,6 +111,12 @@ test "simple variadic function" { return @cVaArg(&ap, c_int); } + fn compatible(_: c_int, ...) callconv(.C) c_int { + var ap = @cVaStart(); + defer @cVaEnd(&ap); + return @cVaArg(&ap, c_int); + } + fn add(count: c_int, ...) callconv(.C) c_int { var ap = @cVaStart(); defer @cVaEnd(&ap); @@ -123,10 +129,13 @@ test "simple variadic function" { } }; - if (builtin.zig_backend != .stage2_c) { // C doesn't support varargs without a preceding runtime arg. + if (builtin.zig_backend != .stage2_c) { + // pre C23 doesn't support varargs without a preceding runtime arg. try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0))); try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024))); } + try std.testing.expectEqual(@as(c_int, 0), S.compatible(undefined, @as(c_int, 0))); + try std.testing.expectEqual(@as(c_int, 1024), S.compatible(undefined, @as(c_int, 1024))); try std.testing.expectEqual(@as(c_int, 0), S.add(0)); try std.testing.expectEqual(@as(c_int, 1), S.add(1, @as(c_int, 1))); try std.testing.expectEqual(@as(c_int, 3), S.add(2, @as(c_int, 1), @as(c_int, 2))); -- cgit v1.2.3