| author | |
| committer | |
| log | bc59a630ab7814ac0ca5487c780a03d7492648f7 |
| tree | 7a998dd9f553f346636fb1103fd38627bdeab328 |
| parent | 149bc79486907745c8ffb12a69f65189b26c6dbe |
Previously, we have confused callee-saved with caller-saved registers
(the actual register sets were swapped). This commit fixes that
for both `.x86` and `.x86_64` native backends.
This commit also fixes the register allocation logic in `genBinMathOp`
for `.x86_64` native backend where in a situation such that we require
to spill a register, we would end up spilling the register that is
already involved in the instruction as the other operand. In such a
case, we make a note of this and spill a subsequent register instead.4 files changed, 58 insertions(+), 25 deletions(-)
src/arch/x86/bits.zig+8-6| ... | ... | @@ -32,11 +32,9 @@ pub const Register = enum(u8) { |
| 32 | 32 | /// Returns the index into `callee_preserved_regs`. |
| 33 | 33 | pub fn allocIndex(self: Register) ?u4 { |
| 34 | 34 | return switch (self) { |
| 35 | .eax, .ax, .al => 0, | |
| 36 | .ecx, .cx, .cl => 1, | |
| 37 | .edx, .dx, .dl => 2, | |
| 38 | .esi, .si => 3, | |
| 39 | .edi, .di => 4, | |
| 35 | .ebx, .bx, .bl => 0, | |
| 36 | .esi, .si => 1, | |
| 37 | .edi, .di => 2, | |
| 40 | 38 | else => null, |
| 41 | 39 | }; |
| 42 | 40 | } |
| ... | ... | @@ -74,7 +72,11 @@ pub const Register = enum(u8) { |
| 74 | 72 | |
| 75 | 73 | // zig fmt: on |
| 76 | 74 | |
| 77 | pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi }; | |
| 75 | /// These registers need to be preserved (saved on the stack) and restored by the callee before getting clobbered | |
| 76 | /// and when the callee returns. | |
| 77 | /// Note that .esp and .ebp also belong to this set, however, we never expect to use them | |
| 78 | /// for anything else but stack offset tracking therefore we exclude them from this set. | |
| 79 | pub const callee_preserved_regs = [_]Register{ .ebx, .esi, .edi }; | |
| 78 | 80 | |
| 79 | 81 | // TODO add these to Register enum and corresponding dwarfLocOp |
| 80 | 82 | // // Return Address register. This is stored in `0(%esp, "")` and is not a physical register. |
src/arch/x86_64/CodeGen.zig+34-7| ... | ... | @@ -159,6 +159,13 @@ pub const MCValue = union(enum) { |
| 159 | 159 | => true, |
| 160 | 160 | }; |
| 161 | 161 | } |
| 162 | ||
| 163 | fn isRegister(mcv: MCValue) bool { | |
| 164 | return switch (mcv) { | |
| 165 | .register => true, | |
| 166 | else => false, | |
| 167 | }; | |
| 168 | } | |
| 162 | 169 | }; |
| 163 | 170 | |
| 164 | 171 | const Branch = struct { |
| ... | ... | @@ -760,6 +767,19 @@ fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCVa |
| 760 | 767 | return MCValue{ .register = reg }; |
| 761 | 768 | } |
| 762 | 769 | |
| 770 | /// Like `copyToNewRegister` but allows to specify a list of excluded registers which | |
| 771 | /// will not be selected for allocation. This can be done via `exceptions` slice. | |
| 772 | fn copyToNewRegisterWithExceptions( | |
| 773 | self: *Self, | |
| 774 | reg_owner: Air.Inst.Index, | |
| 775 | mcv: MCValue, | |
| 776 | exceptions: []const Register, | |
| 777 | ) !MCValue { | |
| 778 | const reg = try self.register_manager.allocReg(reg_owner, exceptions); | |
| 779 | try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); | |
| 780 | return MCValue{ .register = reg }; | |
| 781 | } | |
| 782 | ||
| 763 | 783 | fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { |
| 764 | 784 | const stack_offset = try self.allocMemPtr(inst); |
| 765 | 785 | return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); |
| ... | ... | @@ -1450,11 +1470,9 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: |
| 1450 | 1470 | // as the result MCValue. |
| 1451 | 1471 | var dst_mcv: MCValue = undefined; |
| 1452 | 1472 | var src_mcv: MCValue = undefined; |
| 1453 | var src_inst: Air.Inst.Ref = undefined; | |
| 1454 | 1473 | if (self.reuseOperand(inst, op_lhs, 0, lhs)) { |
| 1455 | 1474 | // LHS dies; use it as the destination. |
| 1456 | 1475 | // Both operands cannot be memory. |
| 1457 | src_inst = op_rhs; | |
| 1458 | 1476 | if (lhs.isMemory() and rhs.isMemory()) { |
| 1459 | 1477 | dst_mcv = try self.copyToNewRegister(inst, lhs); |
| 1460 | 1478 | src_mcv = rhs; |
| ... | ... | @@ -1465,7 +1483,6 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: |
| 1465 | 1483 | } else if (self.reuseOperand(inst, op_rhs, 1, rhs)) { |
| 1466 | 1484 | // RHS dies; use it as the destination. |
| 1467 | 1485 | // Both operands cannot be memory. |
| 1468 | src_inst = op_lhs; | |
| 1469 | 1486 | if (lhs.isMemory() and rhs.isMemory()) { |
| 1470 | 1487 | dst_mcv = try self.copyToNewRegister(inst, rhs); |
| 1471 | 1488 | src_mcv = lhs; |
| ... | ... | @@ -1475,13 +1492,23 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: |
| 1475 | 1492 | } |
| 1476 | 1493 | } else { |
| 1477 | 1494 | if (lhs.isMemory()) { |
| 1478 | dst_mcv = try self.copyToNewRegister(inst, lhs); | |
| 1495 | dst_mcv = if (rhs.isRegister()) | |
| 1496 | // If the allocated register is the same as the rhs register, don't allocate that one | |
| 1497 | // and instead spill a subsequent one. Otherwise, this can result in a miscompilation | |
| 1498 | // in the presence of several binary operations performed in a single block. | |
| 1499 | try self.copyToNewRegisterWithExceptions(inst, lhs, &.{rhs.register}) | |
| 1500 | else | |
| 1501 | try self.copyToNewRegister(inst, lhs); | |
| 1479 | 1502 | src_mcv = rhs; |
| 1480 | src_inst = op_rhs; | |
| 1481 | 1503 | } else { |
| 1482 | dst_mcv = try self.copyToNewRegister(inst, rhs); | |
| 1504 | dst_mcv = if (lhs.isRegister()) | |
| 1505 | // If the allocated register is the same as the rhs register, don't allocate that one | |
| 1506 | // and instead spill a subsequent one. Otherwise, this can result in a miscompilation | |
| 1507 | // in the presence of several binary operations performed in a single block. | |
| 1508 | try self.copyToNewRegisterWithExceptions(inst, rhs, &.{lhs.register}) | |
| 1509 | else | |
| 1510 | try self.copyToNewRegister(inst, rhs); | |
| 1483 | 1511 | src_mcv = lhs; |
| 1484 | src_inst = op_lhs; | |
| 1485 | 1512 | } |
| 1486 | 1513 | } |
| 1487 | 1514 | // This instruction supports only signed 32-bit immediates at most. If the immediate |
src/arch/x86_64/bits.zig+14-10| ... | ... | @@ -84,13 +84,11 @@ pub const Register = enum(u7) { |
| 84 | 84 | /// Returns the index into `callee_preserved_regs`. |
| 85 | 85 | pub fn allocIndex(self: Register) ?u4 { |
| 86 | 86 | return switch (self) { |
| 87 | .rcx, .ecx, .cx, .cl => 0, | |
| 88 | .rsi, .esi, .si => 1, | |
| 89 | .rdi, .edi, .di => 2, | |
| 90 | .r8, .r8d, .r8w, .r8b => 3, | |
| 91 | .r9, .r9d, .r9w, .r9b => 4, | |
| 92 | .r10, .r10d, .r10w, .r10b => 5, | |
| 93 | .r11, .r11d, .r11w, .r11b => 6, | |
| 87 | .rbx, .ebx, .bx, .bl => 0, | |
| 88 | .r12, .r12d, .r12w, .r12b => 1, | |
| 89 | .r13, .r13d, .r13w, .r13b => 2, | |
| 90 | .r14, .r14d, .r14w, .r14b => 3, | |
| 91 | .r15, .r15d, .r15w, .r15b => 4, | |
| 94 | 92 | else => null, |
| 95 | 93 | }; |
| 96 | 94 | } |
| ... | ... | @@ -142,9 +140,15 @@ pub const Register = enum(u7) { |
| 142 | 140 | |
| 143 | 141 | // zig fmt: on |
| 144 | 142 | |
| 145 | /// These registers belong to the called function. | |
| 146 | /// TODO should the return_regs be in this array? | |
| 147 | pub const callee_preserved_regs = [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; | |
| 143 | /// These registers need to be preserved (saved on the stack) and restored by the callee before getting clobbered | |
| 144 | /// and when the callee returns. | |
| 145 | /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them | |
| 146 | /// for anything else but stack offset tracking therefore we exclude them from this set. | |
| 147 | pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 }; | |
| 148 | /// These registers need to be preserved (saved on the stack) and restored by the caller before | |
| 149 | /// the caller relinquishes control to a subroutine via call instruction (or similar). | |
| 150 | /// In other words, these registers are free to use by the callee. | |
| 151 | pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; | |
| 148 | 152 | pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; |
| 149 | 153 | pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; |
| 150 | 154 |
src/link/Elf.zig+2-2| ... | ... | @@ -2604,12 +2604,12 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo |
| 2604 | 2604 | // DW.AT.name, DW.FORM.string |
| 2605 | 2605 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); |
| 2606 | 2606 | } else { |
| 2607 | log.warn("TODO implement .debug_info for type '{}'", .{ty}); | |
| 2607 | log.debug("TODO implement .debug_info for type '{}'", .{ty}); | |
| 2608 | 2608 | try dbg_info_buffer.append(abbrev_pad1); |
| 2609 | 2609 | } |
| 2610 | 2610 | }, |
| 2611 | 2611 | else => { |
| 2612 | log.err("TODO implement .debug_info for type '{}'", .{ty}); | |
| 2612 | log.debug("TODO implement .debug_info for type '{}'", .{ty}); | |
| 2613 | 2613 | try dbg_info_buffer.append(abbrev_pad1); |
| 2614 | 2614 | }, |
| 2615 | 2615 | } |