| ... | ... | @@ -49,6 +49,8 @@ arg_index: u32, |
| 49 | 49 | src_loc: Module.SrcLoc, |
| 50 | 50 | stack_align: u32, |
| 51 | 51 | |
| 52 | ret_backpatch: ?Mir.Inst.Index = null, |
| 53 | |
| 52 | 54 | /// MIR Instructions |
| 53 | 55 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| 54 | 56 | /// MIR extra data |
| ... | ... | @@ -470,6 +472,20 @@ fn gen(self: *Self) InnerError!void { |
| 470 | 472 | }; |
| 471 | 473 | inline for (callee_preserved_regs) |reg, i| { |
| 472 | 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 | 489 | data.regs |= 1 << @intCast(u5, i); |
| 474 | 490 | self.max_end_stack += 8; |
| 475 | 491 | } |
| ... | ... | @@ -3143,14 +3159,28 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 3143 | 3159 | return bt.finishAir(result); |
| 3144 | 3160 | } |
| 3145 | 3161 | |
| 3146 | | fn ret(self: *Self, mcv: MCValue) !void { |
| 3162 | fn 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 | 3165 | const ret_ty = self.fn_type.fnReturnType(); |
| 3148 | 3166 | switch (self.ret_mcv) { |
| 3149 | 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 | 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 | 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 | 3194 | .data = .{ .inst = undefined }, |
| 3165 | 3195 | }); |
| 3166 | 3196 | try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); |
| 3167 | | } |
| 3168 | | |
| 3169 | | fn 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 | 3197 | return self.finishAir(inst, .dead, .{ un_op, .none, .none }); |
| 3174 | 3198 | } |
| 3175 | 3199 | |
| 3176 | 3200 | fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 3177 | 3201 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 3178 | 3202 | const ptr = try self.resolveInst(un_op); |
| 3179 | | // we can reuse self.ret_mcv because it just gets returned |
| 3180 | | try self.load(self.ret_mcv, ptr, self.air.typeOf(un_op)); |
| 3181 | | try self.ret(self.ret_mcv); |
| 3203 | const ptr_ty = self.air.typeOf(un_op); |
| 3204 | const elem_ty = ptr_ty.elemType(); |
| 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 | 3240 | return self.finishAir(inst, .dead, .{ un_op, .none, .none }); |
| 3183 | 3241 | } |
| 3184 | 3242 | |
| ... | ... | @@ -4190,7 +4248,7 @@ fn genInlineMemcpy(self: *Self, stack_offset: i32, ty: Type, val: MCValue, opts: |
| 4190 | 4248 | => { |
| 4191 | 4249 | break :blk try self.loadMemPtrIntoRegister(Type.usize, val); |
| 4192 | 4250 | }, |
| 4193 | | .stack_offset => |off| { |
| 4251 | .ptr_stack_offset, .stack_offset => |off| { |
| 4194 | 4252 | const addr_reg = (try self.register_manager.allocReg(null)).to64(); |
| 4195 | 4253 | _ = try self.addInst(.{ |
| 4196 | 4254 | .tag = .lea, |
| ... | ... | @@ -5134,9 +5192,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5134 | 5192 | // First, split into args that can be passed via registers. |
| 5135 | 5193 | // This will make it easier to then push the rest of args in reverse |
| 5136 | 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 | 5195 | var next_int_reg: usize = 0; |
| 5141 | 5196 | var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator); |
| 5142 | 5197 | defer by_reg.deinit(); |