authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-10-04 03:42:59-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-10-04 14:42:35-04:00
log2a5335d7b6628115ce29c3c712b55c661181e1d1
treed5586d99c167e9da12f02f5fa4a6d82ea5c36e44
parent9748096992620a9b1a2169ed3be34616fe417a99

x86_64: implement C abi for 128-bit integers


4 files changed, 449 insertions(+), 230 deletions(-)

lib/std/os/linux.zig+1-1
...@@ -156,7 +156,7 @@ pub var elf_aux_maybe: ?[*]std.elf.Auxv = null;...@@ -156,7 +156,7 @@ pub var elf_aux_maybe: ?[*]std.elf.Auxv = null;
156156
157pub usingnamespace if (switch (builtin.zig_backend) {157pub usingnamespace if (switch (builtin.zig_backend) {
158 // Calling extern functions is not yet supported with these backends158 // Calling extern functions is not yet supported with these backends
159 .stage2_x86_64, .stage2_aarch64, .stage2_arm, .stage2_riscv64, .stage2_sparc64 => false,159 .stage2_aarch64, .stage2_arm, .stage2_riscv64, .stage2_sparc64 => false,
160 else => !builtin.link_libc,160 else => !builtin.link_libc,
161}) struct {161}) struct {
162 /// See `std.elf` for the constants.162 /// See `std.elf` for the constants.
src/arch/x86_64/CodeGen.zig+374-227
...@@ -178,6 +178,8 @@ pub const MCValue = union(enum) {...@@ -178,6 +178,8 @@ pub const MCValue = union(enum) {
178 eflags: Condition,178 eflags: Condition,
179 /// The value is in a register.179 /// The value is in a register.
180 register: Register,180 register: Register,
181 /// The value is split across two registers.
182 register_pair: [2]Register,
181 /// The value is a constant offset from the value in a register.183 /// The value is a constant offset from the value in a register.
182 register_offset: RegisterOffset,184 register_offset: RegisterOffset,
183 /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register.185 /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register.
...@@ -255,6 +257,16 @@ pub const MCValue = union(enum) {...@@ -255,6 +257,16 @@ pub const MCValue = union(enum) {
255 };257 };
256 }258 }
257259
260 fn getRegs(mcv: *const MCValue) []const Register {
261 return switch (mcv.*) {
262 .register => |*reg| @as(*const [1]Register, reg),
263 .register_pair => |*regs| regs,
264 .register_offset, .indirect => |*ro| @as(*const [1]Register, &ro.reg),
265 .register_overflow => |*ro| @as(*const [1]Register, &ro.reg),
266 else => &.{},
267 };
268 }
269
258 fn getCondition(mcv: MCValue) ?Condition {270 fn getCondition(mcv: MCValue) ?Condition {
259 return switch (mcv) {271 return switch (mcv) {
260 .eflags => |cc| cc,272 .eflags => |cc| cc,
...@@ -272,6 +284,7 @@ pub const MCValue = union(enum) {...@@ -272,6 +284,7 @@ pub const MCValue = union(enum) {
272 .immediate,284 .immediate,
273 .eflags,285 .eflags,
274 .register,286 .register,
287 .register_pair,
275 .register_offset,288 .register_offset,
276 .register_overflow,289 .register_overflow,
277 .lea_direct,290 .lea_direct,
...@@ -300,6 +313,7 @@ pub const MCValue = union(enum) {...@@ -300,6 +313,7 @@ pub const MCValue = union(enum) {
300 .dead,313 .dead,
301 .undef,314 .undef,
302 .eflags,315 .eflags,
316 .register_pair,
303 .register_overflow,317 .register_overflow,
304 .memory,318 .memory,
305 .indirect,319 .indirect,
...@@ -326,7 +340,11 @@ pub const MCValue = union(enum) {...@@ -326,7 +340,11 @@ pub const MCValue = union(enum) {
326 .unreach,340 .unreach,
327 .dead,341 .dead,
328 .undef,342 .undef,
343 .reserved_frame,
344 .air_ref,
345 => unreachable, // not valid
329 .eflags,346 .eflags,
347 .register_pair,
330 .register_overflow,348 .register_overflow,
331 .memory,349 .memory,
332 .indirect,350 .indirect,
...@@ -337,9 +355,10 @@ pub const MCValue = union(enum) {...@@ -337,9 +355,10 @@ pub const MCValue = union(enum) {
337 .load_tlv,355 .load_tlv,
338 .lea_tlv,356 .lea_tlv,
339 .load_frame,357 .load_frame,
340 .reserved_frame,358 => switch (off) {
341 .air_ref,359 0 => mcv,
342 => unreachable, // not offsettable360 else => unreachable, // not offsettable
361 },
343 .immediate => |imm| .{ .immediate = @bitCast(@as(i64, @bitCast(imm)) +% off) },362 .immediate => |imm| .{ .immediate = @bitCast(@as(i64, @bitCast(imm)) +% off) },
344 .register => |reg| .{ .register_offset = .{ .reg = reg, .off = off } },363 .register => |reg| .{ .register_offset = .{ .reg = reg, .off = off } },
345 .register_offset => |reg_off| .{364 .register_offset => |reg_off| .{
...@@ -360,6 +379,7 @@ pub const MCValue = union(enum) {...@@ -360,6 +379,7 @@ pub const MCValue = union(enum) {
360 .immediate,379 .immediate,
361 .eflags,380 .eflags,
362 .register,381 .register,
382 .register_pair,
363 .register_offset,383 .register_offset,
364 .register_overflow,384 .register_overflow,
365 .load_direct,385 .load_direct,
...@@ -398,8 +418,11 @@ pub const MCValue = union(enum) {...@@ -398,8 +418,11 @@ pub const MCValue = union(enum) {
398 .immediate => |pl| try writer.print("0x{x}", .{pl}),418 .immediate => |pl| try writer.print("0x{x}", .{pl}),
399 .memory => |pl| try writer.print("[ds:0x{x}]", .{pl}),419 .memory => |pl| try writer.print("[ds:0x{x}]", .{pl}),
400 inline .eflags, .register => |pl| try writer.print("{s}", .{@tagName(pl)}),420 inline .eflags, .register => |pl| try writer.print("{s}", .{@tagName(pl)}),
421 .register_pair => |pl| try writer.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }),
401 .register_offset => |pl| try writer.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }),422 .register_offset => |pl| try writer.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }),
402 .register_overflow => |pl| try writer.print("{s}:{s}", .{ @tagName(pl.eflags), @tagName(pl.reg) }),423 .register_overflow => |pl| try writer.print("{s}:{s}", .{
424 @tagName(pl.eflags), @tagName(pl.reg),
425 }),
403 .indirect => |pl| try writer.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }),426 .indirect => |pl| try writer.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }),
404 .load_direct => |pl| try writer.print("[direct:{d}]", .{pl}),427 .load_direct => |pl| try writer.print("[direct:{d}]", .{pl}),
405 .lea_direct => |pl| try writer.print("direct:{d}", .{pl}),428 .lea_direct => |pl| try writer.print("direct:{d}", .{pl}),
...@@ -443,6 +466,7 @@ const InstTracking = struct {...@@ -443,6 +466,7 @@ const InstTracking = struct {
443 => unreachable,466 => unreachable,
444 .eflags,467 .eflags,
445 .register,468 .register,
469 .register_pair,
446 .register_offset,470 .register_offset,
447 .register_overflow,471 .register_overflow,
448 .indirect,472 .indirect,
...@@ -454,6 +478,10 @@ const InstTracking = struct {...@@ -454,6 +478,10 @@ const InstTracking = struct {
454 return self.short.getReg();478 return self.short.getReg();
455 }479 }
456480
481 fn getRegs(self: *const InstTracking) []const Register {
482 return self.short.getRegs();
483 }
484
457 fn getCondition(self: InstTracking) ?Condition {485 fn getCondition(self: InstTracking) ?Condition {
458 return self.short.getCondition();486 return self.short.getCondition();
459 }487 }
...@@ -494,6 +522,7 @@ const InstTracking = struct {...@@ -494,6 +522,7 @@ const InstTracking = struct {
494 .dead,522 .dead,
495 .eflags,523 .eflags,
496 .register,524 .register,
525 .register_pair,
497 .register_offset,526 .register_offset,
498 .register_overflow,527 .register_overflow,
499 .indirect,528 .indirect,
...@@ -536,6 +565,7 @@ const InstTracking = struct {...@@ -536,6 +565,7 @@ const InstTracking = struct {
536 .dead,565 .dead,
537 .eflags,566 .eflags,
538 .register,567 .register,
568 .register_pair,
539 .register_offset,569 .register_offset,
540 .register_overflow,570 .register_overflow,
541 .indirect,571 .indirect,
...@@ -2028,7 +2058,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -2028,7 +2058,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
2028 while (it.next()) |index| {2058 while (it.next()) |index| {
2029 const tracked_inst = self.register_manager.registers[index];2059 const tracked_inst = self.register_manager.registers[index];
2030 const tracking = self.getResolvedInstValue(tracked_inst);2060 const tracking = self.getResolvedInstValue(tracked_inst);
2031 assert(RegisterManager.indexOfRegIntoTracked(tracking.getReg().?).? == index);2061 for (tracking.getRegs()) |reg| {
2062 if (RegisterManager.indexOfRegIntoTracked(reg).? == index) break;
2063 } else unreachable; // tracked register not in use
2032 }2064 }
2033 }2065 }
2034 }2066 }
...@@ -2094,27 +2126,20 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {...@@ -2094,27 +2126,20 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {
2094}2126}
20952127
2096fn getValue(self: *Self, value: MCValue, inst: ?Air.Inst.Index) void {2128fn getValue(self: *Self, value: MCValue, inst: ?Air.Inst.Index) void {
2097 const reg = value.getReg() orelse return;2129 for (value.getRegs()) |reg| if (self.register_manager.isRegFree(reg))
2098 if (self.register_manager.isRegFree(reg)) {
2099 self.register_manager.getRegAssumeFree(reg, inst);2130 self.register_manager.getRegAssumeFree(reg, inst);
2100 }
2101}2131}
21022132
2103fn freeValue(self: *Self, value: MCValue) void {2133fn freeValue(self: *Self, value: MCValue) void {
2104 switch (value) {2134 switch (value) {
2105 .register => |reg| {2135 .register => |reg| self.register_manager.freeReg(reg),
2106 self.register_manager.freeReg(reg);2136 .register_pair => |regs| for (regs) |reg| self.register_manager.freeReg(reg),
2107 },2137 .register_offset => |reg_off| self.register_manager.freeReg(reg_off.reg),
2108 .register_offset => |reg_off| {
2109 self.register_manager.freeReg(reg_off.reg);
2110 },
2111 .register_overflow => |reg_ov| {2138 .register_overflow => |reg_ov| {
2112 self.register_manager.freeReg(reg_ov.reg);2139 self.register_manager.freeReg(reg_ov.reg);
2113 self.eflags_inst = null;2140 self.eflags_inst = null;
2114 },2141 },
2115 .eflags => {2142 .eflags => self.eflags_inst = null,
2116 self.eflags_inst = null;
2117 },
2118 else => {}, // TODO process stack allocation death2143 else => {}, // TODO process stack allocation death
2119 }2144 }
2120}2145}
...@@ -2449,7 +2474,9 @@ fn restoreState(self: *Self, state: State, deaths: []const Air.Inst.Index, compt...@@ -2449,7 +2474,9 @@ fn restoreState(self: *Self, state: State, deaths: []const Air.Inst.Index, compt
24492474
2450pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {2475pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
2451 const tracking = self.inst_tracking.getPtr(inst) orelse return;2476 const tracking = self.inst_tracking.getPtr(inst) orelse return;
2452 assert(tracking.getReg().?.id() == reg.id());2477 for (tracking.getRegs()) |tracked_reg| {
2478 if (tracked_reg.id() == reg.id()) break;
2479 } else unreachable; // spilled reg not tracked with spilled instruciton
2453 try tracking.spill(self, inst);2480 try tracking.spill(self, inst);
2454 tracking.trackSpill(self, inst);2481 tracking.trackSpill(self, inst);
2455}2482}
...@@ -2465,9 +2492,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void {...@@ -2465,9 +2492,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void {
2465}2492}
24662493
2467pub fn spillRegisters(self: *Self, registers: []const Register) !void {2494pub fn spillRegisters(self: *Self, registers: []const Register) !void {
2468 for (registers) |reg| {2495 for (registers) |reg| try self.register_manager.getReg(reg, null);
2469 try self.register_manager.getReg(reg, null);
2470 }
2471}2496}
24722497
2473/// Copies a value to a register without tracking the register. The register is not considered2498/// Copies a value to a register without tracking the register. The register is not considered
...@@ -5457,8 +5482,8 @@ fn reuseOperandAdvanced(...@@ -5457,8 +5482,8 @@ fn reuseOperandAdvanced(
5457 return false;5482 return false;
54585483
5459 switch (mcv) {5484 switch (mcv) {
5460 .register => |reg| {5485 .register, .register_pair => for (mcv.getRegs()) |reg| {
5461 // If it's in the registers table, need to associate the register with the5486 // If it's in the registers table, need to associate the register(s) with the
5462 // new instruction.5487 // new instruction.
5463 if (!self.register_manager.isRegFree(reg)) {5488 if (!self.register_manager.isRegFree(reg)) {
5464 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {5489 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
...@@ -5561,6 +5586,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro...@@ -5561,6 +5586,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro
5561 .dead,5586 .dead,
5562 .undef,5587 .undef,
5563 .eflags,5588 .eflags,
5589 .register_pair,
5564 .register_overflow,5590 .register_overflow,
5565 .reserved_frame,5591 .reserved_frame,
5566 => unreachable, // not a valid pointer5592 => unreachable, // not a valid pointer
...@@ -5585,7 +5611,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro...@@ -5585,7 +5611,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro
55855611
5586 try self.genCopy(dst_ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } });5612 try self.genCopy(dst_ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } });
5587 },5613 },
5588 .air_ref => |ref| try self.load(dst_mcv, ptr_ty, try self.resolveInst(ref)),5614 .air_ref => |ptr_ref| try self.load(dst_mcv, ptr_ty, try self.resolveInst(ptr_ref)),
5589 }5615 }
5590}5616}
55915617
...@@ -5707,6 +5733,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr...@@ -5707,6 +5733,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr
5707 .dead,5733 .dead,
5708 .undef,5734 .undef,
5709 .eflags,5735 .eflags,
5736 .register_pair,
5710 .register_overflow,5737 .register_overflow,
5711 .reserved_frame,5738 .reserved_frame,
5712 => unreachable, // not a valid pointer5739 => unreachable, // not a valid pointer
...@@ -5731,7 +5758,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr...@@ -5731,7 +5758,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr
57315758
5732 try self.genCopy(src_ty, .{ .indirect = .{ .reg = addr_reg } }, src_mcv);5759 try self.genCopy(src_ty, .{ .indirect = .{ .reg = addr_reg } }, src_mcv);
5733 },5760 },
5734 .air_ref => |ref| try self.store(ptr_ty, ptr_mcv, try self.resolveInst(ref)),5761 .air_ref => |ptr_ref| try self.store(ptr_ty, ptr_mcv, try self.resolveInst(ptr_ref)),
5735 }5762 }
5736}5763}
57375764
...@@ -6077,6 +6104,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MC...@@ -6077,6 +6104,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MC
6077 .air_ref,6104 .air_ref,
6078 => unreachable, // unmodifiable destination6105 => unreachable, // unmodifiable destination
6079 .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)),6106 .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)),
6107 .register_pair => unreachable, // unimplemented
6080 .memory, .load_got, .load_direct, .load_tlv => {6108 .memory, .load_got, .load_direct, .load_tlv => {
6081 const addr_reg = try self.register_manager.allocReg(null, gp);6109 const addr_reg = try self.register_manager.allocReg(null, gp);
6082 const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);6110 const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
...@@ -6764,7 +6792,7 @@ fn genBinOp(...@@ -6764,7 +6792,7 @@ fn genBinOp(
6764 => {6792 => {
6765 const resolved_src_mcv = switch (src_mcv) {6793 const resolved_src_mcv = switch (src_mcv) {
6766 else => src_mcv,6794 else => src_mcv,
6767 .air_ref => |ref| try self.resolveInst(ref),6795 .air_ref => |src_ref| try self.resolveInst(src_ref),
6768 };6796 };
6769 const mat_src_mcv: MCValue = if (switch (resolved_src_mcv) {6797 const mat_src_mcv: MCValue = if (switch (resolved_src_mcv) {
6770 .immediate,6798 .immediate,
...@@ -6780,6 +6808,9 @@ fn genBinOp(...@@ -6780,6 +6808,9 @@ fn genBinOp(
6780 => true,6808 => true,
6781 .memory => |addr| math.cast(i32, @as(i64, @bitCast(addr))) == null,6809 .memory => |addr| math.cast(i32, @as(i64, @bitCast(addr))) == null,
6782 else => false,6810 else => false,
6811 .register_pair,
6812 .register_overflow,
6813 => unreachable,
6783 })6814 })
6784 .{ .register = try self.copyToTmpRegister(rhs_ty, resolved_src_mcv) }6815 .{ .register = try self.copyToTmpRegister(rhs_ty, resolved_src_mcv) }
6785 else6816 else
...@@ -6820,6 +6851,7 @@ fn genBinOp(...@@ -6820,6 +6851,7 @@ fn genBinOp(
6820 .undef,6851 .undef,
6821 .immediate,6852 .immediate,
6822 .eflags,6853 .eflags,
6854 .register_pair,
6823 .register_offset,6855 .register_offset,
6824 .register_overflow,6856 .register_overflow,
6825 .load_direct,6857 .load_direct,
...@@ -7748,121 +7780,154 @@ fn genBinOpMir(...@@ -7748,121 +7780,154 @@ fn genBinOpMir(
7748 .reserved_frame,7780 .reserved_frame,
7749 .air_ref,7781 .air_ref,
7750 => unreachable, // unmodifiable destination7782 => unreachable, // unmodifiable destination
7751 .register, .register_offset => {7783 .register, .register_pair, .register_offset => {
7752 assert(dst_mcv.isRegister());7784 switch (dst_mcv) {
7753 const dst_reg = dst_mcv.getReg().?;7785 .register, .register_pair => {},
7754 const dst_alias = registerAlias(dst_reg, abi_size);7786 .register_offset => |ro| assert(ro.off == 0),
7755 switch (src_mcv) {7787 else => unreachable,
7756 .none,7788 }
7757 .unreach,7789 for (dst_mcv.getRegs(), 0..) |dst_reg, dst_reg_i| {
7758 .dead,7790 const dst_reg_lock = self.register_manager.lockReg(dst_reg);
7759 .undef,7791 defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock);
7760 .register_overflow,7792
7761 .reserved_frame,7793 const mir_limb_tag: Mir.Inst.FixedTag = switch (dst_reg_i) {
7762 => unreachable,7794 0 => mir_tag,
7763 .register => |src_reg| try self.asmRegisterRegister(7795 1 => switch (mir_tag[1]) {
7764 mir_tag,7796 .add => .{ ._, .adc },
7765 dst_alias,7797 .sub, .cmp => .{ ._, .sbb },
7766 registerAlias(src_reg, abi_size),7798 .@"or", .@"and", .xor => mir_tag,
7767 ),7799 else => return self.fail("TODO genBinOpMir implement large ABI for {s}", .{
7768 .immediate => |imm| switch (self.regBitSize(ty)) {7800 @tagName(mir_tag[1]),
7769 8 => try self.asmRegisterImmediate(7801 }),
7770 mir_tag,7802 },
7771 dst_alias,7803 else => unreachable,
7772 if (math.cast(i8, @as(i64, @bitCast(imm)))) |small|7804 };
7773 Immediate.s(small)7805 const off: u4 = @intCast(dst_reg_i * 8);
7774 else7806 const limb_abi_size = @min(abi_size - off, 8);
7775 Immediate.u(@as(u8, @intCast(imm))),7807 const dst_alias = registerAlias(dst_reg, limb_abi_size);
7776 ),7808 switch (src_mcv) {
7777 16 => try self.asmRegisterImmediate(7809 .none,
7778 mir_tag,7810 .unreach,
7811 .dead,
7812 .undef,
7813 .register_overflow,
7814 .reserved_frame,
7815 => unreachable,
7816 .register, .register_pair => try self.asmRegisterRegister(
7817 mir_limb_tag,
7779 dst_alias,7818 dst_alias,
7780 if (math.cast(i16, @as(i64, @bitCast(imm)))) |small|7819 registerAlias(src_mcv.getRegs()[dst_reg_i], limb_abi_size),
7781 Immediate.s(small)
7782 else
7783 Immediate.u(@as(u16, @intCast(imm))),
7784 ),7820 ),
7785 32 => try self.asmRegisterImmediate(7821 .immediate => |imm| {
7822 assert(off == 0);
7823 switch (self.regBitSize(ty)) {
7824 8 => try self.asmRegisterImmediate(
7825 mir_limb_tag,
7826 dst_alias,
7827 if (math.cast(i8, @as(i64, @bitCast(imm)))) |small|
7828 Immediate.s(small)
7829 else
7830 Immediate.u(@as(u8, @intCast(imm))),
7831 ),
7832 16 => try self.asmRegisterImmediate(
7833 mir_limb_tag,
7834 dst_alias,
7835 if (math.cast(i16, @as(i64, @bitCast(imm)))) |small|
7836 Immediate.s(small)
7837 else
7838 Immediate.u(@as(u16, @intCast(imm))),
7839 ),
7840 32 => try self.asmRegisterImmediate(
7841 mir_limb_tag,
7842 dst_alias,
7843 if (math.cast(i32, @as(i64, @bitCast(imm)))) |small|
7844 Immediate.s(small)
7845 else
7846 Immediate.u(@as(u32, @intCast(imm))),
7847 ),
7848 64 => if (math.cast(i32, @as(i64, @bitCast(imm)))) |small|
7849 try self.asmRegisterImmediate(mir_limb_tag, dst_alias, Immediate.s(small))
7850 else
7851 try self.asmRegisterRegister(mir_limb_tag, dst_alias, registerAlias(
7852 try self.copyToTmpRegister(ty, src_mcv),
7853 limb_abi_size,
7854 )),
7855 else => unreachable,
7856 }
7857 },
7858 .eflags,
7859 .register_offset,
7860 .memory,
7861 .indirect,
7862 .load_direct,
7863 .lea_direct,
7864 .load_got,
7865 .lea_got,
7866 .load_tlv,
7867 .lea_tlv,
7868 .load_frame,
7869 .lea_frame,
7870 => {
7871 blk: {
7872 return self.asmRegisterMemory(
7873 mir_limb_tag,
7874 dst_alias,
7875 Memory.sib(Memory.PtrSize.fromSize(limb_abi_size), switch (src_mcv) {
7876 .memory => |addr| .{
7877 .base = .{ .reg = .ds },
7878 .disp = math.cast(i32, addr + off) orelse break :blk,
7879 },
7880 .indirect => |reg_off| .{
7881 .base = .{ .reg = reg_off.reg },
7882 .disp = reg_off.off + off,
7883 },
7884 .load_frame => |frame_addr| .{
7885 .base = .{ .frame = frame_addr.index },
7886 .disp = frame_addr.off + off,
7887 },
7888 else => break :blk,
7889 }),
7890 );
7891 }
7892
7893 switch (src_mcv) {
7894 .eflags,
7895 .register_offset,
7896 .lea_direct,
7897 .lea_got,
7898 .lea_tlv,
7899 .lea_frame,
7900 => {
7901 assert(off == 0);
7902 const reg = try self.copyToTmpRegister(ty, src_mcv);
7903 return self.genBinOpMir(
7904 mir_limb_tag,
7905 ty,
7906 dst_mcv,
7907 .{ .register = reg },
7908 );
7909 },
7910 .memory,
7911 .load_direct,
7912 .load_got,
7913 .load_tlv,
7914 => {
7915 const ptr_ty = try mod.singleConstPtrType(ty);
7916 const addr_reg = try self.copyToTmpRegister(ptr_ty, src_mcv.address());
7917 return self.genBinOpMir(mir_limb_tag, ty, dst_mcv, .{
7918 .indirect = .{ .reg = addr_reg, .off = off },
7919 });
7920 },
7921 else => unreachable,
7922 }
7923 },
7924 .air_ref => |src_ref| return self.genBinOpMir(
7786 mir_tag,7925 mir_tag,
7787 dst_alias,7926 ty,
7788 if (math.cast(i32, @as(i64, @bitCast(imm)))) |small|7927 dst_mcv,
7789 Immediate.s(small)7928 try self.resolveInst(src_ref),
7790 else
7791 Immediate.u(@as(u32, @intCast(imm))),
7792 ),7929 ),
7793 64 => if (math.cast(i32, @as(i64, @bitCast(imm)))) |small|7930 }
7794 try self.asmRegisterImmediate(mir_tag, dst_alias, Immediate.s(small))
7795 else
7796 try self.asmRegisterRegister(mir_tag, dst_alias, registerAlias(
7797 try self.copyToTmpRegister(ty, src_mcv),
7798 abi_size,
7799 )),
7800 else => unreachable,
7801 },
7802 .eflags,
7803 .register_offset,
7804 .memory,
7805 .indirect,
7806 .load_direct,
7807 .lea_direct,
7808 .load_got,
7809 .lea_got,
7810 .load_tlv,
7811 .lea_tlv,
7812 .load_frame,
7813 .lea_frame,
7814 => {
7815 blk: {
7816 return self.asmRegisterMemory(
7817 mir_tag,
7818 registerAlias(dst_reg, abi_size),
7819 Memory.sib(Memory.PtrSize.fromSize(abi_size), switch (src_mcv) {
7820 .memory => |addr| .{
7821 .base = .{ .reg = .ds },
7822 .disp = math.cast(i32, addr) orelse break :blk,
7823 },
7824 .indirect => |reg_off| .{
7825 .base = .{ .reg = reg_off.reg },
7826 .disp = reg_off.off,
7827 },
7828 .load_frame => |frame_addr| .{
7829 .base = .{ .frame = frame_addr.index },
7830 .disp = frame_addr.off,
7831 },
7832 else => break :blk,
7833 }),
7834 );
7835 }
7836
7837 const dst_reg_lock = self.register_manager.lockReg(dst_reg);
7838 defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock);
7839
7840 switch (src_mcv) {
7841 .eflags,
7842 .register_offset,
7843 .lea_direct,
7844 .lea_got,
7845 .lea_tlv,
7846 .lea_frame,
7847 => {
7848 const reg = try self.copyToTmpRegister(ty, src_mcv);
7849 return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = reg });
7850 },
7851 .memory,
7852 .load_direct,
7853 .load_got,
7854 .load_tlv,
7855 => {
7856 const ptr_ty = try mod.singleConstPtrType(ty);
7857 const addr_reg = try self.copyToTmpRegister(ptr_ty, src_mcv.address());
7858 return self.genBinOpMir(mir_tag, ty, dst_mcv, .{
7859 .indirect = .{ .reg = addr_reg },
7860 });
7861 },
7862 else => unreachable,
7863 }
7864 },
7865 .air_ref => |ref| try self.genBinOpMir(mir_tag, ty, dst_mcv, try self.resolveInst(ref)),
7866 }7931 }
7867 },7932 },
7868 .memory, .indirect, .load_got, .load_direct, .load_tlv, .load_frame => {7933 .memory, .indirect, .load_got, .load_direct, .load_tlv, .load_frame => {
...@@ -7877,10 +7942,7 @@ fn genBinOpMir(...@@ -7877,10 +7942,7 @@ fn genBinOpMir(
7877 errdefer self.register_manager.unlockReg(dst_addr_lock);7942 errdefer self.register_manager.unlockReg(dst_addr_lock);
78787943
7879 try self.genSetReg(dst_addr_reg, Type.usize, dst_mcv.address());7944 try self.genSetReg(dst_addr_reg, Type.usize, dst_mcv.address());
7880 break :dst .{7945 break :dst .{ .addr_reg = dst_addr_reg, .addr_lock = dst_addr_lock };
7881 .addr_reg = dst_addr_reg,
7882 .addr_lock = dst_addr_lock,
7883 };
7884 },7946 },
7885 .load_frame => null,7947 .load_frame => null,
7886 };7948 };
...@@ -7888,7 +7950,7 @@ fn genBinOpMir(...@@ -7888,7 +7950,7 @@ fn genBinOpMir(
78887950
7889 const resolved_src_mcv = switch (src_mcv) {7951 const resolved_src_mcv = switch (src_mcv) {
7890 else => src_mcv,7952 else => src_mcv,
7891 .air_ref => |ref| try self.resolveInst(ref),7953 .air_ref => |src_ref| try self.resolveInst(src_ref),
7892 };7954 };
7893 const src_info: OpInfo = switch (resolved_src_mcv) {7955 const src_info: OpInfo = switch (resolved_src_mcv) {
7894 .none,7956 .none,
...@@ -7901,6 +7963,7 @@ fn genBinOpMir(...@@ -7901,6 +7963,7 @@ fn genBinOpMir(
7901 => unreachable,7963 => unreachable,
7902 .immediate,7964 .immediate,
7903 .register,7965 .register,
7966 .register_pair,
7904 .register_offset,7967 .register_offset,
7905 .eflags,7968 .eflags,
7906 .indirect,7969 .indirect,
...@@ -7924,10 +7987,7 @@ fn genBinOpMir(...@@ -7924,10 +7987,7 @@ fn genBinOpMir(
7924 errdefer self.register_manager.unlockReg(src_addr_lock);7987 errdefer self.register_manager.unlockReg(src_addr_lock);
79257988
7926 try self.genSetReg(src_addr_reg, Type.usize, resolved_src_mcv.address());7989 try self.genSetReg(src_addr_reg, Type.usize, resolved_src_mcv.address());
7927 break :src .{7990 break :src .{ .addr_reg = src_addr_reg, .addr_lock = src_addr_lock };
7928 .addr_reg = src_addr_reg,
7929 .addr_lock = src_addr_lock,
7930 };
7931 },7991 },
7932 };7992 };
7933 defer if (src_info) |info| self.register_manager.unlockReg(info.addr_lock);7993 defer if (src_info) |info| self.register_manager.unlockReg(info.addr_lock);
...@@ -7938,9 +7998,13 @@ fn genBinOpMir(...@@ -7938,9 +7998,13 @@ fn genBinOpMir(
7938 .signed => Type.usize,7998 .signed => Type.usize,
7939 .unsigned => Type.isize,7999 .unsigned => Type.isize,
7940 };8000 };
8001 var limb_i: usize = 0;
7941 var off: i32 = 0;8002 var off: i32 = 0;
7942 while (off < abi_size) : (off += 8) {8003 while (off < abi_size) : ({
7943 const mir_limb_tag: Mir.Inst.FixedTag = switch (off) {8004 limb_i += 1;
8005 off += 8;
8006 }) {
8007 const mir_limb_tag: Mir.Inst.FixedTag = switch (limb_i) {
7944 0 => mir_tag,8008 0 => mir_tag,
7945 else => switch (mir_tag[1]) {8009 else => switch (mir_tag[1]) {
7946 .add => .{ ._, .adc },8010 .add => .{ ._, .adc },
...@@ -7979,16 +8043,8 @@ fn genBinOpMir(...@@ -7979,16 +8043,8 @@ fn genBinOpMir(
7979 .reserved_frame,8043 .reserved_frame,
7980 .air_ref,8044 .air_ref,
7981 => unreachable,8045 => unreachable,
7982 .register => |src_reg| switch (off) {
7983 0 => try self.asmMemoryRegister(
7984 mir_limb_tag,
7985 dst_limb_mem,
7986 registerAlias(src_reg, limb_abi_size),
7987 ),
7988 else => unreachable,
7989 },
7990 .immediate => |src_imm| {8046 .immediate => |src_imm| {
7991 const imm: u64 = switch (off) {8047 const imm: u64 = switch (limb_i) {
7992 0 => src_imm,8048 0 => src_imm,
7993 else => switch (ty_signedness) {8049 else => switch (ty_signedness) {
7994 .signed => @bitCast(@as(i64, @bitCast(src_imm)) >> 63),8050 .signed => @bitCast(@as(i64, @bitCast(src_imm)) >> 63),
...@@ -8038,6 +8094,8 @@ fn genBinOpMir(...@@ -8038,6 +8094,8 @@ fn genBinOpMir(
8038 else => unreachable,8094 else => unreachable,
8039 }8095 }
8040 },8096 },
8097 .register,
8098 .register_pair,
8041 .register_offset,8099 .register_offset,
8042 .eflags,8100 .eflags,
8043 .memory,8101 .memory,
...@@ -8051,16 +8109,19 @@ fn genBinOpMir(...@@ -8051,16 +8109,19 @@ fn genBinOpMir(
8051 .load_frame,8109 .load_frame,
8052 .lea_frame,8110 .lea_frame,
8053 => {8111 => {
8054 const src_limb_reg = try self.copyToTmpRegister(limb_ty, if (src_info) |info| .{8112 const src_limb_mcv: MCValue = if (src_info) |info| .{
8055 .indirect = .{ .reg = info.addr_reg, .off = off },8113 .indirect = .{ .reg = info.addr_reg, .off = off },
8056 } else switch (resolved_src_mcv) {8114 } else switch (resolved_src_mcv) {
8115 .register, .register_pair => .{
8116 .register = resolved_src_mcv.getRegs()[limb_i],
8117 },
8057 .eflags,8118 .eflags,
8058 .register_offset,8119 .register_offset,
8059 .lea_direct,8120 .lea_direct,
8060 .lea_got,8121 .lea_got,
8061 .lea_tlv,8122 .lea_tlv,
8062 .lea_frame,8123 .lea_frame,
8063 => switch (off) {8124 => switch (limb_i) {
8064 0 => resolved_src_mcv,8125 0 => resolved_src_mcv,
8065 else => .{ .immediate = 0 },8126 else => .{ .immediate = 0 },
8066 },8127 },
...@@ -8074,7 +8135,11 @@ fn genBinOpMir(...@@ -8074,7 +8135,11 @@ fn genBinOpMir(
8074 .off = frame_addr.off + off,8135 .off = frame_addr.off + off,
8075 } },8136 } },
8076 else => unreachable,8137 else => unreachable,
8077 });8138 };
8139 const src_limb_reg = if (src_limb_mcv.isRegister())
8140 src_limb_mcv.getReg().?
8141 else
8142 try self.copyToTmpRegister(limb_ty, src_limb_mcv);
8078 try self.asmMemoryRegister(8143 try self.asmMemoryRegister(
8079 mir_limb_tag,8144 mir_limb_tag,
8080 dst_limb_mem,8145 dst_limb_mem,
...@@ -8098,8 +8163,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M...@@ -8098,8 +8163,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
8098 .dead,8163 .dead,
8099 .undef,8164 .undef,
8100 .immediate,8165 .immediate,
8101 .register_offset,
8102 .eflags,8166 .eflags,
8167 .register_offset,
8103 .register_overflow,8168 .register_overflow,
8104 .lea_direct,8169 .lea_direct,
8105 .lea_got,8170 .lea_got,
...@@ -8115,13 +8180,14 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M...@@ -8115,13 +8180,14 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
81158180
8116 const resolved_src_mcv = switch (src_mcv) {8181 const resolved_src_mcv = switch (src_mcv) {
8117 else => src_mcv,8182 else => src_mcv,
8118 .air_ref => |ref| try self.resolveInst(ref),8183 .air_ref => |src_ref| try self.resolveInst(src_ref),
8119 };8184 };
8120 switch (resolved_src_mcv) {8185 switch (resolved_src_mcv) {
8121 .none,8186 .none,
8122 .unreach,8187 .unreach,
8123 .dead,8188 .dead,
8124 .undef,8189 .undef,
8190 .register_pair,
8125 .register_overflow,8191 .register_overflow,
8126 .reserved_frame,8192 .reserved_frame,
8127 .air_ref,8193 .air_ref,
...@@ -8187,6 +8253,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M...@@ -8187,6 +8253,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
8187 ),8253 ),
8188 }8254 }
8189 },8255 },
8256 .register_pair => unreachable, // unimplemented
8190 .memory, .indirect, .load_direct, .load_got, .load_tlv, .load_frame => {8257 .memory, .indirect, .load_direct, .load_got, .load_tlv, .load_frame => {
8191 const tmp_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);8258 const tmp_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);
8192 const tmp_mcv = MCValue{ .register = tmp_reg };8259 const tmp_mcv = MCValue{ .register = tmp_reg };
...@@ -8209,8 +8276,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -8209,8 +8276,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
8209 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {8276 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
8210 const dst_mcv = self.args[arg_index];8277 const dst_mcv = self.args[arg_index];
8211 switch (dst_mcv) {8278 switch (dst_mcv) {
8212 .register => |reg| self.register_manager.getRegAssumeFree(reg, inst),8279 .register, .register_pair, .load_frame => for (dst_mcv.getRegs()) |reg|
8213 .load_frame => {},8280 self.register_manager.getRegAssumeFree(reg, inst),
8214 else => return self.fail("TODO implement arg for {}", .{dst_mcv}),8281 else => return self.fail("TODO implement arg for {}", .{dst_mcv}),
8215 }8282 }
82168283
...@@ -8230,6 +8297,9 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void {...@@ -8230,6 +8297,9 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void {
8230 .dwarf => |dw| {8297 .dwarf => |dw| {
8231 const loc: link.File.Dwarf.DeclState.DbgInfoLoc = switch (mcv) {8298 const loc: link.File.Dwarf.DeclState.DbgInfoLoc = switch (mcv) {
8232 .register => |reg| .{ .register = reg.dwarfNum() },8299 .register => |reg| .{ .register = reg.dwarfNum() },
8300 .register_pair => |regs| .{ .register_pair = .{
8301 regs[0].dwarfNum(), regs[1].dwarfNum(),
8302 } },
8233 // TODO use a frame index8303 // TODO use a frame index
8234 .load_frame => return,8304 .load_frame => return,
8235 //.stack_offset => |off| .{8305 //.stack_offset => |off| .{
...@@ -8390,7 +8460,10 @@ fn genCall(self: *Self, info: union(enum) {...@@ -8390,7 +8460,10 @@ fn genCall(self: *Self, info: union(enum) {
8390 };8460 };
8391 const fn_info = mod.typeToFunc(fn_ty).?;8461 const fn_info = mod.typeToFunc(fn_ty).?;
83928462
8393 const ExpectedContents = [16]Type;8463 const ExpectedContents = extern struct {
8464 var_args: [16][@sizeOf(Type)]u8 align(@alignOf(Type)),
8465 arg_regs: [16][@sizeOf(?RegisterLock)]u8 align(@alignOf(?RegisterLock)),
8466 };
8394 var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) =8467 var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) =
8395 std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa);8468 std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa);
8396 const allocator = stack.get();8469 const allocator = stack.get();
...@@ -8399,6 +8472,11 @@ fn genCall(self: *Self, info: union(enum) {...@@ -8399,6 +8472,11 @@ fn genCall(self: *Self, info: union(enum) {
8399 defer allocator.free(var_args);8472 defer allocator.free(var_args);
8400 for (var_args, arg_types[fn_info.param_types.len..]) |*var_arg, arg_ty| var_arg.* = arg_ty;8473 for (var_args, arg_types[fn_info.param_types.len..]) |*var_arg, arg_ty| var_arg.* = arg_ty;
84018474
8475 var arg_locks = std.ArrayList(?RegisterLock).init(allocator);
8476 defer arg_locks.deinit();
8477 try arg_locks.ensureTotalCapacity(16);
8478 defer for (arg_locks.items) |arg_lock| if (arg_lock) |lock| self.register_manager.unlockReg(lock);
8479
8402 var call_info =8480 var call_info =
8403 try self.resolveCallingConventionValues(fn_info, var_args, .call_frame);8481 try self.resolveCallingConventionValues(fn_info, var_args, .call_frame);
8404 defer call_info.deinit(self);8482 defer call_info.deinit(self);
...@@ -8430,7 +8508,14 @@ fn genCall(self: *Self, info: union(enum) {...@@ -8430,7 +8508,14 @@ fn genCall(self: *Self, info: union(enum) {
8430 }8508 }
8431 for (call_info.args, arg_types, args) |dst_arg, arg_ty, src_arg| switch (dst_arg) {8509 for (call_info.args, arg_types, args) |dst_arg, arg_ty, src_arg| switch (dst_arg) {
8432 .none => {},8510 .none => {},
8433 .register => |reg| try self.spillRegisters(&.{reg}),8511 .register => |reg| {
8512 try self.spillRegisters(&.{reg});
8513 try arg_locks.append(self.register_manager.lockReg(reg));
8514 },
8515 .register_pair => |regs| {
8516 try self.spillRegisters(&regs);
8517 try arg_locks.appendSlice(&self.register_manager.lockRegs(2, regs));
8518 },
8434 .load_frame => try self.genCopy(arg_ty, dst_arg, src_arg),8519 .load_frame => try self.genCopy(arg_ty, dst_arg, src_arg),
8435 else => unreachable,8520 else => unreachable,
8436 };8521 };
...@@ -8451,13 +8536,11 @@ fn genCall(self: *Self, info: union(enum) {...@@ -8451,13 +8536,11 @@ fn genCall(self: *Self, info: union(enum) {
8451 };8536 };
8452 defer if (ret_lock) |lock| self.register_manager.unlockReg(lock);8537 defer if (ret_lock) |lock| self.register_manager.unlockReg(lock);
84538538
8454 for (call_info.args, arg_types, args) |dst_arg, arg_ty, src_arg| {8539 for (call_info.args, arg_types, args) |dst_arg, arg_ty, src_arg| switch (dst_arg) {
8455 switch (dst_arg) {8540 .none, .load_frame => {},
8456 .none, .load_frame => {},8541 .register, .register_pair => try self.genCopy(arg_ty, dst_arg, src_arg),
8457 .register => try self.genCopy(arg_ty, dst_arg, src_arg),8542 else => unreachable,
8458 else => unreachable,8543 };
8459 }
8460 }
84618544
8462 // Due to incremental compilation, how function calls are generated depends8545 // Due to incremental compilation, how function calls are generated depends
8463 // on linking.8546 // on linking.
...@@ -8527,7 +8610,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {...@@ -8527,7 +8610,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
8527 const ret_ty = self.fn_type.fnReturnType(mod);8610 const ret_ty = self.fn_type.fnReturnType(mod);
8528 switch (self.ret_mcv.short) {8611 switch (self.ret_mcv.short) {
8529 .none => {},8612 .none => {},
8530 .register => try self.genCopy(ret_ty, self.ret_mcv.short, operand),8613 .register, .register_pair => try self.genCopy(ret_ty, self.ret_mcv.short, operand),
8531 .indirect => |reg_off| {8614 .indirect => |reg_off| {
8532 try self.register_manager.getReg(reg_off.reg, null);8615 try self.register_manager.getReg(reg_off.reg, null);
8533 const lock = self.register_manager.lockRegAssumeUnused(reg_off.reg);8616 const lock = self.register_manager.lockRegAssumeUnused(reg_off.reg);
...@@ -8612,6 +8695,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {...@@ -8612,6 +8695,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
8612 const lhs_mcv = try self.resolveInst(bin_op.lhs);8695 const lhs_mcv = try self.resolveInst(bin_op.lhs);
8613 const lhs_lock = switch (lhs_mcv) {8696 const lhs_lock = switch (lhs_mcv) {
8614 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),8697 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
8698 .register_offset => |ro| self.register_manager.lockRegAssumeUnused(ro.reg),
8615 else => null,8699 else => null,
8616 };8700 };
8617 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);8701 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
...@@ -8619,6 +8703,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {...@@ -8619,6 +8703,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
8619 const rhs_mcv = try self.resolveInst(bin_op.rhs);8703 const rhs_mcv = try self.resolveInst(bin_op.rhs);
8620 const rhs_lock = switch (rhs_mcv) {8704 const rhs_lock = switch (rhs_mcv) {
8621 .register => |reg| self.register_manager.lockReg(reg),8705 .register => |reg| self.register_manager.lockReg(reg),
8706 .register_offset => |ro| self.register_manager.lockReg(ro.reg),
8622 else => null,8707 else => null,
8623 };8708 };
8624 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);8709 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
...@@ -9039,6 +9124,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC...@@ -9039,6 +9124,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
9039 .undef,9124 .undef,
9040 .immediate,9125 .immediate,
9041 .eflags,9126 .eflags,
9127 .register_pair,
9042 .register_offset,9128 .register_offset,
9043 .register_overflow,9129 .register_overflow,
9044 .lea_direct,9130 .lea_direct,
...@@ -10102,6 +10188,16 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError...@@ -10102,6 +10188,16 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError
10102 .off = -dst_reg_off.off,10188 .off = -dst_reg_off.off,
10103 } },10189 } },
10104 }),10190 }),
10191 .register_pair => |dst_regs| switch (src_mcv) {
10192 .register_pair => |src_regs| for (dst_regs, src_regs) |dst_reg, src_reg|
10193 try self.genSetReg(dst_reg, Type.usize, .{ .register = src_reg }),
10194 else => for (dst_regs, 0..) |dst_reg, dst_reg_i| try self.genSetReg(
10195 dst_reg,
10196 Type.usize,
10197 src_mcv.address().offset(@intCast(dst_reg_i * 8)).deref(),
10198 ),
10199 .air_ref => |src_ref| try self.genCopy(ty, dst_mcv, try self.resolveInst(src_ref)),
10200 },
10105 .indirect => |reg_off| try self.genSetMem(.{ .reg = reg_off.reg }, reg_off.off, ty, src_mcv),10201 .indirect => |reg_off| try self.genSetMem(.{ .reg = reg_off.reg }, reg_off.off, ty, src_mcv),
10106 .memory, .load_direct, .load_got, .load_tlv => {10202 .memory, .load_direct, .load_got, .load_tlv => {
10107 switch (dst_mcv) {10203 switch (dst_mcv) {
...@@ -10135,6 +10231,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr...@@ -10135,6 +10231,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
10135 .none,10231 .none,
10136 .unreach,10232 .unreach,
10137 .dead,10233 .dead,
10234 .register_pair,
10138 .register_overflow,10235 .register_overflow,
10139 .reserved_frame,10236 .reserved_frame,
10140 => unreachable,10237 => unreachable,
...@@ -10312,6 +10409,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr...@@ -10312,6 +10409,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
10312 Immediate.u(0),10409 Immediate.u(0),
10313 ),10410 ),
10314 }10411 }
10412 return;
10315 },10413 },
10316 .load_direct => |sym_index| switch (ty.zigTypeTag(mod)) {10414 .load_direct => |sym_index| switch (ty.zigTypeTag(mod)) {
10317 else => {10415 else => {
...@@ -10403,7 +10501,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr...@@ -10403,7 +10501,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
10403 @tagName(self.bin_file.tag),10501 @tagName(self.bin_file.tag),
10404 });10502 });
10405 },10503 },
10406 .air_ref => |ref| try self.genSetReg(dst_reg, ty, try self.resolveInst(ref)),10504 .air_ref => |src_ref| try self.genSetReg(dst_reg, ty, try self.resolveInst(src_ref)),
10407 }10505 }
10408}10506}
1040910507
...@@ -10480,6 +10578,32 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal...@@ -10480,6 +10578,32 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal
10480 ),10578 ),
10481 }10579 }
10482 },10580 },
10581 .register_pair => |src_regs| for (src_regs, 0..) |src_reg, src_reg_i| {
10582 const part_size = @min(abi_size - src_reg_i * 8, 8);
10583 const dst_mem = Memory.sib(
10584 Memory.PtrSize.fromSize(part_size),
10585 .{ .base = base, .disp = disp + @as(i32, @intCast(src_reg_i * 8)) },
10586 );
10587 const src_alias = registerAlias(src_reg, part_size);
10588 switch (try self.moveStrategy(ty, switch (base) {
10589 .none => ty.abiAlignment(mod).check(@as(u32, @bitCast(disp))),
10590 .reg => |reg| switch (reg) {
10591 .es, .cs, .ss, .ds => ty.abiAlignment(mod).check(@as(u32, @bitCast(disp))),
10592 else => false,
10593 },
10594 .frame => |frame_index| self.getFrameAddrAlignment(
10595 .{ .index = frame_index, .off = disp },
10596 ).compare(.gte, ty.abiAlignment(mod)),
10597 })) {
10598 .move => |tag| try self.asmMemoryRegister(tag, dst_mem, src_alias),
10599 .insert_extract, .vex_insert_extract => |ie| try self.asmMemoryRegisterImmediate(
10600 ie.extract,
10601 dst_mem,
10602 src_alias,
10603 Immediate.u(0),
10604 ),
10605 }
10606 },
10483 .register_overflow => |ro| {10607 .register_overflow => |ro| {
10484 try self.genSetMem(10608 try self.genSetMem(
10485 base,10609 base,
...@@ -10516,7 +10640,7 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal...@@ -10516,7 +10640,7 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal
10516 },10640 },
10517 else => try self.genInlineMemcpy(dst_ptr_mcv, src_mcv.address(), .{ .immediate = abi_size }),10641 else => try self.genInlineMemcpy(dst_ptr_mcv, src_mcv.address(), .{ .immediate = abi_size }),
10518 },10642 },
10519 .air_ref => |ref| try self.genSetMem(base, disp, ty, try self.resolveInst(ref)),10643 .air_ref => |src_ref| try self.genSetMem(base, disp, ty, try self.resolveInst(src_ref)),
10520 }10644 }
10521}10645}
1052210646
...@@ -12306,68 +12430,93 @@ fn resolveCallingConventionValues(...@@ -12306,68 +12430,93 @@ fn resolveCallingConventionValues(
12306 .windows => &[1]abi.Class{abi.classifyWindows(ret_ty, mod)},12430 .windows => &[1]abi.Class{abi.classifyWindows(ret_ty, mod)},
12307 else => mem.sliceTo(&abi.classifySystemV(ret_ty, mod, .ret), .none),12431 else => mem.sliceTo(&abi.classifySystemV(ret_ty, mod, .ret), .none),
12308 };12432 };
12309 if (classes.len > 1) {12433 for (
12310 return self.fail("TODO handle multiple classes per type", .{});12434 classes,
12435 abi.getCAbiIntReturnRegs(self.target.*)[0..classes.len],
12436 0..,
12437 ) |class, ret_reg, ret_reg_i| {
12438 result.return_value = switch (classes[0]) {
12439 .integer => switch (ret_reg_i) {
12440 0 => InstTracking.init(.{ .register = registerAlias(
12441 ret_reg,
12442 @intCast(@min(ret_ty.abiSize(mod), 8)),
12443 ) }),
12444 1 => InstTracking.init(.{ .register_pair = .{
12445 result.return_value.short.register,
12446 registerAlias(ret_reg, @intCast(ret_ty.abiSize(mod) - 8)),
12447 } }),
12448 else => return self.fail("TODO handle multiple classes per type", .{}),
12449 },
12450 .float, .sse => switch (ret_reg_i) {
12451 0 => InstTracking.init(.{ .register = .xmm0 }),
12452 else => return self.fail("TODO handle multiple classes per type", .{}),
12453 },
12454 .memory => switch (ret_reg_i) {
12455 0 => ret: {
12456 const ret_indirect_reg =
12457 abi.getCAbiIntParamRegs(self.target.*)[param_reg_i];
12458 param_reg_i += 1;
12459 break :ret .{
12460 .short = .{ .indirect = .{ .reg = ret_reg } },
12461 .long = .{ .indirect = .{ .reg = ret_indirect_reg } },
12462 };
12463 },
12464 else => return self.fail("TODO handle multiple classes per type", .{}),
12465 },
12466 else => return self.fail("TODO handle calling convention class {s}", .{
12467 @tagName(class),
12468 }),
12469 };
12311 }12470 }
12312 const ret_reg = abi.getCAbiIntReturnRegs(self.target.*)[0];
12313 result.return_value = switch (classes[0]) {
12314 .integer => InstTracking.init(.{ .register = registerAlias(
12315 ret_reg,
12316 @intCast(ret_ty.abiSize(mod)),
12317 ) }),
12318 .float, .sse => InstTracking.init(.{ .register = .xmm0 }),
12319 .memory => ret: {
12320 const ret_indirect_reg = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i];
12321 param_reg_i += 1;
12322 break :ret .{
12323 .short = .{ .indirect = .{ .reg = ret_reg } },
12324 .long = .{ .indirect = .{ .reg = ret_indirect_reg } },
12325 };
12326 },
12327 else => |class| return self.fail("TODO handle calling convention class {s}", .{
12328 @tagName(class),
12329 }),
12330 };
12331 }12471 }
1233212472
12333 // Input params12473 // Input params
12334 for (param_types, result.args) |ty, *arg| {12474 next_param: for (param_types, result.args) |ty, *arg| {
12335 assert(ty.hasRuntimeBitsIgnoreComptime(mod));12475 assert(ty.hasRuntimeBitsIgnoreComptime(mod));
1233612476
12337 const classes = switch (self.target.os.tag) {12477 const classes = switch (self.target.os.tag) {
12338 .windows => &[1]abi.Class{abi.classifyWindows(ty, mod)},12478 .windows => &[1]abi.Class{abi.classifyWindows(ty, mod)},
12339 else => mem.sliceTo(&abi.classifySystemV(ty, mod, .arg), .none),12479 else => mem.sliceTo(&abi.classifySystemV(ty, mod, .arg), .none),
12340 };12480 };
12341 if (classes.len > 1) {12481 for (classes, 0..) |class, class_i| {
12342 return self.fail("TODO handle multiple classes per type", .{});12482 switch (class) {
12343 }12483 .integer => if (param_reg_i < abi.getCAbiIntParamRegs(self.target.*).len) {
12344 switch (classes[0]) {12484 const param_reg = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i];
12345 .integer => if (param_reg_i < abi.getCAbiIntParamRegs(self.target.*).len) {
12346 arg.* = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i] };
12347 param_reg_i += 1;
12348 continue;
12349 },
12350 .float, .sse => switch (self.target.os.tag) {
12351 .windows => if (param_reg_i < 4) {
12352 arg.* = .{
12353 .register = @enumFromInt(@intFromEnum(Register.xmm0) + param_reg_i),
12354 };
12355 param_reg_i += 1;12485 param_reg_i += 1;
12356 continue;12486
12357 },12487 arg.* = switch (class_i) {
12358 else => if (param_sse_reg_i < 8) {12488 0 => .{ .register = param_reg },
12359 arg.* = .{12489 1 => .{ .register_pair = .{ arg.register, param_reg } },
12360 .register = @enumFromInt(@intFromEnum(Register.xmm0) + param_sse_reg_i),12490 else => return self.fail("TODO handle multiple classes per type", .{}),
12361 };12491 };
12362 param_sse_reg_i += 1;
12363 continue;12492 continue;
12493 } else break,
12494 .float, .sse => switch (self.target.os.tag) {
12495 .windows => if (param_reg_i < 4) {
12496 if (class_i > 0)
12497 return self.fail("TODO handle multiple classes per type", .{});
12498 arg.* = .{
12499 .register = @enumFromInt(@intFromEnum(Register.xmm0) + param_reg_i),
12500 };
12501 param_reg_i += 1;
12502 continue;
12503 } else break,
12504 else => if (param_sse_reg_i < 8) {
12505 if (class_i > 0)
12506 return self.fail("TODO handle multiple classes per type", .{});
12507 arg.* = .{ .register = @enumFromInt(
12508 @intFromEnum(Register.xmm0) + param_sse_reg_i,
12509 ) };
12510 param_sse_reg_i += 1;
12511 continue;
12512 } else break,
12364 },12513 },
12365 },12514 .memory => break,
12366 .memory => {}, // fallthrough12515 else => return self.fail("TODO handle calling convention class {s}", .{
12367 else => |class| return self.fail("TODO handle calling convention class {s}", .{12516 @tagName(class),
12368 @tagName(class),12517 }),
12369 }),12518 }
12370 }12519 } else continue :next_param;
1237112520
12372 const param_size: u31 = @intCast(ty.abiSize(mod));12521 const param_size: u31 = @intCast(ty.abiSize(mod));
12373 const param_align: u31 = @intCast(ty.abiAlignment(mod).toByteUnitsOptional().?);12522 const param_align: u31 = @intCast(ty.abiAlignment(mod).toByteUnitsOptional().?);
...@@ -12602,9 +12751,7 @@ fn floatCompilerRtAbiType(self: *Self, ty: Type, other_ty: Type) Type {...@@ -12602,9 +12751,7 @@ fn floatCompilerRtAbiType(self: *Self, ty: Type, other_ty: Type) Type {
1260212751
12603fn floatLibcAbiPrefix(ty: Type) []const u8 {12752fn floatLibcAbiPrefix(ty: Type) []const u8 {
12604 return switch (ty.toIntern()) {12753 return switch (ty.toIntern()) {
12605 .f16_type,12754 .f16_type, .f80_type => "__",
12606 .f80_type,
12607 => "__",
12608 .f32_type, .f64_type, .f128_type, .c_longdouble_type => "",12755 .f32_type, .f64_type, .f128_type, .c_longdouble_type => "",
12609 else => unreachable,12756 else => unreachable,
12610 };12757 };
src/link/Dwarf.zig+74-1
...@@ -566,6 +566,7 @@ pub const DeclState = struct {...@@ -566,6 +566,7 @@ pub const DeclState = struct {
566566
567 pub const DbgInfoLoc = union(enum) {567 pub const DbgInfoLoc = union(enum) {
568 register: u8,568 register: u8,
569 register_pair: [2]u8,
569 stack: struct {570 stack: struct {
570 fp_register: u8,571 fp_register: u8,
571 offset: i32,572 offset: i32,
...@@ -610,6 +611,42 @@ pub const DeclState = struct {...@@ -610,6 +611,42 @@ pub const DeclState = struct {
610 leb128.writeULEB128(dbg_info.writer(), reg) catch unreachable;611 leb128.writeULEB128(dbg_info.writer(), reg) catch unreachable;
611 }612 }
612 },613 },
614 .register_pair => |regs| {
615 const reg_bits = self.mod.getTarget().ptrBitWidth();
616 const reg_bytes = @as(u8, @intCast(@divExact(reg_bits, 8)));
617 const abi_size = ty.abiSize(self.mod);
618 try dbg_info.ensureUnusedCapacity(10);
619 dbg_info.appendAssumeCapacity(@intFromEnum(AbbrevKind.parameter));
620 // DW.AT.location, DW.FORM.exprloc
621 var expr_len = std.io.countingWriter(std.io.null_writer);
622 for (regs, 0..) |reg, reg_i| {
623 if (reg < 32) {
624 expr_len.writer().writeByte(DW.OP.reg0 + reg) catch unreachable;
625 } else {
626 expr_len.writer().writeByte(DW.OP.regx) catch unreachable;
627 leb128.writeULEB128(expr_len.writer(), reg) catch unreachable;
628 }
629 expr_len.writer().writeByte(DW.OP.piece) catch unreachable;
630 leb128.writeULEB128(
631 expr_len.writer(),
632 @min(abi_size - reg_i * reg_bytes, reg_bytes),
633 ) catch unreachable;
634 }
635 leb128.writeULEB128(dbg_info.writer(), expr_len.bytes_written) catch unreachable;
636 for (regs, 0..) |reg, reg_i| {
637 if (reg < 32) {
638 dbg_info.appendAssumeCapacity(DW.OP.reg0 + reg);
639 } else {
640 dbg_info.appendAssumeCapacity(DW.OP.regx);
641 leb128.writeULEB128(dbg_info.writer(), reg) catch unreachable;
642 }
643 dbg_info.appendAssumeCapacity(DW.OP.piece);
644 leb128.writeULEB128(
645 dbg_info.writer(),
646 @min(abi_size - reg_i * reg_bytes, reg_bytes),
647 ) catch unreachable;
648 }
649 },
613 .stack => |info| {650 .stack => |info| {
614 try dbg_info.ensureUnusedCapacity(9);651 try dbg_info.ensureUnusedCapacity(9);
615 dbg_info.appendAssumeCapacity(@intFromEnum(AbbrevKind.parameter));652 dbg_info.appendAssumeCapacity(@intFromEnum(AbbrevKind.parameter));
...@@ -676,7 +713,7 @@ pub const DeclState = struct {...@@ -676,7 +713,7 @@ pub const DeclState = struct {
676713
677 switch (loc) {714 switch (loc) {
678 .register => |reg| {715 .register => |reg| {
679 try dbg_info.ensureUnusedCapacity(4);716 try dbg_info.ensureUnusedCapacity(3);
680 // DW.AT.location, DW.FORM.exprloc717 // DW.AT.location, DW.FORM.exprloc
681 var expr_len = std.io.countingWriter(std.io.null_writer);718 var expr_len = std.io.countingWriter(std.io.null_writer);
682 if (reg < 32) {719 if (reg < 32) {
...@@ -694,6 +731,42 @@ pub const DeclState = struct {...@@ -694,6 +731,42 @@ pub const DeclState = struct {
694 }731 }
695 },732 },
696733
734 .register_pair => |regs| {
735 const reg_bits = self.mod.getTarget().ptrBitWidth();
736 const reg_bytes = @as(u8, @intCast(@divExact(reg_bits, 8)));
737 const abi_size = child_ty.abiSize(self.mod);
738 try dbg_info.ensureUnusedCapacity(9);
739 // DW.AT.location, DW.FORM.exprloc
740 var expr_len = std.io.countingWriter(std.io.null_writer);
741 for (regs, 0..) |reg, reg_i| {
742 if (reg < 32) {
743 expr_len.writer().writeByte(DW.OP.reg0 + reg) catch unreachable;
744 } else {
745 expr_len.writer().writeByte(DW.OP.regx) catch unreachable;
746 leb128.writeULEB128(expr_len.writer(), reg) catch unreachable;
747 }
748 expr_len.writer().writeByte(DW.OP.piece) catch unreachable;
749 leb128.writeULEB128(
750 expr_len.writer(),
751 @min(abi_size - reg_i * reg_bytes, reg_bytes),
752 ) catch unreachable;
753 }
754 leb128.writeULEB128(dbg_info.writer(), expr_len.bytes_written) catch unreachable;
755 for (regs, 0..) |reg, reg_i| {
756 if (reg < 32) {
757 dbg_info.appendAssumeCapacity(DW.OP.reg0 + reg);
758 } else {
759 dbg_info.appendAssumeCapacity(DW.OP.regx);
760 leb128.writeULEB128(dbg_info.writer(), reg) catch unreachable;
761 }
762 dbg_info.appendAssumeCapacity(DW.OP.piece);
763 leb128.writeULEB128(
764 dbg_info.writer(),
765 @min(abi_size - reg_i * reg_bytes, reg_bytes),
766 ) catch unreachable;
767 }
768 },
769
697 .stack => |info| {770 .stack => |info| {
698 try dbg_info.ensureUnusedCapacity(9);771 try dbg_info.ensureUnusedCapacity(9);
699 // DW.AT.location, DW.FORM.exprloc772 // DW.AT.location, DW.FORM.exprloc
test/behavior/bugs/11995.zig-1
...@@ -18,7 +18,6 @@ const wuffs_base__slice_u8 = extern struct {...@@ -18,7 +18,6 @@ const wuffs_base__slice_u8 = extern struct {
18};18};
19test {19test {
20 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO20 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
21 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
22 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO21 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
23 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO22 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
24 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO23 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO