x86_64/Emit: implement restoring callee_preserved_registers
This commit is contained in:
@@ -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{
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user