authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-06-19 19:31:25-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-06-20 21:08:23-04:00
log2e7bd58abaafae8a029bc1a372085ace6738a365
tree7392236a43c7daef8664814d926d482f67fef0cd
parentfba5c230f043a14e5b73c25add54cd1f2b15ba40

x86_64: fix incorrect lowering of win64 varargs


4 files changed, 608 insertions(+), 528 deletions(-)

src/codegen/x86_64/CodeGen.zig+542-512
......@@ -211,10 +211,10 @@ pub const MCValue = union(enum) {
211211 /// The value is in memory at a constant offset from the address in a register.
212212 indirect: bits.RegisterOffset,
213213 indirect_load_frame: bits.FrameAddr,
214 /// The value stored at an offset from a frame index
214 /// The value stored at an offset from a frame index.
215215 /// Payload is a frame address.
216216 load_frame: bits.FrameAddr,
217 /// The address of an offset from a frame index
217 /// The address of an offset from a frame index.
218218 /// Payload is a frame address.
219219 lea_frame: bits.FrameAddr,
220220 load_nav: InternPool.Nav.Index,
......@@ -225,15 +225,17 @@ pub const MCValue = union(enum) {
225225 lea_lazy_sym: link.File.LazySymbol,
226226 load_extern_func: Mir.NullTerminatedString,
227227 lea_extern_func: Mir.NullTerminatedString,
228 /// Supports `integer_per_element` abi
228 /// The value is duplicated in two different registers.
229 register_tee: [4]Register,
230 /// Supports `integer_per_element` abi.
229231 elementwise_gpr: ArgsInfo,
230 /// Supports `sse_per_element` abi
232 /// Supports `sse_per_element` abi.
231233 elementwise_sse: ArgsInfo,
232 /// Supports `sse_per_xword` abi
234 /// Supports `sse_per_xword` abi.
233235 xwordwise_sse: ArgsInfo,
234 /// Supports `sse_per_yword` abi
236 /// Supports `sse_per_yword` abi.
235237 ywordwise_sse: ArgsInfo,
236 /// Supports `sse_per_zword` abi
238 /// Supports `sse_per_zword` abi.
237239 zwordwise_sse: ArgsInfo,
238240 /// This indicates that we have already allocated a frame index for this instruction,
239241 /// but it has not been spilled there yet in the current control flow.
......@@ -261,6 +263,7 @@ pub const MCValue = union(enum) {
261263 .lea_lazy_sym,
262264 .lea_extern_func,
263265 .load_extern_func,
266 .register_tee,
264267 .elementwise_gpr,
265268 .elementwise_sse,
266269 .xwordwise_sse,
......@@ -386,6 +389,7 @@ pub const MCValue = union(enum) {
386389 .lea_uav,
387390 .lea_lazy_sym,
388391 .lea_extern_func,
392 .register_tee,
389393 .elementwise_gpr,
390394 .elementwise_sse,
391395 .xwordwise_sse,
......@@ -427,6 +431,7 @@ pub const MCValue = union(enum) {
427431 .load_uav,
428432 .load_lazy_sym,
429433 .load_extern_func,
434 .register_tee,
430435 .elementwise_gpr,
431436 .elementwise_sse,
432437 .xwordwise_sse,
......@@ -453,6 +458,7 @@ pub const MCValue = union(enum) {
453458 .unreach,
454459 .dead,
455460 .undef,
461 .register_tee,
456462 .elementwise_gpr,
457463 .elementwise_sse,
458464 .xwordwise_sse,
......@@ -511,6 +517,7 @@ pub const MCValue = union(enum) {
511517 .register_mask,
512518 .indirect_load_frame,
513519 .lea_frame,
520 .register_tee,
514521 .elementwise_gpr,
515522 .elementwise_sse,
516523 .xwordwise_sse,
......@@ -593,6 +600,7 @@ pub const MCValue = union(enum) {
593600 .lea_lazy_sym => |pl| try w.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }),
594601 .load_extern_func => |pl| try w.print("[extern:{d}]", .{@intFromEnum(pl)}),
595602 .lea_extern_func => |pl| try w.print("extern:{d}", .{@intFromEnum(pl)}),
603 .register_tee => |pl| try w.print("tee:{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }),
596604 .elementwise_gpr => |pl| try w.print("elementwise:gpr{d}:[{f} + 0x{x}]", .{
597605 pl.info.reg_index, pl.frame_index, pl.info.frame_off,
598606 }),
......@@ -640,6 +648,7 @@ const InstTracking = struct {
640648 .lea_extern_func,
641649 => result,
642650 .dead,
651 .register_tee,
643652 .elementwise_gpr,
644653 .elementwise_sse,
645654 .xwordwise_sse,
......@@ -756,6 +765,7 @@ const InstTracking = struct {
756765 .register_overflow,
757766 .register_mask,
758767 .indirect,
768 .register_tee,
759769 .elementwise_gpr,
760770 .elementwise_sse,
761771 .xwordwise_sse,
......@@ -174266,15 +174276,16 @@ fn restoreState(self: *CodeGen, state: State, deaths: []const Air.Inst.Index, co
174266174276 const ExpectedContents = [@typeInfo(RegisterManager.TrackedRegisters).array.len]RegisterLock;
174267174277 const bfa_buf_len = if (opts.update_tracking) 0 else 1;
174268174278 var bfa_buf: [bfa_buf_len]ExpectedContents = undefined;
174269 var stack = if (opts.update_tracking) {} else std.heap.BufferFirstAllocator.init(@ptrCast(&bfa_buf), self.gpa);
174279 var stack = if (!opts.update_tracking) std.heap.BufferFirstAllocator.init(@ptrCast(&bfa_buf), self.gpa);
174280 const allocator = if (!opts.update_tracking) stack.allocator();
174270174281
174271 var reg_locks = if (opts.update_tracking) {} else try std.array_list.Managed(RegisterLock).initCapacity(
174272 stack.allocator(),
174282 var reg_locks = if (!opts.update_tracking) try std.ArrayList(RegisterLock).initCapacity(
174283 allocator,
174273174284 @typeInfo(ExpectedContents).array.len,
174274174285 );
174275174286 defer if (!opts.update_tracking) {
174276174287 for (reg_locks.items) |lock| self.register_manager.unlockReg(lock);
174277 reg_locks.deinit();
174288 reg_locks.deinit(allocator);
174278174289 };
174279174290
174280174291 for (
......@@ -174304,7 +174315,7 @@ fn restoreState(self: *CodeGen, state: State, deaths: []const Air.Inst.Index, co
174304174315 self.inst_tracking.getPtr(target_inst).?.trackMaterialize(target_inst, reg_tracking);
174305174316 }
174306174317 } else if (target_maybe_inst) |_|
174307 try reg_locks.append(self.register_manager.lockRegIndexAssumeUnused(reg_index));
174318 try reg_locks.append(allocator, self.register_manager.lockRegIndexAssumeUnused(reg_index));
174308174319 }
174309174320 if (opts.emit_instructions) if (self.eflags_inst) |inst|
174310174321 try self.inst_tracking.getPtr(inst).?.spill(self, inst);
......@@ -174517,6 +174528,7 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE
174517174528 .register_overflow,
174518174529 .register_mask,
174519174530 .indirect_load_frame,
174531 .register_tee,
174520174532 .elementwise_gpr,
174521174533 .elementwise_sse,
174522174534 .xwordwise_sse,
......@@ -174574,6 +174586,7 @@ fn store(
174574174586 .register_overflow,
174575174587 .register_mask,
174576174588 .indirect_load_frame,
174589 .register_tee,
174577174590 .elementwise_gpr,
174578174591 .elementwise_sse,
174579174592 .xwordwise_sse,
......@@ -174628,6 +174641,7 @@ fn genUnOpMir(self: *CodeGen, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv:
174628174641 .lea_uav,
174629174642 .lea_lazy_sym,
174630174643 .lea_extern_func,
174644 .register_tee,
174631174645 .elementwise_gpr,
174632174646 .elementwise_sse,
174633174647 .xwordwise_sse,
......@@ -175322,6 +175336,7 @@ fn genBinOpMir(
175322175336 .lea_lazy_sym,
175323175337 .lea_extern_func,
175324175338 .indirect_load_frame,
175339 .register_tee,
175325175340 .elementwise_gpr,
175326175341 .elementwise_sse,
175327175342 .xwordwise_sse,
......@@ -175363,6 +175378,7 @@ fn genBinOpMir(
175363175378 .register_overflow,
175364175379 .register_mask,
175365175380 .indirect_load_frame,
175381 .register_tee,
175366175382 .elementwise_gpr,
175367175383 .elementwise_sse,
175368175384 .xwordwise_sse,
......@@ -175539,6 +175555,7 @@ fn genBinOpMir(
175539175555 .register_overflow,
175540175556 .register_mask,
175541175557 .indirect_load_frame,
175558 .register_tee,
175542175559 .elementwise_gpr,
175543175560 .elementwise_sse,
175544175561 .xwordwise_sse,
......@@ -175647,6 +175664,7 @@ fn genBinOpMir(
175647175664 .register_overflow,
175648175665 .register_mask,
175649175666 .indirect_load_frame,
175667 .register_tee,
175650175668 .elementwise_gpr,
175651175669 .elementwise_sse,
175652175670 .xwordwise_sse,
......@@ -176002,6 +176020,7 @@ fn genLocalDebugInfo(cg: *CodeGen, air_tag: Air.Inst.Tag, ty: Type, mcv: MCValue
176002176020 .none,
176003176021 .unreach,
176004176022 .dead,
176023 .register_tee,
176005176024 .elementwise_gpr,
176006176025 .elementwise_sse,
176007176026 .xwordwise_sse,
......@@ -176060,6 +176079,7 @@ fn genLocalDebugInfo(cg: *CodeGen, air_tag: Air.Inst.Tag, ty: Type, mcv: MCValue
176060176079 .none,
176061176080 .unreach,
176062176081 .dead,
176082 .register_tee,
176063176083 .elementwise_gpr,
176064176084 .elementwise_sse,
176065176085 .xwordwise_sse,
......@@ -176197,14 +176217,14 @@ fn genCall(cg: *CodeGen, info: union(enum) {
176197176217
176198176218 const var_args = try allocator.alloc(Type, args.len - fn_info.param_types.len);
176199176219 defer allocator.free(var_args);
176200 for (var_args, arg_types[fn_info.param_types.len..]) |*var_arg, arg_ty| var_arg.* = arg_ty;
176220 @memcpy(var_args, arg_types[fn_info.param_types.len..]);
176201176221
176202176222 const frame_indices = try allocator.alloc(FrameIndex, args.len);
176203176223 defer allocator.free(frame_indices);
176204176224
176205 var reg_locks: std.array_list.Managed(?RegisterLock) = .init(allocator);
176206 defer reg_locks.deinit();
176207 try reg_locks.ensureTotalCapacity(16);
176225 var reg_locks: std.ArrayList(?RegisterLock) = .empty;
176226 defer reg_locks.deinit(allocator);
176227 try reg_locks.ensureTotalCapacity(allocator, 16);
176208176228 defer for (reg_locks.items) |reg_lock| if (reg_lock) |lock| cg.register_manager.unlockReg(lock);
176209176229
176210176230 var call_info = try cg.resolveCallingConventionValues(fn_info, var_args, .call_frame);
......@@ -176235,169 +176255,165 @@ fn genCall(cg: *CodeGen, info: union(enum) {
176235176255 .indirect => |reg_off| try cg.register_manager.getReg(reg_off.reg, null),
176236176256 else => unreachable,
176237176257 }
176238 for (call_info.args, arg_types, args, frame_indices, 0..) |dst_arg, arg_ty, src_arg, *frame_index, arg_i|
176239 switch (dst_arg) {
176240 .none => {},
176241 .register => |reg| {
176258 for (call_info.args, arg_types, args, frame_indices) |dst_arg, arg_ty, src_arg, *frame_index| switch (dst_arg) {
176259 .none => {},
176260 .register => |reg| {
176261 try cg.register_manager.getReg(reg, null);
176262 try reg_locks.append(allocator, cg.register_manager.lockReg(reg));
176263 },
176264 inline .register_pair, .register_triple, .register_quadruple => |regs| {
176265 for (regs) |reg| try cg.register_manager.getReg(reg, null);
176266 try reg_locks.appendSlice(allocator, &cg.register_manager.lockRegs(regs.len, regs));
176267 },
176268 .indirect => |reg_off| {
176269 frame_index.* = try cg.allocFrameIndex(.initType(arg_ty, zcu));
176270 try cg.genSetMem(.{ .frame = frame_index.* }, 0, arg_ty, src_arg, opts);
176271 try cg.register_manager.getReg(reg_off.reg, null);
176272 try reg_locks.append(allocator, cg.register_manager.lockReg(reg_off.reg));
176273 },
176274 .indirect_load_frame => |frame_addr| {
176275 frame_index.* = try cg.allocFrameIndex(.initType(arg_ty, zcu));
176276 try cg.genSetMem(.{ .frame = frame_index.* }, 0, arg_ty, src_arg, opts);
176277 try cg.genSetMem(
176278 .{ .frame = frame_addr.index },
176279 frame_addr.off,
176280 .usize,
176281 .{ .lea_frame = .{ .index = frame_index.* } },
176282 opts,
176283 );
176284 },
176285 .load_frame => {
176286 try cg.genCopy(arg_ty, dst_arg, src_arg, opts);
176287 try cg.freeValue(src_arg, .{});
176288 },
176289 .register_tee => |regs| {
176290 try reg_locks.ensureUnusedCapacity(allocator, regs.len);
176291 for (regs) |reg| if (reg != .none) {
176242176292 try cg.register_manager.getReg(reg, null);
176243 try reg_locks.append(cg.register_manager.lockReg(reg));
176244
176245 if (fn_info.is_var_args and
176246 fn_info.cc == .x86_64_win and
176247 reg.class() == .sse and
176248 arg_i < abi.Win64.c_abi_int_param_regs.len)
176249 {
176250 // Floating point arguments must be duplicated into the equivalent integer registers on this ABI
176251 const int_reg = abi.Win64.c_abi_int_param_regs[arg_i];
176252 try reg_locks.append(cg.register_manager.lockReg(int_reg));
176253 }
176254 },
176255 .register_pair => |regs| {
176256 for (regs) |reg| try cg.register_manager.getReg(reg, null);
176257 try reg_locks.appendSlice(&cg.register_manager.lockRegs(2, regs));
176258 },
176259 .indirect => |reg_off| {
176260 frame_index.* = try cg.allocFrameIndex(.initType(arg_ty, zcu));
176261 try cg.genSetMem(.{ .frame = frame_index.* }, 0, arg_ty, src_arg, opts);
176262 try cg.register_manager.getReg(reg_off.reg, null);
176263 try reg_locks.append(cg.register_manager.lockReg(reg_off.reg));
176264 },
176265 .indirect_load_frame => |frame_addr| {
176266 frame_index.* = try cg.allocFrameIndex(.initType(arg_ty, zcu));
176267 try cg.genSetMem(.{ .frame = frame_index.* }, 0, arg_ty, src_arg, opts);
176268 try cg.genSetMem(
176269 .{ .frame = frame_addr.index },
176270 frame_addr.off,
176271 .usize,
176272 .{ .lea_frame = .{ .index = frame_index.* } },
176273 opts,
176274 );
176275 },
176276 .load_frame => {
176277 try cg.genCopy(arg_ty, dst_arg, src_arg, opts);
176278 try cg.freeValue(src_arg, .{});
176279 },
176280 .elementwise_gpr, .elementwise_sse => |regs_frame_addr| {
176281 const src_mem: Memory = if (src_arg.isBase()) try src_arg.mem(cg, .{ .size = .dword }) else .{
176282 .base = .{ .reg = try cg.copyToTmpRegister(.usize, switch (src_arg) {
176283 else => src_arg,
176284 .air_ref => |src_ref| try cg.resolveInst(src_ref),
176285 }.address()) },
176286 .mod = .{ .rm = .{ .size = .dword } },
176287 };
176288 const src_lock = switch (src_mem.base) {
176289 .reg => |src_reg| cg.register_manager.lockReg(src_reg),
176290 else => null,
176291 };
176292 defer if (src_lock) |lock| cg.register_manager.unlockReg(lock);
176293
176294 const elem_rc: Register.Class, const param_regs = switch (dst_arg) {
176295 else => unreachable,
176296 .elementwise_gpr => .{ .general_purpose, abi.getCAbiIntParamRegs(fn_info.cc) },
176297 .elementwise_sse => .{ .sse, abi.getCAbiSseParamRegs(fn_info.cc, cg.target) },
176298 };
176299 const len = arg_ty.vectorLen(zcu);
176300 const param_reg_len: u31 =
176301 @intCast(@min(param_regs.len - regs_frame_addr.info.reg_index, len));
176293 reg_locks.appendAssumeCapacity(cg.register_manager.lockReg(reg));
176294 };
176295 },
176296 .elementwise_gpr, .elementwise_sse => |regs_frame_addr| {
176297 const src_mem: Memory = if (src_arg.isBase()) try src_arg.mem(cg, .{ .size = .dword }) else .{
176298 .base = .{ .reg = try cg.copyToTmpRegister(.usize, switch (src_arg) {
176299 else => src_arg,
176300 .air_ref => |src_ref| try cg.resolveInst(src_ref),
176301 }.address()) },
176302 .mod = .{ .rm = .{ .size = .dword } },
176303 };
176304 const src_lock = switch (src_mem.base) {
176305 .reg => |src_reg| cg.register_manager.lockReg(src_reg),
176306 else => null,
176307 };
176308 defer if (src_lock) |lock| cg.register_manager.unlockReg(lock);
176302176309
176303 if (len - param_reg_len > 0) {
176304 const index_reg = try cg.register_manager.allocReg(null, abi.RegisterClass.gp);
176305 const index_lock = cg.register_manager.lockRegAssumeUnused(index_reg);
176306 defer cg.register_manager.unlockReg(index_lock);
176307 try cg.asmRegisterImmediate(.{ ._, .mov }, index_reg.to32(), .u(param_reg_len));
176310 const elem_rc: Register.Class, const param_regs = switch (dst_arg) {
176311 else => unreachable,
176312 .elementwise_gpr => .{ .general_purpose, abi.getCAbiIntParamRegs(fn_info.cc) },
176313 .elementwise_sse => .{ .sse, abi.getCAbiSseParamRegs(fn_info.cc, cg.target) },
176314 };
176315 const len = arg_ty.vectorLen(zcu);
176316 const param_reg_len: u31 =
176317 @intCast(@min(param_regs.len - regs_frame_addr.info.reg_index, len));
176308176318
176309 const loop: Mir.Inst.Index = @intCast(cg.mir_instructions.len);
176310 const elem_ty = arg_ty.childType(zcu);
176311 if (elem_ty.toIntern() == .bool_type) {
176312 try cg.asmMemoryRegister(.{ ._, .bt }, src_mem, index_reg.to32());
176313 try cg.asmSetccMemory(.c, .{
176314 .base = .{ .frame = regs_frame_addr.frame_index },
176315 .mod = .{ .rm = .{
176316 .size = .byte,
176317 .index = index_reg.to64(),
176318 .scale = .@"8",
176319 .disp = @as(i32, regs_frame_addr.info.frame_off) - 8 * param_reg_len,
176320 } },
176321 });
176322 } else {
176323 const elem_reg =
176324 try cg.register_manager.allocReg(null, regSetForRegClass(elem_rc));
176325 const elem_lock = cg.register_manager.lockRegAssumeUnused(elem_reg);
176326 defer cg.register_manager.unlockReg(elem_lock);
176327 const elem_size = cg.memSize(elem_ty, .general_purpose);
176328 const elem_alias = elem_reg.toSize(elem_size, cg.target);
176319 if (len - param_reg_len > 0) {
176320 const index_reg = try cg.register_manager.allocReg(null, abi.RegisterClass.gp);
176321 const index_lock = cg.register_manager.lockRegAssumeUnused(index_reg);
176322 defer cg.register_manager.unlockReg(index_lock);
176323 try cg.asmRegisterImmediate(.{ ._, .mov }, index_reg.to32(), .u(param_reg_len));
176329176324
176330 const strat = try cg.moveStrategy(elem_ty, elem_rc, true);
176331 assert(src_mem.mod.rm.index == .none and src_mem.mod.rm.scale == .@"1");
176332 try strat.read(cg, elem_alias, .{
176333 .base = src_mem.base,
176334 .mod = .{ .rm = .{
176335 .size = elem_size,
176336 .index = index_reg.to64(),
176337 .scale = .fromFactor(@intCast(@divExact(elem_size.bitSize(cg.target), 8))),
176338 .disp = src_mem.mod.rm.disp,
176339 } },
176340 });
176341 try strat.write(cg, .{
176342 .base = .{ .frame = regs_frame_addr.frame_index },
176343 .mod = .{ .rm = .{
176344 .size = elem_size,
176345 .index = index_reg.to64(),
176346 .scale = .@"8",
176347 .disp = @as(i32, regs_frame_addr.info.frame_off) - 8 * param_reg_len,
176348 } },
176349 }, elem_alias);
176350 }
176351 if (cg.hasFeature(.slow_incdec)) {
176352 try cg.asmRegisterImmediate(.{ ._, .add }, index_reg.to32(), .u(1));
176353 } else {
176354 try cg.asmRegister(.{ ._c, .in }, index_reg.to32());
176355 }
176356 try cg.asmRegisterImmediate(.{ ._, .cmp }, index_reg.to32(), .u(len));
176357 _ = try cg.asmJccReloc(.b, loop);
176325 const loop: Mir.Inst.Index = @intCast(cg.mir_instructions.len);
176326 const elem_ty = arg_ty.childType(zcu);
176327 if (elem_ty.toIntern() == .bool_type) {
176328 try cg.asmMemoryRegister(.{ ._, .bt }, src_mem, index_reg.to32());
176329 try cg.asmSetccMemory(.c, .{
176330 .base = .{ .frame = regs_frame_addr.frame_index },
176331 .mod = .{ .rm = .{
176332 .size = .byte,
176333 .index = index_reg.to64(),
176334 .scale = .@"8",
176335 .disp = @as(i32, regs_frame_addr.info.frame_off) - 8 * param_reg_len,
176336 } },
176337 });
176338 } else {
176339 const elem_reg =
176340 try cg.register_manager.allocReg(null, regSetForRegClass(elem_rc));
176341 const elem_lock = cg.register_manager.lockRegAssumeUnused(elem_reg);
176342 defer cg.register_manager.unlockReg(elem_lock);
176343 const elem_size = cg.memSize(elem_ty, .general_purpose);
176344 const elem_alias = elem_reg.toSize(elem_size, cg.target);
176345
176346 const strat = try cg.moveStrategy(elem_ty, elem_rc, true);
176347 assert(src_mem.mod.rm.index == .none and src_mem.mod.rm.scale == .@"1");
176348 try strat.read(cg, elem_alias, .{
176349 .base = src_mem.base,
176350 .mod = .{ .rm = .{
176351 .size = elem_size,
176352 .index = index_reg.to64(),
176353 .scale = .fromFactor(@intCast(@divExact(elem_size.bitSize(cg.target), 8))),
176354 .disp = src_mem.mod.rm.disp,
176355 } },
176356 });
176357 try strat.write(cg, .{
176358 .base = .{ .frame = regs_frame_addr.frame_index },
176359 .mod = .{ .rm = .{
176360 .size = elem_size,
176361 .index = index_reg.to64(),
176362 .scale = .@"8",
176363 .disp = @as(i32, regs_frame_addr.info.frame_off) - 8 * param_reg_len,
176364 } },
176365 }, elem_alias);
176358176366 }
176359
176360 for (param_regs[regs_frame_addr.info.reg_index..][0..param_reg_len]) |dst_reg| {
176361 try cg.register_manager.getReg(dst_reg, null);
176362 try reg_locks.append(cg.register_manager.lockReg(dst_reg));
176367 if (cg.hasFeature(.slow_incdec)) {
176368 try cg.asmRegisterImmediate(.{ ._, .add }, index_reg.to32(), .u(1));
176369 } else {
176370 try cg.asmRegister(.{ ._c, .in }, index_reg.to32());
176363176371 }
176364 },
176365 .xwordwise_sse, .ywordwise_sse, .zwordwise_sse => |regs_frame_addr| {
176366 const elem_size: u31 = switch (dst_arg) {
176367 else => unreachable,
176368 .xwordwise_sse => 16,
176369 .ywordwise_sse => 32,
176370 .zwordwise_sse => 64,
176371 };
176372 const param_gpr_regs = abi.getCAbiIntParamRegs(fn_info.cc);
176373 const arg_size: u31 = @intCast(arg_ty.abiSize(zcu));
176374 const len = @divExact(arg_size, elem_size);
176375 const param_gpr_len: u31 =
176376 @intCast(@min(param_gpr_regs.len - regs_frame_addr.info.reg_index, len));
176372 try cg.asmRegisterImmediate(.{ ._, .cmp }, index_reg.to32(), .u(len));
176373 _ = try cg.asmJccReloc(.b, loop);
176374 }
176377176375
176378 frame_index.* = try cg.allocFrameIndex(.initType(arg_ty, zcu));
176379 try cg.genSetMem(.{ .frame = frame_index.* }, 0, arg_ty, src_arg, opts);
176380
176381 var frame_offset: i32 = regs_frame_addr.info.frame_off;
176382 var arg_offset = elem_size * param_gpr_len;
176383 while (arg_size - arg_offset != 0) : ({
176384 frame_offset += 8;
176385 arg_offset += elem_size;
176386 }) try cg.genSetMem(
176387 .{ .frame = regs_frame_addr.frame_index },
176388 frame_offset,
176389 .usize,
176390 .{ .lea_frame = .{ .index = frame_index.*, .off = arg_offset } },
176391 opts,
176392 );
176376 for (param_regs[regs_frame_addr.info.reg_index..][0..param_reg_len]) |dst_reg| {
176377 try cg.register_manager.getReg(dst_reg, null);
176378 try reg_locks.append(allocator, cg.register_manager.lockReg(dst_reg));
176379 }
176380 },
176381 .xwordwise_sse, .ywordwise_sse, .zwordwise_sse => |regs_frame_addr| {
176382 const elem_size: u31 = switch (dst_arg) {
176383 else => unreachable,
176384 .xwordwise_sse => 16,
176385 .ywordwise_sse => 32,
176386 .zwordwise_sse => 64,
176387 };
176388 const param_gpr_regs = abi.getCAbiIntParamRegs(fn_info.cc);
176389 const arg_size: u31 = @intCast(arg_ty.abiSize(zcu));
176390 const len = @divExact(arg_size, elem_size);
176391 const param_gpr_len: u31 =
176392 @intCast(@min(param_gpr_regs.len - regs_frame_addr.info.reg_index, len));
176393
176394 frame_index.* = try cg.allocFrameIndex(.initType(arg_ty, zcu));
176395 try cg.genSetMem(.{ .frame = frame_index.* }, 0, arg_ty, src_arg, opts);
176396
176397 var frame_offset: i32 = regs_frame_addr.info.frame_off;
176398 var arg_offset = elem_size * param_gpr_len;
176399 while (arg_size - arg_offset != 0) : ({
176400 frame_offset += 8;
176401 arg_offset += elem_size;
176402 }) try cg.genSetMem(
176403 .{ .frame = regs_frame_addr.frame_index },
176404 frame_offset,
176405 .usize,
176406 .{ .lea_frame = .{ .index = frame_index.*, .off = arg_offset } },
176407 opts,
176408 );
176393176409
176394 for (param_gpr_regs[regs_frame_addr.info.reg_index..][0..param_gpr_len]) |dst_reg| {
176395 try cg.register_manager.getReg(dst_reg, null);
176396 try reg_locks.append(cg.register_manager.lockReg(dst_reg));
176397 }
176398 },
176399 else => unreachable,
176400 };
176410 for (param_gpr_regs[regs_frame_addr.info.reg_index..][0..param_gpr_len]) |dst_reg| {
176411 try cg.register_manager.getReg(dst_reg, null);
176412 try reg_locks.append(allocator, cg.register_manager.lockReg(dst_reg));
176413 }
176414 },
176415 else => unreachable,
176416 };
176401176417
176402176418 if (call_info.err_ret_trace_reg != .none) {
176403176419 if (cg.inst_tracking.getPtr(err_ret_trace_index)) |err_ret_trace| {
......@@ -176406,7 +176422,7 @@ fn genCall(cg: *CodeGen, info: union(enum) {
176406176422 else => true,
176407176423 }) {
176408176424 try cg.register_manager.getReg(call_info.err_ret_trace_reg, err_ret_trace_index);
176409 try reg_locks.append(cg.register_manager.lockReg(call_info.err_ret_trace_reg));
176425 try reg_locks.append(allocator, cg.register_manager.lockReg(call_info.err_ret_trace_reg));
176410176426
176411176427 try cg.genSetReg(call_info.err_ret_trace_reg, .usize, err_ret_trace.short, opts);
176412176428 err_ret_trace.trackMaterialize(err_ret_trace_index, .{
......@@ -176427,114 +176443,102 @@ fn genCall(cg: *CodeGen, info: union(enum) {
176427176443 .lea_frame = .{ .index = frame_index, .off = -reg_off.off },
176428176444 }, opts);
176429176445 call_info.return_value.short = .{ .load_frame = .{ .index = frame_index } };
176430 try reg_locks.append(cg.register_manager.lockReg(reg_off.reg));
176446 try reg_locks.append(allocator, cg.register_manager.lockReg(reg_off.reg));
176431176447 },
176432176448 else => unreachable,
176433176449 }
176434176450
176435 for (call_info.args, arg_types, args, frame_indices, 0..) |dst_arg, arg_ty, src_arg, frame_index, arg_i|
176436 switch (dst_arg) {
176437 .none, .load_frame, .indirect_load_frame => {},
176438 .register => |dst_reg| switch (fn_info.cc) {
176439 else => try cg.genSetReg(registerAlias(
176440 dst_reg,
176441 @intCast(arg_ty.abiSize(zcu)),
176442 ), arg_ty, src_arg, opts),
176443 .x86_64_sysv, .x86_64_win => {
176444 const promoted_ty = cg.promoteInt(arg_ty);
176445 const promoted_unaligned_size: u32 = @intCast(cg.unalignedSize(promoted_ty));
176446 const dst_alias = registerAlias(dst_reg, promoted_unaligned_size);
176447 try cg.genSetReg(dst_alias, promoted_ty, src_arg, opts);
176448 if (promoted_ty.toIntern() != arg_ty.toIntern())
176449 try cg.truncateRegister(arg_ty, dst_alias);
176450
176451 if (fn_info.is_var_args and
176452 fn_info.cc == .x86_64_win and
176453 dst_reg.class() == .sse and
176454 arg_i < abi.Win64.c_abi_int_param_regs.len)
176455 {
176456 const int_dst_reg = abi.Win64.c_abi_int_param_regs[arg_i];
176457 const int_dst_alias = registerAlias(int_dst_reg, promoted_unaligned_size);
176458 try cg.genSetReg(int_dst_alias, promoted_ty, .{ .register = dst_alias }, opts);
176459 }
176460 },
176461 },
176462 .register_pair => try cg.genCopy(arg_ty, dst_arg, src_arg, opts),
176463 .indirect => |reg_off| try cg.genSetReg(reg_off.reg, .usize, .{
176464 .lea_frame = .{ .index = frame_index, .off = -reg_off.off },
176465 }, opts),
176466 .elementwise_gpr, .elementwise_sse => |regs_frame_addr| {
176467 const src_mem: Memory = if (src_arg.isBase()) try src_arg.mem(cg, .{ .size = .dword }) else .{
176468 .base = .{ .reg = try cg.copyToTmpRegister(
176469 .usize,
176470 switch (src_arg) {
176471 else => src_arg,
176472 .air_ref => |src_ref| try cg.resolveInst(src_ref),
176473 }.address(),
176474 ) },
176475 .mod = .{ .rm = .{ .size = .dword } },
176476 };
176477 const src_lock = switch (src_mem.base) {
176478 .reg => |src_reg| cg.register_manager.lockReg(src_reg),
176479 else => null,
176480 };
176481 defer if (src_lock) |lock| cg.register_manager.unlockReg(lock);
176451 for (call_info.args, arg_types, args, frame_indices) |dst_arg, arg_ty, src_arg, frame_index| switch (dst_arg) {
176452 .none, .load_frame, .indirect_load_frame => {},
176453 .register => |dst_reg| try cg.genSetReg(registerAlias(
176454 dst_reg,
176455 @intCast(cg.unalignedSize(arg_ty)),
176456 ), arg_ty, src_arg, opts),
176457 .register_pair,
176458 .register_triple,
176459 .register_quadruple,
176460 => try cg.genCopy(arg_ty, dst_arg, src_arg, opts),
176461 .indirect => |reg_off| try cg.genSetReg(reg_off.reg, .usize, .{
176462 .lea_frame = .{ .index = frame_index, .off = -reg_off.off },
176463 }, opts),
176464 .register_tee => |dst_regs| {
176465 try cg.genSetReg(dst_regs[0], arg_ty, src_arg, opts);
176466 for (dst_regs[1..]) |dst_reg| if (dst_reg != .none) try cg.genSetReg(dst_reg, arg_ty, .{
176467 .register = dst_regs[0],
176468 }, opts);
176469 },
176470 .elementwise_gpr, .elementwise_sse => |regs_frame_addr| {
176471 const src_mem: Memory = if (src_arg.isBase()) try src_arg.mem(cg, .{ .size = .dword }) else .{
176472 .base = .{ .reg = try cg.copyToTmpRegister(
176473 .usize,
176474 switch (src_arg) {
176475 else => src_arg,
176476 .air_ref => |src_ref| try cg.resolveInst(src_ref),
176477 }.address(),
176478 ) },
176479 .mod = .{ .rm = .{ .size = .dword } },
176480 };
176481 const src_lock = switch (src_mem.base) {
176482 .reg => |src_reg| cg.register_manager.lockReg(src_reg),
176483 else => null,
176484 };
176485 defer if (src_lock) |lock| cg.register_manager.unlockReg(lock);
176482176486
176483 const elem_rc: Register.Class, const param_regs = switch (dst_arg) {
176484 else => unreachable,
176485 .elementwise_gpr => .{ .general_purpose, abi.getCAbiIntParamRegs(fn_info.cc) },
176486 .elementwise_sse => .{ .sse, abi.getCAbiSseParamRegs(fn_info.cc, cg.target) },
176487 };
176488 const len = arg_ty.vectorLen(zcu);
176489 const elem_ty = arg_ty.childType(zcu);
176490 const elem_size = cg.memSize(elem_ty, .general_purpose);
176491 const elem_abi_size = @divExact(elem_size.bitSize(cg.target), 8);
176492 const param_reg_len: u31 =
176493 @intCast(@min(param_regs.len - regs_frame_addr.info.reg_index, len));
176494 const strat = if (elem_ty.toIntern() == .bool_type) strat: {
176495 try cg.spillEflagsIfOccupied();
176496 break :strat undefined;
176497 } else try cg.moveStrategy(elem_ty, elem_rc, true);
176498 for (
176499 param_regs[regs_frame_addr.info.reg_index..][0..param_reg_len],
176500 0..,
176501 ) |dst_reg, elem_index| if (elem_ty.toIntern() == .bool_type) {
176502 try cg.asmRegisterRegister(.{ ._, .xor }, dst_reg.to32(), dst_reg.to32());
176503 try cg.asmMemoryImmediate(.{ ._, .bt }, src_mem, .u(elem_index));
176504 try cg.asmSetccRegister(.c, dst_reg.to8());
176505 } else try strat.read(cg, dst_reg.toSize(elem_size, cg.target), .{
176506 .base = src_mem.base,
176507 .mod = .{ .rm = .{
176508 .size = elem_size,
176509 .disp = src_mem.mod.rm.disp + @as(u31, @intCast(elem_abi_size * elem_index)),
176510 } },
176511 });
176512 },
176513 .xwordwise_sse, .ywordwise_sse, .zwordwise_sse => |regs_frame_addr| {
176514 const elem_size: u31 = switch (dst_arg) {
176515 else => unreachable,
176516 .xwordwise_sse => 16,
176517 .ywordwise_sse => 32,
176518 .zwordwise_sse => 64,
176519 };
176520 const param_gpr_regs = abi.getCAbiIntParamRegs(fn_info.cc);
176521 const len = @divExact(arg_ty.abiSize(zcu), elem_size);
176522 const param_gpr_len: u31 =
176523 @intCast(@min(param_gpr_regs.len - regs_frame_addr.info.reg_index, len));
176487 const elem_rc: Register.Class, const param_regs = switch (dst_arg) {
176488 else => unreachable,
176489 .elementwise_gpr => .{ .general_purpose, abi.getCAbiIntParamRegs(fn_info.cc) },
176490 .elementwise_sse => .{ .sse, abi.getCAbiSseParamRegs(fn_info.cc, cg.target) },
176491 };
176492 const len = arg_ty.vectorLen(zcu);
176493 const elem_ty = arg_ty.childType(zcu);
176494 const elem_size = cg.memSize(elem_ty, .general_purpose);
176495 const elem_abi_size = @divExact(elem_size.bitSize(cg.target), 8);
176496 const param_reg_len: u31 =
176497 @intCast(@min(param_regs.len - regs_frame_addr.info.reg_index, len));
176498 const strat = if (elem_ty.toIntern() == .bool_type) strat: {
176499 try cg.spillEflagsIfOccupied();
176500 break :strat undefined;
176501 } else try cg.moveStrategy(elem_ty, elem_rc, true);
176502 for (
176503 param_regs[regs_frame_addr.info.reg_index..][0..param_reg_len],
176504 0..,
176505 ) |dst_reg, elem_index| if (elem_ty.toIntern() == .bool_type) {
176506 try cg.asmRegisterRegister(.{ ._, .xor }, dst_reg.to32(), dst_reg.to32());
176507 try cg.asmMemoryImmediate(.{ ._, .bt }, src_mem, .u(elem_index));
176508 try cg.asmSetccRegister(.c, dst_reg.to8());
176509 } else try strat.read(cg, dst_reg.toSize(elem_size, cg.target), .{
176510 .base = src_mem.base,
176511 .mod = .{ .rm = .{
176512 .size = elem_size,
176513 .disp = src_mem.mod.rm.disp + @as(u31, @intCast(elem_abi_size * elem_index)),
176514 } },
176515 });
176516 },
176517 .xwordwise_sse, .ywordwise_sse, .zwordwise_sse => |regs_frame_addr| {
176518 const elem_size: u31 = switch (dst_arg) {
176519 else => unreachable,
176520 .xwordwise_sse => 16,
176521 .ywordwise_sse => 32,
176522 .zwordwise_sse => 64,
176523 };
176524 const param_gpr_regs = abi.getCAbiIntParamRegs(fn_info.cc);
176525 const len = @divExact(arg_ty.abiSize(zcu), elem_size);
176526 const param_gpr_len: u31 =
176527 @intCast(@min(param_gpr_regs.len - regs_frame_addr.info.reg_index, len));
176524176528
176525 var arg_offset: u31 = 0;
176526 for (param_gpr_regs[regs_frame_addr.info.reg_index..][0..param_gpr_len]) |dst_reg| {
176527 try cg.genSetReg(
176528 dst_reg,
176529 .usize,
176530 .{ .lea_frame = .{ .index = frame_index, .off = arg_offset } },
176531 opts,
176532 );
176533 arg_offset += elem_size;
176534 }
176535 },
176536 else => unreachable,
176537 };
176529 var arg_offset: u31 = 0;
176530 for (param_gpr_regs[regs_frame_addr.info.reg_index..][0..param_gpr_len]) |dst_reg| {
176531 try cg.genSetReg(
176532 dst_reg,
176533 .usize,
176534 .{ .lea_frame = .{ .index = frame_index, .off = arg_offset } },
176535 opts,
176536 );
176537 arg_offset += elem_size;
176538 }
176539 },
176540 else => unreachable,
176541 };
176538176542
176539176543 if (fn_info.is_var_args and fn_info.cc == .x86_64_sysv)
176540176544 try cg.asmRegisterImmediate(.{ ._, .mov }, .al, .u(call_info.fp_count));
......@@ -177474,13 +177478,13 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
177474177478 const inputs = unwrapped_asm.inputs;
177475177479
177476177480 var result: MCValue = .none;
177477 var args: std.array_list.Managed(MCValue) = .init(self.gpa);
177478 try args.ensureTotalCapacity(outputs.len + inputs.len);
177481 var args = try self.gpa.alloc(MCValue, outputs.len + inputs.len);
177482 var args_len: usize = 0;
177479177483 defer {
177480 for (args.items) |arg| if (arg.getReg()) |reg| self.register_manager.unlockReg(.{
177484 for (args[0..args_len]) |arg| if (arg.getReg()) |reg| self.register_manager.unlockReg(.{
177481177485 .tracked_index = RegisterManager.indexOfRegIntoTracked(reg) orelse continue,
177482177486 });
177483 args.deinit();
177487 self.gpa.free(args);
177484177488 }
177485177489 var arg_map: std.StringHashMap(u8) = .init(self.gpa);
177486177490 try arg_map.ensureTotalCapacity(@intCast(outputs.len + inputs.len));
......@@ -177539,10 +177543,10 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
177539177543 return self.fail("invalid register constraint: '{s}'", .{out.constraint})
177540177544 else if (rest.len == 1 and std.ascii.isDigit(rest[0])) {
177541177545 const index = std.fmt.charToDigit(rest[0], 10) catch unreachable;
177542 if (index >= args.items.len) return self.fail("constraint out of bounds: '{s}'", .{
177546 if (index >= args_len) return self.fail("constraint out of bounds: '{s}'", .{
177543177547 out.constraint,
177544177548 });
177545 break :arg_mcv args.items[index];
177549 break :arg_mcv args[index];
177546177550 } else return self.fail("invalid constraint: '{s}'", .{out.constraint});
177547177551 break :arg_mcv if (arg_maybe_reg) |reg| .{ .register = reg } else arg: {
177548177552 const ptr_mcv = try self.resolveInst(out.operand);
......@@ -177560,8 +177564,9 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
177560177564 _ = self.register_manager.lockRegIndexAssumeUnused(tracked_index);
177561177565 };
177562177566 if (!std.mem.eql(u8, out.name, "_"))
177563 arg_map.putAssumeCapacityNoClobber(out.name, @intCast(args.items.len));
177564 args.appendAssumeCapacity(arg_mcv);
177567 arg_map.putAssumeCapacityNoClobber(out.name, @intCast(args_len));
177568 args[args_len] = arg_mcv;
177569 args_len += 1;
177565177570 if (out.operand == .none) result = arg_mcv;
177566177571 if (is_read) try self.load(arg_mcv, self.typeOf(out.operand), .{ .air_ref = out.operand });
177567177572 }
......@@ -177644,17 +177649,19 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
177644177649 break :arg .{ .register = reg };
177645177650 } else if (in.constraint.len == 1 and std.ascii.isDigit(in.constraint[0])) arg: {
177646177651 const index = std.fmt.charToDigit(in.constraint[0], 10) catch unreachable;
177647 if (index >= args.items.len) return self.fail("constraint out of bounds: '{s}'", .{in.constraint});
177648 try self.genCopy(ty, args.items[index], input_mcv, .{});
177649 break :arg args.items[index];
177652 if (index >= args_len) return self.fail("constraint out of bounds: '{s}'", .{in.constraint});
177653 try self.genCopy(ty, args[index], input_mcv, .{});
177654 break :arg args[index];
177650177655 } else return self.fail("invalid constraint: '{s}'", .{in.constraint});
177651177656 if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| {
177652177657 _ = self.register_manager.lockReg(reg);
177653177658 };
177654177659 if (!std.mem.eql(u8, in.name, "_"))
177655 arg_map.putAssumeCapacityNoClobber(in.name, @intCast(args.items.len));
177656 args.appendAssumeCapacity(arg_mcv);
177660 arg_map.putAssumeCapacityNoClobber(in.name, @intCast(args_len));
177661 args[args_len] = arg_mcv;
177662 args_len += 1;
177657177663 }
177664 assert(args_len == args.len);
177658177665
177659177666 const ip = &zcu.intern_pool;
177660177667 const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
......@@ -177887,7 +177894,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
177887177894 op_str[colon_pos + ":".len .. op_str.len - "]".len]
177888177895 else
177889177896 "";
177890 op.* = switch (args.items[
177897 op.* = switch (args[
177891177898 arg_map.get(op_str["%[".len .. colon orelse op_str.len - "]".len]) orelse
177892177899 return self.fail("no matching constraint: '{s}'", .{op_str})
177893177900 ]) {
......@@ -178031,7 +178038,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
178031178038 op_str[colon_pos + ":".len .. open - "]".len]
178032178039 else
178033178040 "";
178034 break :disp switch (args.items[
178041 break :disp switch (args[
178035178042 arg_map.get(op_str["%[".len .. colon orelse open - "]".len]) orelse
178036178043 return self.fail("no matching constraint: '{s}'", .{op_str})
178037178044 ]) {
......@@ -178191,7 +178198,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
178191178198
178192178199 it = unwrapped_asm.iterateOutputs();
178193178200 while (it.next()) |out| {
178194 const arg_mcv = args.items[it.current - 1];
178201 const arg_mcv = args[it.current - 1];
178195178202 if (out.operand == .none) continue;
178196178203 if (arg_mcv != .register) continue;
178197178204 if (out.constraint.len == 2 and std.ascii.isDigit(out.constraint[1])) continue;
......@@ -178638,6 +178645,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C
178638178645 .lea_uav,
178639178646 .lea_lazy_sym,
178640178647 .lea_extern_func,
178648 .register_tee,
178641178649 .elementwise_gpr,
178642178650 .elementwise_sse,
178643178651 .xwordwise_sse,
......@@ -178653,6 +178661,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C
178653178661 .dead,
178654178662 .undef,
178655178663 .register_overflow,
178664 .register_tee,
178656178665 .elementwise_gpr,
178657178666 .elementwise_sse,
178658178667 .xwordwise_sse,
......@@ -178844,6 +178853,7 @@ fn genSetReg(
178844178853 .unreach,
178845178854 .dead,
178846178855 .indirect_load_frame,
178856 .register_tee,
178847178857 .elementwise_gpr,
178848178858 .elementwise_sse,
178849178859 .xwordwise_sse,
......@@ -179422,6 +179432,7 @@ fn genSetMem(
179422179432 .unreach,
179423179433 .dead,
179424179434 .indirect_load_frame,
179435 .register_tee,
179425179436 .elementwise_gpr,
179426179437 .elementwise_sse,
179427179438 .xwordwise_sse,
......@@ -181569,142 +181580,139 @@ fn resolveCallingConventionValues(
181569181580 .x86_64_win => result.stack_byte_count += @intCast(win64_shadow_space),
181570181581 }
181571181582
181572 // Return values
181573 if (ret_ty.isNoReturn(zcu)) {
181574 result.return_value = .init(.unreach);
181575 } else if (!ret_ty.hasRuntimeBits(zcu)) {
181576 // TODO: is this even possible for C calling convention?
181577 result.return_value = .init(.none);
181578 } else {
181579 var ret_tracking: [4]InstTracking = undefined;
181580 var ret_tracking_len: u32 = 0;
181581 var ret_gpr = abi.getCAbiIntReturnRegs(cc);
181582 var ret_sse = abi.getCAbiSseReturnRegs(cc);
181583 var ret_x87 = abi.getCAbiX87ReturnRegs(cc);
181584
181585 var classes_buf: [8]abi.Class = undefined;
181586 const classes = classes: switch (cc) {
181587 else => unreachable,
181588 .x86_64_sysv => {
181589 classes_buf = abi.classifySystemV(ret_ty, zcu, cg.target, .ret);
181590 break :classes std.mem.sliceTo(&classes_buf, .none);
181591 },
181592 .x86_64_win => {
181593 classes_buf[0] = abi.classifyWindows(ret_ty, zcu, cg.target, .ret);
181594 break :classes classes_buf[0..1];
181595 },
181596 };
181597 for (classes) |class| switch (class) {
181598 .integer => {
181599 ret_tracking[ret_tracking_len] = .init(.{ .register = registerAlias(
181600 ret_gpr[0],
181601 @intCast(@min(ret_ty.abiSize(zcu), 8)),
181602 ) });
181603 ret_tracking_len += 1;
181604 ret_gpr = ret_gpr[1..];
181605 },
181606 .sse, .float, .float_combine, .win_i128 => {
181607 ret_tracking[ret_tracking_len] = .init(.{
181608 .register = registerAlias(ret_sse[0], @intCast(ret_ty.abiSize(zcu))),
181609 });
181610 ret_tracking_len += 1;
181611 ret_sse = ret_sse[1..];
181612 },
181613 .sseup => assert(ret_tracking[ret_tracking_len - 1].short.register.isClass(.sse)),
181614 .x87 => {
181615 ret_tracking[ret_tracking_len] = .init(.{ .register = ret_x87[0] });
181616 ret_tracking_len += 1;
181617 ret_x87 = ret_x87[1..];
181618 },
181619 .x87up => assert(ret_tracking[ret_tracking_len - 1].short.register.isClass(.x87)),
181620 .none => unreachable,
181621 .memory => {
181622 ret_tracking[ret_tracking_len] = .{
181623 .short = .{ .indirect = .{ .reg = ret_gpr[0].to64() } },
181624 .long = .{ .indirect = .{ .reg = param_gpr[param_gpr_index].to64() } },
181625 };
181626 ret_tracking_len += 1;
181627 ret_gpr = ret_gpr[1..];
181628 param_gpr_index += 1;
181629 },
181630 .integer_per_element => {
181631 const len: u32 = @intCast(ret_ty.vectorLen(zcu));
181632 const alias_size: u32 = @intCast(@min(ret_ty.childType(zcu).abiSize(zcu), 8));
181633 for (ret_tracking[ret_tracking_len..][0..len], ret_gpr[0..len]) |*tracking, gpr|
181634 tracking.* = .init(.{ .register = registerAlias(gpr, alias_size) });
181635 ret_tracking_len += len;
181636 ret_gpr = ret_gpr[len..];
181637 },
181638 .sse_per_element => {
181639 const len: u32 = @intCast(ret_ty.vectorLen(zcu));
181640 const alias_size: u32 = @intCast(@min(ret_ty.childType(zcu).abiSize(zcu), 8));
181641 for (ret_tracking[ret_tracking_len..][0..len], ret_sse[0..len]) |*tracking, sse|
181642 tracking.* = .init(.{ .register = registerAlias(sse, alias_size) });
181643 ret_tracking_len += len;
181644 ret_sse = ret_sse[len..];
181645 },
181646 .sse_sse_x87_per_qword, .sse_per_xword, .sse_per_yword, .sse_per_zword => {
181647 const reg_size: u32 = switch (class) {
181648 else => unreachable,
181649 .sse_sse_x87_per_qword => 8,
181650 .sse_per_xword => 16,
181651 .sse_per_yword => 32,
181652 .sse_per_zword => 64,
181653 };
181654 var byte_offset: u32 = 0;
181655 const unaligned_size = cg.unalignedSize(ret_ty);
181656 while (byte_offset < unaligned_size) : (byte_offset += reg_size) {
181657 switch (@as(enum { sse, x87 }, switch (class) {
181583 result.return_value = switch (ret_ty.classify(zcu)) {
181584 .no_possible_value => .init(.unreach),
181585 .one_possible_value => .init(.none),
181586 .runtime, .partially_comptime => return_value: {
181587 var ret_tracking: [4]InstTracking = undefined;
181588 var ret_tracking_len: u32 = 0;
181589 var ret_gpr = abi.getCAbiIntReturnRegs(cc);
181590 var ret_sse = abi.getCAbiSseReturnRegs(cc);
181591 var ret_x87 = abi.getCAbiX87ReturnRegs(cc);
181592
181593 var classes_buf: [8]abi.Class = undefined;
181594 const classes = classes: switch (cc) {
181595 else => unreachable,
181596 .x86_64_sysv => {
181597 classes_buf = abi.classifySystemV(ret_ty, zcu, cg.target, .ret);
181598 break :classes &classes_buf;
181599 },
181600 .x86_64_win => {
181601 classes_buf[0] = abi.classifyWindows(ret_ty, zcu, cg.target, .ret);
181602 break :classes classes_buf[0..1];
181603 },
181604 };
181605 for (classes) |class| switch (class) {
181606 .integer => {
181607 ret_tracking[ret_tracking_len] = .init(.{ .register = registerAlias(
181608 ret_gpr[0],
181609 @intCast(@min(ret_ty.abiSize(zcu), 8)),
181610 ) });
181611 ret_tracking_len += 1;
181612 ret_gpr = ret_gpr[1..];
181613 },
181614 .sse, .float, .float_combine, .win_i128 => {
181615 ret_tracking[ret_tracking_len] = .init(.{
181616 .register = registerAlias(ret_sse[0], @intCast(ret_ty.abiSize(zcu))),
181617 });
181618 ret_tracking_len += 1;
181619 ret_sse = ret_sse[1..];
181620 },
181621 .sseup => assert(ret_tracking[ret_tracking_len - 1].short.register.isClass(.sse)),
181622 .x87 => {
181623 ret_tracking[ret_tracking_len] = .init(.{ .register = ret_x87[0] });
181624 ret_tracking_len += 1;
181625 ret_x87 = ret_x87[1..];
181626 },
181627 .x87up => assert(ret_tracking[ret_tracking_len - 1].short.register.isClass(.x87)),
181628 .none => {},
181629 .memory => {
181630 ret_tracking[ret_tracking_len] = .{
181631 .short = .{ .indirect = .{ .reg = ret_gpr[0].to64() } },
181632 .long = .{ .indirect = .{ .reg = param_gpr[param_gpr_index].to64() } },
181633 };
181634 ret_tracking_len += 1;
181635 ret_gpr = ret_gpr[1..];
181636 param_gpr_index += 1;
181637 },
181638 .integer_per_element => {
181639 const len: u32 = @intCast(ret_ty.vectorLen(zcu));
181640 const alias_size: u32 = @intCast(@min(ret_ty.childType(zcu).abiSize(zcu), 8));
181641 for (ret_tracking[ret_tracking_len..][0..len], ret_gpr[0..len]) |*tracking, gpr|
181642 tracking.* = .init(.{ .register = registerAlias(gpr, alias_size) });
181643 ret_tracking_len += len;
181644 ret_gpr = ret_gpr[len..];
181645 },
181646 .sse_per_element => {
181647 const len: u32 = @intCast(ret_ty.vectorLen(zcu));
181648 const alias_size: u32 = @intCast(@min(ret_ty.childType(zcu).abiSize(zcu), 8));
181649 for (ret_tracking[ret_tracking_len..][0..len], ret_sse[0..len]) |*tracking, sse|
181650 tracking.* = .init(.{ .register = registerAlias(sse, alias_size) });
181651 ret_tracking_len += len;
181652 ret_sse = ret_sse[len..];
181653 },
181654 .sse_sse_x87_per_qword, .sse_per_xword, .sse_per_yword, .sse_per_zword => {
181655 const reg_size: u32 = switch (class) {
181658181656 else => unreachable,
181659 .sse_sse_x87_per_qword => switch (byte_offset) {
181660 0 => .sse, // duck
181661 8 => .sse, // duck
181662 else => .x87, // goose!
181663 },
181664 .sse_per_xword, .sse_per_yword, .sse_per_zword => .sse,
181665 })) {
181666 .sse => {
181667 ret_tracking[ret_tracking_len] = .init(.{
181668 .register = registerAlias(ret_sse[0], reg_size),
181669 });
181670 ret_tracking_len += 1;
181671 ret_sse = ret_sse[1..];
181672 },
181673 .x87 => {
181674 ret_tracking[ret_tracking_len] = .init(.{
181675 .register = registerAlias(ret_x87[0], reg_size),
181676 });
181677 ret_tracking_len += 1;
181678 ret_x87 = ret_x87[1..];
181679 },
181657 .sse_sse_x87_per_qword => 8,
181658 .sse_per_xword => 16,
181659 .sse_per_yword => 32,
181660 .sse_per_zword => 64,
181661 };
181662 var byte_offset: u32 = 0;
181663 const unaligned_size = cg.unalignedSize(ret_ty);
181664 while (byte_offset < unaligned_size) : (byte_offset += reg_size) {
181665 switch (@as(enum { sse, x87 }, switch (class) {
181666 else => unreachable,
181667 .sse_sse_x87_per_qword => switch (byte_offset) {
181668 0 => .sse, // duck
181669 8 => .sse, // duck
181670 else => .x87, // goose!
181671 },
181672 .sse_per_xword, .sse_per_yword, .sse_per_zword => .sse,
181673 })) {
181674 .sse => {
181675 ret_tracking[ret_tracking_len] = .init(.{
181676 .register = registerAlias(ret_sse[0], reg_size),
181677 });
181678 ret_tracking_len += 1;
181679 ret_sse = ret_sse[1..];
181680 },
181681 .x87 => {
181682 ret_tracking[ret_tracking_len] = .init(.{
181683 .register = registerAlias(ret_x87[0], reg_size),
181684 });
181685 ret_tracking_len += 1;
181686 ret_x87 = ret_x87[1..];
181687 },
181688 }
181680181689 }
181681 }
181682 },
181683 };
181684 result.return_value = switch (ret_tracking_len) {
181685 else => unreachable,
181686 1 => ret_tracking[0],
181687 2 => .init(.{ .register_pair = .{
181688 ret_tracking[0].short.register,
181689 ret_tracking[1].short.register,
181690 } }),
181691 3 => .init(.{ .register_triple = .{
181692 ret_tracking[0].short.register,
181693 ret_tracking[1].short.register,
181694 ret_tracking[2].short.register,
181695 } }),
181696 4 => .init(.{ .register_quadruple = .{
181697 ret_tracking[0].short.register,
181698 ret_tracking[1].short.register,
181699 ret_tracking[2].short.register,
181700 ret_tracking[3].short.register,
181701 } }),
181702 };
181703 }
181690 },
181691 };
181692 break :return_value switch (ret_tracking_len) {
181693 else => unreachable,
181694 1 => ret_tracking[0],
181695 2 => .init(.{ .register_pair = .{
181696 ret_tracking[0].short.register,
181697 ret_tracking[1].short.register,
181698 } }),
181699 3 => .init(.{ .register_triple = .{
181700 ret_tracking[0].short.register,
181701 ret_tracking[1].short.register,
181702 ret_tracking[2].short.register,
181703 } }),
181704 4 => .init(.{ .register_quadruple = .{
181705 ret_tracking[0].short.register,
181706 ret_tracking[1].short.register,
181707 ret_tracking[2].short.register,
181708 ret_tracking[3].short.register,
181709 } }),
181710 };
181711 },
181712 .fully_comptime => unreachable,
181713 };
181704181714
181705 // Input params
181706 params: for (param_types, result.args) |ty, *arg| {
181707 assert(ty.hasRuntimeBits(zcu));
181715 params: for (0.., param_types, result.args) |param_index, ty, *arg| {
181708181716 result.air_arg_count += 1;
181709181717 switch (cc) {
181710181718 else => unreachable,
......@@ -181726,7 +181734,7 @@ fn resolveCallingConventionValues(
181726181734 else => unreachable,
181727181735 .x86_64_sysv => {
181728181736 classes_buf = abi.classifySystemV(ty, zcu, cg.target, .arg);
181729 break :classes std.mem.sliceTo(&classes_buf, .none);
181737 break :classes &classes_buf;
181730181738 },
181731181739 .x86_64_win => {
181732181740 classes_buf[0] = abi.classifyWindows(ty, zcu, cg.target, .arg);
......@@ -181749,12 +181757,28 @@ fn resolveCallingConventionValues(
181749181757 var byte_offset: u32 = 0;
181750181758 while (byte_offset < abi_size) : (byte_offset += reg_size) {
181751181759 if (param_sse_index >= param_sse.len) break :classes;
181752
181753 const param_sse_reg = registerAlias(param_sse[param_sse_index], reg_size);
181754 param_sse_index += 1;
181755
181756 arg_mcv[arg_mcv_len] = .{ .register = param_sse_reg };
181760 arg_mcv[arg_mcv_len] = arg_mcv: {
181761 const param_sse_reg =
181762 registerAlias(param_sse[param_sse_index], reg_size);
181763 switch (cc) {
181764 else => unreachable,
181765 .x86_64_sysv => {},
181766 .x86_64_win => if (param_index >= fn_info.param_types.len) {
181767 const param_gpr_reg =
181768 registerAlias(param_gpr[param_gpr_index], reg_size);
181769 param_gpr_index += 1;
181770 break :arg_mcv .{ .register_tee = .{
181771 param_sse_reg,
181772 param_gpr_reg,
181773 .none,
181774 .none,
181775 } };
181776 },
181777 }
181778 break :arg_mcv .{ .register = param_sse_reg };
181779 };
181757181780 arg_mcv_len += 1;
181781 param_sse_index += 1;
181758181782 }
181759181783 },
181760181784 .sseup => assert(arg_mcv[arg_mcv_len - 1].register.isClass(.sse)),
......@@ -181764,13 +181788,16 @@ fn resolveCallingConventionValues(
181764181788 .x87, .x87up, .memory => break,
181765181789 },
181766181790 .x86_64_win => if (param_gpr_index < param_gpr.len) {
181767 arg_mcv[arg_mcv_len] = .{ .indirect = .{ .reg = param_gpr[param_gpr_index].to64() } };
181791 arg_mcv[arg_mcv_len] = .{ .indirect = .{
181792 .reg = param_gpr[param_gpr_index].to64(),
181793 } };
181768181794 arg_mcv_len += 1;
181769181795 param_gpr_index += 1;
181770181796 } else {
181771181797 assert(arg_mcv_len == 0);
181772181798 const param_align = Type.usize.abiAlignment(zcu);
181773 result.stack_byte_count = @intCast(param_align.forward(result.stack_byte_count));
181799 result.stack_byte_count =
181800 @intCast(param_align.forward(result.stack_byte_count));
181774181801 result.stack_align = result.stack_align.max(param_align);
181775181802 arg.* = .{ .indirect_load_frame = .{
181776181803 .index = stack_frame_base,
......@@ -181781,7 +181808,7 @@ fn resolveCallingConventionValues(
181781181808 },
181782181809 else => unreachable,
181783181810 },
181784 .none => unreachable,
181811 .none => {},
181785181812 .integer_per_element,
181786181813 .sse_per_element,
181787181814 .sse_per_xword,
......@@ -181850,6 +181877,7 @@ fn resolveCallingConventionValues(
181850181877 } else {
181851181878 arg.* = switch (arg_mcv_len) {
181852181879 else => unreachable,
181880 0 => .none,
181853181881 1 => arg_mcv[0],
181854181882 2 => .{ .register_pair = .{
181855181883 arg_mcv[0].register,
......@@ -181899,82 +181927,82 @@ fn resolveCallingConventionValues(
181899181927 param_gpr = param_gpr[0 .. param_gpr.len - 1];
181900181928 }
181901181929
181902 // Return values
181903 result.return_value = if (ret_ty.isNoReturn(zcu))
181904 .init(.unreach)
181905 else if (!ret_ty.hasRuntimeBits(zcu))
181906 .init(.none)
181907 else return_value: {
181908 const ret_gpr = abi.getCAbiIntReturnRegs(cc);
181909 const ret_size: u31 = @intCast(ret_ty.abiSize(zcu));
181910 if (abi.zigcc.return_in_regs) switch (cg.regClassForType(ret_ty)) {
181911 .general_purpose, .gphi => if (ret_size <= @as(u4, switch (cg.target.cpu.arch) {
181912 else => unreachable,
181913 .x86 => 4,
181914 .x86_64 => 8,
181915 }))
181916 break :return_value .init(.{ .register = registerAlias(ret_gpr[0], ret_size) })
181917 else if (ret_gpr.len >= 2 and ret_ty.isSliceAtRuntime(zcu))
181918 break :return_value .init(.{ .register_pair = ret_gpr[0..2].* }),
181919 .segment, .mmx, .ip, .cr, .dr => unreachable,
181920 .x87 => if (ret_size <= 16) break :return_value .init(.{ .register = .st0 }),
181921 .sse => if (ret_size <= cg.vectorSize(.float)) break :return_value .init(.{
181922 .register = registerAlias(abi.getCAbiSseReturnRegs(cc)[0], @max(ret_size, 16)),
181923 }),
181924 };
181925 const ret_indirect_reg = param_gpr[0];
181926 param_gpr = param_gpr[1..];
181927 break :return_value .{
181928 .short = .{ .indirect = .{ .reg = ret_gpr[0] } },
181929 .long = .{ .indirect = .{ .reg = ret_indirect_reg } },
181930 };
181930 result.return_value = switch (ret_ty.classify(zcu)) {
181931 .no_possible_value => .init(.unreach),
181932 .one_possible_value => .init(.none),
181933 .runtime, .partially_comptime => return_value: {
181934 const ret_gpr = abi.getCAbiIntReturnRegs(cc);
181935 const ret_size: u31 = @intCast(ret_ty.abiSize(zcu));
181936 if (abi.zigcc.return_in_regs) switch (cg.regClassForType(ret_ty)) {
181937 .general_purpose, .gphi => if (ret_size <= @as(u4, switch (cg.target.cpu.arch) {
181938 else => unreachable,
181939 .x86 => 4,
181940 .x86_64 => 8,
181941 }))
181942 break :return_value .init(.{ .register = registerAlias(ret_gpr[0], ret_size) })
181943 else if (ret_gpr.len >= 2 and ret_ty.isSliceAtRuntime(zcu))
181944 break :return_value .init(.{ .register_pair = ret_gpr[0..2].* }),
181945 .segment, .mmx, .ip, .cr, .dr => unreachable,
181946 .x87 => if (ret_size <= 16) break :return_value .init(.{ .register = .st0 }),
181947 .sse => if (ret_size <= cg.vectorSize(.float)) break :return_value .init(.{
181948 .register = registerAlias(abi.getCAbiSseReturnRegs(cc)[0], @max(ret_size, 16)),
181949 }),
181950 };
181951 const ret_indirect_reg = param_gpr[0];
181952 param_gpr = param_gpr[1..];
181953 break :return_value .{
181954 .short = .{ .indirect = .{ .reg = ret_gpr[0] } },
181955 .long = .{ .indirect = .{ .reg = ret_indirect_reg } },
181956 };
181957 },
181958 .fully_comptime => unreachable,
181931181959 };
181932181960
181933 // Input params
181934 for (param_types, result.args) |param_ty, *arg| {
181935 if (!param_ty.hasRuntimeBits(zcu)) {
181936 arg.* = .none;
181937 continue;
181938 }
181939 result.air_arg_count += 1;
181940 const param_size: u31 = @intCast(param_ty.abiSize(zcu));
181941 if (abi.zigcc.params_in_regs) switch (cg.regClassForType(param_ty)) {
181942 .general_purpose, .gphi => if (param_gpr.len >= 1 and param_size <= @as(u4, switch (cg.target.cpu.arch) {
181943 else => unreachable,
181944 .x86 => 4,
181945 .x86_64 => 8,
181946 })) {
181947 arg.* = .{ .register = registerAlias(param_gpr[0], param_size) };
181948 param_gpr = param_gpr[1..];
181949 continue;
181950 } else if (param_gpr.len >= 2 and param_ty.isSliceAtRuntime(zcu)) {
181951 arg.* = .{ .register_pair = param_gpr[0..2].* };
181952 param_gpr = param_gpr[2..];
181953 continue;
181954 },
181955 .segment, .mmx, .ip, .cr, .dr => unreachable,
181956 .x87 => if (param_x87.len >= 1 and param_size <= 16) {
181957 arg.* = .{ .register = param_x87[0] };
181958 param_x87 = param_x87[1..];
181959 continue;
181960 },
181961 .sse => if (param_sse.len >= 1 and param_size <= cg.vectorSize(.float)) {
181962 arg.* = .{
181963 .register = registerAlias(param_sse[0], @max(param_size, 16)),
181964 };
181965 param_sse = param_sse[1..];
181966 continue;
181967 },
181968 };
181969 const param_align = param_ty.abiAlignment(zcu);
181970 result.stack_byte_count = @intCast(param_align.forward(result.stack_byte_count));
181971 result.stack_align = result.stack_align.max(param_align);
181972 arg.* = .{ .load_frame = .{
181973 .index = stack_frame_base,
181974 .off = result.stack_byte_count,
181975 } };
181976 result.stack_byte_count += param_size;
181977 }
181961 for (param_types, result.args) |param_ty, *arg| switch (param_ty.classify(zcu)) {
181962 .no_possible_value => arg.* = .unreach,
181963 .one_possible_value => arg.* = .none,
181964 .runtime, .partially_comptime => {
181965 result.air_arg_count += 1;
181966 const param_size: u31 = @intCast(param_ty.abiSize(zcu));
181967 if (abi.zigcc.params_in_regs) switch (cg.regClassForType(param_ty)) {
181968 .general_purpose, .gphi => if (param_gpr.len >= 1 and param_size <= @as(u4, switch (cg.target.cpu.arch) {
181969 else => unreachable,
181970 .x86 => 4,
181971 .x86_64 => 8,
181972 })) {
181973 arg.* = .{ .register = registerAlias(param_gpr[0], param_size) };
181974 param_gpr = param_gpr[1..];
181975 continue;
181976 } else if (param_gpr.len >= 2 and param_ty.isSliceAtRuntime(zcu)) {
181977 arg.* = .{ .register_pair = param_gpr[0..2].* };
181978 param_gpr = param_gpr[2..];
181979 continue;
181980 },
181981 .segment, .mmx, .ip, .cr, .dr => unreachable,
181982 .x87 => if (param_x87.len >= 1 and param_size <= 16) {
181983 arg.* = .{ .register = param_x87[0] };
181984 param_x87 = param_x87[1..];
181985 continue;
181986 },
181987 .sse => if (param_sse.len >= 1 and param_size <= cg.vectorSize(.float)) {
181988 arg.* = .{
181989 .register = registerAlias(param_sse[0], @max(param_size, 16)),
181990 };
181991 param_sse = param_sse[1..];
181992 continue;
181993 },
181994 };
181995 const param_align = param_ty.abiAlignment(zcu);
181996 result.stack_byte_count = @intCast(param_align.forward(result.stack_byte_count));
181997 result.stack_align = result.stack_align.max(param_align);
181998 arg.* = .{ .load_frame = .{
181999 .index = stack_frame_base,
182000 .off = result.stack_byte_count,
182001 } };
182002 result.stack_byte_count += param_size;
182003 },
182004 .fully_comptime => unreachable,
182005 };
181978182006 },
181979182007 else => return cg.fail("TODO implement function parameters and return values for {} on x86_64", .{cc}),
181980182008 }
......@@ -182241,13 +182269,11 @@ fn typeOfIndex(self: *CodeGen, inst: Air.Inst.Index) Type {
182241182269 return Temp.typeOf(.{ .index = inst }, self);
182242182270}
182243182271
182244fn promoteInt(self: *CodeGen, ty: Type) Type {
182245 const pt = self.pt;
182272fn promoteInt(cg: *CodeGen, ty: Type) Type {
182273 const pt = cg.pt;
182246182274 const zcu = pt.zcu;
182247 const int_info: InternPool.Key.IntType = switch (ty.toIntern()) {
182248 .bool_type => .{ .signedness = .unsigned, .bits = 1 },
182249 else => if (ty.isAbiInt(zcu)) ty.intInfo(zcu) else return ty,
182250 };
182275 const int_info = cg.intInfo(ty) orelse return ty;
182276 if (int_info.bits == 0) return .void;
182251182277 for ([_]Type{
182252182278 .c_int, .c_uint,
182253182279 .c_long, .c_ulong,
......@@ -182424,6 +182450,7 @@ const Temp = struct {
182424182450 .lea_lazy_sym,
182425182451 .lea_extern_func,
182426182452 .load_extern_func,
182453 .register_tee,
182427182454 .elementwise_gpr,
182428182455 .elementwise_sse,
182429182456 .xwordwise_sse,
......@@ -182891,6 +182918,7 @@ const Temp = struct {
182891182918 .register_overflow,
182892182919 .register_mask,
182893182920 .indirect_load_frame,
182921 .register_tee,
182894182922 .elementwise_gpr,
182895182923 .elementwise_sse,
182896182924 .xwordwise_sse,
......@@ -188002,6 +188030,7 @@ const Temp = struct {
188002188030 .unreach,
188003188031 .dead,
188004188032 .indirect_load_frame,
188033 .register_tee,
188005188034 .elementwise_gpr,
188006188035 .elementwise_sse,
188007188036 .xwordwise_sse,
......@@ -190673,6 +190702,7 @@ const Select = struct {
190673190702 .lea_extern_func => |extern_func| .{ .imm = .{ .extern_func = extern_func } },
190674190703 else => |mcv| .{ .mem = try mcv.mem(s.cg, .{ .size = op.flags.base.size }) },
190675190704 .lea_frame,
190705 .register_tee,
190676190706 .elementwise_gpr,
190677190707 .elementwise_sse,
190678190708 .xwordwise_sse,
src/codegen/x86_64/abi.zig+18-10
......@@ -37,6 +37,8 @@ pub const Class = enum {
3737 /// Clang passes each 64 bytes in a separate `Class.sse`.
3838 sse_per_zword,
3939
40 pub const zero_bit: [8]Class = .{ .none, .none, .none, .none, .none, .none, .none, .none };
41
4042 pub const one_integer: [8]Class = .{ .integer, .none, .none, .none, .none, .none, .none, .none };
4143 pub const two_integers: [8]Class = .{ .integer, .integer, .none, .none, .none, .none, .none, .none };
4244 pub const three_integers: [8]Class = .{ .integer, .integer, .integer, .none, .none, .none, .none, .none };
......@@ -100,12 +102,11 @@ pub fn classifyWindows(init_ty: Type, zcu: *Zcu, target: *const std.Target, ctx:
100102 // as if they were integers of the same size."
101103 var ty = init_ty;
102104 while (true) return switch (ty.zigTypeTag(zcu)) {
105 .void => return .none,
106 .bool,
103107 .pointer,
104108 .int,
105 .bool,
106109 .@"enum",
107 .void,
108 .noreturn,
109110 .error_set,
110111 .@"struct",
111112 .@"union",
......@@ -115,7 +116,7 @@ pub fn classifyWindows(init_ty: Type, zcu: *Zcu, target: *const std.Target, ctx:
115116 .@"anyframe",
116117 .frame,
117118 => switch (ty.abiSize(zcu)) {
118 0 => unreachable,
119 0 => .none,
119120 1, 2, 4, 8 => .integer,
120121 else => switch (ty.zigTypeTag(zcu)) {
121122 .int => .win_i128,
......@@ -126,6 +127,7 @@ pub fn classifyWindows(init_ty: Type, zcu: *Zcu, target: *const std.Target, ctx:
126127 else => .memory,
127128 },
128129 },
130 .noreturn => unreachable,
129131 .float => switch (ty.floatBits(target)) {
130132 16, 32, 64 => .sse,
131133 80 => .memory,
......@@ -134,6 +136,7 @@ pub fn classifyWindows(init_ty: Type, zcu: *Zcu, target: *const std.Target, ctx:
134136 },
135137 .vector => {
136138 const len = ty.vectorLen(zcu);
139 if (len == 0) return .none;
137140 const elem_ty = ty.childType(zcu);
138141 if (len == 1) {
139142 ty = elem_ty;
......@@ -178,19 +181,18 @@ pub fn classifyWindows(init_ty: Type, zcu: *Zcu, target: *const std.Target, ctx:
178181/// the beginning of the array; unused slots are filled with .none.
179182pub fn classifySystemV(ty: Type, zcu: *Zcu, target: *const std.Target, ctx: Context) [8]Class {
180183 switch (ty.zigTypeTag(zcu)) {
181 .pointer => switch (ty.ptrSize(zcu)) {
182 .slice => return Class.two_integers,
183 else => return Class.one_integer,
184 },
184 .void => return Class.zero_bit,
185 .bool => return Class.one_integer,
186 .noreturn => unreachable,
185187 .int, .@"enum", .error_set => {
186188 const bits = ty.intInfo(zcu).bits;
189 if (bits == 0) return Class.zero_bit;
187190 if (bits <= 64 * 1) return Class.one_integer;
188191 if (bits <= 64 * 2) return Class.two_integers;
189192 if (bits <= 64 * 3) return Class.three_integers;
190193 if (bits <= 64 * 4) return Class.four_integers;
191194 return Class.stack;
192195 },
193 .bool, .void, .noreturn => return Class.one_integer,
194196 .float => switch (ty.floatBits(target)) {
195197 16 => {
196198 if (ctx == .other) return Class.stack;
......@@ -209,9 +211,13 @@ pub fn classifySystemV(ty: Type, zcu: *Zcu, target: *const std.Target, ctx: Cont
209211 80 => return Class.f80,
210212 else => unreachable,
211213 },
214 .pointer => switch (ty.ptrSize(zcu)) {
215 .slice => return Class.two_integers,
216 else => return Class.one_integer,
217 },
212218 .vector => {
213219 const len = ty.vectorLen(zcu);
214 if (len == 0) return Class.one_integer;
220 if (len == 0) return Class.zero_bit;
215221 const elem_ty = ty.childType(zcu);
216222 if (elem_ty.toIntern() == .bool_type) {
217223 if (len <= 32) return Class.one_integer;
......@@ -275,6 +281,7 @@ pub fn classifySystemV(ty: Type, zcu: *Zcu, target: *const std.Target, ctx: Cont
275281 // "If the size of the aggregate exceeds a single eightbyte, each is classified
276282 // separately.".
277283 const ty_size = ty.abiSize(zcu);
284 if (ty_size == 0) return Class.zero_bit;
278285 switch (ty.containerLayout(zcu)) {
279286 .auto => unreachable,
280287 .@"extern" => {},
......@@ -325,6 +332,7 @@ pub fn classifySystemV(ty: Type, zcu: *Zcu, target: *const std.Target, ctx: Cont
325332 },
326333 .array => {
327334 const ty_size = ty.abiSize(zcu);
335 if (ty_size == 0) return Class.zero_bit;
328336 if (ty_size <= 8) return Class.one_integer;
329337 if (ty_size <= 16) return Class.two_integers;
330338 return Class.stack;
test/c_abi/cfuncs.c+15
......@@ -16211,3 +16211,18 @@ void __attribute__((vectorcall)) c_vectorcall_check(int a, float b, double c, vo
1621116211 zig_vectorcall_check(a, b, c, d, e, f, g, h, i, j);
1621216212}
1621316213#endif
16214
16215#if defined(__x86_64__) && defined(_WIN64)
16216void c_win64_varargs_u64_f64_u64_f64(uint64_t a, double b, uint64_t c, double d) {
16217 assert_or_panic(a == UINT64_C(0x3ff0000000000000));
16218 assert_or_panic(b == 2.0);
16219 assert_or_panic(c == UINT64_C(0x4008000000000000));
16220 assert_or_panic(d == 4.0);
16221}
16222void c_win64_varargs_f64_u64_f64_u64(double a, uint64_t b, double c, uint64_t d) {
16223 assert_or_panic(a == 5.0);
16224 assert_or_panic(b == UINT64_C(0x4018000000000000));
16225 assert_or_panic(c == 7.0);
16226 assert_or_panic(d == UINT64_C(0x4020000000000000));
16227}
16228#endif
test/c_abi/main.zig+33-6
......@@ -17140,9 +17140,7 @@ test "byval tail callsite attribute" {
1714017140}
1714117141
1714217142test "x86 fastcall calling convention" {
17143 if (builtin.cpu.arch != .x86) return error.SkipZigTest;
17144 if (builtin.os.tag != .windows) return error.SkipZigTest;
17145 if (builtin.abi != .msvc) return error.SkipZigTest;
17143 if (builtin.cpu.arch != .x86 or builtin.os.tag != .windows or builtin.abi != .msvc) return error.SkipZigTest;
1714617144
1714717145 const static = struct {
1714817146 const fastcall: std.builtin.CallingConvention = .{ .x86_fastcall = .{} };
......@@ -17252,9 +17250,7 @@ test "x86 fastcall calling convention" {
1725217250}
1725317251
1725417252test "x86 vectorcall calling convention" {
17255 if (builtin.cpu.arch != .x86) return error.SkipZigTest;
17256 if (builtin.os.tag != .windows) return error.SkipZigTest;
17257 if (builtin.abi != .msvc) return error.SkipZigTest;
17253 if (builtin.cpu.arch != .x86 or builtin.os.tag != .windows or builtin.abi != .msvc) return error.SkipZigTest;
1725817254
1725917255 const static = struct {
1726017256 extern fn c_vectorcall_check(a: c_int, b: f32, c: f64, d: *anyopaque, e: f32, f: f64, g: f64, h: f32, i: f32, j: c_int) callconv(.{ .x86_vectorcall = .{} }) void;
......@@ -17273,3 +17269,34 @@ test "x86 vectorcall calling convention" {
1727317269 };
1727417270 static.c_vectorcall_check(1, 2.0, 3.0, @ptrFromInt(4), 5.0, 6.0, 7.0, 8.0, 9.0, 10);
1727517271}
17272
17273extern fn c_win64_varargs_u64_f64_u64_f64(...) void;
17274extern fn c_win64_varargs_f64_u64_f64_u64(...) void;
17275
17276test "win64 varargs" {
17277 if (builtin.cpu.arch != .x86_64 or builtin.os.tag != .windows) return error.SkipZigTest;
17278
17279 const Opv = extern struct {};
17280 c_win64_varargs_u64_f64_u64_f64(
17281 @as(Opv, .{}),
17282 @as(f32, 1),
17283 @as(Opv, .{}),
17284 @as(f32, 2.0),
17285 @as(Opv, .{}),
17286 @as(f64, 3),
17287 @as(Opv, .{}),
17288 @as(f64, 4.0),
17289 @as(Opv, .{}),
17290 );
17291 c_win64_varargs_f64_u64_f64_u64(
17292 @as(Opv, .{}),
17293 @as(f32, 5),
17294 @as(Opv, .{}),
17295 @as(f32, 6.0),
17296 @as(Opv, .{}),
17297 @as(f64, 7),
17298 @as(Opv, .{}),
17299 @as(f64, 8.0),
17300 @as(Opv, .{}),
17301 );
17302}