| ... | ... | @@ -61,8 +61,6 @@ end_di_column: u32, |
| 61 | 61 | /// which is a relative jump, based on the address following the reloc. |
| 62 | 62 | exitlude_jump_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}, |
| 63 | 63 | |
| 64 | | stack_args_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}, |
| 65 | | |
| 66 | 64 | /// Whenever there is a runtime branch, we push a Branch onto this stack, |
| 67 | 65 | /// and pop it off when the runtime branch joins. This provides an "overlay" |
| 68 | 66 | /// of the table of mappings from instructions to `MCValue` from within the branch. |
| ... | ... | @@ -286,7 +284,6 @@ pub fn generate( |
| 286 | 284 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 287 | 285 | defer function.mir_instructions.deinit(bin_file.allocator); |
| 288 | 286 | defer function.mir_extra.deinit(bin_file.allocator); |
| 289 | | defer function.stack_args_relocs.deinit(bin_file.allocator); |
| 290 | 287 | defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit(); |
| 291 | 288 | |
| 292 | 289 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| ... | ... | @@ -378,13 +375,6 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { |
| 378 | 375 | fn gen(self: *Self) InnerError!void { |
| 379 | 376 | const cc = self.fn_type.fnCallingConvention(); |
| 380 | 377 | if (cc != .Naked) { |
| 381 | | // push the callee_preserved_regs that were used |
| 382 | | const backpatch_push_callee_preserved_regs_i = try self.addInst(.{ |
| 383 | | .tag = .push_regs_from_callee_preserved_regs, |
| 384 | | .ops = undefined, |
| 385 | | .data = .{ .regs_to_push_or_pop = undefined }, // to be backpatched |
| 386 | | }); |
| 387 | | |
| 388 | 378 | _ = try self.addInst(.{ |
| 389 | 379 | .tag = .push, |
| 390 | 380 | .ops = (Mir.Ops{ |
| ... | ... | @@ -416,6 +406,15 @@ fn gen(self: *Self) InnerError!void { |
| 416 | 406 | .data = undefined, |
| 417 | 407 | }); |
| 418 | 408 | |
| 409 | // push the callee_preserved_regs that were used |
| 410 | const backpatch_push_callee_preserved_regs_i = try self.addInst(.{ |
| 411 | .tag = .push_regs_from_callee_preserved_regs, |
| 412 | .ops = (Mir.Ops{ |
| 413 | .reg1 = .rbp, |
| 414 | }).encode(), |
| 415 | .data = .{ .payload = undefined }, // to be backpatched |
| 416 | }); |
| 417 | |
| 419 | 418 | try self.genBody(self.air.getMainBody()); |
| 420 | 419 | |
| 421 | 420 | // TODO can single exitlude jump reloc be elided? What if it is not at the end of the code? |
| ... | ... | @@ -429,6 +428,33 @@ fn gen(self: *Self) InnerError!void { |
| 429 | 428 | self.mir_instructions.items(.data)[jmp_reloc].inst = @intCast(u32, self.mir_instructions.len); |
| 430 | 429 | } |
| 431 | 430 | |
| 431 | // calculate the data for callee_preserved_regs to be pushed and popped |
| 432 | const callee_preserved_regs_payload = blk: { |
| 433 | var data = Mir.RegsToPushOrPop{ |
| 434 | .regs = 0, |
| 435 | .disp = mem.alignForwardGeneric(u32, self.next_stack_offset, 8), |
| 436 | }; |
| 437 | inline for (callee_preserved_regs) |reg, i| { |
| 438 | if (self.register_manager.isRegAllocated(reg)) { |
| 439 | data.regs |= 1 << @intCast(u5, i); |
| 440 | self.max_end_stack += 8; |
| 441 | } |
| 442 | } |
| 443 | break :blk try self.addExtra(data); |
| 444 | }; |
| 445 | |
| 446 | const data = self.mir_instructions.items(.data); |
| 447 | // backpatch the push instruction |
| 448 | data[backpatch_push_callee_preserved_regs_i].payload = callee_preserved_regs_payload; |
| 449 | // pop the callee_preserved_regs |
| 450 | _ = try self.addInst(.{ |
| 451 | .tag = .pop_regs_from_callee_preserved_regs, |
| 452 | .ops = (Mir.Ops{ |
| 453 | .reg1 = .rbp, |
| 454 | }).encode(), |
| 455 | .data = .{ .payload = callee_preserved_regs_payload }, |
| 456 | }); |
| 457 | |
| 432 | 458 | _ = try self.addInst(.{ |
| 433 | 459 | .tag = .dbg_epilogue_begin, |
| 434 | 460 | .ops = undefined, |
| ... | ... | @@ -450,34 +476,6 @@ fn gen(self: *Self) InnerError!void { |
| 450 | 476 | .data = undefined, |
| 451 | 477 | }); |
| 452 | 478 | |
| 453 | | // calculate the data for callee_preserved_regs to be pushed and popped |
| 454 | | var callee_preserved_regs_push_data: u32 = 0x0; |
| 455 | | // TODO this is required on macOS since macOS actively checks for stack alignment |
| 456 | | // at every extern call site. As far as I can tell, macOS accounts for the typical |
| 457 | | // function prologue first 2 instructions of: |
| 458 | | // ... |
| 459 | | // push rbp |
| 460 | | // mov rsp, rbp |
| 461 | | // ... |
| 462 | | // Thus we don't need to adjust the stack for the first push instruction. However, |
| 463 | | // any subsequent push of values on the stack such as when preserving registers, |
| 464 | | // needs to be taken into account here. |
| 465 | | var stack_adjustment: u32 = 0; |
| 466 | | inline for (callee_preserved_regs) |reg, i| { |
| 467 | | if (self.register_manager.isRegAllocated(reg)) { |
| 468 | | callee_preserved_regs_push_data |= 1 << @intCast(u5, i); |
| 469 | | stack_adjustment += @divExact(reg.size(), 8); |
| 470 | | } |
| 471 | | } |
| 472 | | const data = self.mir_instructions.items(.data); |
| 473 | | // backpatch the push instruction |
| 474 | | data[backpatch_push_callee_preserved_regs_i].regs_to_push_or_pop = callee_preserved_regs_push_data; |
| 475 | | // pop the callee_preserved_regs |
| 476 | | _ = try self.addInst(.{ |
| 477 | | .tag = .pop_regs_from_callee_preserved_regs, |
| 478 | | .ops = undefined, |
| 479 | | .data = .{ .regs_to_push_or_pop = callee_preserved_regs_push_data }, |
| 480 | | }); |
| 481 | 479 | _ = try self.addInst(.{ |
| 482 | 480 | .tag = .ret, |
| 483 | 481 | .ops = (Mir.Ops{ |
| ... | ... | @@ -488,36 +486,28 @@ fn gen(self: *Self) InnerError!void { |
| 488 | 486 | |
| 489 | 487 | // Adjust the stack |
| 490 | 488 | const stack_end = self.max_end_stack; |
| 491 | | if (stack_end > math.maxInt(i32) - stack_adjustment) { |
| 489 | if (stack_end > math.maxInt(i32)) { |
| 492 | 490 | return self.failSymbol("too much stack used in call parameters", .{}); |
| 493 | 491 | } |
| 494 | 492 | // TODO we should reuse this mechanism to align the stack when calling any function even if |
| 495 | 493 | // we do not pass any args on the stack BUT we still push regs to stack with `push` inst. |
| 496 | 494 | const aligned_stack_end = @intCast(u32, mem.alignForward(stack_end, self.stack_align)); |
| 497 | | if (aligned_stack_end > 0 or (stack_adjustment > 0 and self.target.isDarwin())) { |
| 498 | | const imm = if (self.target.isDarwin()) aligned_stack_end + stack_adjustment else aligned_stack_end; |
| 495 | if (aligned_stack_end > 0) { |
| 499 | 496 | self.mir_instructions.set(backpatch_stack_sub, .{ |
| 500 | 497 | .tag = .sub, |
| 501 | 498 | .ops = (Mir.Ops{ |
| 502 | 499 | .reg1 = .rsp, |
| 503 | 500 | }).encode(), |
| 504 | | .data = .{ .imm = imm }, |
| 501 | .data = .{ .imm = aligned_stack_end }, |
| 505 | 502 | }); |
| 506 | 503 | self.mir_instructions.set(backpatch_stack_add, .{ |
| 507 | 504 | .tag = .add, |
| 508 | 505 | .ops = (Mir.Ops{ |
| 509 | 506 | .reg1 = .rsp, |
| 510 | 507 | }).encode(), |
| 511 | | .data = .{ .imm = imm }, |
| 508 | .data = .{ .imm = aligned_stack_end }, |
| 512 | 509 | }); |
| 513 | 510 | } |
| 514 | | while (self.stack_args_relocs.popOrNull()) |index| { |
| 515 | | // TODO like above, gotta figure out the alignment shenanigans for macOS, etc. |
| 516 | | const adjustment = if (self.target.isDarwin()) 2 * stack_adjustment else stack_adjustment; |
| 517 | | // +16 bytes to account for saved return address of the `call` instruction and |
| 518 | | // `push rbp`. |
| 519 | | self.mir_instructions.items(.data)[index].imm += adjustment + aligned_stack_end + 16; |
| 520 | | } |
| 521 | 511 | } else { |
| 522 | 512 | _ = try self.addInst(.{ |
| 523 | 513 | .tag = .dbg_prologue_end, |
| ... | ... | @@ -2194,16 +2184,15 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2194 | 2184 | |
| 2195 | 2185 | if (abi_size <= 8) { |
| 2196 | 2186 | const reg = try self.register_manager.allocReg(inst, &.{}); |
| 2197 | | const reloc = try self.addInst(.{ |
| 2187 | _ = try self.addInst(.{ |
| 2198 | 2188 | .tag = .mov, |
| 2199 | 2189 | .ops = (Mir.Ops{ |
| 2200 | 2190 | .reg1 = registerAlias(reg, @intCast(u32, abi_size)), |
| 2201 | | .reg2 = .rsp, |
| 2191 | .reg2 = .rbp, |
| 2202 | 2192 | .flags = 0b01, |
| 2203 | 2193 | }).encode(), |
| 2204 | | .data = .{ .imm = off }, |
| 2194 | .data = .{ .imm = off + 16 }, |
| 2205 | 2195 | }); |
| 2206 | | try self.stack_args_relocs.append(self.bin_file.allocator, reloc); |
| 2207 | 2196 | break :blk .{ .register = reg }; |
| 2208 | 2197 | } |
| 2209 | 2198 | |
| ... | ... | @@ -2217,15 +2206,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2217 | 2206 | try self.register_manager.getReg(.rax, null); |
| 2218 | 2207 | try self.register_manager.getReg(.rcx, null); |
| 2219 | 2208 | |
| 2220 | | const reloc = try self.addInst(.{ |
| 2209 | _ = try self.addInst(.{ |
| 2221 | 2210 | .tag = .lea, |
| 2222 | 2211 | .ops = (Mir.Ops{ |
| 2223 | 2212 | .reg1 = addr_reg.to64(), |
| 2224 | | .reg2 = .rsp, |
| 2213 | .reg2 = .rbp, |
| 2225 | 2214 | }).encode(), |
| 2226 | | .data = .{ .imm = off }, |
| 2215 | .data = .{ .imm = off + 16 }, |
| 2227 | 2216 | }); |
| 2228 | | try self.stack_args_relocs.append(self.bin_file.allocator, reloc); |
| 2229 | 2217 | |
| 2230 | 2218 | // TODO allow for abi_size to be u64 |
| 2231 | 2219 | try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) }); |