| ... | ... | @@ -178,6 +178,8 @@ pub const MCValue = union(enum) { |
| 178 | 178 | eflags: Condition, |
| 179 | 179 | /// The value is in a register. |
| 180 | 180 | register: Register, |
| 181 | /// The value is split across two registers. |
| 182 | register_pair: [2]Register, |
| 181 | 183 | /// The value is a constant offset from the value in a register. |
| 182 | 184 | register_offset: RegisterOffset, |
| 183 | 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 | 257 | }; |
| 256 | 258 | } |
| 257 | 259 | |
| 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 | 270 | fn getCondition(mcv: MCValue) ?Condition { |
| 259 | 271 | return switch (mcv) { |
| 260 | 272 | .eflags => |cc| cc, |
| ... | ... | @@ -272,6 +284,7 @@ pub const MCValue = union(enum) { |
| 272 | 284 | .immediate, |
| 273 | 285 | .eflags, |
| 274 | 286 | .register, |
| 287 | .register_pair, |
| 275 | 288 | .register_offset, |
| 276 | 289 | .register_overflow, |
| 277 | 290 | .lea_direct, |
| ... | ... | @@ -300,6 +313,7 @@ pub const MCValue = union(enum) { |
| 300 | 313 | .dead, |
| 301 | 314 | .undef, |
| 302 | 315 | .eflags, |
| 316 | .register_pair, |
| 303 | 317 | .register_overflow, |
| 304 | 318 | .memory, |
| 305 | 319 | .indirect, |
| ... | ... | @@ -326,7 +340,11 @@ pub const MCValue = union(enum) { |
| 326 | 340 | .unreach, |
| 327 | 341 | .dead, |
| 328 | 342 | .undef, |
| 343 | .reserved_frame, |
| 344 | .air_ref, |
| 345 | => unreachable, // not valid |
| 329 | 346 | .eflags, |
| 347 | .register_pair, |
| 330 | 348 | .register_overflow, |
| 331 | 349 | .memory, |
| 332 | 350 | .indirect, |
| ... | ... | @@ -337,9 +355,10 @@ pub const MCValue = union(enum) { |
| 337 | 355 | .load_tlv, |
| 338 | 356 | .lea_tlv, |
| 339 | 357 | .load_frame, |
| 340 | | .reserved_frame, |
| 341 | | .air_ref, |
| 342 | | => unreachable, // not offsettable |
| 358 | => switch (off) { |
| 359 | 0 => mcv, |
| 360 | else => unreachable, // not offsettable |
| 361 | }, |
| 343 | 362 | .immediate => |imm| .{ .immediate = @bitCast(@as(i64, @bitCast(imm)) +% off) }, |
| 344 | 363 | .register => |reg| .{ .register_offset = .{ .reg = reg, .off = off } }, |
| 345 | 364 | .register_offset => |reg_off| .{ |
| ... | ... | @@ -360,6 +379,7 @@ pub const MCValue = union(enum) { |
| 360 | 379 | .immediate, |
| 361 | 380 | .eflags, |
| 362 | 381 | .register, |
| 382 | .register_pair, |
| 363 | 383 | .register_offset, |
| 364 | 384 | .register_overflow, |
| 365 | 385 | .load_direct, |
| ... | ... | @@ -398,8 +418,11 @@ pub const MCValue = union(enum) { |
| 398 | 418 | .immediate => |pl| try writer.print("0x{x}", .{pl}), |
| 399 | 419 | .memory => |pl| try writer.print("[ds:0x{x}]", .{pl}), |
| 400 | 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 | 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 | 426 | .indirect => |pl| try writer.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }), |
| 404 | 427 | .load_direct => |pl| try writer.print("[direct:{d}]", .{pl}), |
| 405 | 428 | .lea_direct => |pl| try writer.print("direct:{d}", .{pl}), |
| ... | ... | @@ -443,6 +466,7 @@ const InstTracking = struct { |
| 443 | 466 | => unreachable, |
| 444 | 467 | .eflags, |
| 445 | 468 | .register, |
| 469 | .register_pair, |
| 446 | 470 | .register_offset, |
| 447 | 471 | .register_overflow, |
| 448 | 472 | .indirect, |
| ... | ... | @@ -454,6 +478,10 @@ const InstTracking = struct { |
| 454 | 478 | return self.short.getReg(); |
| 455 | 479 | } |
| 456 | 480 | |
| 481 | fn getRegs(self: *const InstTracking) []const Register { |
| 482 | return self.short.getRegs(); |
| 483 | } |
| 484 | |
| 457 | 485 | fn getCondition(self: InstTracking) ?Condition { |
| 458 | 486 | return self.short.getCondition(); |
| 459 | 487 | } |
| ... | ... | @@ -494,6 +522,7 @@ const InstTracking = struct { |
| 494 | 522 | .dead, |
| 495 | 523 | .eflags, |
| 496 | 524 | .register, |
| 525 | .register_pair, |
| 497 | 526 | .register_offset, |
| 498 | 527 | .register_overflow, |
| 499 | 528 | .indirect, |
| ... | ... | @@ -536,6 +565,7 @@ const InstTracking = struct { |
| 536 | 565 | .dead, |
| 537 | 566 | .eflags, |
| 538 | 567 | .register, |
| 568 | .register_pair, |
| 539 | 569 | .register_offset, |
| 540 | 570 | .register_overflow, |
| 541 | 571 | .indirect, |
| ... | ... | @@ -777,7 +807,7 @@ pub fn generate( |
| 777 | 807 | .lower = .{ |
| 778 | 808 | .allocator = bin_file.allocator, |
| 779 | 809 | .mir = mir, |
| 780 | | .target = &bin_file.options.target, |
| 810 | .cc = abi.resolveCallingConvention(fn_info.cc, function.target.*), |
| 781 | 811 | .src_loc = src_loc, |
| 782 | 812 | }, |
| 783 | 813 | .bin_file = bin_file, |
| ... | ... | @@ -863,7 +893,7 @@ pub fn generateLazy( |
| 863 | 893 | .lower = .{ |
| 864 | 894 | .allocator = bin_file.allocator, |
| 865 | 895 | .mir = mir, |
| 866 | | .target = &bin_file.options.target, |
| 896 | .cc = abi.resolveCallingConvention(.Unspecified, function.target.*), |
| 867 | 897 | .src_loc = src_loc, |
| 868 | 898 | }, |
| 869 | 899 | .bin_file = bin_file, |
| ... | ... | @@ -950,6 +980,7 @@ fn formatWipMir( |
| 950 | 980 | _: std.fmt.FormatOptions, |
| 951 | 981 | writer: anytype, |
| 952 | 982 | ) @TypeOf(writer).Error!void { |
| 983 | const mod = data.self.bin_file.options.module.?; |
| 953 | 984 | var lower = Lower{ |
| 954 | 985 | .allocator = data.self.gpa, |
| 955 | 986 | .mir = .{ |
| ... | ... | @@ -957,7 +988,7 @@ fn formatWipMir( |
| 957 | 988 | .extra = data.self.mir_extra.items, |
| 958 | 989 | .frame_locs = (std.MultiArrayList(Mir.FrameLoc){}).slice(), |
| 959 | 990 | }, |
| 960 | | .target = data.self.target, |
| 991 | .cc = mod.typeToFunc(data.self.fn_type).?.cc, |
| 961 | 992 | .src_loc = data.self.src_loc, |
| 962 | 993 | }; |
| 963 | 994 | for ((lower.lowerMir(data.inst) catch |err| switch (err) { |
| ... | ... | @@ -1650,7 +1681,7 @@ fn gen(self: *Self) InnerError!void { |
| 1650 | 1681 | try self.asmRegister(.{ ._, .pop }, .rbp); |
| 1651 | 1682 | try self.asmOpOnly(.{ ._, .ret }); |
| 1652 | 1683 | |
| 1653 | | const frame_layout = try self.computeFrameLayout(); |
| 1684 | const frame_layout = try self.computeFrameLayout(cc); |
| 1654 | 1685 | const need_frame_align = frame_layout.stack_mask != math.maxInt(u32); |
| 1655 | 1686 | const need_stack_adjust = frame_layout.stack_adjust > 0; |
| 1656 | 1687 | const need_save_reg = frame_layout.save_reg_list.count() > 0; |
| ... | ... | @@ -2028,7 +2059,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 2028 | 2059 | while (it.next()) |index| { |
| 2029 | 2060 | const tracked_inst = self.register_manager.registers[index]; |
| 2030 | 2061 | const tracking = self.getResolvedInstValue(tracked_inst); |
| 2031 | | assert(RegisterManager.indexOfRegIntoTracked(tracking.getReg().?).? == index); |
| 2062 | for (tracking.getRegs()) |reg| { |
| 2063 | if (RegisterManager.indexOfRegIntoTracked(reg).? == index) break; |
| 2064 | } else unreachable; // tracked register not in use |
| 2032 | 2065 | } |
| 2033 | 2066 | } |
| 2034 | 2067 | } |
| ... | ... | @@ -2043,7 +2076,8 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { |
| 2043 | 2076 | const enum_ty = lazy_sym.ty; |
| 2044 | 2077 | wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(self.bin_file.options.module.?)}); |
| 2045 | 2078 | |
| 2046 | | const param_regs = abi.getCAbiIntParamRegs(self.target.*); |
| 2079 | const resolved_cc = abi.resolveCallingConvention(.Unspecified, self.target.*); |
| 2080 | const param_regs = abi.getCAbiIntParamRegs(resolved_cc); |
| 2047 | 2081 | const param_locks = self.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*); |
| 2048 | 2082 | defer for (param_locks) |lock| self.register_manager.unlockReg(lock); |
| 2049 | 2083 | |
| ... | ... | @@ -2094,27 +2128,20 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { |
| 2094 | 2128 | } |
| 2095 | 2129 | |
| 2096 | 2130 | fn getValue(self: *Self, value: MCValue, inst: ?Air.Inst.Index) void { |
| 2097 | | const reg = value.getReg() orelse return; |
| 2098 | | if (self.register_manager.isRegFree(reg)) { |
| 2131 | for (value.getRegs()) |reg| if (self.register_manager.isRegFree(reg)) |
| 2099 | 2132 | self.register_manager.getRegAssumeFree(reg, inst); |
| 2100 | | } |
| 2101 | 2133 | } |
| 2102 | 2134 | |
| 2103 | 2135 | fn freeValue(self: *Self, value: MCValue) void { |
| 2104 | 2136 | switch (value) { |
| 2105 | | .register => |reg| { |
| 2106 | | self.register_manager.freeReg(reg); |
| 2107 | | }, |
| 2108 | | .register_offset => |reg_off| { |
| 2109 | | self.register_manager.freeReg(reg_off.reg); |
| 2110 | | }, |
| 2137 | .register => |reg| self.register_manager.freeReg(reg), |
| 2138 | .register_pair => |regs| for (regs) |reg| self.register_manager.freeReg(reg), |
| 2139 | .register_offset => |reg_off| self.register_manager.freeReg(reg_off.reg), |
| 2111 | 2140 | .register_overflow => |reg_ov| { |
| 2112 | 2141 | self.register_manager.freeReg(reg_ov.reg); |
| 2113 | 2142 | self.eflags_inst = null; |
| 2114 | 2143 | }, |
| 2115 | | .eflags => { |
| 2116 | | self.eflags_inst = null; |
| 2117 | | }, |
| 2144 | .eflags => self.eflags_inst = null, |
| 2118 | 2145 | else => {}, // TODO process stack allocation death |
| 2119 | 2146 | } |
| 2120 | 2147 | } |
| ... | ... | @@ -2183,7 +2210,7 @@ fn setFrameLoc( |
| 2183 | 2210 | offset.* += self.frame_allocs.items(.abi_size)[frame_i]; |
| 2184 | 2211 | } |
| 2185 | 2212 | |
| 2186 | | fn computeFrameLayout(self: *Self) !FrameLayout { |
| 2213 | fn computeFrameLayout(self: *Self, cc: std.builtin.CallingConvention) !FrameLayout { |
| 2187 | 2214 | const frame_allocs_len = self.frame_allocs.len; |
| 2188 | 2215 | try self.frame_locs.resize(self.gpa, frame_allocs_len); |
| 2189 | 2216 | const stack_frame_order = try self.gpa.alloc(FrameIndex, frame_allocs_len - FrameIndex.named_count); |
| ... | ... | @@ -2215,7 +2242,8 @@ fn computeFrameLayout(self: *Self) !FrameLayout { |
| 2215 | 2242 | // Create list of registers to save in the prologue. |
| 2216 | 2243 | // TODO handle register classes |
| 2217 | 2244 | var save_reg_list = Mir.RegisterList{}; |
| 2218 | | const callee_preserved_regs = abi.getCalleePreservedRegs(self.target.*); |
| 2245 | const callee_preserved_regs = |
| 2246 | abi.getCalleePreservedRegs(abi.resolveCallingConvention(cc, self.target.*)); |
| 2219 | 2247 | for (callee_preserved_regs) |reg| { |
| 2220 | 2248 | if (self.register_manager.isRegAllocated(reg)) { |
| 2221 | 2249 | save_reg_list.push(callee_preserved_regs, reg); |
| ... | ... | @@ -2449,7 +2477,9 @@ fn restoreState(self: *Self, state: State, deaths: []const Air.Inst.Index, compt |
| 2449 | 2477 | |
| 2450 | 2478 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 2451 | 2479 | const tracking = self.inst_tracking.getPtr(inst) orelse return; |
| 2452 | | assert(tracking.getReg().?.id() == reg.id()); |
| 2480 | for (tracking.getRegs()) |tracked_reg| { |
| 2481 | if (tracked_reg.id() == reg.id()) break; |
| 2482 | } else unreachable; // spilled reg not tracked with spilled instruciton |
| 2453 | 2483 | try tracking.spill(self, inst); |
| 2454 | 2484 | tracking.trackSpill(self, inst); |
| 2455 | 2485 | } |
| ... | ... | @@ -2465,9 +2495,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void { |
| 2465 | 2495 | } |
| 2466 | 2496 | |
| 2467 | 2497 | pub fn spillRegisters(self: *Self, registers: []const Register) !void { |
| 2468 | | for (registers) |reg| { |
| 2469 | | try self.register_manager.getReg(reg, null); |
| 2470 | | } |
| 2498 | for (registers) |reg| try self.register_manager.getReg(reg, null); |
| 2471 | 2499 | } |
| 2472 | 2500 | |
| 2473 | 2501 | /// Copies a value to a register without tracking the register. The register is not considered |
| ... | ... | @@ -5457,8 +5485,8 @@ fn reuseOperandAdvanced( |
| 5457 | 5485 | return false; |
| 5458 | 5486 | |
| 5459 | 5487 | switch (mcv) { |
| 5460 | | .register => |reg| { |
| 5461 | | // If it's in the registers table, need to associate the register with the |
| 5488 | .register, .register_pair => for (mcv.getRegs()) |reg| { |
| 5489 | // If it's in the registers table, need to associate the register(s) with the |
| 5462 | 5490 | // new instruction. |
| 5463 | 5491 | if (!self.register_manager.isRegFree(reg)) { |
| 5464 | 5492 | if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { |
| ... | ... | @@ -5561,6 +5589,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro |
| 5561 | 5589 | .dead, |
| 5562 | 5590 | .undef, |
| 5563 | 5591 | .eflags, |
| 5592 | .register_pair, |
| 5564 | 5593 | .register_overflow, |
| 5565 | 5594 | .reserved_frame, |
| 5566 | 5595 | => unreachable, // not a valid pointer |
| ... | ... | @@ -5585,7 +5614,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro |
| 5585 | 5614 | |
| 5586 | 5615 | try self.genCopy(dst_ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } }); |
| 5587 | 5616 | }, |
| 5588 | | .air_ref => |ref| try self.load(dst_mcv, ptr_ty, try self.resolveInst(ref)), |
| 5617 | .air_ref => |ptr_ref| try self.load(dst_mcv, ptr_ty, try self.resolveInst(ptr_ref)), |
| 5589 | 5618 | } |
| 5590 | 5619 | } |
| 5591 | 5620 | |
| ... | ... | @@ -5707,6 +5736,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr |
| 5707 | 5736 | .dead, |
| 5708 | 5737 | .undef, |
| 5709 | 5738 | .eflags, |
| 5739 | .register_pair, |
| 5710 | 5740 | .register_overflow, |
| 5711 | 5741 | .reserved_frame, |
| 5712 | 5742 | => unreachable, // not a valid pointer |
| ... | ... | @@ -5731,7 +5761,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr |
| 5731 | 5761 | |
| 5732 | 5762 | try self.genCopy(src_ty, .{ .indirect = .{ .reg = addr_reg } }, src_mcv); |
| 5733 | 5763 | }, |
| 5734 | | .air_ref => |ref| try self.store(ptr_ty, ptr_mcv, try self.resolveInst(ref)), |
| 5764 | .air_ref => |ptr_ref| try self.store(ptr_ty, ptr_mcv, try self.resolveInst(ptr_ref)), |
| 5735 | 5765 | } |
| 5736 | 5766 | } |
| 5737 | 5767 | |
| ... | ... | @@ -6077,6 +6107,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MC |
| 6077 | 6107 | .air_ref, |
| 6078 | 6108 | => unreachable, // unmodifiable destination |
| 6079 | 6109 | .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)), |
| 6110 | .register_pair => unreachable, // unimplemented |
| 6080 | 6111 | .memory, .load_got, .load_direct, .load_tlv => { |
| 6081 | 6112 | const addr_reg = try self.register_manager.allocReg(null, gp); |
| 6082 | 6113 | const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); |
| ... | ... | @@ -6764,7 +6795,7 @@ fn genBinOp( |
| 6764 | 6795 | => { |
| 6765 | 6796 | const resolved_src_mcv = switch (src_mcv) { |
| 6766 | 6797 | else => src_mcv, |
| 6767 | | .air_ref => |ref| try self.resolveInst(ref), |
| 6798 | .air_ref => |src_ref| try self.resolveInst(src_ref), |
| 6768 | 6799 | }; |
| 6769 | 6800 | const mat_src_mcv: MCValue = if (switch (resolved_src_mcv) { |
| 6770 | 6801 | .immediate, |
| ... | ... | @@ -6780,6 +6811,9 @@ fn genBinOp( |
| 6780 | 6811 | => true, |
| 6781 | 6812 | .memory => |addr| math.cast(i32, @as(i64, @bitCast(addr))) == null, |
| 6782 | 6813 | else => false, |
| 6814 | .register_pair, |
| 6815 | .register_overflow, |
| 6816 | => unreachable, |
| 6783 | 6817 | }) |
| 6784 | 6818 | .{ .register = try self.copyToTmpRegister(rhs_ty, resolved_src_mcv) } |
| 6785 | 6819 | else |
| ... | ... | @@ -6820,6 +6854,7 @@ fn genBinOp( |
| 6820 | 6854 | .undef, |
| 6821 | 6855 | .immediate, |
| 6822 | 6856 | .eflags, |
| 6857 | .register_pair, |
| 6823 | 6858 | .register_offset, |
| 6824 | 6859 | .register_overflow, |
| 6825 | 6860 | .load_direct, |
| ... | ... | @@ -7748,121 +7783,154 @@ fn genBinOpMir( |
| 7748 | 7783 | .reserved_frame, |
| 7749 | 7784 | .air_ref, |
| 7750 | 7785 | => unreachable, // unmodifiable destination |
| 7751 | | .register, .register_offset => { |
| 7752 | | assert(dst_mcv.isRegister()); |
| 7753 | | const dst_reg = dst_mcv.getReg().?; |
| 7754 | | const dst_alias = registerAlias(dst_reg, abi_size); |
| 7755 | | switch (src_mcv) { |
| 7756 | | .none, |
| 7757 | | .unreach, |
| 7758 | | .dead, |
| 7759 | | .undef, |
| 7760 | | .register_overflow, |
| 7761 | | .reserved_frame, |
| 7762 | | => unreachable, |
| 7763 | | .register => |src_reg| try self.asmRegisterRegister( |
| 7764 | | mir_tag, |
| 7765 | | dst_alias, |
| 7766 | | registerAlias(src_reg, abi_size), |
| 7767 | | ), |
| 7768 | | .immediate => |imm| switch (self.regBitSize(ty)) { |
| 7769 | | 8 => try self.asmRegisterImmediate( |
| 7770 | | mir_tag, |
| 7771 | | dst_alias, |
| 7772 | | if (math.cast(i8, @as(i64, @bitCast(imm)))) |small| |
| 7773 | | Immediate.s(small) |
| 7774 | | else |
| 7775 | | Immediate.u(@as(u8, @intCast(imm))), |
| 7776 | | ), |
| 7777 | | 16 => try self.asmRegisterImmediate( |
| 7778 | | mir_tag, |
| 7786 | .register, .register_pair, .register_offset => { |
| 7787 | switch (dst_mcv) { |
| 7788 | .register, .register_pair => {}, |
| 7789 | .register_offset => |ro| assert(ro.off == 0), |
| 7790 | else => unreachable, |
| 7791 | } |
| 7792 | for (dst_mcv.getRegs(), 0..) |dst_reg, dst_reg_i| { |
| 7793 | const dst_reg_lock = self.register_manager.lockReg(dst_reg); |
| 7794 | defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 7795 | |
| 7796 | const mir_limb_tag: Mir.Inst.FixedTag = switch (dst_reg_i) { |
| 7797 | 0 => mir_tag, |
| 7798 | 1 => switch (mir_tag[1]) { |
| 7799 | .add => .{ ._, .adc }, |
| 7800 | .sub, .cmp => .{ ._, .sbb }, |
| 7801 | .@"or", .@"and", .xor => mir_tag, |
| 7802 | else => return self.fail("TODO genBinOpMir implement large ABI for {s}", .{ |
| 7803 | @tagName(mir_tag[1]), |
| 7804 | }), |
| 7805 | }, |
| 7806 | else => unreachable, |
| 7807 | }; |
| 7808 | const off: u4 = @intCast(dst_reg_i * 8); |
| 7809 | const limb_abi_size = @min(abi_size - off, 8); |
| 7810 | const dst_alias = registerAlias(dst_reg, limb_abi_size); |
| 7811 | switch (src_mcv) { |
| 7812 | .none, |
| 7813 | .unreach, |
| 7814 | .dead, |
| 7815 | .undef, |
| 7816 | .register_overflow, |
| 7817 | .reserved_frame, |
| 7818 | => unreachable, |
| 7819 | .register, .register_pair => try self.asmRegisterRegister( |
| 7820 | mir_limb_tag, |
| 7779 | 7821 | dst_alias, |
| 7780 | | if (math.cast(i16, @as(i64, @bitCast(imm)))) |small| |
| 7781 | | Immediate.s(small) |
| 7782 | | else |
| 7783 | | Immediate.u(@as(u16, @intCast(imm))), |
| 7822 | registerAlias(src_mcv.getRegs()[dst_reg_i], limb_abi_size), |
| 7784 | 7823 | ), |
| 7785 | | 32 => try self.asmRegisterImmediate( |
| 7824 | .immediate => |imm| { |
| 7825 | assert(off == 0); |
| 7826 | switch (self.regBitSize(ty)) { |
| 7827 | 8 => try self.asmRegisterImmediate( |
| 7828 | mir_limb_tag, |
| 7829 | dst_alias, |
| 7830 | if (math.cast(i8, @as(i64, @bitCast(imm)))) |small| |
| 7831 | Immediate.s(small) |
| 7832 | else |
| 7833 | Immediate.u(@as(u8, @intCast(imm))), |
| 7834 | ), |
| 7835 | 16 => try self.asmRegisterImmediate( |
| 7836 | mir_limb_tag, |
| 7837 | dst_alias, |
| 7838 | if (math.cast(i16, @as(i64, @bitCast(imm)))) |small| |
| 7839 | Immediate.s(small) |
| 7840 | else |
| 7841 | Immediate.u(@as(u16, @intCast(imm))), |
| 7842 | ), |
| 7843 | 32 => try self.asmRegisterImmediate( |
| 7844 | mir_limb_tag, |
| 7845 | dst_alias, |
| 7846 | if (math.cast(i32, @as(i64, @bitCast(imm)))) |small| |
| 7847 | Immediate.s(small) |
| 7848 | else |
| 7849 | Immediate.u(@as(u32, @intCast(imm))), |
| 7850 | ), |
| 7851 | 64 => if (math.cast(i32, @as(i64, @bitCast(imm)))) |small| |
| 7852 | try self.asmRegisterImmediate(mir_limb_tag, dst_alias, Immediate.s(small)) |
| 7853 | else |
| 7854 | try self.asmRegisterRegister(mir_limb_tag, dst_alias, registerAlias( |
| 7855 | try self.copyToTmpRegister(ty, src_mcv), |
| 7856 | limb_abi_size, |
| 7857 | )), |
| 7858 | else => unreachable, |
| 7859 | } |
| 7860 | }, |
| 7861 | .eflags, |
| 7862 | .register_offset, |
| 7863 | .memory, |
| 7864 | .indirect, |
| 7865 | .load_direct, |
| 7866 | .lea_direct, |
| 7867 | .load_got, |
| 7868 | .lea_got, |
| 7869 | .load_tlv, |
| 7870 | .lea_tlv, |
| 7871 | .load_frame, |
| 7872 | .lea_frame, |
| 7873 | => { |
| 7874 | blk: { |
| 7875 | return self.asmRegisterMemory( |
| 7876 | mir_limb_tag, |
| 7877 | dst_alias, |
| 7878 | Memory.sib(Memory.PtrSize.fromSize(limb_abi_size), switch (src_mcv) { |
| 7879 | .memory => |addr| .{ |
| 7880 | .base = .{ .reg = .ds }, |
| 7881 | .disp = math.cast(i32, addr + off) orelse break :blk, |
| 7882 | }, |
| 7883 | .indirect => |reg_off| .{ |
| 7884 | .base = .{ .reg = reg_off.reg }, |
| 7885 | .disp = reg_off.off + off, |
| 7886 | }, |
| 7887 | .load_frame => |frame_addr| .{ |
| 7888 | .base = .{ .frame = frame_addr.index }, |
| 7889 | .disp = frame_addr.off + off, |
| 7890 | }, |
| 7891 | else => break :blk, |
| 7892 | }), |
| 7893 | ); |
| 7894 | } |
| 7895 | |
| 7896 | switch (src_mcv) { |
| 7897 | .eflags, |
| 7898 | .register_offset, |
| 7899 | .lea_direct, |
| 7900 | .lea_got, |
| 7901 | .lea_tlv, |
| 7902 | .lea_frame, |
| 7903 | => { |
| 7904 | assert(off == 0); |
| 7905 | const reg = try self.copyToTmpRegister(ty, src_mcv); |
| 7906 | return self.genBinOpMir( |
| 7907 | mir_limb_tag, |
| 7908 | ty, |
| 7909 | dst_mcv, |
| 7910 | .{ .register = reg }, |
| 7911 | ); |
| 7912 | }, |
| 7913 | .memory, |
| 7914 | .load_direct, |
| 7915 | .load_got, |
| 7916 | .load_tlv, |
| 7917 | => { |
| 7918 | const ptr_ty = try mod.singleConstPtrType(ty); |
| 7919 | const addr_reg = try self.copyToTmpRegister(ptr_ty, src_mcv.address()); |
| 7920 | return self.genBinOpMir(mir_limb_tag, ty, dst_mcv, .{ |
| 7921 | .indirect = .{ .reg = addr_reg, .off = off }, |
| 7922 | }); |
| 7923 | }, |
| 7924 | else => unreachable, |
| 7925 | } |
| 7926 | }, |
| 7927 | .air_ref => |src_ref| return self.genBinOpMir( |
| 7786 | 7928 | mir_tag, |
| 7787 | | dst_alias, |
| 7788 | | if (math.cast(i32, @as(i64, @bitCast(imm)))) |small| |
| 7789 | | Immediate.s(small) |
| 7790 | | else |
| 7791 | | Immediate.u(@as(u32, @intCast(imm))), |
| 7929 | ty, |
| 7930 | dst_mcv, |
| 7931 | try self.resolveInst(src_ref), |
| 7792 | 7932 | ), |
| 7793 | | 64 => if (math.cast(i32, @as(i64, @bitCast(imm)))) |small| |
| 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)), |
| 7933 | } |
| 7866 | 7934 | } |
| 7867 | 7935 | }, |
| 7868 | 7936 | .memory, .indirect, .load_got, .load_direct, .load_tlv, .load_frame => { |
| ... | ... | @@ -7877,10 +7945,7 @@ fn genBinOpMir( |
| 7877 | 7945 | errdefer self.register_manager.unlockReg(dst_addr_lock); |
| 7878 | 7946 | |
| 7879 | 7947 | try self.genSetReg(dst_addr_reg, Type.usize, dst_mcv.address()); |
| 7880 | | break :dst .{ |
| 7881 | | .addr_reg = dst_addr_reg, |
| 7882 | | .addr_lock = dst_addr_lock, |
| 7883 | | }; |
| 7948 | break :dst .{ .addr_reg = dst_addr_reg, .addr_lock = dst_addr_lock }; |
| 7884 | 7949 | }, |
| 7885 | 7950 | .load_frame => null, |
| 7886 | 7951 | }; |
| ... | ... | @@ -7888,7 +7953,7 @@ fn genBinOpMir( |
| 7888 | 7953 | |
| 7889 | 7954 | const resolved_src_mcv = switch (src_mcv) { |
| 7890 | 7955 | else => src_mcv, |
| 7891 | | .air_ref => |ref| try self.resolveInst(ref), |
| 7956 | .air_ref => |src_ref| try self.resolveInst(src_ref), |
| 7892 | 7957 | }; |
| 7893 | 7958 | const src_info: OpInfo = switch (resolved_src_mcv) { |
| 7894 | 7959 | .none, |
| ... | ... | @@ -7900,9 +7965,10 @@ fn genBinOpMir( |
| 7900 | 7965 | .air_ref, |
| 7901 | 7966 | => unreachable, |
| 7902 | 7967 | .immediate, |
| 7968 | .eflags, |
| 7903 | 7969 | .register, |
| 7970 | .register_pair, |
| 7904 | 7971 | .register_offset, |
| 7905 | | .eflags, |
| 7906 | 7972 | .indirect, |
| 7907 | 7973 | .lea_direct, |
| 7908 | 7974 | .lea_got, |
| ... | ... | @@ -7924,10 +7990,7 @@ fn genBinOpMir( |
| 7924 | 7990 | errdefer self.register_manager.unlockReg(src_addr_lock); |
| 7925 | 7991 | |
| 7926 | 7992 | try self.genSetReg(src_addr_reg, Type.usize, resolved_src_mcv.address()); |
| 7927 | | break :src .{ |
| 7928 | | .addr_reg = src_addr_reg, |
| 7929 | | .addr_lock = src_addr_lock, |
| 7930 | | }; |
| 7993 | break :src .{ .addr_reg = src_addr_reg, .addr_lock = src_addr_lock }; |
| 7931 | 7994 | }, |
| 7932 | 7995 | }; |
| 7933 | 7996 | defer if (src_info) |info| self.register_manager.unlockReg(info.addr_lock); |
| ... | ... | @@ -7938,9 +8001,13 @@ fn genBinOpMir( |
| 7938 | 8001 | .signed => Type.usize, |
| 7939 | 8002 | .unsigned => Type.isize, |
| 7940 | 8003 | }; |
| 8004 | var limb_i: usize = 0; |
| 7941 | 8005 | var off: i32 = 0; |
| 7942 | | while (off < abi_size) : (off += 8) { |
| 7943 | | const mir_limb_tag: Mir.Inst.FixedTag = switch (off) { |
| 8006 | while (off < abi_size) : ({ |
| 8007 | limb_i += 1; |
| 8008 | off += 8; |
| 8009 | }) { |
| 8010 | const mir_limb_tag: Mir.Inst.FixedTag = switch (limb_i) { |
| 7944 | 8011 | 0 => mir_tag, |
| 7945 | 8012 | else => switch (mir_tag[1]) { |
| 7946 | 8013 | .add => .{ ._, .adc }, |
| ... | ... | @@ -7979,16 +8046,8 @@ fn genBinOpMir( |
| 7979 | 8046 | .reserved_frame, |
| 7980 | 8047 | .air_ref, |
| 7981 | 8048 | => 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 | 8049 | .immediate => |src_imm| { |
| 7991 | | const imm: u64 = switch (off) { |
| 8050 | const imm: u64 = switch (limb_i) { |
| 7992 | 8051 | 0 => src_imm, |
| 7993 | 8052 | else => switch (ty_signedness) { |
| 7994 | 8053 | .signed => @bitCast(@as(i64, @bitCast(src_imm)) >> 63), |
| ... | ... | @@ -8038,6 +8097,8 @@ fn genBinOpMir( |
| 8038 | 8097 | else => unreachable, |
| 8039 | 8098 | } |
| 8040 | 8099 | }, |
| 8100 | .register, |
| 8101 | .register_pair, |
| 8041 | 8102 | .register_offset, |
| 8042 | 8103 | .eflags, |
| 8043 | 8104 | .memory, |
| ... | ... | @@ -8051,16 +8112,19 @@ fn genBinOpMir( |
| 8051 | 8112 | .load_frame, |
| 8052 | 8113 | .lea_frame, |
| 8053 | 8114 | => { |
| 8054 | | const src_limb_reg = try self.copyToTmpRegister(limb_ty, if (src_info) |info| .{ |
| 8115 | const src_limb_mcv: MCValue = if (src_info) |info| .{ |
| 8055 | 8116 | .indirect = .{ .reg = info.addr_reg, .off = off }, |
| 8056 | 8117 | } else switch (resolved_src_mcv) { |
| 8118 | .register, .register_pair => .{ |
| 8119 | .register = resolved_src_mcv.getRegs()[limb_i], |
| 8120 | }, |
| 8057 | 8121 | .eflags, |
| 8058 | 8122 | .register_offset, |
| 8059 | 8123 | .lea_direct, |
| 8060 | 8124 | .lea_got, |
| 8061 | 8125 | .lea_tlv, |
| 8062 | 8126 | .lea_frame, |
| 8063 | | => switch (off) { |
| 8127 | => switch (limb_i) { |
| 8064 | 8128 | 0 => resolved_src_mcv, |
| 8065 | 8129 | else => .{ .immediate = 0 }, |
| 8066 | 8130 | }, |
| ... | ... | @@ -8074,7 +8138,11 @@ fn genBinOpMir( |
| 8074 | 8138 | .off = frame_addr.off + off, |
| 8075 | 8139 | } }, |
| 8076 | 8140 | else => unreachable, |
| 8077 | | }); |
| 8141 | }; |
| 8142 | const src_limb_reg = if (src_limb_mcv.isRegister()) |
| 8143 | src_limb_mcv.getReg().? |
| 8144 | else |
| 8145 | try self.copyToTmpRegister(limb_ty, src_limb_mcv); |
| 8078 | 8146 | try self.asmMemoryRegister( |
| 8079 | 8147 | mir_limb_tag, |
| 8080 | 8148 | dst_limb_mem, |
| ... | ... | @@ -8098,8 +8166,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 8098 | 8166 | .dead, |
| 8099 | 8167 | .undef, |
| 8100 | 8168 | .immediate, |
| 8101 | | .register_offset, |
| 8102 | 8169 | .eflags, |
| 8170 | .register_offset, |
| 8103 | 8171 | .register_overflow, |
| 8104 | 8172 | .lea_direct, |
| 8105 | 8173 | .lea_got, |
| ... | ... | @@ -8115,13 +8183,14 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 8115 | 8183 | |
| 8116 | 8184 | const resolved_src_mcv = switch (src_mcv) { |
| 8117 | 8185 | else => src_mcv, |
| 8118 | | .air_ref => |ref| try self.resolveInst(ref), |
| 8186 | .air_ref => |src_ref| try self.resolveInst(src_ref), |
| 8119 | 8187 | }; |
| 8120 | 8188 | switch (resolved_src_mcv) { |
| 8121 | 8189 | .none, |
| 8122 | 8190 | .unreach, |
| 8123 | 8191 | .dead, |
| 8124 | 8192 | .undef, |
| 8193 | .register_pair, |
| 8125 | 8194 | .register_overflow, |
| 8126 | 8195 | .reserved_frame, |
| 8127 | 8196 | .air_ref, |
| ... | ... | @@ -8187,6 +8256,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 8187 | 8256 | ), |
| 8188 | 8257 | } |
| 8189 | 8258 | }, |
| 8259 | .register_pair => unreachable, // unimplemented |
| 8190 | 8260 | .memory, .indirect, .load_direct, .load_got, .load_tlv, .load_frame => { |
| 8191 | 8261 | const tmp_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); |
| 8192 | 8262 | const tmp_mcv = MCValue{ .register = tmp_reg }; |
| ... | ... | @@ -8209,8 +8279,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 8209 | 8279 | const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: { |
| 8210 | 8280 | const dst_mcv = self.args[arg_index]; |
| 8211 | 8281 | switch (dst_mcv) { |
| 8212 | | .register => |reg| self.register_manager.getRegAssumeFree(reg, inst), |
| 8213 | | .load_frame => {}, |
| 8282 | .register, .register_pair, .load_frame => for (dst_mcv.getRegs()) |reg| |
| 8283 | self.register_manager.getRegAssumeFree(reg, inst), |
| 8214 | 8284 | else => return self.fail("TODO implement arg for {}", .{dst_mcv}), |
| 8215 | 8285 | } |
| 8216 | 8286 | |
| ... | ... | @@ -8230,6 +8300,9 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void { |
| 8230 | 8300 | .dwarf => |dw| { |
| 8231 | 8301 | const loc: link.File.Dwarf.DeclState.DbgInfoLoc = switch (mcv) { |
| 8232 | 8302 | .register => |reg| .{ .register = reg.dwarfNum() }, |
| 8303 | .register_pair => |regs| .{ .register_pair = .{ |
| 8304 | regs[0].dwarfNum(), regs[1].dwarfNum(), |
| 8305 | } }, |
| 8233 | 8306 | // TODO use a frame index |
| 8234 | 8307 | .load_frame => return, |
| 8235 | 8308 | //.stack_offset => |off| .{ |
| ... | ... | @@ -8390,7 +8463,10 @@ fn genCall(self: *Self, info: union(enum) { |
| 8390 | 8463 | }; |
| 8391 | 8464 | const fn_info = mod.typeToFunc(fn_ty).?; |
| 8392 | 8465 | |
| 8393 | | const ExpectedContents = [16]Type; |
| 8466 | const ExpectedContents = extern struct { |
| 8467 | var_args: [16][@sizeOf(Type)]u8 align(@alignOf(Type)), |
| 8468 | arg_regs: [16][@sizeOf(?RegisterLock)]u8 align(@alignOf(?RegisterLock)), |
| 8469 | }; |
| 8394 | 8470 | var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = |
| 8395 | 8471 | std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); |
| 8396 | 8472 | const allocator = stack.get(); |
| ... | ... | @@ -8399,8 +8475,12 @@ fn genCall(self: *Self, info: union(enum) { |
| 8399 | 8475 | defer allocator.free(var_args); |
| 8400 | 8476 | for (var_args, arg_types[fn_info.param_types.len..]) |*var_arg, arg_ty| var_arg.* = arg_ty; |
| 8401 | 8477 | |
| 8402 | | var call_info = |
| 8403 | | try self.resolveCallingConventionValues(fn_info, var_args, .call_frame); |
| 8478 | var arg_locks = std.ArrayList(?RegisterLock).init(allocator); |
| 8479 | defer arg_locks.deinit(); |
| 8480 | try arg_locks.ensureTotalCapacity(16); |
| 8481 | defer for (arg_locks.items) |arg_lock| if (arg_lock) |lock| self.register_manager.unlockReg(lock); |
| 8482 | |
| 8483 | var call_info = try self.resolveCallingConventionValues(fn_info, var_args, .call_frame); |
| 8404 | 8484 | defer call_info.deinit(self); |
| 8405 | 8485 | |
| 8406 | 8486 | // We need a properly aligned and sized call frame to be able to call this function. |
| ... | ... | @@ -8419,7 +8499,9 @@ fn genCall(self: *Self, info: union(enum) { |
| 8419 | 8499 | } |
| 8420 | 8500 | |
| 8421 | 8501 | try self.spillEflagsIfOccupied(); |
| 8422 | | try self.spillRegisters(abi.getCallerPreservedRegs(self.target.*)); |
| 8502 | try self.spillRegisters(abi.getCallerPreservedRegs( |
| 8503 | abi.resolveCallingConvention(fn_info.cc, self.target.*), |
| 8504 | )); |
| 8423 | 8505 | |
| 8424 | 8506 | // set stack arguments first because this can clobber registers |
| 8425 | 8507 | // also clobber spill arguments as we go |
| ... | ... | @@ -8430,7 +8512,14 @@ fn genCall(self: *Self, info: union(enum) { |
| 8430 | 8512 | } |
| 8431 | 8513 | for (call_info.args, arg_types, args) |dst_arg, arg_ty, src_arg| switch (dst_arg) { |
| 8432 | 8514 | .none => {}, |
| 8433 | | .register => |reg| try self.spillRegisters(&.{reg}), |
| 8515 | .register => |reg| { |
| 8516 | try self.spillRegisters(&.{reg}); |
| 8517 | try arg_locks.append(self.register_manager.lockReg(reg)); |
| 8518 | }, |
| 8519 | .register_pair => |regs| { |
| 8520 | try self.spillRegisters(&regs); |
| 8521 | try arg_locks.appendSlice(&self.register_manager.lockRegs(2, regs)); |
| 8522 | }, |
| 8434 | 8523 | .load_frame => try self.genCopy(arg_ty, dst_arg, src_arg), |
| 8435 | 8524 | else => unreachable, |
| 8436 | 8525 | }; |
| ... | ... | @@ -8451,13 +8540,11 @@ fn genCall(self: *Self, info: union(enum) { |
| 8451 | 8540 | }; |
| 8452 | 8541 | defer if (ret_lock) |lock| self.register_manager.unlockReg(lock); |
| 8453 | 8542 | |
| 8454 | | for (call_info.args, arg_types, args) |dst_arg, arg_ty, src_arg| { |
| 8455 | | switch (dst_arg) { |
| 8456 | | .none, .load_frame => {}, |
| 8457 | | .register => try self.genCopy(arg_ty, dst_arg, src_arg), |
| 8458 | | else => unreachable, |
| 8459 | | } |
| 8460 | | } |
| 8543 | for (call_info.args, arg_types, args) |dst_arg, arg_ty, src_arg| switch (dst_arg) { |
| 8544 | .none, .load_frame => {}, |
| 8545 | .register, .register_pair => try self.genCopy(arg_ty, dst_arg, src_arg), |
| 8546 | else => unreachable, |
| 8547 | }; |
| 8461 | 8548 | |
| 8462 | 8549 | // Due to incremental compilation, how function calls are generated depends |
| 8463 | 8550 | // on linking. |
| ... | ... | @@ -8527,7 +8614,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { |
| 8527 | 8614 | const ret_ty = self.fn_type.fnReturnType(mod); |
| 8528 | 8615 | switch (self.ret_mcv.short) { |
| 8529 | 8616 | .none => {}, |
| 8530 | | .register => try self.genCopy(ret_ty, self.ret_mcv.short, operand), |
| 8617 | .register, .register_pair => try self.genCopy(ret_ty, self.ret_mcv.short, operand), |
| 8531 | 8618 | .indirect => |reg_off| { |
| 8532 | 8619 | try self.register_manager.getReg(reg_off.reg, null); |
| 8533 | 8620 | const lock = self.register_manager.lockRegAssumeUnused(reg_off.reg); |
| ... | ... | @@ -8612,6 +8699,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 8612 | 8699 | const lhs_mcv = try self.resolveInst(bin_op.lhs); |
| 8613 | 8700 | const lhs_lock = switch (lhs_mcv) { |
| 8614 | 8701 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 8702 | .register_offset => |ro| self.register_manager.lockRegAssumeUnused(ro.reg), |
| 8615 | 8703 | else => null, |
| 8616 | 8704 | }; |
| 8617 | 8705 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| ... | ... | @@ -8619,6 +8707,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 8619 | 8707 | const rhs_mcv = try self.resolveInst(bin_op.rhs); |
| 8620 | 8708 | const rhs_lock = switch (rhs_mcv) { |
| 8621 | 8709 | .register => |reg| self.register_manager.lockReg(reg), |
| 8710 | .register_offset => |ro| self.register_manager.lockReg(ro.reg), |
| 8622 | 8711 | else => null, |
| 8623 | 8712 | }; |
| 8624 | 8713 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| ... | ... | @@ -8665,33 +8754,122 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 8665 | 8754 | .lt, .gte => {}, |
| 8666 | 8755 | .lte, .gt => unreachable, |
| 8667 | 8756 | .eq, .neq => { |
| 8668 | | const dst_addr_mcv: MCValue = switch (dst_mcv) { |
| 8669 | | .memory, .indirect, .load_frame => dst_mcv.address(), |
| 8670 | | else => .{ .register = try self.copyToTmpRegister( |
| 8671 | | Type.usize, |
| 8672 | | dst_mcv.address(), |
| 8673 | | ) }, |
| 8757 | const OpInfo = ?struct { addr_reg: Register, addr_lock: RegisterLock }; |
| 8758 | |
| 8759 | const resolved_dst_mcv = switch (dst_mcv) { |
| 8760 | else => dst_mcv, |
| 8761 | .air_ref => |dst_ref| try self.resolveInst(dst_ref), |
| 8674 | 8762 | }; |
| 8675 | | const dst_addr_lock = if (dst_addr_mcv.getReg()) |reg| |
| 8676 | | self.register_manager.lockReg(reg) |
| 8677 | | else |
| 8678 | | null; |
| 8679 | | defer if (dst_addr_lock) |lock| self.register_manager.unlockReg(lock); |
| 8763 | const dst_info: OpInfo = switch (resolved_dst_mcv) { |
| 8764 | .none, |
| 8765 | .unreach, |
| 8766 | .dead, |
| 8767 | .undef, |
| 8768 | .immediate, |
| 8769 | .eflags, |
| 8770 | .register, |
| 8771 | .register_offset, |
| 8772 | .register_overflow, |
| 8773 | .indirect, |
| 8774 | .lea_direct, |
| 8775 | .lea_got, |
| 8776 | .lea_tlv, |
| 8777 | .lea_frame, |
| 8778 | .reserved_frame, |
| 8779 | .air_ref, |
| 8780 | => unreachable, |
| 8781 | .register_pair, .load_frame => null, |
| 8782 | .memory, .load_got, .load_direct, .load_tlv => dst: { |
| 8783 | switch (resolved_dst_mcv) { |
| 8784 | .memory => |addr| if (math.cast( |
| 8785 | i32, |
| 8786 | @as(i64, @bitCast(addr)), |
| 8787 | ) != null and math.cast( |
| 8788 | i32, |
| 8789 | @as(i64, @bitCast(addr)) + abi_size - 8, |
| 8790 | ) != null) break :dst null, |
| 8791 | .load_got, .load_direct, .load_tlv => {}, |
| 8792 | else => unreachable, |
| 8793 | } |
| 8794 | |
| 8795 | const dst_addr_reg = |
| 8796 | (try self.register_manager.allocReg(null, gp)).to64(); |
| 8797 | const dst_addr_lock = |
| 8798 | self.register_manager.lockRegAssumeUnused(dst_addr_reg); |
| 8799 | errdefer self.register_manager.unlockReg(dst_addr_lock); |
| 8800 | |
| 8801 | try self.genSetReg( |
| 8802 | dst_addr_reg, |
| 8803 | Type.usize, |
| 8804 | resolved_dst_mcv.address(), |
| 8805 | ); |
| 8806 | break :dst .{ |
| 8807 | .addr_reg = dst_addr_reg, |
| 8808 | .addr_lock = dst_addr_lock, |
| 8809 | }; |
| 8810 | }, |
| 8811 | }; |
| 8812 | defer if (dst_info) |info| |
| 8813 | self.register_manager.unlockReg(info.addr_lock); |
| 8680 | 8814 | |
| 8681 | | const src_addr_mcv: MCValue = switch (src_mcv) { |
| 8682 | | .memory, .indirect, .load_frame => src_mcv.address(), |
| 8683 | | else => .{ .register = try self.copyToTmpRegister( |
| 8684 | | Type.usize, |
| 8685 | | src_mcv.address(), |
| 8686 | | ) }, |
| 8815 | const resolved_src_mcv = switch (src_mcv) { |
| 8816 | else => src_mcv, |
| 8817 | .air_ref => |src_ref| try self.resolveInst(src_ref), |
| 8687 | 8818 | }; |
| 8688 | | const src_addr_lock = if (src_addr_mcv.getReg()) |reg| |
| 8689 | | self.register_manager.lockReg(reg) |
| 8690 | | else |
| 8691 | | null; |
| 8692 | | defer if (src_addr_lock) |lock| self.register_manager.unlockReg(lock); |
| 8819 | const src_info: OpInfo = switch (resolved_src_mcv) { |
| 8820 | .none, |
| 8821 | .unreach, |
| 8822 | .dead, |
| 8823 | .undef, |
| 8824 | .immediate, |
| 8825 | .eflags, |
| 8826 | .register, |
| 8827 | .register_offset, |
| 8828 | .register_overflow, |
| 8829 | .indirect, |
| 8830 | .lea_direct, |
| 8831 | .lea_got, |
| 8832 | .lea_tlv, |
| 8833 | .lea_frame, |
| 8834 | .reserved_frame, |
| 8835 | .air_ref, |
| 8836 | => unreachable, |
| 8837 | .register_pair, .load_frame => null, |
| 8838 | .memory, .load_got, .load_direct, .load_tlv => src: { |
| 8839 | switch (resolved_src_mcv) { |
| 8840 | .memory => |addr| if (math.cast( |
| 8841 | i32, |
| 8842 | @as(i64, @bitCast(addr)), |
| 8843 | ) != null and math.cast( |
| 8844 | i32, |
| 8845 | @as(i64, @bitCast(addr)) + abi_size - 8, |
| 8846 | ) != null) break :src null, |
| 8847 | .load_got, .load_direct, .load_tlv => {}, |
| 8848 | else => unreachable, |
| 8849 | } |
| 8850 | |
| 8851 | const src_addr_reg = |
| 8852 | (try self.register_manager.allocReg(null, gp)).to64(); |
| 8853 | const src_addr_lock = |
| 8854 | self.register_manager.lockRegAssumeUnused(src_addr_reg); |
| 8855 | errdefer self.register_manager.unlockReg(src_addr_lock); |
| 8856 | |
| 8857 | try self.genSetReg( |
| 8858 | src_addr_reg, |
| 8859 | Type.usize, |
| 8860 | resolved_src_mcv.address(), |
| 8861 | ); |
| 8862 | break :src .{ |
| 8863 | .addr_reg = src_addr_reg, |
| 8864 | .addr_lock = src_addr_lock, |
| 8865 | }; |
| 8866 | }, |
| 8867 | }; |
| 8868 | defer if (src_info) |info| |
| 8869 | self.register_manager.unlockReg(info.addr_lock); |
| 8693 | 8870 | |
| 8694 | | const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); |
| 8871 | const regs = |
| 8872 | try self.register_manager.allocRegs(2, .{ null, null }, gp); |
| 8695 | 8873 | const acc_reg = regs[0].to64(); |
| 8696 | 8874 | const locks = self.register_manager.lockRegsAssumeUnused(2, regs); |
| 8697 | 8875 | defer for (locks) |lock| self.register_manager.unlockReg(lock); |
| ... | ... | @@ -8699,18 +8877,52 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 8699 | 8877 | const limbs_len = std.math.divCeil(u16, abi_size, 8) catch unreachable; |
| 8700 | 8878 | var limb_i: u16 = 0; |
| 8701 | 8879 | while (limb_i < limbs_len) : (limb_i += 1) { |
| 8880 | const off = limb_i * 8; |
| 8702 | 8881 | const tmp_reg = regs[@min(limb_i, 1)].to64(); |
| 8703 | | try self.genSetReg( |
| 8704 | | tmp_reg, |
| 8705 | | Type.usize, |
| 8706 | | dst_addr_mcv.offset(limb_i * 8).deref(), |
| 8707 | | ); |
| 8882 | |
| 8883 | try self.genSetReg(tmp_reg, Type.usize, if (dst_info) |info| .{ |
| 8884 | .indirect = .{ .reg = info.addr_reg, .off = off }, |
| 8885 | } else switch (resolved_dst_mcv) { |
| 8886 | .register_pair => |dst_regs| .{ .register = dst_regs[limb_i] }, |
| 8887 | .memory => |dst_addr| .{ |
| 8888 | .memory = @bitCast(@as(i64, @bitCast(dst_addr)) + off), |
| 8889 | }, |
| 8890 | .indirect => |reg_off| .{ .indirect = .{ |
| 8891 | .reg = reg_off.reg, |
| 8892 | .off = reg_off.off + off, |
| 8893 | } }, |
| 8894 | .load_frame => |frame_addr| .{ .load_frame = .{ |
| 8895 | .index = frame_addr.index, |
| 8896 | .off = frame_addr.off + off, |
| 8897 | } }, |
| 8898 | else => unreachable, |
| 8899 | }); |
| 8900 | |
| 8708 | 8901 | try self.genBinOpMir( |
| 8709 | 8902 | .{ ._, .xor }, |
| 8710 | 8903 | Type.usize, |
| 8711 | 8904 | .{ .register = tmp_reg }, |
| 8712 | | src_addr_mcv.offset(limb_i * 8).deref(), |
| 8905 | if (src_info) |info| .{ |
| 8906 | .indirect = .{ .reg = info.addr_reg, .off = off }, |
| 8907 | } else switch (resolved_src_mcv) { |
| 8908 | .register_pair => |src_regs| .{ |
| 8909 | .register = src_regs[limb_i], |
| 8910 | }, |
| 8911 | .memory => |src_addr| .{ |
| 8912 | .memory = @bitCast(@as(i64, @bitCast(src_addr)) + off), |
| 8913 | }, |
| 8914 | .indirect => |reg_off| .{ .indirect = .{ |
| 8915 | .reg = reg_off.reg, |
| 8916 | .off = reg_off.off + off, |
| 8917 | } }, |
| 8918 | .load_frame => |frame_addr| .{ .load_frame = .{ |
| 8919 | .index = frame_addr.index, |
| 8920 | .off = frame_addr.off + off, |
| 8921 | } }, |
| 8922 | else => unreachable, |
| 8923 | }, |
| 8713 | 8924 | ); |
| 8925 | |
| 8714 | 8926 | if (limb_i > 0) try self.asmRegisterRegister( |
| 8715 | 8927 | .{ ._, .@"or" }, |
| 8716 | 8928 | acc_reg, |
| ... | ... | @@ -9039,6 +9251,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC |
| 9039 | 9251 | .undef, |
| 9040 | 9252 | .immediate, |
| 9041 | 9253 | .eflags, |
| 9254 | .register_pair, |
| 9042 | 9255 | .register_offset, |
| 9043 | 9256 | .register_overflow, |
| 9044 | 9257 | .lea_direct, |
| ... | ... | @@ -9528,13 +9741,16 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 9528 | 9741 | extra_i += inputs.len; |
| 9529 | 9742 | |
| 9530 | 9743 | var result: MCValue = .none; |
| 9531 | | var args = std.StringArrayHashMap(MCValue).init(self.gpa); |
| 9744 | var args = std.ArrayList(MCValue).init(self.gpa); |
| 9532 | 9745 | try args.ensureTotalCapacity(outputs.len + inputs.len); |
| 9533 | 9746 | defer { |
| 9534 | | for (args.values()) |arg| if (arg.getReg()) |reg| |
| 9747 | for (args.items) |arg| if (arg.getReg()) |reg| |
| 9535 | 9748 | self.register_manager.unlockReg(.{ .register = reg }); |
| 9536 | 9749 | args.deinit(); |
| 9537 | 9750 | } |
| 9751 | var arg_map = std.StringHashMap(u8).init(self.gpa); |
| 9752 | try arg_map.ensureTotalCapacity(@intCast(outputs.len + inputs.len)); |
| 9753 | defer arg_map.deinit(); |
| 9538 | 9754 | |
| 9539 | 9755 | var outputs_extra_i = extra_i; |
| 9540 | 9756 | for (outputs) |output| { |
| ... | ... | @@ -9585,7 +9801,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 9585 | 9801 | if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| { |
| 9586 | 9802 | _ = self.register_manager.lockRegAssumeUnused(reg); |
| 9587 | 9803 | }; |
| 9588 | | args.putAssumeCapacity(name, arg_mcv); |
| 9804 | if (!std.mem.eql(u8, name, "_")) |
| 9805 | arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len)); |
| 9806 | args.appendAssumeCapacity(arg_mcv); |
| 9589 | 9807 | if (output == .none) result = arg_mcv; |
| 9590 | 9808 | } |
| 9591 | 9809 | |
| ... | ... | @@ -9643,7 +9861,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 9643 | 9861 | if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| { |
| 9644 | 9862 | _ = self.register_manager.lockReg(reg); |
| 9645 | 9863 | }; |
| 9646 | | args.putAssumeCapacity(name, arg_mcv); |
| 9864 | if (!std.mem.eql(u8, name, "_")) |
| 9865 | arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len)); |
| 9866 | args.appendAssumeCapacity(arg_mcv); |
| 9647 | 9867 | } |
| 9648 | 9868 | |
| 9649 | 9869 | { |
| ... | ... | @@ -9712,8 +9932,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 9712 | 9932 | op_str[colon_pos + 1 .. op_str.len - "]".len] |
| 9713 | 9933 | else |
| 9714 | 9934 | ""; |
| 9715 | | op.* = switch (args.get(op_str["%[".len .. colon orelse op_str.len - "]".len]) orelse |
| 9716 | | return self.fail("no matching constraint: '{s}'", .{op_str})) { |
| 9935 | op.* = switch (args.items[ |
| 9936 | arg_map.get(op_str["%[".len .. colon orelse op_str.len - "]".len]) orelse |
| 9937 | return self.fail("no matching constraint: '{s}'", .{op_str}) |
| 9938 | ]) { |
| 9717 | 9939 | .register => |reg| if (std.mem.eql(u8, modifier, "")) |
| 9718 | 9940 | .{ .reg = reg } |
| 9719 | 9941 | else |
| ... | ... | @@ -9826,7 +10048,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 9826 | 10048 | }; |
| 9827 | 10049 | } |
| 9828 | 10050 | |
| 9829 | | for (outputs, args.values()[0..outputs.len]) |output, mcv| { |
| 10051 | for (outputs, args.items[0..outputs.len]) |output, mcv| { |
| 9830 | 10052 | const extra_bytes = std.mem.sliceAsBytes(self.air.extra[outputs_extra_i..]); |
| 9831 | 10053 | const constraint = |
| 9832 | 10054 | std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[outputs_extra_i..]), 0); |
| ... | ... | @@ -10093,6 +10315,19 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError |
| 10093 | 10315 | .off = -dst_reg_off.off, |
| 10094 | 10316 | } }, |
| 10095 | 10317 | }), |
| 10318 | .register_pair => |dst_regs| for (dst_regs, 0..) |dst_reg, dst_reg_i| switch (src_mcv) { |
| 10319 | .register_pair => |src_regs| try self.genSetReg( |
| 10320 | dst_reg, |
| 10321 | Type.usize, |
| 10322 | .{ .register = src_regs[dst_reg_i] }, |
| 10323 | ), |
| 10324 | else => try self.genSetReg( |
| 10325 | dst_reg, |
| 10326 | Type.usize, |
| 10327 | src_mcv.address().offset(@intCast(dst_reg_i * 8)).deref(), |
| 10328 | ), |
| 10329 | .air_ref => |src_ref| return self.genCopy(ty, dst_mcv, try self.resolveInst(src_ref)), |
| 10330 | }, |
| 10096 | 10331 | .indirect => |reg_off| try self.genSetMem(.{ .reg = reg_off.reg }, reg_off.off, ty, src_mcv), |
| 10097 | 10332 | .memory, .load_direct, .load_got, .load_tlv => { |
| 10098 | 10333 | switch (dst_mcv) { |
| ... | ... | @@ -10126,6 +10361,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr |
| 10126 | 10361 | .none, |
| 10127 | 10362 | .unreach, |
| 10128 | 10363 | .dead, |
| 10364 | .register_pair, |
| 10129 | 10365 | .register_overflow, |
| 10130 | 10366 | .reserved_frame, |
| 10131 | 10367 | => unreachable, |
| ... | ... | @@ -10203,17 +10439,13 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr |
| 10203 | 10439 | .sse => try self.asmRegisterRegister( |
| 10204 | 10440 | @as(?Mir.Inst.FixedTag, switch (ty.scalarType(mod).zigTypeTag(mod)) { |
| 10205 | 10441 | else => switch (abi_size) { |
| 10206 | | 1...4 => if (self.hasFeature(.avx)) .{ .v_d, .mov } else .{ ._d, .mov }, |
| 10207 | | 5...8 => if (self.hasFeature(.avx)) .{ .v_q, .mov } else .{ ._q, .mov }, |
| 10208 | | 9...16 => if (self.hasFeature(.avx)) .{ .v_, .movdqa } else .{ ._, .movdqa }, |
| 10442 | 1...16 => if (self.hasFeature(.avx)) .{ .v_, .movdqa } else .{ ._, .movdqa }, |
| 10209 | 10443 | 17...32 => if (self.hasFeature(.avx)) .{ .v_, .movdqa } else null, |
| 10210 | 10444 | else => null, |
| 10211 | 10445 | }, |
| 10212 | 10446 | .Float => switch (ty.scalarType(mod).floatBits(self.target.*)) { |
| 10213 | 10447 | 16, 128 => switch (abi_size) { |
| 10214 | | 2...4 => if (self.hasFeature(.avx)) .{ .v_d, .mov } else .{ ._d, .mov }, |
| 10215 | | 5...8 => if (self.hasFeature(.avx)) .{ .v_q, .mov } else .{ ._q, .mov }, |
| 10216 | | 9...16 => if (self.hasFeature(.avx)) |
| 10448 | 2...16 => if (self.hasFeature(.avx)) |
| 10217 | 10449 | .{ .v_, .movdqa } |
| 10218 | 10450 | else |
| 10219 | 10451 | .{ ._, .movdqa }, |
| ... | ... | @@ -10307,6 +10539,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr |
| 10307 | 10539 | Immediate.u(0), |
| 10308 | 10540 | ), |
| 10309 | 10541 | } |
| 10542 | return; |
| 10310 | 10543 | }, |
| 10311 | 10544 | .load_direct => |sym_index| switch (ty.zigTypeTag(mod)) { |
| 10312 | 10545 | else => { |
| ... | ... | @@ -10398,7 +10631,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr |
| 10398 | 10631 | @tagName(self.bin_file.tag), |
| 10399 | 10632 | }); |
| 10400 | 10633 | }, |
| 10401 | | .air_ref => |ref| try self.genSetReg(dst_reg, ty, try self.resolveInst(ref)), |
| 10634 | .air_ref => |src_ref| try self.genSetReg(dst_reg, ty, try self.resolveInst(src_ref)), |
| 10402 | 10635 | } |
| 10403 | 10636 | } |
| 10404 | 10637 | |
| ... | ... | @@ -10475,6 +10708,32 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal |
| 10475 | 10708 | ), |
| 10476 | 10709 | } |
| 10477 | 10710 | }, |
| 10711 | .register_pair => |src_regs| for (src_regs, 0..) |src_reg, src_reg_i| { |
| 10712 | const part_size = @min(abi_size - src_reg_i * 8, 8); |
| 10713 | const dst_mem = Memory.sib( |
| 10714 | Memory.PtrSize.fromSize(part_size), |
| 10715 | .{ .base = base, .disp = disp + @as(i32, @intCast(src_reg_i * 8)) }, |
| 10716 | ); |
| 10717 | const src_alias = registerAlias(src_reg, part_size); |
| 10718 | switch (try self.moveStrategy(ty, switch (base) { |
| 10719 | .none => ty.abiAlignment(mod).check(@as(u32, @bitCast(disp))), |
| 10720 | .reg => |reg| switch (reg) { |
| 10721 | .es, .cs, .ss, .ds => ty.abiAlignment(mod).check(@as(u32, @bitCast(disp))), |
| 10722 | else => false, |
| 10723 | }, |
| 10724 | .frame => |frame_index| self.getFrameAddrAlignment( |
| 10725 | .{ .index = frame_index, .off = disp }, |
| 10726 | ).compare(.gte, ty.abiAlignment(mod)), |
| 10727 | })) { |
| 10728 | .move => |tag| try self.asmMemoryRegister(tag, dst_mem, src_alias), |
| 10729 | .insert_extract, .vex_insert_extract => |ie| try self.asmMemoryRegisterImmediate( |
| 10730 | ie.extract, |
| 10731 | dst_mem, |
| 10732 | src_alias, |
| 10733 | Immediate.u(0), |
| 10734 | ), |
| 10735 | } |
| 10736 | }, |
| 10478 | 10737 | .register_overflow => |ro| { |
| 10479 | 10738 | try self.genSetMem( |
| 10480 | 10739 | base, |
| ... | ... | @@ -10511,7 +10770,7 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal |
| 10511 | 10770 | }, |
| 10512 | 10771 | else => try self.genInlineMemcpy(dst_ptr_mcv, src_mcv.address(), .{ .immediate = abi_size }), |
| 10513 | 10772 | }, |
| 10514 | | .air_ref => |ref| try self.genSetMem(base, disp, ty, try self.resolveInst(ref)), |
| 10773 | .air_ref => |src_ref| try self.genSetMem(base, disp, ty, try self.resolveInst(src_ref)), |
| 10515 | 10774 | } |
| 10516 | 10775 | } |
| 10517 | 10776 | |
| ... | ... | @@ -11475,6 +11734,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { |
| 11475 | 11734 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 11476 | 11735 | const inst_ty = self.typeOfIndex(inst); |
| 11477 | 11736 | const enum_ty = self.typeOf(un_op); |
| 11737 | const resolved_cc = abi.resolveCallingConvention(.Unspecified, self.target.*); |
| 11478 | 11738 | |
| 11479 | 11739 | // We need a properly aligned and sized call frame to be able to call this function. |
| 11480 | 11740 | { |
| ... | ... | @@ -11492,9 +11752,9 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { |
| 11492 | 11752 | } |
| 11493 | 11753 | |
| 11494 | 11754 | try self.spillEflagsIfOccupied(); |
| 11495 | | try self.spillRegisters(abi.getCallerPreservedRegs(self.target.*)); |
| 11755 | try self.spillRegisters(abi.getCallerPreservedRegs(resolved_cc)); |
| 11496 | 11756 | |
| 11497 | | const param_regs = abi.getCAbiIntParamRegs(self.target.*); |
| 11757 | const param_regs = abi.getCAbiIntParamRegs(resolved_cc); |
| 11498 | 11758 | |
| 11499 | 11759 | const dst_mcv = try self.allocRegOrMem(inst, false); |
| 11500 | 11760 | try self.genSetReg(param_regs[0], Type.usize, dst_mcv.address()); |
| ... | ... | @@ -12271,23 +12531,27 @@ fn resolveCallingConventionValues( |
| 12271 | 12531 | |
| 12272 | 12532 | const ret_ty = fn_info.return_type.toType(); |
| 12273 | 12533 | |
| 12534 | const resolved_cc = abi.resolveCallingConvention(cc, self.target.*); |
| 12274 | 12535 | switch (cc) { |
| 12275 | 12536 | .Naked => { |
| 12276 | 12537 | assert(result.args.len == 0); |
| 12277 | 12538 | result.return_value = InstTracking.init(.unreach); |
| 12278 | 12539 | result.stack_align = .@"8"; |
| 12279 | 12540 | }, |
| 12280 | | .C => { |
| 12281 | | var param_reg_i: usize = 0; |
| 12541 | .C, .SysV, .Win64 => { |
| 12542 | var ret_int_reg_i: usize = 0; |
| 12543 | var ret_sse_reg_i: usize = 0; |
| 12544 | var param_int_reg_i: usize = 0; |
| 12282 | 12545 | var param_sse_reg_i: usize = 0; |
| 12283 | 12546 | result.stack_align = .@"16"; |
| 12284 | 12547 | |
| 12285 | | switch (self.target.os.tag) { |
| 12286 | | .windows => { |
| 12548 | switch (resolved_cc) { |
| 12549 | .SysV => {}, |
| 12550 | .Win64 => { |
| 12287 | 12551 | // Align the stack to 16bytes before allocating shadow stack space (if any). |
| 12288 | 12552 | result.stack_byte_count += @intCast(4 * Type.usize.abiSize(mod)); |
| 12289 | 12553 | }, |
| 12290 | | else => {}, |
| 12554 | else => unreachable, |
| 12291 | 12555 | } |
| 12292 | 12556 | |
| 12293 | 12557 | // Return values |
| ... | ... | @@ -12297,71 +12561,133 @@ fn resolveCallingConventionValues( |
| 12297 | 12561 | // TODO: is this even possible for C calling convention? |
| 12298 | 12562 | result.return_value = InstTracking.init(.none); |
| 12299 | 12563 | } else { |
| 12300 | | const classes = switch (self.target.os.tag) { |
| 12301 | | .windows => &[1]abi.Class{abi.classifyWindows(ret_ty, mod)}, |
| 12302 | | else => mem.sliceTo(&abi.classifySystemV(ret_ty, mod, .ret), .none), |
| 12564 | var ret_tracking: [2]InstTracking = undefined; |
| 12565 | var ret_tracking_i: usize = 0; |
| 12566 | |
| 12567 | const classes = switch (resolved_cc) { |
| 12568 | .SysV => mem.sliceTo(&abi.classifySystemV(ret_ty, mod, .ret), .none), |
| 12569 | .Win64 => &[1]abi.Class{abi.classifyWindows(ret_ty, mod)}, |
| 12570 | else => unreachable, |
| 12303 | 12571 | }; |
| 12304 | | if (classes.len > 1) { |
| 12305 | | return self.fail("TODO handle multiple classes per type", .{}); |
| 12306 | | } |
| 12307 | | const ret_reg = abi.getCAbiIntReturnRegs(self.target.*)[0]; |
| 12308 | | result.return_value = switch (classes[0]) { |
| 12309 | | .integer => InstTracking.init(.{ .register = registerAlias( |
| 12310 | | ret_reg, |
| 12311 | | @intCast(ret_ty.abiSize(mod)), |
| 12312 | | ) }), |
| 12313 | | .float, .sse => InstTracking.init(.{ .register = .xmm0 }), |
| 12314 | | .memory => ret: { |
| 12315 | | const ret_indirect_reg = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i]; |
| 12316 | | param_reg_i += 1; |
| 12317 | | break :ret .{ |
| 12318 | | .short = .{ .indirect = .{ .reg = ret_reg } }, |
| 12572 | for (classes) |class| switch (class) { |
| 12573 | .integer => { |
| 12574 | const ret_int_reg = registerAlias( |
| 12575 | abi.getCAbiIntReturnRegs(resolved_cc)[ret_int_reg_i], |
| 12576 | @intCast(@min(ret_ty.abiSize(mod), 8)), |
| 12577 | ); |
| 12578 | ret_int_reg_i += 1; |
| 12579 | |
| 12580 | ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = ret_int_reg }); |
| 12581 | ret_tracking_i += 1; |
| 12582 | }, |
| 12583 | .sse, .float, .float_combine, .win_i128 => { |
| 12584 | const ret_sse_reg = registerAlias( |
| 12585 | abi.getCAbiSseReturnRegs(resolved_cc)[ret_sse_reg_i], |
| 12586 | @intCast(ret_ty.abiSize(mod)), |
| 12587 | ); |
| 12588 | ret_sse_reg_i += 1; |
| 12589 | |
| 12590 | ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = ret_sse_reg }); |
| 12591 | ret_tracking_i += 1; |
| 12592 | }, |
| 12593 | .sseup => assert(ret_tracking[ret_tracking_i - 1].short.register.class() == .sse), |
| 12594 | .x87 => { |
| 12595 | ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = .st0 }); |
| 12596 | ret_tracking_i += 1; |
| 12597 | }, |
| 12598 | .x87up => assert(ret_tracking[ret_tracking_i - 1].short.register.class() == .x87), |
| 12599 | .complex_x87 => { |
| 12600 | ret_tracking[ret_tracking_i] = |
| 12601 | InstTracking.init(.{ .register_pair = .{ .st0, .st1 } }); |
| 12602 | ret_tracking_i += 1; |
| 12603 | }, |
| 12604 | .memory => { |
| 12605 | const ret_int_reg = abi.getCAbiIntReturnRegs(resolved_cc)[ret_int_reg_i].to64(); |
| 12606 | ret_int_reg_i += 1; |
| 12607 | const ret_indirect_reg = abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i]; |
| 12608 | param_int_reg_i += 1; |
| 12609 | |
| 12610 | ret_tracking[ret_tracking_i] = .{ |
| 12611 | .short = .{ .indirect = .{ .reg = ret_int_reg } }, |
| 12319 | 12612 | .long = .{ .indirect = .{ .reg = ret_indirect_reg } }, |
| 12320 | 12613 | }; |
| 12614 | ret_tracking_i += 1; |
| 12321 | 12615 | }, |
| 12322 | | else => |class| return self.fail("TODO handle calling convention class {s}", .{ |
| 12323 | | @tagName(class), |
| 12324 | | }), |
| 12616 | .none => unreachable, |
| 12617 | }; |
| 12618 | result.return_value = switch (ret_tracking_i) { |
| 12619 | else => unreachable, |
| 12620 | 1 => ret_tracking[0], |
| 12621 | 2 => InstTracking.init(.{ .register_pair = .{ |
| 12622 | ret_tracking[0].short.register, ret_tracking[1].short.register, |
| 12623 | } }), |
| 12325 | 12624 | }; |
| 12326 | 12625 | } |
| 12327 | 12626 | |
| 12328 | 12627 | // Input params |
| 12329 | 12628 | for (param_types, result.args) |ty, *arg| { |
| 12330 | 12629 | assert(ty.hasRuntimeBitsIgnoreComptime(mod)); |
| 12630 | switch (resolved_cc) { |
| 12631 | .SysV => {}, |
| 12632 | .Win64 => { |
| 12633 | param_int_reg_i = @max(param_int_reg_i, param_sse_reg_i); |
| 12634 | param_sse_reg_i = param_int_reg_i; |
| 12635 | }, |
| 12636 | else => unreachable, |
| 12637 | } |
| 12638 | |
| 12639 | var arg_mcv: [2]MCValue = undefined; |
| 12640 | var arg_mcv_i: usize = 0; |
| 12331 | 12641 | |
| 12332 | 12642 | const classes = switch (self.target.os.tag) { |
| 12333 | 12643 | .windows => &[1]abi.Class{abi.classifyWindows(ty, mod)}, |
| 12334 | 12644 | else => mem.sliceTo(&abi.classifySystemV(ty, mod, .arg), .none), |
| 12335 | 12645 | }; |
| 12336 | | if (classes.len > 1) { |
| 12337 | | return self.fail("TODO handle multiple classes per type", .{}); |
| 12338 | | } |
| 12339 | | switch (classes[0]) { |
| 12340 | | .integer => if (param_reg_i < abi.getCAbiIntParamRegs(self.target.*).len) { |
| 12341 | | arg.* = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i] }; |
| 12342 | | param_reg_i += 1; |
| 12343 | | continue; |
| 12646 | for (classes) |class| switch (class) { |
| 12647 | .integer => { |
| 12648 | const param_int_regs = abi.getCAbiIntParamRegs(resolved_cc); |
| 12649 | if (param_int_reg_i >= param_int_regs.len) break; |
| 12650 | |
| 12651 | const param_int_reg = registerAlias( |
| 12652 | abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i], |
| 12653 | @intCast(@min(ty.abiSize(mod), 8)), |
| 12654 | ); |
| 12655 | param_int_reg_i += 1; |
| 12656 | |
| 12657 | arg_mcv[arg_mcv_i] = .{ .register = param_int_reg }; |
| 12658 | arg_mcv_i += 1; |
| 12344 | 12659 | }, |
| 12345 | | .float, .sse => switch (self.target.os.tag) { |
| 12346 | | .windows => if (param_reg_i < 4) { |
| 12347 | | arg.* = .{ |
| 12348 | | .register = @enumFromInt(@intFromEnum(Register.xmm0) + param_reg_i), |
| 12349 | | }; |
| 12350 | | param_reg_i += 1; |
| 12351 | | continue; |
| 12352 | | }, |
| 12353 | | else => if (param_sse_reg_i < 8) { |
| 12354 | | arg.* = .{ |
| 12355 | | .register = @enumFromInt(@intFromEnum(Register.xmm0) + param_sse_reg_i), |
| 12356 | | }; |
| 12357 | | param_sse_reg_i += 1; |
| 12358 | | continue; |
| 12359 | | }, |
| 12660 | .sse, .float, .float_combine => { |
| 12661 | const param_sse_regs = abi.getCAbiSseParamRegs(resolved_cc); |
| 12662 | if (param_sse_reg_i >= param_sse_regs.len) break; |
| 12663 | |
| 12664 | const param_sse_reg = registerAlias( |
| 12665 | abi.getCAbiSseParamRegs(resolved_cc)[param_sse_reg_i], |
| 12666 | @intCast(ty.abiSize(mod)), |
| 12667 | ); |
| 12668 | param_sse_reg_i += 1; |
| 12669 | |
| 12670 | arg_mcv[arg_mcv_i] = .{ .register = param_sse_reg }; |
| 12671 | arg_mcv_i += 1; |
| 12360 | 12672 | }, |
| 12361 | | .memory => {}, // fallthrough |
| 12362 | | else => |class| return self.fail("TODO handle calling convention class {s}", .{ |
| 12363 | | @tagName(class), |
| 12364 | | }), |
| 12673 | .sseup => assert(arg_mcv[arg_mcv_i - 1].register.class() == .sse), |
| 12674 | .x87, .x87up, .complex_x87, .memory => break, |
| 12675 | .none => unreachable, |
| 12676 | .win_i128 => { |
| 12677 | const param_int_reg = |
| 12678 | abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i].to64(); |
| 12679 | param_int_reg_i += 1; |
| 12680 | |
| 12681 | arg_mcv[arg_mcv_i] = .{ .indirect = .{ .reg = param_int_reg } }; |
| 12682 | arg_mcv_i += 1; |
| 12683 | }, |
| 12684 | } else { |
| 12685 | arg.* = switch (arg_mcv_i) { |
| 12686 | else => unreachable, |
| 12687 | 1 => arg_mcv[0], |
| 12688 | 2 => .{ .register_pair = .{ arg_mcv[0].register, arg_mcv[1].register } }, |
| 12689 | }; |
| 12690 | continue; |
| 12365 | 12691 | } |
| 12366 | 12692 | |
| 12367 | 12693 | const param_size: u31 = @intCast(ty.abiSize(mod)); |
| ... | ... | @@ -12384,13 +12710,13 @@ fn resolveCallingConventionValues( |
| 12384 | 12710 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 12385 | 12711 | result.return_value = InstTracking.init(.none); |
| 12386 | 12712 | } else { |
| 12387 | | const ret_reg = abi.getCAbiIntReturnRegs(self.target.*)[0]; |
| 12713 | const ret_reg = abi.getCAbiIntReturnRegs(resolved_cc)[0]; |
| 12388 | 12714 | const ret_ty_size: u31 = @intCast(ret_ty.abiSize(mod)); |
| 12389 | 12715 | if (ret_ty_size <= 8 and !ret_ty.isRuntimeFloat()) { |
| 12390 | 12716 | const aliased_reg = registerAlias(ret_reg, ret_ty_size); |
| 12391 | 12717 | result.return_value = .{ .short = .{ .register = aliased_reg }, .long = .none }; |
| 12392 | 12718 | } else { |
| 12393 | | const ret_indirect_reg = abi.getCAbiIntParamRegs(self.target.*)[0]; |
| 12719 | const ret_indirect_reg = abi.getCAbiIntParamRegs(resolved_cc)[0]; |
| 12394 | 12720 | result.return_value = .{ |
| 12395 | 12721 | .short = .{ .indirect = .{ .reg = ret_reg } }, |
| 12396 | 12722 | .long = .{ .indirect = .{ .reg = ret_indirect_reg } }, |
| ... | ... | @@ -12462,7 +12788,10 @@ fn registerAlias(reg: Register, size_bytes: u32) Register { |
| 12462 | 12788 | reg |
| 12463 | 12789 | else |
| 12464 | 12790 | unreachable, |
| 12465 | | .x87 => unreachable, |
| 12791 | .x87 => if (size_bytes == 16) |
| 12792 | reg |
| 12793 | else |
| 12794 | unreachable, |
| 12466 | 12795 | .mmx => if (size_bytes <= 8) |
| 12467 | 12796 | reg |
| 12468 | 12797 | else |
| ... | ... | @@ -12597,9 +12926,7 @@ fn floatCompilerRtAbiType(self: *Self, ty: Type, other_ty: Type) Type { |
| 12597 | 12926 | |
| 12598 | 12927 | fn floatLibcAbiPrefix(ty: Type) []const u8 { |
| 12599 | 12928 | return switch (ty.toIntern()) { |
| 12600 | | .f16_type, |
| 12601 | | .f80_type, |
| 12602 | | => "__", |
| 12929 | .f16_type, .f80_type => "__", |
| 12603 | 12930 | .f32_type, .f64_type, .f128_type, .c_longdouble_type => "", |
| 12604 | 12931 | else => unreachable, |
| 12605 | 12932 | }; |