authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-03-30 11:22:35+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-30 11:22:35+02:00
logb153e156b1d431995e7f89150c93ec05aa3966f8
tree9311b768bbf2547767df8a3dc8c58ececdac190f
parentc21f046a8b019c42aa0dbb0fb9c592b590edf977
parent376d0878ec2e366633e2dbc7c91ab7ae2a6ae5b7
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11342 from ziglang/stage2-x64-mul-div

x64: use all available registers (caller and callee), and fix how we multiply (fixes @mulWithOverflow for byte-size operands)

5 files changed, 198 insertions(+), 145 deletions(-)

src/arch/x86_64/CodeGen.zig+181-135
...@@ -31,6 +31,8 @@ const bits = @import("bits.zig");...@@ -31,6 +31,8 @@ const bits = @import("bits.zig");
31const abi = @import("abi.zig");31const abi = @import("abi.zig");
32const Register = bits.Register;32const Register = bits.Register;
33const callee_preserved_regs = abi.callee_preserved_regs;33const callee_preserved_regs = abi.callee_preserved_regs;
34const caller_preserved_regs = abi.caller_preserved_regs;
35const allocatable_registers = abi.allocatable_registers;
34const c_abi_int_param_regs = abi.c_abi_int_param_regs;36const c_abi_int_param_regs = abi.c_abi_int_param_regs;
35const c_abi_int_return_regs = abi.c_abi_int_return_regs;37const c_abi_int_return_regs = abi.c_abi_int_return_regs;
3638
...@@ -40,7 +42,7 @@ const InnerError = error{...@@ -40,7 +42,7 @@ const InnerError = error{
40 OutOfRegisters,42 OutOfRegisters,
41};43};
4244
43const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs);45const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers);
4446
45gpa: Allocator,47gpa: Allocator,
46air: Air,48air: Air,
...@@ -56,7 +58,6 @@ arg_index: u32,...@@ -56,7 +58,6 @@ arg_index: u32,
56src_loc: Module.SrcLoc,58src_loc: Module.SrcLoc,
57stack_align: u32,59stack_align: u32,
5860
59ret_backpatches: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
60compare_flags_inst: ?Air.Inst.Index = null,61compare_flags_inst: ?Air.Inst.Index = null,
6162
62/// MIR Instructions63/// MIR Instructions
...@@ -351,7 +352,6 @@ pub fn generate(...@@ -351,7 +352,6 @@ pub fn generate(
351 std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index).init(bin_file.allocator)352 std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index).init(bin_file.allocator)
352 else {},353 else {},
353 };354 };
354 defer function.ret_backpatches.deinit(bin_file.allocator);
355 defer function.stack.deinit(bin_file.allocator);355 defer function.stack.deinit(bin_file.allocator);
356 defer function.blocks.deinit(bin_file.allocator);356 defer function.blocks.deinit(bin_file.allocator);
357 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);357 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
...@@ -480,6 +480,21 @@ fn gen(self: *Self) InnerError!void {...@@ -480,6 +480,21 @@ fn gen(self: *Self) InnerError!void {
480 .data = undefined,480 .data = undefined,
481 });481 });
482482
483 if (self.ret_mcv == .stack_offset) {
484 // The address where to store the return value for the caller is in `.rdi`
485 // register which the callee is free to clobber. Therefore, we purposely
486 // spill it to stack immediately.
487 const ptr_ty = Type.usize;
488 const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*));
489 const abi_align = ptr_ty.abiAlignment(self.target.*);
490 const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset + abi_size, abi_align);
491 self.next_stack_offset = stack_offset;
492 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);
493 try self.genSetStack(ptr_ty, @intCast(i32, stack_offset), MCValue{ .register = .rdi }, .{});
494 self.ret_mcv = MCValue{ .stack_offset = @intCast(i32, stack_offset) };
495 log.debug("gen: spilling .rdi to stack at offset {}", .{stack_offset});
496 }
497
483 _ = try self.addInst(.{498 _ = try self.addInst(.{
484 .tag = .dbg_prologue_end,499 .tag = .dbg_prologue_end,
485 .ops = undefined,500 .ops = undefined,
...@@ -517,23 +532,6 @@ fn gen(self: *Self) InnerError!void {...@@ -517,23 +532,6 @@ fn gen(self: *Self) InnerError!void {
517 var disp = data.disp + 8;532 var disp = data.disp + 8;
518 inline for (callee_preserved_regs) |reg, i| {533 inline for (callee_preserved_regs) |reg, i| {
519 if (self.register_manager.isRegAllocated(reg)) {534 if (self.register_manager.isRegAllocated(reg)) {
520 if (reg.to64() == .rdi) {
521 for (self.ret_backpatches.items) |inst| {
522 log.debug(".rdi was spilled, backpatching with mov from stack at offset {}", .{
523 -@intCast(i32, disp),
524 });
525 const ops = Mir.Ops.decode(self.mir_instructions.items(.ops)[inst]);
526 self.mir_instructions.set(inst, Mir.Inst{
527 .tag = .mov,
528 .ops = (Mir.Ops{
529 .reg1 = ops.reg1,
530 .reg2 = .rbp,
531 .flags = 0b01,
532 }).encode(),
533 .data = .{ .imm = @bitCast(u32, -@intCast(i32, disp)) },
534 });
535 }
536 }
537 data.regs |= 1 << @intCast(u5, i);535 data.regs |= 1 << @intCast(u5, i);
538 self.max_end_stack += 8;536 self.max_end_stack += 8;
539 disp += 8;537 disp += 8;
...@@ -912,8 +910,7 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u...@@ -912,8 +910,7 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u
912 // TODO find a free slot instead of always appending910 // TODO find a free slot instead of always appending
913 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset + abi_size, abi_align);911 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset + abi_size, abi_align);
914 self.next_stack_offset = offset;912 self.next_stack_offset = offset;
915 if (self.next_stack_offset > self.max_end_stack)913 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);
916 self.max_end_stack = self.next_stack_offset;
917 try self.stack.putNoClobber(self.gpa, offset, .{914 try self.stack.putNoClobber(self.gpa, offset, .{
918 .inst = inst,915 .inst = inst,
919 .size = abi_size,916 .size = abi_size,
...@@ -1256,7 +1253,7 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r...@@ -1256,7 +1253,7 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r
1256 offset_mcv.freezeIfRegister(&self.register_manager);1253 offset_mcv.freezeIfRegister(&self.register_manager);
1257 defer offset_mcv.unfreezeIfRegister(&self.register_manager);1254 defer offset_mcv.unfreezeIfRegister(&self.register_manager);
12581255
1259 try self.genIMulOpMir(offset_ty, offset_mcv, .{ .immediate = elem_size });1256 try self.genIntMulComplexOpMir(offset_ty, offset_mcv, .{ .immediate = elem_size });
12601257
1261 const tag = self.air.instructions.items(.tag)[inst];1258 const tag = self.air.instructions.items(.tag)[inst];
1262 switch (tag) {1259 switch (tag) {
...@@ -1398,10 +1395,29 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -1398,10 +1395,29 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
13981395
1399fn airMul(self: *Self, inst: Air.Inst.Index) !void {1396fn airMul(self: *Self, inst: Air.Inst.Index) !void {
1400 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1397 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1401 const result: MCValue = if (self.liveness.isUnused(inst))1398 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1402 .dead1399 const ty = self.air.typeOfIndex(inst);
1403 else1400
1404 try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs);1401 if (ty.zigTypeTag() != .Int) {
1402 return self.fail("TODO implement 'mul' for operands of dst type {}", .{ty.zigTypeTag()});
1403 }
1404
1405 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
1406 try self.register_manager.getReg(.rax, inst);
1407 try self.register_manager.getReg(.rdx, null);
1408 self.register_manager.freezeRegs(&.{ .rax, .rdx });
1409 defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
1410
1411 const lhs = try self.resolveInst(bin_op.lhs);
1412 const rhs = try self.resolveInst(bin_op.rhs);
1413
1414 const signedness = ty.intInfo(self.target.*).signedness;
1415 try self.genIntMulDivOpMir(switch (signedness) {
1416 .signed => .imul,
1417 .unsigned => .mul,
1418 }, ty, signedness, lhs, rhs);
1419 break :result MCValue{ .register = .rax };
1420 };
1405 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1421 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1406}1422}
14071423
...@@ -1476,23 +1492,33 @@ fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1476,23 +1492,33 @@ fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1476fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1492fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1477 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1493 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1478 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;1494 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1495 const result = if (self.liveness.isUnused(inst)) .dead else result: {
1496 const ty = self.air.typeOf(bin_op.lhs);
1497 const signedness: std.builtin.Signedness = blk: {
1498 if (ty.zigTypeTag() != .Int) {
1499 return self.fail("TODO implement airMulWithOverflow for type {}", .{ty.fmtDebug()});
1500 }
1501 break :blk ty.intInfo(self.target.*).signedness;
1502 };
14791503
1480 if (self.liveness.isUnused(inst)) {1504 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
1481 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });1505 try self.register_manager.getReg(.rax, inst);
1482 }1506 try self.register_manager.getReg(.rdx, null);
1507 self.register_manager.freezeRegs(&.{ .rax, .rdx });
1508 defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
14831509
1484 const ty = self.air.typeOf(bin_op.lhs);1510 const lhs = try self.resolveInst(bin_op.lhs);
1485 const signedness: std.builtin.Signedness = blk: {1511 const rhs = try self.resolveInst(bin_op.rhs);
1486 if (ty.zigTypeTag() != .Int) {
1487 return self.fail("TODO implement airMulWithOverflow for type {}", .{ty.fmtDebug()});
1488 }
1489 break :blk ty.intInfo(self.target.*).signedness;
1490 };
14911512
1492 const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs);1513 try self.genIntMulDivOpMir(switch (signedness) {
1493 const result: MCValue = switch (signedness) {1514 .signed => .imul,
1494 .signed => .{ .register_overflow_signed = partial.register },1515 .unsigned => .mul,
1495 .unsigned => .{ .register_overflow_unsigned = partial.register },1516 }, ty, signedness, lhs, rhs);
1517
1518 switch (signedness) {
1519 .signed => break :result MCValue{ .register_overflow_signed = .rax },
1520 .unsigned => break :result MCValue{ .register_overflow_unsigned = .rax },
1521 }
1496 };1522 };
14971523
1498 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1524 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
...@@ -1503,11 +1529,12 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1503,11 +1529,12 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1503 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});1529 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
1504}1530}
15051531
1506/// Generates signed or unsigned integer division.1532/// Generates signed or unsigned integer multiplication/division.
1507/// Requires use of .rax and .rdx registers. Spills them if necessary.1533/// Clobbers .rax and .rdx registers.
1508/// Quotient is saved in .rax and remainder in .rdx.1534/// Quotient is saved in .rax and remainder in .rdx.
1509fn genIntDivOpMir(1535fn genIntMulDivOpMir(
1510 self: *Self,1536 self: *Self,
1537 tag: Mir.Inst.Tag,
1511 ty: Type,1538 ty: Type,
1512 signedness: std.builtin.Signedness,1539 signedness: std.builtin.Signedness,
1513 lhs: MCValue,1540 lhs: MCValue,
...@@ -1515,25 +1542,10 @@ fn genIntDivOpMir(...@@ -1515,25 +1542,10 @@ fn genIntDivOpMir(
1515) !void {1542) !void {
1516 const abi_size = @intCast(u32, ty.abiSize(self.target.*));1543 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
1517 if (abi_size > 8) {1544 if (abi_size > 8) {
1518 return self.fail("TODO implement genIntDivOpMir for ABI size larger than 8", .{});1545 return self.fail("TODO implement genIntMulDivOpMir for ABI size larger than 8", .{});
1519 }1546 }
15201547
1521 try self.register_manager.getReg(.rax, null);1548 try self.genSetReg(ty, .rax, lhs);
1522 try self.register_manager.getReg(.rdx, null);
1523 self.register_manager.freezeRegs(&.{ .rax, .rdx });
1524 defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
1525
1526 const dividend = switch (lhs) {
1527 .register => lhs,
1528 else => blk: {
1529 const reg = try self.copyToTmpRegister(ty, lhs);
1530 break :blk MCValue{ .register = reg };
1531 },
1532 };
1533 try self.genSetReg(ty, .rax, dividend);
1534
1535 self.register_manager.freezeRegs(&.{dividend.register});
1536 defer self.register_manager.unfreezeRegs(&.{dividend.register});
15371549
1538 switch (signedness) {1550 switch (signedness) {
1539 .signed => {1551 .signed => {
...@@ -1557,22 +1569,19 @@ fn genIntDivOpMir(...@@ -1557,22 +1569,19 @@ fn genIntDivOpMir(
1557 },1569 },
1558 }1570 }
15591571
1560 const divisor = switch (rhs) {1572 const factor = switch (rhs) {
1561 .register => rhs,1573 .register => rhs,
1574 .stack_offset => rhs,
1562 else => blk: {1575 else => blk: {
1563 const reg = try self.copyToTmpRegister(ty, rhs);1576 const reg = try self.copyToTmpRegister(ty, rhs);
1564 break :blk MCValue{ .register = reg };1577 break :blk MCValue{ .register = reg };
1565 },1578 },
1566 };1579 };
1567 const op_tag: Mir.Inst.Tag = switch (signedness) {
1568 .signed => .idiv,
1569 .unsigned => .div,
1570 };
15711580
1572 switch (divisor) {1581 switch (factor) {
1573 .register => |reg| {1582 .register => |reg| {
1574 _ = try self.addInst(.{1583 _ = try self.addInst(.{
1575 .tag = op_tag,1584 .tag = tag,
1576 .ops = (Mir.Ops{1585 .ops = (Mir.Ops{
1577 .reg1 = reg,1586 .reg1 = reg,
1578 }).encode(),1587 }).encode(),
...@@ -1581,7 +1590,7 @@ fn genIntDivOpMir(...@@ -1581,7 +1590,7 @@ fn genIntDivOpMir(
1581 },1590 },
1582 .stack_offset => |off| {1591 .stack_offset => |off| {
1583 _ = try self.addInst(.{1592 _ = try self.addInst(.{
1584 .tag = op_tag,1593 .tag = tag,
1585 .ops = (Mir.Ops{1594 .ops = (Mir.Ops{
1586 .reg2 = .rbp,1595 .reg2 = .rbp,
1587 .flags = switch (abi_size) {1596 .flags = switch (abi_size) {
...@@ -1599,6 +1608,7 @@ fn genIntDivOpMir(...@@ -1599,6 +1608,7 @@ fn genIntDivOpMir(
1599 }1608 }
1600}1609}
16011610
1611/// Clobbers .rax and .rdx registers.
1602fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCValue {1612fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCValue {
1603 const signedness = ty.intInfo(self.target.*).signedness;1613 const signedness = ty.intInfo(self.target.*).signedness;
1604 const dividend = switch (lhs) {1614 const dividend = switch (lhs) {
...@@ -1614,7 +1624,10 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa...@@ -1614,7 +1624,10 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa
1614 self.register_manager.freezeRegs(&.{divisor});1624 self.register_manager.freezeRegs(&.{divisor});
1615 defer self.register_manager.unfreezeRegs(&.{ dividend, divisor });1625 defer self.register_manager.unfreezeRegs(&.{ dividend, divisor });
16161626
1617 try self.genIntDivOpMir(Type.isize, signedness, .{ .register = dividend }, .{ .register = divisor });1627 try self.genIntMulDivOpMir(switch (signedness) {
1628 .signed => .idiv,
1629 .unsigned => .div,
1630 }, Type.isize, signedness, .{ .register = dividend }, .{ .register = divisor });
16181631
1619 _ = try self.addInst(.{1632 _ = try self.addInst(.{
1620 .tag = .xor,1633 .tag = .xor,
...@@ -1666,22 +1679,53 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void {...@@ -1666,22 +1679,53 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
1666 return self.fail("TODO implement {}", .{tag});1679 return self.fail("TODO implement {}", .{tag});
1667 }1680 }
16681681
1682 const signedness = ty.intInfo(self.target.*).signedness;
1683
1669 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.1684 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
1670 try self.register_manager.getReg(.rax, null);1685 const track_rax: ?Air.Inst.Index = blk: {
1686 if (signedness == .unsigned) break :blk inst;
1687 switch (tag) {
1688 .div_exact, .div_trunc => break :blk inst,
1689 else => break :blk null,
1690 }
1691 };
1692 try self.register_manager.getReg(.rax, track_rax);
1671 try self.register_manager.getReg(.rdx, null);1693 try self.register_manager.getReg(.rdx, null);
1694 self.register_manager.freezeRegs(&.{ .rax, .rdx });
1695 defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
16721696
1673 const lhs = try self.resolveInst(bin_op.lhs);1697 const lhs = try self.resolveInst(bin_op.lhs);
1674 const rhs = try self.resolveInst(bin_op.rhs);1698 lhs.freezeIfRegister(&self.register_manager);
1699 defer lhs.unfreezeIfRegister(&self.register_manager);
1700
1701 const rhs = blk: {
1702 const rhs = try self.resolveInst(bin_op.rhs);
1703 if (signedness == .signed) {
1704 switch (tag) {
1705 .div_floor => {
1706 rhs.freezeIfRegister(&self.register_manager);
1707 defer rhs.unfreezeIfRegister(&self.register_manager);
1708 break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs);
1709 },
1710 else => {},
1711 }
1712 }
1713 break :blk rhs;
1714 };
1715 rhs.freezeIfRegister(&self.register_manager);
1716 defer rhs.unfreezeIfRegister(&self.register_manager);
16751717
1676 const signedness = ty.intInfo(self.target.*).signedness;
1677 if (signedness == .unsigned) {1718 if (signedness == .unsigned) {
1678 try self.genIntDivOpMir(ty, signedness, lhs, rhs);1719 try self.genIntMulDivOpMir(.div, ty, signedness, lhs, rhs);
1679 break :result MCValue{ .register = .rax };1720 break :result MCValue{ .register = .rax };
1680 }1721 }
16811722
1682 switch (tag) {1723 switch (tag) {
1683 .div_exact, .div_trunc => {1724 .div_exact, .div_trunc => {
1684 try self.genIntDivOpMir(ty, signedness, lhs, rhs);1725 try self.genIntMulDivOpMir(switch (signedness) {
1726 .signed => .idiv,
1727 .unsigned => .div,
1728 }, ty, signedness, lhs, rhs);
1685 break :result MCValue{ .register = .rax };1729 break :result MCValue{ .register = .rax };
1686 },1730 },
1687 .div_floor => {1731 .div_floor => {
...@@ -1702,11 +1746,18 @@ fn airRem(self: *Self, inst: Air.Inst.Index) !void {...@@ -1702,11 +1746,18 @@ fn airRem(self: *Self, inst: Air.Inst.Index) !void {
1702 }1746 }
1703 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.1747 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
1704 try self.register_manager.getReg(.rax, null);1748 try self.register_manager.getReg(.rax, null);
1705 try self.register_manager.getReg(.rdx, null);1749 try self.register_manager.getReg(.rdx, inst);
1750 self.register_manager.freezeRegs(&.{ .rax, .rdx });
1751 defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
1752
1706 const lhs = try self.resolveInst(bin_op.lhs);1753 const lhs = try self.resolveInst(bin_op.lhs);
1707 const rhs = try self.resolveInst(bin_op.rhs);1754 const rhs = try self.resolveInst(bin_op.rhs);
1755
1708 const signedness = ty.intInfo(self.target.*).signedness;1756 const signedness = ty.intInfo(self.target.*).signedness;
1709 try self.genIntDivOpMir(ty, signedness, lhs, rhs);1757 try self.genIntMulDivOpMir(switch (signedness) {
1758 .signed => .idiv,
1759 .unsigned => .div,
1760 }, ty, signedness, lhs, rhs);
1710 break :result MCValue{ .register = .rdx };1761 break :result MCValue{ .register = .rdx };
1711 };1762 };
1712 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1763 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
...@@ -1719,25 +1770,33 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void {...@@ -1719,25 +1770,33 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void {
1719 if (ty.zigTypeTag() != .Int) {1770 if (ty.zigTypeTag() != .Int) {
1720 return self.fail("TODO implement .mod for operands of dst type {}", .{ty.zigTypeTag()});1771 return self.fail("TODO implement .mod for operands of dst type {}", .{ty.zigTypeTag()});
1721 }1772 }
1773 const signedness = ty.intInfo(self.target.*).signedness;
1774
1722 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.1775 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
1723 try self.register_manager.getReg(.rax, null);1776 try self.register_manager.getReg(.rax, null);
1724 try self.register_manager.getReg(.rdx, null);1777 try self.register_manager.getReg(.rdx, if (signedness == .unsigned) inst else null);
1778 self.register_manager.freezeRegs(&.{ .rax, .rdx });
1779 defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
1780
1725 const lhs = try self.resolveInst(bin_op.lhs);1781 const lhs = try self.resolveInst(bin_op.lhs);
1726 const rhs = try self.resolveInst(bin_op.rhs);1782 const rhs = try self.resolveInst(bin_op.rhs);
1727 const signedness = ty.intInfo(self.target.*).signedness;1783
1728 switch (signedness) {1784 switch (signedness) {
1729 .unsigned => {1785 .unsigned => {
1730 try self.genIntDivOpMir(ty, signedness, lhs, rhs);1786 try self.genIntMulDivOpMir(switch (signedness) {
1787 .signed => .idiv,
1788 .unsigned => .div,
1789 }, ty, signedness, lhs, rhs);
1731 break :result MCValue{ .register = .rdx };1790 break :result MCValue{ .register = .rdx };
1732 },1791 },
1733 .signed => {1792 .signed => {
1734 const div_floor = try self.genInlineIntDivFloor(ty, lhs, rhs);1793 const div_floor = try self.genInlineIntDivFloor(ty, lhs, rhs);
1735 try self.genIMulOpMir(ty, div_floor, rhs);1794 try self.genIntMulComplexOpMir(ty, div_floor, rhs);
17361795
1737 const reg = try self.copyToTmpRegister(ty, lhs);1796 const result = try self.copyToRegisterWithInstTracking(inst, ty, lhs);
1738 try self.genBinMathOpMir(.sub, ty, .{ .register = reg }, div_floor);1797 try self.genBinMathOpMir(.sub, ty, result, div_floor);
17391798
1740 break :result MCValue{ .register = reg };1799 break :result result;
1741 },1800 },
1742 }1801 }
1743 };1802 };
...@@ -2134,7 +2193,7 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2134,7 +2193,7 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
21342193
2135fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Register {2194fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Register {
2136 const reg = try self.copyToTmpRegister(index_ty, index);2195 const reg = try self.copyToTmpRegister(index_ty, index);
2137 try self.genIMulOpMir(index_ty, .{ .register = reg }, .{ .immediate = elem_size });2196 try self.genIntMulComplexOpMir(index_ty, .{ .register = reg }, .{ .immediate = elem_size });
2138 return reg;2197 return reg;
2139}2198}
21402199
...@@ -3098,7 +3157,6 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:...@@ -3098,7 +3157,6 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
3098 .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, dst_mcv, src_mcv),3157 .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, dst_mcv, src_mcv),
3099 .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, src_mcv),3158 .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, src_mcv),
3100 .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, dst_mcv, src_mcv),3159 .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, dst_mcv, src_mcv),
3101 .mul, .mulwrap, .mul_with_overflow => try self.genIMulOpMir(dst_ty, dst_mcv, src_mcv),
3102 else => unreachable,3160 else => unreachable,
3103 }3161 }
3104 return dst_mcv;3162 return dst_mcv;
...@@ -3254,8 +3312,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC...@@ -3254,8 +3312,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
3254 }3312 }
3255}3313}
32563314
3257// Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv.3315/// Performs multi-operand integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv.
3258fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {3316/// Does not support byte-size operands.
3317fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
3259 const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));3318 const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));
3260 switch (dst_mcv) {3319 switch (dst_mcv) {
3261 .none => unreachable,3320 .none => unreachable,
...@@ -3301,7 +3360,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !...@@ -3301,7 +3360,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
3301 } else {3360 } else {
3302 // TODO verify we don't spill and assign to the same register as dst_mcv3361 // TODO verify we don't spill and assign to the same register as dst_mcv
3303 const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv);3362 const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv);
3304 return self.genIMulOpMir(dst_ty, dst_mcv, MCValue{ .register = src_reg });3363 return self.genIntMulComplexOpMir(dst_ty, dst_mcv, MCValue{ .register = src_reg });
3305 }3364 }
3306 },3365 },
3307 .stack_offset => |off| {3366 .stack_offset => |off| {
...@@ -3458,15 +3517,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3458,15 +3517,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
34583517
3459 try self.spillCompareFlagsIfOccupied();3518 try self.spillCompareFlagsIfOccupied();
34603519
3520 for (caller_preserved_regs) |reg| {
3521 try self.register_manager.getReg(reg, null);
3522 }
3523
3461 if (info.return_value == .stack_offset) {3524 if (info.return_value == .stack_offset) {
3462 const ret_ty = fn_ty.fnReturnType();3525 const ret_ty = fn_ty.fnReturnType();
3463 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));3526 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
3464 const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));3527 const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));
3465 const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align));3528 const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align));
3529 log.debug("airCall: return value on stack at offset {}", .{stack_offset});
34663530
3467 try self.register_manager.getReg(.rdi, null);3531 try self.register_manager.getReg(.rdi, null);
3468 self.register_manager.freezeRegs(&.{.rdi});
3469 try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset });3532 try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset });
3533 self.register_manager.freezeRegs(&.{.rdi});
34703534
3471 info.return_value.stack_offset = stack_offset;3535 info.return_value.stack_offset = stack_offset;
3472 }3536 }
...@@ -3653,15 +3717,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3653,15 +3717,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
36533717
3654 const result: MCValue = result: {3718 const result: MCValue = result: {
3655 switch (info.return_value) {3719 switch (info.return_value) {
3656 .register => |reg| {3720 .register => {
3657 if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) {3721 // Save function return value in a new register
3658 // Save function return value in a callee saved register3722 break :result try self.copyToRegisterWithInstTracking(
3659 break :result try self.copyToRegisterWithInstTracking(3723 inst,
3660 inst,3724 self.air.typeOfIndex(inst),
3661 self.air.typeOfIndex(inst),3725 info.return_value,
3662 info.return_value,3726 );
3663 );
3664 }
3665 },3727 },
3666 else => {},3728 else => {},
3667 }3729 }
...@@ -3688,19 +3750,11 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {...@@ -3688,19 +3750,11 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
3688 const ret_ty = self.fn_type.fnReturnType();3750 const ret_ty = self.fn_type.fnReturnType();
3689 switch (self.ret_mcv) {3751 switch (self.ret_mcv) {
3690 .stack_offset => {3752 .stack_offset => {
3691 // TODO audit register allocation!3753 self.register_manager.freezeRegs(&.{ .rax, .rcx });
3692 self.register_manager.freezeRegs(&.{ .rax, .rcx, .rdi });3754 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx });
3693 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx, .rdi });3755 const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv);
3694 const reg = try self.register_manager.allocReg(null);3756 self.register_manager.freezeRegs(&.{reg});
3695 const backpatch = try self.addInst(.{3757 defer self.register_manager.unfreezeRegs(&.{reg});
3696 .tag = .mov,
3697 .ops = (Mir.Ops{
3698 .reg1 = reg,
3699 .reg2 = .rdi,
3700 }).encode(),
3701 .data = undefined,
3702 });
3703 try self.ret_backpatches.append(self.gpa, backpatch);
3704 try self.genSetStack(ret_ty, 0, operand, .{3758 try self.genSetStack(ret_ty, 0, operand, .{
3705 .source_stack_base = .rbp,3759 .source_stack_base = .rbp,
3706 .dest_stack_base = reg,3760 .dest_stack_base = reg,
...@@ -3731,19 +3785,11 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -3731,19 +3785,11 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
3731 const elem_ty = ptr_ty.elemType();3785 const elem_ty = ptr_ty.elemType();
3732 switch (self.ret_mcv) {3786 switch (self.ret_mcv) {
3733 .stack_offset => {3787 .stack_offset => {
3734 // TODO audit register allocation!3788 self.register_manager.freezeRegs(&.{ .rax, .rcx });
3735 self.register_manager.freezeRegs(&.{ .rax, .rcx, .rdi });3789 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx });
3736 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx, .rdi });3790 const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv);
3737 const reg = try self.register_manager.allocReg(null);3791 self.register_manager.freezeRegs(&.{reg});
3738 const backpatch = try self.addInst(.{3792 defer self.register_manager.unfreezeRegs(&.{reg});
3739 .tag = .mov,
3740 .ops = (Mir.Ops{
3741 .reg1 = reg,
3742 .reg2 = .rdi,
3743 }).encode(),
3744 .data = undefined,
3745 });
3746 try self.ret_backpatches.append(self.gpa, backpatch);
3747 try self.genInlineMemcpy(.{ .stack_offset = 0 }, ptr, .{ .immediate = elem_ty.abiSize(self.target.*) }, .{3793 try self.genInlineMemcpy(.{ .stack_offset = 0 }, ptr, .{ .immediate = elem_ty.abiSize(self.target.*) }, .{
3748 .source_stack_base = .rbp,3794 .source_stack_base = .rbp,
3749 .dest_stack_base = reg,3795 .dest_stack_base = reg,
...@@ -5033,6 +5079,7 @@ const InlineMemcpyOpts = struct {...@@ -5033,6 +5079,7 @@ const InlineMemcpyOpts = struct {
5033 dest_stack_base: ?Register = null,5079 dest_stack_base: ?Register = null,
5034};5080};
50355081
5082/// Spills .rax and .rcx.
5036fn genInlineMemcpy(5083fn genInlineMemcpy(
5037 self: *Self,5084 self: *Self,
5038 dst_ptr: MCValue,5085 dst_ptr: MCValue,
...@@ -5040,13 +5087,7 @@ fn genInlineMemcpy(...@@ -5040,13 +5087,7 @@ fn genInlineMemcpy(
5040 len: MCValue,5087 len: MCValue,
5041 opts: InlineMemcpyOpts,5088 opts: InlineMemcpyOpts,
5042) InnerError!void {5089) InnerError!void {
5043 // TODO this is wrong. We should check first if any of the operands is in `.rax` or `.rcx` before
5044 // spilling. Consolidate with other TODOs regarding register allocation mechanics.
5045 try self.register_manager.getReg(.rax, null);
5046 try self.register_manager.getReg(.rcx, null);
5047
5048 self.register_manager.freezeRegs(&.{ .rax, .rcx });5090 self.register_manager.freezeRegs(&.{ .rax, .rcx });
5049 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx });
50505091
5051 if (opts.source_stack_base) |reg| self.register_manager.freezeRegs(&.{reg});5092 if (opts.source_stack_base) |reg| self.register_manager.freezeRegs(&.{reg});
5052 defer if (opts.source_stack_base) |reg| self.register_manager.unfreezeRegs(&.{reg});5093 defer if (opts.source_stack_base) |reg| self.register_manager.unfreezeRegs(&.{reg});
...@@ -5086,7 +5127,6 @@ fn genInlineMemcpy(...@@ -5086,7 +5127,6 @@ fn genInlineMemcpy(
5086 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});5127 return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});
5087 },5128 },
5088 }5129 }
5089
5090 self.register_manager.freezeRegs(&.{dst_addr_reg});5130 self.register_manager.freezeRegs(&.{dst_addr_reg});
5091 defer self.register_manager.unfreezeRegs(&.{dst_addr_reg});5131 defer self.register_manager.unfreezeRegs(&.{dst_addr_reg});
50925132
...@@ -5122,7 +5162,6 @@ fn genInlineMemcpy(...@@ -5122,7 +5162,6 @@ fn genInlineMemcpy(
5122 return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr});5162 return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr});
5123 },5163 },
5124 }5164 }
5125
5126 self.register_manager.freezeRegs(&.{src_addr_reg});5165 self.register_manager.freezeRegs(&.{src_addr_reg});
5127 defer self.register_manager.unfreezeRegs(&.{src_addr_reg});5166 defer self.register_manager.unfreezeRegs(&.{src_addr_reg});
51285167
...@@ -5130,6 +5169,11 @@ fn genInlineMemcpy(...@@ -5130,6 +5169,11 @@ fn genInlineMemcpy(
5130 const count_reg = regs[0].to64();5169 const count_reg = regs[0].to64();
5131 const tmp_reg = regs[1].to8();5170 const tmp_reg = regs[1].to8();
51325171
5172 self.register_manager.unfreezeRegs(&.{ .rax, .rcx });
5173
5174 try self.register_manager.getReg(.rax, null);
5175 try self.register_manager.getReg(.rcx, null);
5176
5133 try self.genSetReg(Type.usize, count_reg, len);5177 try self.genSetReg(Type.usize, count_reg, len);
51345178
5135 // mov rcx, 05179 // mov rcx, 0
...@@ -5225,6 +5269,7 @@ fn genInlineMemcpy(...@@ -5225,6 +5269,7 @@ fn genInlineMemcpy(
5225 try self.performReloc(loop_reloc);5269 try self.performReloc(loop_reloc);
5226}5270}
52275271
5272/// Spills .rax register.
5228fn genInlineMemset(5273fn genInlineMemset(
5229 self: *Self,5274 self: *Self,
5230 dst_ptr: MCValue,5275 dst_ptr: MCValue,
...@@ -5232,9 +5277,7 @@ fn genInlineMemset(...@@ -5232,9 +5277,7 @@ fn genInlineMemset(
5232 len: MCValue,5277 len: MCValue,
5233 opts: InlineMemcpyOpts,5278 opts: InlineMemcpyOpts,
5234) InnerError!void {5279) InnerError!void {
5235 try self.register_manager.getReg(.rax, null);
5236 self.register_manager.freezeRegs(&.{.rax});5280 self.register_manager.freezeRegs(&.{.rax});
5237 defer self.register_manager.unfreezeRegs(&.{.rax});
52385281
5239 const addr_reg = try self.register_manager.allocReg(null);5282 const addr_reg = try self.register_manager.allocReg(null);
5240 switch (dst_ptr) {5283 switch (dst_ptr) {
...@@ -5271,6 +5314,9 @@ fn genInlineMemset(...@@ -5271,6 +5314,9 @@ fn genInlineMemset(
5271 self.register_manager.freezeRegs(&.{addr_reg});5314 self.register_manager.freezeRegs(&.{addr_reg});
5272 defer self.register_manager.unfreezeRegs(&.{addr_reg});5315 defer self.register_manager.unfreezeRegs(&.{addr_reg});
52735316
5317 self.register_manager.unfreezeRegs(&.{.rax});
5318 try self.register_manager.getReg(.rax, null);
5319
5274 try self.genSetReg(Type.usize, .rax, len);5320 try self.genSetReg(Type.usize, .rax, len);
5275 try self.genBinMathOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 });5321 try self.genBinMathOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 });
52765322
src/arch/x86_64/Emit.zig+5-2
...@@ -145,6 +145,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void {...@@ -145,6 +145,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
145 .sar => try emit.mirShift(.sar, inst),145 .sar => try emit.mirShift(.sar, inst),
146146
147 .imul => try emit.mirMulDiv(.imul, inst),147 .imul => try emit.mirMulDiv(.imul, inst),
148 .mul => try emit.mirMulDiv(.mul, inst),
148 .idiv => try emit.mirMulDiv(.idiv, inst),149 .idiv => try emit.mirMulDiv(.idiv, inst),
149 .div => try emit.mirMulDiv(.div, inst),150 .div => try emit.mirMulDiv(.div, inst),
150 .imul_complex => try emit.mirIMulComplex(inst),151 .imul_complex => try emit.mirIMulComplex(inst),
...@@ -1164,6 +1165,7 @@ const Tag = enum {...@@ -1164,6 +1165,7 @@ const Tag = enum {
1164 brk,1165 brk,
1165 nop,1166 nop,
1166 imul,1167 imul,
1168 mul,
1167 idiv,1169 idiv,
1168 div,1170 div,
1169 syscall,1171 syscall,
...@@ -1411,7 +1413,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {...@@ -1411,7 +1413,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
1411 .setnl, .setge => OpCode.twoByte(0x0f, 0x9d),1413 .setnl, .setge => OpCode.twoByte(0x0f, 0x9d),
1412 .setle, .setng => OpCode.twoByte(0x0f, 0x9e),1414 .setle, .setng => OpCode.twoByte(0x0f, 0x9e),
1413 .setnle, .setg => OpCode.twoByte(0x0f, 0x9f),1415 .setnle, .setg => OpCode.twoByte(0x0f, 0x9f),
1414 .idiv, .div, .imul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),1416 .idiv, .div, .imul, .mul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),
1415 .fisttp16 => OpCode.oneByte(0xdf),1417 .fisttp16 => OpCode.oneByte(0xdf),
1416 .fisttp32 => OpCode.oneByte(0xdb),1418 .fisttp32 => OpCode.oneByte(0xdb),
1417 .fisttp64 => OpCode.oneByte(0xdd),1419 .fisttp64 => OpCode.oneByte(0xdd),
...@@ -1554,9 +1556,10 @@ inline fn getModRmExt(tag: Tag) ?u3 {...@@ -1554,9 +1556,10 @@ inline fn getModRmExt(tag: Tag) ?u3 {
1554 => 0x4,1556 => 0x4,
1555 .shr => 0x5,1557 .shr => 0x5,
1556 .sar => 0x7,1558 .sar => 0x7,
1559 .mul => 0x4,
1557 .imul => 0x5,1560 .imul => 0x5,
1558 .idiv => 0x7,
1559 .div => 0x6,1561 .div => 0x6,
1562 .idiv => 0x7,
1560 .fisttp16 => 0x1,1563 .fisttp16 => 0x1,
1561 .fisttp32 => 0x1,1564 .fisttp32 => 0x1,
1562 .fisttp64 => 0x1,1565 .fisttp64 => 0x1,
src/arch/x86_64/Mir.zig+1
...@@ -227,6 +227,7 @@ pub const Inst = struct {...@@ -227,6 +227,7 @@ pub const Inst = struct {
227 /// 0b11 qword ptr [reg2 + imm32]227 /// 0b11 qword ptr [reg2 + imm32]
228 imul,228 imul,
229 idiv,229 idiv,
230 mul,
230 div,231 div,
231232
232 /// ops flags: form:233 /// ops flags: form:
src/arch/x86_64/abi.zig+8-3
...@@ -370,8 +370,13 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class {...@@ -370,8 +370,13 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class {
370 }370 }
371}371}
372372
373/// These registers need to be preserved (saved on the stack) and restored by the callee before getting clobbered373/// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
374/// and when the callee returns.374/// for anything else but stack offset tracking therefore we exclude them from this set.
375pub const callee_preserved_regs = [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 };375pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 };
376/// These registers need to be preserved (saved on the stack) and restored by the caller before
377/// the caller relinquishes control to a subroutine via call instruction (or similar).
378/// In other words, these registers are free to use by the callee.
379pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
380pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs;
376pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };381pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
377pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };382pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
test/behavior/math.zig+3-5
...@@ -697,11 +697,9 @@ test "@mulWithOverflow" {...@@ -697,11 +697,9 @@ test "@mulWithOverflow" {
697 try expect(!@mulWithOverflow(u8, a, b, &result));697 try expect(!@mulWithOverflow(u8, a, b, &result));
698 try expect(result == 246);698 try expect(result == 246);
699699
700 if (builtin.zig_backend != .stage2_x86_64) { // TODO fix mul/imul on x86_64700 b = 4;
701 b = 4;701 try expect(@mulWithOverflow(u8, a, b, &result));
702 try expect(@mulWithOverflow(u8, a, b, &result));702 try expect(result == 236);
703 try expect(result == 236);
704 }
705}703}
706704
707test "@subWithOverflow" {705test "@subWithOverflow" {