authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-20 15:36:11+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-22 21:56:34+01:00
log358b5441570916a48f2f7df9ebe8b9cb5f87676b
treefba2051ea63f29e5dd2e3ab705f81f148f5d0ba2
parent630da643c664591846c295d75277475ca1801d75

x64: recover address in rdi if was spilled


1 files changed, 71 insertions(+), 16 deletions(-)

src/arch/x86_64/CodeGen.zig+71-16
...@@ -49,6 +49,8 @@ arg_index: u32,...@@ -49,6 +49,8 @@ arg_index: u32,
49src_loc: Module.SrcLoc,49src_loc: Module.SrcLoc,
50stack_align: u32,50stack_align: u32,
5151
52ret_backpatch: ?Mir.Inst.Index = null,
53
52/// MIR Instructions54/// MIR Instructions
53mir_instructions: std.MultiArrayList(Mir.Inst) = .{},55mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
54/// MIR extra data56/// MIR extra data
...@@ -470,6 +472,20 @@ fn gen(self: *Self) InnerError!void {...@@ -470,6 +472,20 @@ fn gen(self: *Self) InnerError!void {
470 };472 };
471 inline for (callee_preserved_regs) |reg, i| {473 inline for (callee_preserved_regs) |reg, i| {
472 if (self.register_manager.isRegAllocated(reg)) {474 if (self.register_manager.isRegAllocated(reg)) {
475 if (self.ret_backpatch) |inst| {
476 if (reg.to64() == .rdi) {
477 const ops = Mir.Ops.decode(self.mir_instructions.items(.ops)[inst]);
478 self.mir_instructions.set(inst, Mir.Inst{
479 .tag = .mov,
480 .ops = (Mir.Ops{
481 .reg1 = ops.reg1,
482 .reg2 = .rbp,
483 .flags = 0b01,
484 }).encode(),
485 .data = .{ .imm = @bitCast(u32, -@intCast(i32, self.max_end_stack + 8)) },
486 });
487 }
488 }
473 data.regs |= 1 << @intCast(u5, i);489 data.regs |= 1 << @intCast(u5, i);
474 self.max_end_stack += 8;490 self.max_end_stack += 8;
475 }491 }
...@@ -3143,14 +3159,28 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -3143,14 +3159,28 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
3143 return bt.finishAir(result);3159 return bt.finishAir(result);
3144}3160}
31453161
3146fn ret(self: *Self, mcv: MCValue) !void {3162fn airRet(self: *Self, inst: Air.Inst.Index) !void {
3163 const un_op = self.air.instructions.items(.data)[inst].un_op;
3164 const operand = try self.resolveInst(un_op);
3147 const ret_ty = self.fn_type.fnReturnType();3165 const ret_ty = self.fn_type.fnReturnType();
3148 switch (self.ret_mcv) {3166 switch (self.ret_mcv) {
3149 .stack_offset => {3167 .stack_offset => {
3150 try self.genSetStack(ret_ty, 0, .rdi, mcv);3168 // TODO audit register allocation!
3169 self.register_manager.freezeRegs(&.{.rdi});
3170 defer self.register_manager.unfreezeRegs(&.{.rdi});
3171 const reg = try self.register_manager.allocReg(null);
3172 self.ret_backpatch = try self.addInst(.{
3173 .tag = .mov,
3174 .ops = (Mir.Ops{
3175 .reg1 = reg,
3176 .reg2 = .rdi,
3177 }).encode(),
3178 .data = undefined,
3179 });
3180 try self.genSetStack(ret_ty, 0, reg, operand);
3151 },3181 },
3152 else => {3182 else => {
3153 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);3183 try self.setRegOrMem(ret_ty, self.ret_mcv, operand);
3154 },3184 },
3155 }3185 }
3156 // TODO when implementing defer, this will need to jump to the appropriate defer expression.3186 // TODO when implementing defer, this will need to jump to the appropriate defer expression.
...@@ -3164,21 +3194,49 @@ fn ret(self: *Self, mcv: MCValue) !void {...@@ -3164,21 +3194,49 @@ fn ret(self: *Self, mcv: MCValue) !void {
3164 .data = .{ .inst = undefined },3194 .data = .{ .inst = undefined },
3165 });3195 });
3166 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);3196 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);
3167}
3168
3169fn airRet(self: *Self, inst: Air.Inst.Index) !void {
3170 const un_op = self.air.instructions.items(.data)[inst].un_op;
3171 const operand = try self.resolveInst(un_op);
3172 try self.ret(operand);
3173 return self.finishAir(inst, .dead, .{ un_op, .none, .none });3197 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
3174}3198}
31753199
3176fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {3200fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
3177 const un_op = self.air.instructions.items(.data)[inst].un_op;3201 const un_op = self.air.instructions.items(.data)[inst].un_op;
3178 const ptr = try self.resolveInst(un_op);3202 const ptr = try self.resolveInst(un_op);
3179 // we can reuse self.ret_mcv because it just gets returned3203 const ptr_ty = self.air.typeOf(un_op);
3180 try self.load(self.ret_mcv, ptr, self.air.typeOf(un_op));3204 const elem_ty = ptr_ty.elemType();
3181 try self.ret(self.ret_mcv);3205 switch (self.ret_mcv) {
3206 .stack_offset => {
3207 // TODO audit register allocation!
3208 self.register_manager.freezeRegs(&.{ .rax, .rcx, .rdi });
3209 defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx, .rdi });
3210 const reg = try self.register_manager.allocReg(null);
3211 self.ret_backpatch = try self.addInst(.{
3212 .tag = .mov,
3213 .ops = (Mir.Ops{
3214 .reg1 = reg,
3215 .reg2 = .rdi,
3216 }).encode(),
3217 .data = undefined,
3218 });
3219 try self.genInlineMemcpy(0, elem_ty, ptr, .{
3220 .source_stack_base = .rbp,
3221 .dest_stack_base = reg,
3222 });
3223 },
3224 else => {
3225 try self.load(self.ret_mcv, ptr, ptr_ty);
3226 try self.setRegOrMem(elem_ty, self.ret_mcv, self.ret_mcv);
3227 },
3228 }
3229 // TODO when implementing defer, this will need to jump to the appropriate defer expression.
3230 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
3231 // which is available if the jump is 127 bytes or less forward.
3232 const jmp_reloc = try self.addInst(.{
3233 .tag = .jmp,
3234 .ops = (Mir.Ops{
3235 .flags = 0b00,
3236 }).encode(),
3237 .data = .{ .inst = undefined },
3238 });
3239 try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc);
3182 return self.finishAir(inst, .dead, .{ un_op, .none, .none });3240 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
3183}3241}
31843242
...@@ -4190,7 +4248,7 @@ fn genInlineMemcpy(self: *Self, stack_offset: i32, ty: Type, val: MCValue, opts:...@@ -4190,7 +4248,7 @@ fn genInlineMemcpy(self: *Self, stack_offset: i32, ty: Type, val: MCValue, opts:
4190 => {4248 => {
4191 break :blk try self.loadMemPtrIntoRegister(Type.usize, val);4249 break :blk try self.loadMemPtrIntoRegister(Type.usize, val);
4192 },4250 },
4193 .stack_offset => |off| {4251 .ptr_stack_offset, .stack_offset => |off| {
4194 const addr_reg = (try self.register_manager.allocReg(null)).to64();4252 const addr_reg = (try self.register_manager.allocReg(null)).to64();
4195 _ = try self.addInst(.{4253 _ = try self.addInst(.{
4196 .tag = .lea,4254 .tag = .lea,
...@@ -5134,9 +5192,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -5134,9 +5192,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
5134 // First, split into args that can be passed via registers.5192 // First, split into args that can be passed via registers.
5135 // This will make it easier to then push the rest of args in reverse5193 // This will make it easier to then push the rest of args in reverse
5136 // order on the stack.5194 // order on the stack.
5137 // TODO if we want to be C ABI compatible (well, SysV compatible), passing return value
5138 // on the stack requires consuming `.rdi` set with the stack location where to save
5139 // the return value.
5140 var next_int_reg: usize = 0;5195 var next_int_reg: usize = 0;
5141 var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator);5196 var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator);
5142 defer by_reg.deinit();5197 defer by_reg.deinit();