| ... | @@ -61,8 +61,6 @@ end_di_column: u32, | ... | @@ -61,8 +61,6 @@ end_di_column: u32, |
| 61 | /// which is a relative jump, based on the address following the reloc. | 61 | /// which is a relative jump, based on the address following the reloc. |
| 62 | exitlude_jump_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}, | 62 | exitlude_jump_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}, |
| 63 | | 63 | |
| 64 | stack_args_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}, | | |
| 65 | | | |
| 66 | /// Whenever there is a runtime branch, we push a Branch onto this stack, | 64 | /// Whenever there is a runtime branch, we push a Branch onto this stack, |
| 67 | /// and pop it off when the runtime branch joins. This provides an "overlay" | 65 | /// and pop it off when the runtime branch joins. This provides an "overlay" |
| 68 | /// of the table of mappings from instructions to `MCValue` from within the branch. | 66 | /// of the table of mappings from instructions to `MCValue` from within the branch. |
| ... | @@ -286,7 +284,6 @@ pub fn generate( | ... | @@ -286,7 +284,6 @@ pub fn generate( |
| 286 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); | 284 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 287 | defer function.mir_instructions.deinit(bin_file.allocator); | 285 | defer function.mir_instructions.deinit(bin_file.allocator); |
| 288 | defer function.mir_extra.deinit(bin_file.allocator); | 286 | defer function.mir_extra.deinit(bin_file.allocator); |
| 289 | defer function.stack_args_relocs.deinit(bin_file.allocator); | | |
| 290 | defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit(); | 287 | defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit(); |
| 291 | | 288 | |
| 292 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { | 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,13 +375,6 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { |
| 378 | fn gen(self: *Self) InnerError!void { | 375 | fn gen(self: *Self) InnerError!void { |
| 379 | const cc = self.fn_type.fnCallingConvention(); | 376 | const cc = self.fn_type.fnCallingConvention(); |
| 380 | if (cc != .Naked) { | 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 | _ = try self.addInst(.{ | 378 | _ = try self.addInst(.{ |
| 389 | .tag = .push, | 379 | .tag = .push, |
| 390 | .ops = (Mir.Ops{ | 380 | .ops = (Mir.Ops{ |
| ... | @@ -416,6 +406,15 @@ fn gen(self: *Self) InnerError!void { | ... | @@ -416,6 +406,15 @@ fn gen(self: *Self) InnerError!void { |
| 416 | .data = undefined, | 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 | try self.genBody(self.air.getMainBody()); | 418 | try self.genBody(self.air.getMainBody()); |
| 420 | | 419 | |
| 421 | // TODO can single exitlude jump reloc be elided? What if it is not at the end of the code? | 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,6 +428,33 @@ fn gen(self: *Self) InnerError!void { |
| 429 | self.mir_instructions.items(.data)[jmp_reloc].inst = @intCast(u32, self.mir_instructions.len); | 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 | _ = try self.addInst(.{ | 458 | _ = try self.addInst(.{ |
| 433 | .tag = .dbg_epilogue_begin, | 459 | .tag = .dbg_epilogue_begin, |
| 434 | .ops = undefined, | 460 | .ops = undefined, |
| ... | @@ -450,34 +476,6 @@ fn gen(self: *Self) InnerError!void { | ... | @@ -450,34 +476,6 @@ fn gen(self: *Self) InnerError!void { |
| 450 | .data = undefined, | 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 | _ = try self.addInst(.{ | 479 | _ = try self.addInst(.{ |
| 482 | .tag = .ret, | 480 | .tag = .ret, |
| 483 | .ops = (Mir.Ops{ | 481 | .ops = (Mir.Ops{ |
| ... | @@ -488,36 +486,28 @@ fn gen(self: *Self) InnerError!void { | ... | @@ -488,36 +486,28 @@ fn gen(self: *Self) InnerError!void { |
| 488 | | 486 | |
| 489 | // Adjust the stack | 487 | // Adjust the stack |
| 490 | const stack_end = self.max_end_stack; | 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 | return self.failSymbol("too much stack used in call parameters", .{}); | 490 | return self.failSymbol("too much stack used in call parameters", .{}); |
| 493 | } | 491 | } |
| 494 | // TODO we should reuse this mechanism to align the stack when calling any function even if | 492 | // TODO we should reuse this mechanism to align the stack when calling any function even if |
| 495 | // we do not pass any args on the stack BUT we still push regs to stack with `push` inst. | 493 | // we do not pass any args on the stack BUT we still push regs to stack with `push` inst. |
| 496 | const aligned_stack_end = @intCast(u32, mem.alignForward(stack_end, self.stack_align)); | 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())) { | 495 | if (aligned_stack_end > 0) { |
| 498 | const imm = if (self.target.isDarwin()) aligned_stack_end + stack_adjustment else aligned_stack_end; | | |
| 499 | self.mir_instructions.set(backpatch_stack_sub, .{ | 496 | self.mir_instructions.set(backpatch_stack_sub, .{ |
| 500 | .tag = .sub, | 497 | .tag = .sub, |
| 501 | .ops = (Mir.Ops{ | 498 | .ops = (Mir.Ops{ |
| 502 | .reg1 = .rsp, | 499 | .reg1 = .rsp, |
| 503 | }).encode(), | 500 | }).encode(), |
| 504 | .data = .{ .imm = imm }, | 501 | .data = .{ .imm = aligned_stack_end }, |
| 505 | }); | 502 | }); |
| 506 | self.mir_instructions.set(backpatch_stack_add, .{ | 503 | self.mir_instructions.set(backpatch_stack_add, .{ |
| 507 | .tag = .add, | 504 | .tag = .add, |
| 508 | .ops = (Mir.Ops{ | 505 | .ops = (Mir.Ops{ |
| 509 | .reg1 = .rsp, | 506 | .reg1 = .rsp, |
| 510 | }).encode(), | 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 | } else { | 511 | } else { |
| 522 | _ = try self.addInst(.{ | 512 | _ = try self.addInst(.{ |
| 523 | .tag = .dbg_prologue_end, | 513 | .tag = .dbg_prologue_end, |
| ... | @@ -2194,16 +2184,15 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2194,16 +2184,15 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2194 | | 2184 | |
| 2195 | if (abi_size <= 8) { | 2185 | if (abi_size <= 8) { |
| 2196 | const reg = try self.register_manager.allocReg(inst, &.{}); | 2186 | const reg = try self.register_manager.allocReg(inst, &.{}); |
| 2197 | const reloc = try self.addInst(.{ | 2187 | _ = try self.addInst(.{ |
| 2198 | .tag = .mov, | 2188 | .tag = .mov, |
| 2199 | .ops = (Mir.Ops{ | 2189 | .ops = (Mir.Ops{ |
| 2200 | .reg1 = registerAlias(reg, @intCast(u32, abi_size)), | 2190 | .reg1 = registerAlias(reg, @intCast(u32, abi_size)), |
| 2201 | .reg2 = .rsp, | 2191 | .reg2 = .rbp, |
| 2202 | .flags = 0b01, | 2192 | .flags = 0b01, |
| 2203 | }).encode(), | 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 | break :blk .{ .register = reg }; | 2196 | break :blk .{ .register = reg }; |
| 2208 | } | 2197 | } |
| 2209 | | 2198 | |
| ... | @@ -2217,15 +2206,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2217,15 +2206,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2217 | try self.register_manager.getReg(.rax, null); | 2206 | try self.register_manager.getReg(.rax, null); |
| 2218 | try self.register_manager.getReg(.rcx, null); | 2207 | try self.register_manager.getReg(.rcx, null); |
| 2219 | | 2208 | |
| 2220 | const reloc = try self.addInst(.{ | 2209 | _ = try self.addInst(.{ |
| 2221 | .tag = .lea, | 2210 | .tag = .lea, |
| 2222 | .ops = (Mir.Ops{ | 2211 | .ops = (Mir.Ops{ |
| 2223 | .reg1 = addr_reg.to64(), | 2212 | .reg1 = addr_reg.to64(), |
| 2224 | .reg2 = .rsp, | 2213 | .reg2 = .rbp, |
| 2225 | }).encode(), | 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 | // TODO allow for abi_size to be u64 | 2218 | // TODO allow for abi_size to be u64 |
| 2231 | try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) }); | 2219 | try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) }); |