Commit 8fc3a707a4

Jacob G-W <jacoblevgw@gmail.com>
2021-11-13 02:00:59
x86_64/Emit: implement restoring callee_preserved_registers
1 parent f950d76
Changed files (3)
src/arch/x86_64/CodeGen.zig
@@ -349,6 +349,13 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
 fn gen(self: *Self) InnerError!void {
     const cc = self.fn_type.fnCallingConvention();
     if (cc != .Naked) {
+        // push the callee_preserved_regs that were used
+        const backpatch_push_callee_preserved_regs_i = try self.addInst(.{
+            .tag = .push_regs_from_callee_preserved_regs,
+            .ops = undefined,
+            .data = .{ .regs_to_push_or_pop = undefined }, // to be backpatched
+        });
+
         _ = try self.addInst(.{
             .tag = .push,
             .ops = (Mir.Ops{
@@ -423,6 +430,22 @@ fn gen(self: *Self) InnerError!void {
             }).encode(),
             .data = undefined,
         });
+        // calculate the data for callee_preserved_regs to be pushed and popped
+        var callee_preserved_regs_push_data: u32 = 0x0;
+        inline for (callee_preserved_regs) |reg, i| {
+            if (self.register_manager.isRegAllocated(reg)) {
+                callee_preserved_regs_push_data |= 1 << @intCast(u5, i);
+            }
+        }
+        const data = self.mir_instructions.items(.data);
+        // backpatch the push instruction
+        data[backpatch_push_callee_preserved_regs_i].regs_to_push_or_pop = callee_preserved_regs_push_data;
+        // pop the callee_preserved_regs
+        _ = try self.addInst(.{
+            .tag = .pop_regs_from_callee_preserved_regs,
+            .ops = undefined,
+            .data = .{ .regs_to_push_or_pop = callee_preserved_regs_push_data },
+        });
         _ = try self.addInst(.{
             .tag = .ret,
             .ops = (Mir.Ops{
src/arch/x86_64/Emit.zig
@@ -142,6 +142,9 @@ pub fn emitMir(emit: *Emit) InnerError!void {
             .dbg_epilogue_begin => try emit.mirDbgEpilogueBegin(inst),
             .arg_dbg_info => try emit.mirArgDbgInfo(inst),
 
+            .push_regs_from_callee_preserved_regs => try emit.mirPushPopRegsFromCalleePreservedRegs(.push, inst),
+            .pop_regs_from_callee_preserved_regs => try emit.mirPushPopRegsFromCalleePreservedRegs(.pop, inst),
+
             else => {
                 return emit.fail("Implement MIR->Isel lowering for x86_64 for pseudo-inst: {s}", .{tag});
             },
@@ -244,6 +247,39 @@ fn mirPushPop(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!v
         0b11 => unreachable,
     }
 }
+fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
+    const callee_preserved_regs = bits.callee_preserved_regs;
+    // PUSH/POP reg
+    const opc: u8 = switch (tag) {
+        .push => 0x50,
+        .pop => 0x58,
+        else => unreachable,
+    };
+
+    const regs = emit.mir.instructions.items(.data)[inst].regs_to_push_or_pop;
+    if (tag == .push) {
+        for (callee_preserved_regs) |reg, i| {
+            if ((regs >> @intCast(u5, i)) & 1 == 0) continue;
+            const encoder = try Encoder.init(emit.code, 2);
+            encoder.rex(.{
+                .b = reg.isExtended(),
+            });
+            encoder.opcode_withReg(opc, reg.lowId());
+        }
+    } else {
+        // pop in the reverse direction
+        var i = callee_preserved_regs.len;
+        while (i > 0) : (i -= 1) {
+            const reg = callee_preserved_regs[i - 1];
+            if ((regs >> @intCast(u5, i - 1)) & 1 == 0) continue;
+            const encoder = try Encoder.init(emit.code, 2);
+            encoder.rex(.{
+                .b = reg.isExtended(),
+            });
+            encoder.opcode_withReg(opc, reg.lowId());
+        }
+    }
+}
 
 fn mirJmpCall(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
     const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
src/arch/x86_64/Mir.zig
@@ -264,8 +264,21 @@ pub const Inst = struct {
 
         /// arg debug info
         arg_dbg_info,
-    };
 
+        /// push registers from the callee_preserved_regs
+        /// data is the bitfield of which regs to push 
+        /// for example on x86_64, the callee_preserved_regs are [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 };    };
+        /// so to push rcx and r8 one would make data 0b00000000_00000000_00000000_00001001 (the first and fourth bits are set)
+        /// ops is unused
+        push_regs_from_callee_preserved_regs,
+
+        /// pop registers from the callee_preserved_regs
+        /// data is the bitfield of which regs to pop
+        /// for example on x86_64, the callee_preserved_regs are [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 };    };
+        /// so to pop rcx and r8 one would make data 0b00000000_00000000_00000000_00001001 (the first and fourth bits are set)
+        /// ops is unused
+        pop_regs_from_callee_preserved_regs,
+    };
     /// The position of an MIR instruction within the `Mir` instructions array.
     pub const Index = u32;
 
@@ -284,6 +297,8 @@ pub const Inst = struct {
         got_entry: u32,
         /// Index into `extra`. Meaning of what can be found there is context-dependent.
         payload: u32,
+        /// A bitfield of which callee_preserved_regs to push
+        regs_to_push_or_pop: u32,
     };
 
     // Make sure we don't accidentally make instructions bigger than expected.