authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-11-19 20:01:35+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-11-19 20:01:35+01:00
logbc59a630ab7814ac0ca5487c780a03d7492648f7
tree7a998dd9f553f346636fb1103fd38627bdeab328
parent149bc79486907745c8ffb12a69f65189b26c6dbe

stage2,x86_64: fix genBinMathOp and clarify callee-saved regs

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,11 +32,9 @@ pub const Register = enum(u8) {
32 /// Returns the index into `callee_preserved_regs`.32 /// Returns the index into `callee_preserved_regs`.
33 pub fn allocIndex(self: Register) ?u4 {33 pub fn allocIndex(self: Register) ?u4 {
34 return switch (self) {34 return switch (self) {
35 .eax, .ax, .al => 0,35 .ebx, .bx, .bl => 0,
36 .ecx, .cx, .cl => 1,36 .esi, .si => 1,
37 .edx, .dx, .dl => 2,37 .edi, .di => 2,
38 .esi, .si => 3,
39 .edi, .di => 4,
40 else => null,38 else => null,
41 };39 };
42 }40 }
...@@ -74,7 +72,11 @@ pub const Register = enum(u8) {...@@ -74,7 +72,11 @@ pub const Register = enum(u8) {
7472
75// zig fmt: on73// zig fmt: on
7674
77pub 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.
79pub const callee_preserved_regs = [_]Register{ .ebx, .esi, .edi };
7880
79// TODO add these to Register enum and corresponding dwarfLocOp81// TODO add these to Register enum and corresponding dwarfLocOp
80// // Return Address register. This is stored in `0(%esp, "")` and is not a physical register.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,6 +159,13 @@ pub const MCValue = union(enum) {
159 => true,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};
163170
164const Branch = struct {171const Branch = struct {
...@@ -760,6 +767,19 @@ fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCVa...@@ -760,6 +767,19 @@ fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCVa
760 return MCValue{ .register = reg };767 return MCValue{ .register = reg };
761}768}
762769
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.
772fn 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
763fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {783fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
764 const stack_offset = try self.allocMemPtr(inst);784 const stack_offset = try self.allocMemPtr(inst);
765 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });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,11 +1470,9 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
1450 // as the result MCValue.1470 // as the result MCValue.
1451 var dst_mcv: MCValue = undefined;1471 var dst_mcv: MCValue = undefined;
1452 var src_mcv: MCValue = undefined;1472 var src_mcv: MCValue = undefined;
1453 var src_inst: Air.Inst.Ref = undefined;
1454 if (self.reuseOperand(inst, op_lhs, 0, lhs)) {1473 if (self.reuseOperand(inst, op_lhs, 0, lhs)) {
1455 // LHS dies; use it as the destination.1474 // LHS dies; use it as the destination.
1456 // Both operands cannot be memory.1475 // Both operands cannot be memory.
1457 src_inst = op_rhs;
1458 if (lhs.isMemory() and rhs.isMemory()) {1476 if (lhs.isMemory() and rhs.isMemory()) {
1459 dst_mcv = try self.copyToNewRegister(inst, lhs);1477 dst_mcv = try self.copyToNewRegister(inst, lhs);
1460 src_mcv = rhs;1478 src_mcv = rhs;
...@@ -1465,7 +1483,6 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:...@@ -1465,7 +1483,6 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
1465 } else if (self.reuseOperand(inst, op_rhs, 1, rhs)) {1483 } else if (self.reuseOperand(inst, op_rhs, 1, rhs)) {
1466 // RHS dies; use it as the destination.1484 // RHS dies; use it as the destination.
1467 // Both operands cannot be memory.1485 // Both operands cannot be memory.
1468 src_inst = op_lhs;
1469 if (lhs.isMemory() and rhs.isMemory()) {1486 if (lhs.isMemory() and rhs.isMemory()) {
1470 dst_mcv = try self.copyToNewRegister(inst, rhs);1487 dst_mcv = try self.copyToNewRegister(inst, rhs);
1471 src_mcv = lhs;1488 src_mcv = lhs;
...@@ -1475,13 +1492,23 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:...@@ -1475,13 +1492,23 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
1475 }1492 }
1476 } else {1493 } else {
1477 if (lhs.isMemory()) {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 src_mcv = rhs;1502 src_mcv = rhs;
1480 src_inst = op_rhs;
1481 } else {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 src_mcv = lhs;1511 src_mcv = lhs;
1484 src_inst = op_lhs;
1485 }1512 }
1486 }1513 }
1487 // This instruction supports only signed 32-bit immediates at most. If the immediate1514 // 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,13 +84,11 @@ pub const Register = enum(u7) {
84 /// Returns the index into `callee_preserved_regs`.84 /// Returns the index into `callee_preserved_regs`.
85 pub fn allocIndex(self: Register) ?u4 {85 pub fn allocIndex(self: Register) ?u4 {
86 return switch (self) {86 return switch (self) {
87 .rcx, .ecx, .cx, .cl => 0,87 .rbx, .ebx, .bx, .bl => 0,
88 .rsi, .esi, .si => 1,88 .r12, .r12d, .r12w, .r12b => 1,
89 .rdi, .edi, .di => 2,89 .r13, .r13d, .r13w, .r13b => 2,
90 .r8, .r8d, .r8w, .r8b => 3,90 .r14, .r14d, .r14w, .r14b => 3,
91 .r9, .r9d, .r9w, .r9b => 4,91 .r15, .r15d, .r15w, .r15b => 4,
92 .r10, .r10d, .r10w, .r10b => 5,
93 .r11, .r11d, .r11w, .r11b => 6,
94 else => null,92 else => null,
95 };93 };
96 }94 }
...@@ -142,9 +140,15 @@ pub const Register = enum(u7) {...@@ -142,9 +140,15 @@ pub const Register = enum(u7) {
142140
143// zig fmt: on141// zig fmt: on
144142
145/// These registers belong to the called function.143/// These registers need to be preserved (saved on the stack) and restored by the callee before getting clobbered
146/// TODO should the return_regs be in this array?144/// and when the callee returns.
147pub const callee_preserved_regs = [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 };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.
147pub 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.
151pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
148pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };152pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
149pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };153pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
150154
src/link/Elf.zig+2-2
...@@ -2604,12 +2604,12 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo...@@ -2604,12 +2604,12 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo
2604 // DW.AT.name, DW.FORM.string2604 // DW.AT.name, DW.FORM.string
2605 try dbg_info_buffer.writer().print("{}\x00", .{ty});2605 try dbg_info_buffer.writer().print("{}\x00", .{ty});
2606 } else {2606 } else {
2607 log.warn("TODO implement .debug_info for type '{}'", .{ty});2607 log.debug("TODO implement .debug_info for type '{}'", .{ty});
2608 try dbg_info_buffer.append(abbrev_pad1);2608 try dbg_info_buffer.append(abbrev_pad1);
2609 }2609 }
2610 },2610 },
2611 else => {2611 else => {
2612 log.err("TODO implement .debug_info for type '{}'", .{ty});2612 log.debug("TODO implement .debug_info for type '{}'", .{ty});
2613 try dbg_info_buffer.append(abbrev_pad1);2613 try dbg_info_buffer.append(abbrev_pad1);
2614 },2614 },
2615 }2615 }