Commit 57f6adf85d
Changed files (3)
lib/zig.h
@@ -5,6 +5,7 @@
#endif
#include <float.h>
#include <limits.h>
+#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
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
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;
}