diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 2de2796983..0f060771e8 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/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{ diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 7430b8065b..7fd81d46c2 100644 --- a/src/arch/x86_64/Emit.zig +++ b/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]); diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 0d83bfed7c..05d374a856 100644 --- a/src/arch/x86_64/Mir.zig +++ b/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.