| ... | ... | @@ -409,13 +409,11 @@ fn gen(self: *Self) InnerError!void { |
| 409 | 409 | // The address where to store the return value for the caller is in `.rdi` |
| 410 | 410 | // register which the callee is free to clobber. Therefore, we purposely |
| 411 | 411 | // spill it to stack immediately. |
| 412 | | const ptr_ty = Type.usize; |
| 413 | | const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*)); |
| 414 | | const abi_align = ptr_ty.abiAlignment(self.target.*); |
| 415 | | const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset + abi_size, abi_align); |
| 412 | const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset + 8, 8); |
| 416 | 413 | self.next_stack_offset = stack_offset; |
| 417 | 414 | self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); |
| 418 | | try self.genSetStack(ptr_ty, @intCast(i32, stack_offset), MCValue{ .register = .rdi }, .{}); |
| 415 | |
| 416 | try self.genSetStack(Type.usize, @intCast(i32, stack_offset), MCValue{ .register = .rdi }, .{}); |
| 419 | 417 | self.ret_mcv = MCValue{ .stack_offset = @intCast(i32, stack_offset) }; |
| 420 | 418 | log.debug("gen: spilling .rdi to stack at offset {}", .{stack_offset}); |
| 421 | 419 | } |
| ... | ... | @@ -426,11 +424,11 @@ fn gen(self: *Self) InnerError!void { |
| 426 | 424 | .data = undefined, |
| 427 | 425 | }); |
| 428 | 426 | |
| 429 | | // push the callee_preserved_regs that were used |
| 430 | | const backpatch_push_callee_preserved_regs_i = try self.addInst(.{ |
| 431 | | .tag = .push_regs_from_callee_preserved_regs, |
| 432 | | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), |
| 433 | | .data = .{ .payload = undefined }, // to be backpatched |
| 427 | // Push callee-preserved regs that were used actually in use. |
| 428 | const backpatch_push_callee_preserved_regs = try self.addInst(.{ |
| 429 | .tag = .nop, |
| 430 | .ops = undefined, |
| 431 | .data = undefined, |
| 434 | 432 | }); |
| 435 | 433 | |
| 436 | 434 | try self.genBody(self.air.getMainBody()); |
| ... | ... | @@ -446,31 +444,21 @@ fn gen(self: *Self) InnerError!void { |
| 446 | 444 | self.mir_instructions.items(.data)[jmp_reloc].inst = @intCast(u32, self.mir_instructions.len); |
| 447 | 445 | } |
| 448 | 446 | |
| 449 | | // calculate the data for callee_preserved_regs to be pushed and popped |
| 450 | | const callee_preserved_regs_payload = blk: { |
| 451 | | var data = Mir.RegsToPushOrPop{ |
| 452 | | .regs = 0, |
| 453 | | .disp = mem.alignForwardGeneric(u32, self.next_stack_offset, 8), |
| 454 | | }; |
| 455 | | var disp = data.disp + 8; |
| 456 | | inline for (callee_preserved_regs) |reg, i| { |
| 457 | | if (self.register_manager.isRegAllocated(reg)) { |
| 458 | | data.regs |= 1 << @intCast(u5, i); |
| 459 | | self.max_end_stack += 8; |
| 460 | | disp += 8; |
| 461 | | } |
| 447 | // Create list of registers to save in the prologue. |
| 448 | // TODO handle register classes |
| 449 | var reg_list: Mir.RegisterList(Register, &callee_preserved_regs) = .{}; |
| 450 | inline for (callee_preserved_regs) |reg| { |
| 451 | if (self.register_manager.isRegAllocated(reg)) { |
| 452 | reg_list.push(reg); |
| 462 | 453 | } |
| 463 | | break :blk try self.addExtra(data); |
| 464 | | }; |
| 454 | } |
| 455 | const saved_regs_stack_space: u32 = reg_list.count() * 8; |
| 465 | 456 | |
| 466 | | const data = self.mir_instructions.items(.data); |
| 467 | | // backpatch the push instruction |
| 468 | | data[backpatch_push_callee_preserved_regs_i].payload = callee_preserved_regs_payload; |
| 469 | | // pop the callee_preserved_regs |
| 470 | | _ = try self.addInst(.{ |
| 471 | | .tag = .pop_regs_from_callee_preserved_regs, |
| 472 | | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), |
| 473 | | .data = .{ .payload = callee_preserved_regs_payload }, |
| 457 | // Pop saved callee-preserved regs. |
| 458 | const backpatch_pop_callee_preserved_regs = try self.addInst(.{ |
| 459 | .tag = .nop, |
| 460 | .ops = undefined, |
| 461 | .data = undefined, |
| 474 | 462 | }); |
| 475 | 463 | |
| 476 | 464 | _ = try self.addInst(.{ |
| ... | ... | @@ -502,9 +490,11 @@ fn gen(self: *Self) InnerError!void { |
| 502 | 490 | if (self.max_end_stack > math.maxInt(i32)) { |
| 503 | 491 | return self.failSymbol("too much stack used in call parameters", .{}); |
| 504 | 492 | } |
| 505 | | // TODO we should reuse this mechanism to align the stack when calling any function even if |
| 506 | | // we do not pass any args on the stack BUT we still push regs to stack with `push` inst. |
| 507 | | const aligned_stack_end = @intCast(u32, mem.alignForward(self.max_end_stack, self.stack_align)); |
| 493 | |
| 494 | const aligned_stack_end = @intCast( |
| 495 | u32, |
| 496 | mem.alignForward(self.max_end_stack + saved_regs_stack_space, self.stack_align), |
| 497 | ); |
| 508 | 498 | if (aligned_stack_end > 0) { |
| 509 | 499 | self.mir_instructions.set(backpatch_stack_sub, .{ |
| 510 | 500 | .tag = .sub, |
| ... | ... | @@ -516,6 +506,21 @@ fn gen(self: *Self) InnerError!void { |
| 516 | 506 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), |
| 517 | 507 | .data = .{ .imm = aligned_stack_end }, |
| 518 | 508 | }); |
| 509 | |
| 510 | const save_reg_list = try self.addExtra(Mir.SaveRegisterList{ |
| 511 | .register_list = reg_list.asInt(), |
| 512 | .stack_end = aligned_stack_end, |
| 513 | }); |
| 514 | self.mir_instructions.set(backpatch_push_callee_preserved_regs, .{ |
| 515 | .tag = .push_regs, |
| 516 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), |
| 517 | .data = .{ .payload = save_reg_list }, |
| 518 | }); |
| 519 | self.mir_instructions.set(backpatch_pop_callee_preserved_regs, .{ |
| 520 | .tag = .pop_regs, |
| 521 | .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), |
| 522 | .data = .{ .payload = save_reg_list }, |
| 523 | }); |
| 519 | 524 | } |
| 520 | 525 | } else { |
| 521 | 526 | _ = try self.addInst(.{ |
| ... | ... | @@ -907,6 +912,39 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 907 | 912 | return MCValue{ .stack_offset = @intCast(i32, stack_offset) }; |
| 908 | 913 | } |
| 909 | 914 | |
| 915 | const State = struct { |
| 916 | next_stack_offset: u32, |
| 917 | registers: abi.RegisterManager.TrackedRegisters, |
| 918 | free_registers: abi.RegisterManager.RegisterBitSet, |
| 919 | eflags_inst: ?Air.Inst.Index, |
| 920 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation), |
| 921 | |
| 922 | fn deinit(state: *State, gpa: Allocator) void { |
| 923 | state.stack.deinit(gpa); |
| 924 | } |
| 925 | }; |
| 926 | |
| 927 | fn captureState(self: *Self) !State { |
| 928 | return State{ |
| 929 | .next_stack_offset = self.next_stack_offset, |
| 930 | .registers = self.register_manager.registers, |
| 931 | .free_registers = self.register_manager.free_registers, |
| 932 | .eflags_inst = self.eflags_inst, |
| 933 | .stack = try self.stack.clone(self.gpa), |
| 934 | }; |
| 935 | } |
| 936 | |
| 937 | fn revertState(self: *Self, state: State) void { |
| 938 | self.register_manager.registers = state.registers; |
| 939 | self.eflags_inst = state.eflags_inst; |
| 940 | |
| 941 | self.stack.deinit(self.gpa); |
| 942 | self.stack = state.stack; |
| 943 | |
| 944 | self.next_stack_offset = state.next_stack_offset; |
| 945 | self.register_manager.free_registers = state.free_registers; |
| 946 | } |
| 947 | |
| 910 | 948 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 911 | 949 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 912 | 950 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); |
| ... | ... | @@ -4503,12 +4541,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4503 | 4541 | } |
| 4504 | 4542 | |
| 4505 | 4543 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 4506 | | const parent_next_stack_offset = self.next_stack_offset; |
| 4507 | | const parent_free_registers = self.register_manager.free_registers; |
| 4508 | | const parent_eflags_inst = self.eflags_inst; |
| 4509 | | var parent_stack = try self.stack.clone(self.gpa); |
| 4510 | | defer parent_stack.deinit(self.gpa); |
| 4511 | | const parent_registers = self.register_manager.registers; |
| 4544 | const saved_state = try self.captureState(); |
| 4512 | 4545 | |
| 4513 | 4546 | try self.branch_stack.append(.{}); |
| 4514 | 4547 | errdefer { |
| ... | ... | @@ -4526,17 +4559,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4526 | 4559 | var saved_then_branch = self.branch_stack.pop(); |
| 4527 | 4560 | defer saved_then_branch.deinit(self.gpa); |
| 4528 | 4561 | |
| 4529 | | self.register_manager.registers = parent_registers; |
| 4530 | | self.eflags_inst = parent_eflags_inst; |
| 4531 | | |
| 4532 | | self.stack.deinit(self.gpa); |
| 4533 | | self.stack = parent_stack; |
| 4534 | | parent_stack = .{}; |
| 4535 | | |
| 4536 | | self.next_stack_offset = parent_next_stack_offset; |
| 4537 | | self.register_manager.free_registers = parent_free_registers; |
| 4562 | self.revertState(saved_state); |
| 4538 | 4563 | |
| 4539 | 4564 | try self.performReloc(reloc); |
| 4565 | |
| 4540 | 4566 | const else_branch = self.branch_stack.addOneAssumeCapacity(); |
| 4541 | 4567 | else_branch.* = .{}; |
| 4542 | 4568 | |
| ... | ... | @@ -5021,12 +5047,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5021 | 5047 | } |
| 5022 | 5048 | |
| 5023 | 5049 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 5024 | | const parent_next_stack_offset = self.next_stack_offset; |
| 5025 | | const parent_free_registers = self.register_manager.free_registers; |
| 5026 | | const parent_eflags_inst = self.eflags_inst; |
| 5027 | | var parent_stack = try self.stack.clone(self.gpa); |
| 5028 | | defer parent_stack.deinit(self.gpa); |
| 5029 | | const parent_registers = self.register_manager.registers; |
| 5050 | const saved_state = try self.captureState(); |
| 5030 | 5051 | |
| 5031 | 5052 | try self.branch_stack.append(.{}); |
| 5032 | 5053 | errdefer { |
| ... | ... | @@ -5044,14 +5065,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5044 | 5065 | var saved_case_branch = self.branch_stack.pop(); |
| 5045 | 5066 | defer saved_case_branch.deinit(self.gpa); |
| 5046 | 5067 | |
| 5047 | | self.register_manager.registers = parent_registers; |
| 5048 | | self.eflags_inst = parent_eflags_inst; |
| 5049 | | self.stack.deinit(self.gpa); |
| 5050 | | self.stack = parent_stack; |
| 5051 | | parent_stack = .{}; |
| 5052 | | |
| 5053 | | self.next_stack_offset = parent_next_stack_offset; |
| 5054 | | self.register_manager.free_registers = parent_free_registers; |
| 5068 | self.revertState(saved_state); |
| 5055 | 5069 | |
| 5056 | 5070 | for (relocs) |reloc| { |
| 5057 | 5071 | try self.performReloc(reloc); |