From 4e3fcbea84de17afdf391293dc143a1fcc7b2ef7 Mon Sep 17 00:00:00 2001 From: kcbanner Date: Wed, 25 Mar 2026 00:58:37 -0400 Subject: [PATCH 1/5] - Build: support installing the compiler_rt.dll This is an extension of the existing hack used for the x86_64 backend combined with the Coff linker --- lib/std/Build/Step/Compile.zig | 16 ++++++++++++++++ lib/std/Build/Step/InstallArtifact.zig | 18 ++++++++++++++++++ lib/std/zig.zig | 4 ++++ 3 files changed, 38 insertions(+) diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index f4177a9a23..a1987e5633 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -219,6 +219,8 @@ generated_docs: ?*GeneratedFile, generated_asm: ?*GeneratedFile, generated_bin: ?*GeneratedFile, generated_pdb: ?*GeneratedFile, +// hack for stage2_x86_64 + coff +generated_compiler_rt_dyn_lib: ?*GeneratedFile, generated_implib: ?*GeneratedFile, generated_llvm_bc: ?*GeneratedFile, generated_llvm_ir: ?*GeneratedFile, @@ -441,6 +443,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .generated_asm = null, .generated_bin = null, .generated_pdb = null, + .generated_compiler_rt_dyn_lib = null, .generated_implib = null, .generated_llvm_bc = null, .generated_llvm_ir = null, @@ -691,6 +694,11 @@ pub fn producesPdbFile(compile: *Compile) bool { return compile.isDynamicLibrary() or compile.kind == .exe or compile.kind == .@"test"; } +pub fn producesCompilerRtDynLib(compile: *Compile) bool { + if (compile.rootModuleTarget().ofmt != .coff) return false; + return compile.use_llvm == false; +} + pub fn producesImplib(compile: *Compile) bool { return compile.isDll(); } @@ -869,6 +877,12 @@ pub fn getEmittedPdb(compile: *Compile) LazyPath { return compile.getEmittedFileGeneric(&compile.generated_pdb); } +/// Returns the generated compiler_rt dynamic library. +/// This is a hack for stage2_x86_64 + coff. +pub fn getEmittedCompilerRtDynLib(compile: *Compile) ?LazyPath { + return compile.getEmittedFileGeneric(&compile.generated_compiler_rt_dyn_lib); +} + /// Returns the path to the generated documentation directory. pub fn getEmittedDocs(compile: *Compile) LazyPath { return compile.getEmittedFileGeneric(&compile.generated_docs); @@ -1794,6 +1808,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void { // zig fmt: off if (compile.generated_bin) |lp| lp.path = compile.outputPath(output_dir, .bin); if (compile.generated_pdb) |lp| lp.path = compile.outputPath(output_dir, .pdb); + // hack for stage2_x86_64 + coff + if (compile.generated_compiler_rt_dyn_lib) |lp| lp.path = compile.outputPath(output_dir, .compiler_rt_dyn_lib); if (compile.generated_implib) |lp| lp.path = compile.outputPath(output_dir, .implib); if (compile.generated_h) |lp| lp.path = compile.outputPath(output_dir, .h); if (compile.generated_docs) |lp| lp.path = compile.outputPath(output_dir, .docs); diff --git a/lib/std/Build/Step/InstallArtifact.zig b/lib/std/Build/Step/InstallArtifact.zig index c3c9d6c853..aafd18f01c 100644 --- a/lib/std/Build/Step/InstallArtifact.zig +++ b/lib/std/Build/Step/InstallArtifact.zig @@ -17,6 +17,10 @@ emitted_implib: ?LazyPath, pdb_dir: ?InstallDir, emitted_pdb: ?LazyPath, +// hack for stage2_x86_64 + coff +compiler_rt_dyn_lib_dir: ?InstallDir, +emitted_compiler_rt_dyn_lib: ?LazyPath, + h_dir: ?InstallDir, emitted_h: ?LazyPath, @@ -35,6 +39,7 @@ pub const Options = struct { /// Which installation directory to put the main output file into. dest_dir: Dir = .default, pdb_dir: Dir = .default, + compiler_rt_dyn_lib_dir: Dir = .default, h_dir: Dir = .default, implib_dir: Dir = .default, @@ -75,6 +80,11 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins .default => if (artifact.producesPdbFile()) dest_dir else null, .override => |o| o, }, + .compiler_rt_dyn_lib_dir = switch (options.compiler_rt_dyn_lib_dir) { + .disabled => null, + .default => if (artifact.producesCompilerRtDynLib()) dest_dir else null, + .override => |o| o, + }, .h_dir = switch (options.h_dir) { .disabled => null, .default => if (artifact.kind == .lib) .header else null, @@ -98,6 +108,7 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins .emitted_bin = null, .emitted_pdb = null, + .emitted_compiler_rt_dyn_lib = null, .emitted_h = null, .emitted_implib = null, @@ -107,6 +118,7 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins install_artifact.step.dependOn(&artifact.step); if (install_artifact.dest_dir != null) install_artifact.emitted_bin = artifact.getEmittedBin(); + if (install_artifact.compiler_rt_dyn_lib_dir != null) install_artifact.emitted_compiler_rt_dyn_lib = artifact.getEmittedCompilerRtDynLib(); if (install_artifact.pdb_dir != null) install_artifact.emitted_pdb = artifact.getEmittedPdb(); // https://github.com/ziglang/zig/issues/9698 //if (install_artifact.h_dir != null) install_artifact.emitted_h = artifact.getEmittedH(); @@ -135,6 +147,12 @@ fn make(step: *Step, options: Step.MakeOptions) !void { install_artifact.artifact.installed_path = full_dest_path; } + if (install_artifact.compiler_rt_dyn_lib_dir) |compiler_rt_dir| { + const full_compiler_rt_path = b.getInstallPath(compiler_rt_dir, install_artifact.emitted_compiler_rt_dyn_lib.?.basename(b, step)); + const p = try step.installFile(install_artifact.emitted_compiler_rt_dyn_lib.?, full_compiler_rt_path); + all_cached = all_cached and p == .fresh; + } + if (install_artifact.implib_dir) |implib_dir| { const full_implib_path = b.getInstallPath(implib_dir, install_artifact.emitted_implib.?.basename(b, step)); const p = try step.installFile(install_artifact.emitted_implib.?, full_implib_path); diff --git a/lib/std/zig.zig b/lib/std/zig.zig index fdb945c7ce..997fe8a925 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -985,11 +985,14 @@ pub const EmitArtifact = enum { docs, pdb, h, + compiler_rt_dyn_lib, /// If using `Server` to communicate with the compiler, it will place requested artifacts in /// paths under the output directory, where those paths are named according to this function. /// Returned string is allocated with `gpa` and owned by the caller. pub fn cacheName(ea: EmitArtifact, gpa: Allocator, opts: BinNameOptions) Allocator.Error![]const u8 { + // hack for stage2_x86_64 + coff + if (ea == .compiler_rt_dyn_lib) return "compiler_rt.dll"; const suffix: []const u8 = switch (ea) { .bin => return binNameAlloc(gpa, opts), .@"asm" => ".s", @@ -999,6 +1002,7 @@ pub const EmitArtifact = enum { .docs => "-docs", .pdb => ".pdb", .h => ".h", + .compiler_rt_dyn_lib => unreachable, }; return std.fmt.allocPrint(gpa, "{s}{s}", .{ opts.root_name, suffix }); } From ca99896d7383f02812d6cea549ad4af276558f64 Mon Sep 17 00:00:00 2001 From: kcbanner Date: Wed, 25 Mar 2026 02:54:38 -0400 Subject: [PATCH 2/5] - x86_64: Copy arguments into the shadow store when generating a variadic function on Win64 - x86_64: Implement @cVaStart for Win64 - x86_64: Implement @cVaArg for Win64 - x86_64: Duplicate floating point register args equivalent integer registers when calling variadic functions on Win64 - tests: Enable var_args tests for the self-hosted backend on windows - tests: Add var_args test for floating point arguments --- lib/std/zig.zig | 2 +- src/codegen/x86_64/CodeGen.zig | 176 +++++++++++++++++++++++++-------- test/behavior/var_args.zig | 66 ++++++++++++- 3 files changed, 198 insertions(+), 46 deletions(-) diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 997fe8a925..fa40535018 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -991,7 +991,7 @@ pub const EmitArtifact = enum { /// paths under the output directory, where those paths are named according to this function. /// Returned string is allocated with `gpa` and owned by the caller. pub fn cacheName(ea: EmitArtifact, gpa: Allocator, opts: BinNameOptions) Allocator.Error![]const u8 { - // hack for stage2_x86_64 + coff + // hack for stage2_x86_64 + coff. See Coff.flush. if (ea == .compiler_rt_dyn_lib) return "compiler_rt.dll"; const suffix: []const u8 = switch (ea) { .bin => return binNameAlloc(gpa, opts), diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig index 9ba98c3e83..1008305f95 100644 --- a/src/codegen/x86_64/CodeGen.zig +++ b/src/codegen/x86_64/CodeGen.zig @@ -2050,7 +2050,16 @@ fn gen( self.performReloc(skip_sse_reloc); }, - .x86_64_win => return self.fail("TODO implement gen var arg function for Win64", .{}), + .x86_64_win => { + for (abi.Win64.c_abi_int_param_regs[0..], 0..) |reg, reg_i| + try self.genSetMem( + .{ .frame = .args_frame }, + @intCast(reg_i * 8), + .usize, + .{ .register = reg }, + .{}, + ); + }, else => |cc| return self.fail("{s} does not support var args", .{@tagName(cc)}), }; @@ -176020,12 +176029,22 @@ fn genCall(self: *CodeGen, info: union(enum) { .indirect => |reg_off| try self.register_manager.getReg(reg_off.reg, null), else => unreachable, } - for (call_info.args, arg_types, args, frame_indices) |dst_arg, arg_ty, src_arg, *frame_index| + for (call_info.args, arg_types, args, frame_indices, 0..) |dst_arg, arg_ty, src_arg, *frame_index, arg_i| switch (dst_arg) { .none => {}, .register => |reg| { try self.register_manager.getReg(reg, null); try reg_locks.append(self.register_manager.lockReg(reg)); + + if (fn_info.is_var_args and + fn_info.cc == .x86_64_win and + reg.class() == .sse and + arg_i < abi.Win64.c_abi_int_param_regs.len) + { + // Floating point arguments must be duplicated into the equivalent integer registers on this ABI + const int_reg = abi.Win64.c_abi_int_param_regs[arg_i]; + try reg_locks.append(self.register_manager.lockReg(int_reg)); + } }, .register_pair => |regs| { for (regs) |reg| try self.register_manager.getReg(reg, null); @@ -176129,7 +176148,7 @@ fn genCall(self: *CodeGen, info: union(enum) { else => unreachable, } - for (call_info.args, arg_types, args, frame_indices) |dst_arg, arg_ty, src_arg, frame_index| + for (call_info.args, arg_types, args, frame_indices, 0..) |dst_arg, arg_ty, src_arg, frame_index, arg_i| switch (dst_arg) { .none, .load_frame => {}, .register => |dst_reg| switch (fn_info.cc) { @@ -176144,6 +176163,16 @@ fn genCall(self: *CodeGen, info: union(enum) { try self.genSetReg(dst_alias, promoted_ty, src_arg, opts); if (promoted_ty.toIntern() != arg_ty.toIntern()) try self.truncateRegister(arg_ty, dst_alias); + + if (fn_info.is_var_args and + fn_info.cc == .x86_64_win and + dst_reg.class() == .sse and + arg_i < abi.Win64.c_abi_int_param_regs.len) + { + const int_dst_reg = abi.Win64.c_abi_int_param_regs[arg_i]; + const int_dst_alias = registerAlias(int_dst_reg, promoted_abi_size); + try self.genSetReg(int_dst_alias, promoted_ty, .{ .register = dst_alias }, opts); + } }, }, .register_pair => try self.genCopy(arg_ty, dst_arg, src_arg, opts), @@ -176180,7 +176209,8 @@ fn genCall(self: *CodeGen, info: union(enum) { else => unreachable, }; - if (fn_info.is_var_args) try self.asmRegisterImmediate(.{ ._, .mov }, .al, .u(call_info.fp_count)); + if (fn_info.is_var_args and fn_info.cc == .x86_64_sysv) + try self.asmRegisterImmediate(.{ ._, .mov }, .al, .u(call_info.fp_count)); // Due to incremental compilation, how function calls are generated depends // on linking. @@ -180782,7 +180812,18 @@ fn airVaStart(self: *CodeGen, inst: Air.Inst.Index) !void { field_off += @intCast(ptr_anyopaque_ty.abiSize(zcu)); break :result .{ .load_frame = .{ .index = dst_fi } }; }, - .x86_64_win => return self.fail("TODO implement c_va_start for Win64", .{}), + .x86_64_win => result: { + const dst_fi = try self.allocFrameIndex(.initSpill(va_list_ty, zcu)); + const fn_info = zcu.typeToFunc(self.fn_type).?; + try self.genSetMem( + .{ .frame = dst_fi }, + 0, + ptr_anyopaque_ty, + .{ .lea_frame = .{ .index = .args_frame, .off = @intCast(fn_info.param_types.len * 8) } }, + .{}, + ); + break :result .{ .load_frame = .{ .index = dst_fi } }; + }, else => |cc| return self.fail("{s} does not support var args", .{@tagName(cc)}), }; return self.finishAir(inst, result, .{ .none, .none, .none }); @@ -180921,48 +180962,103 @@ fn airVaArg(self: *CodeGen, inst: Air.Inst.Index) !void { break :result dst_mcv; } - assert(ty.toIntern() == .f32_type and promote_ty.toIntern() == .f64_type); - const dst_mcv = if (promote_mcv.isRegister()) - promote_mcv - else - try self.copyToRegisterWithInstTracking(inst, ty, promote_mcv); - const dst_reg = dst_mcv.getReg().?.to128(); - const dst_lock = self.register_manager.lockReg(dst_reg); - defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); - - if (self.hasFeature(.avx)) if (promote_mcv.isBase()) try self.asmRegisterRegisterMemory( - .{ .v_ss, .cvtsd2 }, - dst_reg, - dst_reg, - try promote_mcv.mem(self, .{ .size = .qword }), - ) else try self.asmRegisterRegisterRegister( - .{ .v_ss, .cvtsd2 }, - dst_reg, - dst_reg, - (if (promote_mcv.isRegister()) - promote_mcv.getReg().? - else - try self.copyToTmpRegister(promote_ty, promote_mcv)).to128(), - ) else if (promote_mcv.isBase()) try self.asmRegisterMemory( - .{ ._ss, .cvtsd2 }, - dst_reg, - try promote_mcv.mem(self, .{ .size = .qword }), - ) else try self.asmRegisterRegister( - .{ ._ss, .cvtsd2 }, - dst_reg, - (if (promote_mcv.isRegister()) - promote_mcv.getReg().? - else - try self.copyToTmpRegister(promote_ty, promote_mcv)).to128(), - ); + try self.convertFloatVarArg(inst, ty, promote_ty, promote_mcv); + break :result promote_mcv; + }, + .x86_64_win => result: { + try self.spillEflagsIfOccupied(); + + const promote_mcv = try self.allocTempRegOrMem(promote_ty, true); + const promote_lock = switch (promote_mcv) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (promote_lock) |lock| self.register_manager.unlockReg(lock); + + const ptr_arg_list_reg = + try self.copyToTmpRegister(self.typeOf(ty_op.operand), .{ .air_ref = ty_op.operand }); + const ptr_arg_list_lock = self.register_manager.lockRegAssumeUnused(ptr_arg_list_reg); + defer self.register_manager.unlockReg(ptr_arg_list_lock); + + const next_arg_ptr: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg } }; + + const class = abi.classifyWindows(promote_ty, zcu, self.target, .arg); + switch (class) { + .integer, .sse => { + const next_arg_ptr_reg = try self.copyToTmpRegister(.usize, next_arg_ptr); + if (!unused) try self.genCopy(promote_ty, promote_mcv, .{ + .indirect = .{ .reg = next_arg_ptr_reg }, + }, .{}); + try self.asmRegisterMemory(.{ ._, .lea }, next_arg_ptr_reg, .{ + .base = .{ .reg = next_arg_ptr_reg.to64() }, + .mod = .{ .rm = .{ .disp = 8 } }, + }); + try self.genCopy(.usize, next_arg_ptr, .{ .register = next_arg_ptr_reg }, .{}); + }, + .memory => unreachable, + else => return self.fail("TODO implement c_va_arg for {f} on Win64", .{promote_ty.fmt(pt)}), + } + + if (unused) break :result .unreach; + if (ty.toIntern() == promote_ty.toIntern()) break :result promote_mcv; + + if (!promote_ty.isRuntimeFloat()) { + const dst_mcv = try self.allocRegOrMem(inst, true); + try self.genCopy(ty, dst_mcv, promote_mcv, .{}); + break :result dst_mcv; + } + + try self.convertFloatVarArg(inst, ty, promote_ty, promote_mcv); break :result promote_mcv; }, - .x86_64_win => return self.fail("TODO implement c_va_arg for Win64", .{}), else => |cc| return self.fail("{s} does not support var args", .{@tagName(cc)}), }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn convertFloatVarArg( + self: *CodeGen, + inst: Air.Inst.Index, + ty: Type, + promote_ty: Type, + promote_mcv: MCValue, +) !void { + assert(ty.toIntern() == .f32_type and promote_ty.toIntern() == .f64_type); + const dst_mcv = if (promote_mcv.isRegister()) + promote_mcv + else + try self.copyToRegisterWithInstTracking(inst, ty, promote_mcv); + const dst_reg = dst_mcv.getReg().?.to128(); + const dst_lock = self.register_manager.lockReg(dst_reg); + defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); + + if (self.hasFeature(.avx)) if (promote_mcv.isBase()) try self.asmRegisterRegisterMemory( + .{ .v_ss, .cvtsd2 }, + dst_reg, + dst_reg, + try promote_mcv.mem(self, .{ .size = .qword }), + ) else try self.asmRegisterRegisterRegister( + .{ .v_ss, .cvtsd2 }, + dst_reg, + dst_reg, + (if (promote_mcv.isRegister()) + promote_mcv.getReg().? + else + try self.copyToTmpRegister(promote_ty, promote_mcv)).to128(), + ) else if (promote_mcv.isBase()) try self.asmRegisterMemory( + .{ ._ss, .cvtsd2 }, + dst_reg, + try promote_mcv.mem(self, .{ .size = .qword }), + ) else try self.asmRegisterRegister( + .{ ._ss, .cvtsd2 }, + dst_reg, + (if (promote_mcv.isRegister()) + promote_mcv.getReg().? + else + try self.copyToTmpRegister(promote_ty, promote_mcv)).to128(), + ); +} + fn airVaCopy(self: *CodeGen, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const ptr_va_list_ty = self.typeOf(ty_op.operand); diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index 5b71bcb4c3..a8affddfc4 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -101,7 +101,7 @@ test "simple variadic function" { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } - if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO + if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend != .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350 if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718 if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064 @@ -163,7 +163,7 @@ test "coerce reference to var arg" { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } - if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO + if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend != .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350 const S = struct { @@ -195,7 +195,7 @@ test "variadic functions" { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } - if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO + if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend != .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350 if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718 if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064 @@ -248,7 +248,7 @@ test "copy VaList" { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } - if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO + if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend != .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350 if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718 if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064 @@ -283,7 +283,7 @@ test "unused VaList arg" { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } - if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) { + if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend != .stage2_x86_64) { // https://github.com/ziglang/zig/issues/16961 return error.SkipZigTest; // TODO } @@ -305,3 +305,59 @@ test "unused VaList arg" { const x = S.thirdArg(0, @as(c_int, 1), @as(c_int, 2)); try std.testing.expectEqual(@as(c_int, 2), x); } + +test "floating point VaList args" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) { + // https://github.com/ziglang/zig/issues/14096 + return error.SkipZigTest; + } + if (builtin.cpu.arch == .x86_64 and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; + if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend != .stage2_x86_64) { + // https://github.com/ziglang/zig/issues/16961 + return error.SkipZigTest; // TODO + } + if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350 + if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718 + if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064 + + // Float register arguments are handled specially on cc == .x86_64_win, so it's important that we test all 4 slots + const S = struct { + fn proxy(...) callconv(.c) void { + var ap = @cVaStart(); + defer @cVaEnd(&ap); + + var out_f32: [3]f32 = undefined; + var out_f64: [3]f64 = undefined; + out_f32[0] = @cVaArg(&ap, f32); + out_f64[0] = @cVaArg(&ap, f64); + out_f32[1] = @cVaArg(&ap, f32); + out_f64[1] = @cVaArg(&ap, f64); + out_f32[2] = @cVaArg(&ap, f32); + out_f64[2] = @cVaArg(&ap, f64); + @cVaArg(&ap, *[3]f32).* = out_f32; + @cVaArg(&ap, *[3]f64).* = out_f64; + } + }; + + const expected_f32: []const f32 = &.{ 1000, std.math.floatMax(f32), std.math.floatMin(f32) }; + const expected_f64: []const f64 = &.{ 2000, std.math.floatMax(f64), std.math.floatMin(f64) }; + var actual_f32: [3]f32 = undefined; + var actual_f64: [3]f64 = undefined; + S.proxy( + expected_f32[0], + expected_f64[0], + expected_f32[1], + expected_f64[1], + expected_f32[2], + expected_f64[2], + &actual_f32, + &actual_f64, + ); + + try std.testing.expectEqualSlices(f32, expected_f32, &actual_f32); + try std.testing.expectEqualSlices(f64, expected_f64, &actual_f64); +} From 571b151423a5616802bcc235ed3bb0e76adf29d9 Mon Sep 17 00:00:00 2001 From: kcbanner Date: Thu, 26 Mar 2026 00:33:15 -0400 Subject: [PATCH 3/5] - Skip "floating point VaList args" on failing platforms --- test/behavior/var_args.zig | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index a8affddfc4..a82e8046e5 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -311,20 +311,12 @@ test "floating point VaList args" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) { - // https://github.com/ziglang/zig/issues/14096 - return error.SkipZigTest; - } - if (builtin.cpu.arch == .x86_64 and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend != .stage2_x86_64) { - // https://github.com/ziglang/zig/issues/16961 - return error.SkipZigTest; // TODO - } - if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350 - if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718 - if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064 + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/16961 + + // Float register arguments are handled specially on cc == .x86_64_win, so it's important that we test all 4 slots, + // and pre-C23 doesn't allow a variadic function without at least one non-variadic argument. + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - // Float register arguments are handled specially on cc == .x86_64_win, so it's important that we test all 4 slots const S = struct { fn proxy(...) callconv(.c) void { var ap = @cVaStart(); From 2b72c0d30473d020583b967fc8b494335f4fe75c Mon Sep 17 00:00:00 2001 From: kcbanner Date: Sat, 28 Mar 2026 02:23:14 -0400 Subject: [PATCH 4/5] - Fixup `producesCompilerRtDynLib` conditions - Fixup skipping the new float varags test on .stage2_llvm --- lib/std/Build/Step/Compile.zig | 4 +++- test/behavior/var_args.zig | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index a1987e5633..529b58a0dd 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -696,7 +696,9 @@ pub fn producesPdbFile(compile: *Compile) bool { pub fn producesCompilerRtDynLib(compile: *Compile) bool { if (compile.rootModuleTarget().ofmt != .coff) return false; - return compile.use_llvm == false; + if (compile.bundle_compiler_rt orelse (compile.kind == .exe or compile.isDynamicLibrary())) + return compile.use_llvm == false; + return false; } pub fn producesImplib(compile: *Compile) bool { diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index a82e8046e5..1ad6d7ddc0 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -101,7 +101,7 @@ test "simple variadic function" { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } - if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend != .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350 if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718 if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064 From 91dce64d10ec0da057c1bdceb586e56f7d265880 Mon Sep 17 00:00:00 2001 From: kcbanner Date: Sat, 11 Apr 2026 14:53:55 -0400 Subject: [PATCH 5/5] - Fixup != .stage2_x86_64 -> == .stage2_llvm in var_args tests --- test/behavior/var_args.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index 1ad6d7ddc0..21b35394cf 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -163,7 +163,7 @@ test "coerce reference to var arg" { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } - if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend != .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350 const S = struct { @@ -195,7 +195,7 @@ test "variadic functions" { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } - if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend != .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350 if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718 if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064 @@ -248,7 +248,7 @@ test "copy VaList" { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } - if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend != .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO if (builtin.cpu.arch == .s390x and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21350 if (builtin.cpu.arch.isSPARC() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23718 if (builtin.cpu.arch.isRISCV() and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25064 @@ -283,7 +283,7 @@ test "unused VaList arg" { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } - if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend != .stage2_x86_64) { + if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows and builtin.zig_backend == .stage2_llvm) { // https://github.com/ziglang/zig/issues/16961 return error.SkipZigTest; // TODO }