| ... | ... | @@ -43,7 +43,7 @@ err_msg: ?*ErrorMsg, |
| 43 | 43 | args: []MCValue, |
| 44 | 44 | ret_mcv: MCValue, |
| 45 | 45 | fn_type: Type, |
| 46 | | arg_index: usize, |
| 46 | arg_index: u32, |
| 47 | 47 | src_loc: Module.SrcLoc, |
| 48 | 48 | stack_align: u32, |
| 49 | 49 | |
| ... | ... | @@ -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. |
| ... | ... | @@ -119,9 +117,9 @@ pub const MCValue = union(enum) { |
| 119 | 117 | memory: u64, |
| 120 | 118 | /// The value is one of the stack variables. |
| 121 | 119 | /// If the type is a pointer, it means the pointer address is in the stack at this offset. |
| 122 | | stack_offset: u32, |
| 120 | stack_offset: i32, |
| 123 | 121 | /// The value is a pointer to one of the stack variables (payload is stack offset). |
| 124 | | ptr_stack_offset: u32, |
| 122 | ptr_stack_offset: i32, |
| 125 | 123 | /// The value is in the compare flags assuming an unsigned operation, |
| 126 | 124 | /// with this operator applied on top of it. |
| 127 | 125 | compare_flags_unsigned: math.CompareOperator, |
| ... | ... | @@ -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{ |
| ... | ... | @@ -487,37 +485,28 @@ fn gen(self: *Self) InnerError!void { |
| 487 | 485 | }); |
| 488 | 486 | |
| 489 | 487 | // Adjust the stack |
| 490 | | const stack_end = self.max_end_stack; |
| 491 | | if (stack_end > math.maxInt(i32) - stack_adjustment) { |
| 488 | if (self.max_end_stack > math.maxInt(i32)) { |
| 492 | 489 | return self.failSymbol("too much stack used in call parameters", .{}); |
| 493 | 490 | } |
| 494 | 491 | // TODO we should reuse this mechanism to align the stack when calling any function even if |
| 495 | 492 | // 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)); |
| 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; |
| 493 | const aligned_stack_end = @intCast(u32, mem.alignForward(self.max_end_stack, self.stack_align)); |
| 494 | if (aligned_stack_end > 0) { |
| 499 | 495 | self.mir_instructions.set(backpatch_stack_sub, .{ |
| 500 | 496 | .tag = .sub, |
| 501 | 497 | .ops = (Mir.Ops{ |
| 502 | 498 | .reg1 = .rsp, |
| 503 | 499 | }).encode(), |
| 504 | | .data = .{ .imm = imm }, |
| 500 | .data = .{ .imm = aligned_stack_end }, |
| 505 | 501 | }); |
| 506 | 502 | self.mir_instructions.set(backpatch_stack_add, .{ |
| 507 | 503 | .tag = .add, |
| 508 | 504 | .ops = (Mir.Ops{ |
| 509 | 505 | .reg1 = .rsp, |
| 510 | 506 | }).encode(), |
| 511 | | .data = .{ .imm = imm }, |
| 507 | .data = .{ .imm = aligned_stack_end }, |
| 512 | 508 | }); |
| 513 | 509 | } |
| 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 | 510 | } else { |
| 522 | 511 | _ = try self.addInst(.{ |
| 523 | 512 | .tag = .dbg_prologue_end, |
| ... | ... | @@ -808,7 +797,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 808 | 797 | } |
| 809 | 798 | } |
| 810 | 799 | const stack_offset = try self.allocMem(inst, abi_size, abi_align); |
| 811 | | return MCValue{ .stack_offset = stack_offset }; |
| 800 | return MCValue{ .stack_offset = @intCast(i32, stack_offset) }; |
| 812 | 801 | } |
| 813 | 802 | |
| 814 | 803 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -854,12 +843,12 @@ fn copyToNewRegisterWithExceptions( |
| 854 | 843 | |
| 855 | 844 | fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { |
| 856 | 845 | const stack_offset = try self.allocMemPtr(inst); |
| 857 | | return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); |
| 846 | return self.finishAir(inst, .{ .ptr_stack_offset = @intCast(i32, stack_offset) }, .{ .none, .none, .none }); |
| 858 | 847 | } |
| 859 | 848 | |
| 860 | 849 | fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 861 | 850 | const stack_offset = try self.allocMemPtr(inst); |
| 862 | | return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); |
| 851 | return self.finishAir(inst, .{ .ptr_stack_offset = @intCast(i32, stack_offset) }, .{ .none, .none, .none }); |
| 863 | 852 | } |
| 864 | 853 | |
| 865 | 854 | fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -1419,7 +1408,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1419 | 1408 | .reg1 = addr_reg.to64(), |
| 1420 | 1409 | .reg2 = .rbp, |
| 1421 | 1410 | }).encode(), |
| 1422 | | .data = .{ .imm = @bitCast(u32, -@intCast(i32, off + array_abi_size)) }, |
| 1411 | .data = .{ .imm = @bitCast(u32, -(off + @intCast(i32, array_abi_size))) }, |
| 1423 | 1412 | }); |
| 1424 | 1413 | }, |
| 1425 | 1414 | else => return self.fail("TODO implement array_elem_val when array is {}", .{array}), |
| ... | ... | @@ -1623,7 +1612,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1623 | 1612 | try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) }); |
| 1624 | 1613 | |
| 1625 | 1614 | return self.genInlineMemcpy( |
| 1626 | | @bitCast(u32, -@intCast(i32, off + abi_size)), |
| 1615 | -(off + @intCast(i32, abi_size)), |
| 1627 | 1616 | .rbp, |
| 1628 | 1617 | registerAlias(addr_reg, @divExact(reg.size(), 8)), |
| 1629 | 1618 | count_reg.to64(), |
| ... | ... | @@ -1780,10 +1769,10 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 1780 | 1769 | return if (self.liveness.isUnused(inst)) .dead else result: { |
| 1781 | 1770 | const mcv = try self.resolveInst(operand); |
| 1782 | 1771 | const struct_ty = self.air.typeOf(operand).childType(); |
| 1783 | | const struct_size = @intCast(u32, struct_ty.abiSize(self.target.*)); |
| 1784 | | const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); |
| 1772 | const struct_size = @intCast(i32, struct_ty.abiSize(self.target.*)); |
| 1773 | const struct_field_offset = @intCast(i32, struct_ty.structFieldOffset(index, self.target.*)); |
| 1785 | 1774 | const struct_field_ty = struct_ty.structFieldType(index); |
| 1786 | | const struct_field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)); |
| 1775 | const struct_field_size = @intCast(i32, struct_field_ty.abiSize(self.target.*)); |
| 1787 | 1776 | |
| 1788 | 1777 | switch (mcv) { |
| 1789 | 1778 | .ptr_stack_offset => |off| { |
| ... | ... | @@ -1803,10 +1792,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1803 | 1792 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1804 | 1793 | const mcv = try self.resolveInst(operand); |
| 1805 | 1794 | const struct_ty = self.air.typeOf(operand); |
| 1806 | | const struct_size = @intCast(u32, struct_ty.abiSize(self.target.*)); |
| 1807 | | const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); |
| 1795 | const struct_size = @intCast(i32, struct_ty.abiSize(self.target.*)); |
| 1796 | const struct_field_offset = @intCast(i32, struct_ty.structFieldOffset(index, self.target.*)); |
| 1808 | 1797 | const struct_field_ty = struct_ty.structFieldType(index); |
| 1809 | | const struct_field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)); |
| 1798 | const struct_field_size = @intCast(i32, struct_field_ty.abiSize(self.target.*)); |
| 1810 | 1799 | |
| 1811 | 1800 | switch (mcv) { |
| 1812 | 1801 | .stack_offset => |off| { |
| ... | ... | @@ -1970,7 +1959,7 @@ fn genBinMathOpMir( |
| 1970 | 1959 | return self.fail("stack offset too large", .{}); |
| 1971 | 1960 | } |
| 1972 | 1961 | const abi_size = dst_ty.abiSize(self.target.*); |
| 1973 | | const adj_off = off + abi_size; |
| 1962 | const adj_off = off + @intCast(i32, abi_size); |
| 1974 | 1963 | _ = try self.addInst(.{ |
| 1975 | 1964 | .tag = mir_tag, |
| 1976 | 1965 | .ops = (Mir.Ops{ |
| ... | ... | @@ -1978,7 +1967,7 @@ fn genBinMathOpMir( |
| 1978 | 1967 | .reg2 = .rbp, |
| 1979 | 1968 | .flags = 0b01, |
| 1980 | 1969 | }).encode(), |
| 1981 | | .data = .{ .imm = @bitCast(u32, -@intCast(i32, adj_off)) }, |
| 1970 | .data = .{ .imm = @bitCast(u32, -adj_off) }, |
| 1982 | 1971 | }); |
| 1983 | 1972 | }, |
| 1984 | 1973 | .compare_flags_unsigned => { |
| ... | ... | @@ -1997,7 +1986,7 @@ fn genBinMathOpMir( |
| 1997 | 1986 | if (abi_size > 8) { |
| 1998 | 1987 | return self.fail("TODO implement ADD/SUB/CMP for stack dst with large ABI", .{}); |
| 1999 | 1988 | } |
| 2000 | | const adj_off = off + abi_size; |
| 1989 | const adj_off = off + @intCast(i32, abi_size); |
| 2001 | 1990 | |
| 2002 | 1991 | switch (src_mcv) { |
| 2003 | 1992 | .none => unreachable, |
| ... | ... | @@ -2013,7 +2002,7 @@ fn genBinMathOpMir( |
| 2013 | 2002 | .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), |
| 2014 | 2003 | .flags = 0b10, |
| 2015 | 2004 | }).encode(), |
| 2016 | | .data = .{ .imm = @bitCast(u32, -@intCast(i32, adj_off)) }, |
| 2005 | .data = .{ .imm = @bitCast(u32, -adj_off) }, |
| 2017 | 2006 | }); |
| 2018 | 2007 | }, |
| 2019 | 2008 | .immediate => |imm| { |
| ... | ... | @@ -2034,7 +2023,7 @@ fn genBinMathOpMir( |
| 2034 | 2023 | else => unreachable, |
| 2035 | 2024 | }; |
| 2036 | 2025 | const payload = try self.addExtra(Mir.ImmPair{ |
| 2037 | | .dest_off = @bitCast(u32, -@intCast(i32, adj_off)), |
| 2026 | .dest_off = @bitCast(u32, -adj_off), |
| 2038 | 2027 | .operand = @truncate(u32, imm), |
| 2039 | 2028 | }); |
| 2040 | 2029 | _ = try self.addInst(.{ |
| ... | ... | @@ -2172,7 +2161,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2172 | 2161 | const mcv = self.args[arg_index]; |
| 2173 | 2162 | const payload = try self.addExtra(Mir.ArgDbgInfo{ |
| 2174 | 2163 | .air_inst = inst, |
| 2175 | | .arg_index = @truncate(u32, arg_index), // TODO can arg_index: u32? |
| 2164 | .arg_index = arg_index, |
| 2176 | 2165 | }); |
| 2177 | 2166 | _ = try self.addInst(.{ |
| 2178 | 2167 | .tag = .arg_dbg_info, |
| ... | ... | @@ -2188,58 +2177,13 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2188 | 2177 | self.register_manager.getRegAssumeFree(reg.to64(), inst); |
| 2189 | 2178 | break :blk mcv; |
| 2190 | 2179 | }, |
| 2191 | | .stack_offset => |off| { |
| 2180 | .stack_offset => { |
| 2192 | 2181 | const ty = self.air.typeOfIndex(inst); |
| 2193 | 2182 | const abi_size = ty.abiSize(self.target.*); |
| 2194 | | |
| 2195 | | if (abi_size <= 8) { |
| 2196 | | const reg = try self.register_manager.allocReg(inst, &.{}); |
| 2197 | | const reloc = try self.addInst(.{ |
| 2198 | | .tag = .mov, |
| 2199 | | .ops = (Mir.Ops{ |
| 2200 | | .reg1 = registerAlias(reg, @intCast(u32, abi_size)), |
| 2201 | | .reg2 = .rsp, |
| 2202 | | .flags = 0b01, |
| 2203 | | }).encode(), |
| 2204 | | .data = .{ .imm = off }, |
| 2205 | | }); |
| 2206 | | try self.stack_args_relocs.append(self.bin_file.allocator, reloc); |
| 2207 | | break :blk .{ .register = reg }; |
| 2208 | | } |
| 2209 | | |
| 2210 | | // TODO copy ellision |
| 2211 | | const dst_mcv = try self.allocRegOrMem(inst, false); |
| 2212 | | const regs = try self.register_manager.allocRegs(3, .{ null, null, null }, &.{ .rax, .rcx }); |
| 2213 | | const addr_reg = regs[0]; |
| 2214 | | const count_reg = regs[1]; |
| 2215 | | const tmp_reg = regs[2]; |
| 2216 | | |
| 2217 | | try self.register_manager.getReg(.rax, null); |
| 2218 | | try self.register_manager.getReg(.rcx, null); |
| 2219 | | |
| 2220 | | const reloc = try self.addInst(.{ |
| 2221 | | .tag = .lea, |
| 2222 | | .ops = (Mir.Ops{ |
| 2223 | | .reg1 = addr_reg.to64(), |
| 2224 | | .reg2 = .rsp, |
| 2225 | | }).encode(), |
| 2226 | | .data = .{ .imm = off }, |
| 2227 | | }); |
| 2228 | | try self.stack_args_relocs.append(self.bin_file.allocator, reloc); |
| 2229 | | |
| 2230 | | // TODO allow for abi_size to be u64 |
| 2231 | | try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) }); |
| 2232 | | try self.genInlineMemcpy( |
| 2233 | | @bitCast(u32, -@intCast(i32, dst_mcv.stack_offset + abi_size)), |
| 2234 | | .rbp, |
| 2235 | | addr_reg.to64(), |
| 2236 | | count_reg.to64(), |
| 2237 | | tmp_reg.to8(), |
| 2238 | | ); |
| 2239 | | |
| 2240 | | break :blk dst_mcv; |
| 2183 | const off = @intCast(i32, (arg_index + 1) * abi_size) + 16; |
| 2184 | break :blk MCValue{ .stack_offset = -off }; |
| 2241 | 2185 | }, |
| 2242 | | else => unreachable, |
| 2186 | else => return self.fail("TODO implement arg for {}", .{mcv}), |
| 2243 | 2187 | } |
| 2244 | 2188 | }; |
| 2245 | 2189 | |
| ... | ... | @@ -2264,64 +2208,6 @@ fn airFence(self: *Self) !void { |
| 2264 | 2208 | //return self.finishAirBookkeeping(); |
| 2265 | 2209 | } |
| 2266 | 2210 | |
| 2267 | | fn genSetStackArg(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { |
| 2268 | | const abi_size = ty.abiSize(self.target.*); |
| 2269 | | switch (mcv) { |
| 2270 | | .dead => unreachable, |
| 2271 | | .ptr_embedded_in_code => unreachable, |
| 2272 | | .unreach, .none => return, |
| 2273 | | .register => |reg| { |
| 2274 | | _ = try self.addInst(.{ |
| 2275 | | .tag = .mov, |
| 2276 | | .ops = (Mir.Ops{ |
| 2277 | | .reg1 = .rsp, |
| 2278 | | .reg2 = registerAlias(reg, @intCast(u32, abi_size)), |
| 2279 | | .flags = 0b10, |
| 2280 | | }).encode(), |
| 2281 | | .data = .{ .imm = @bitCast(u32, -@intCast(i32, stack_offset + abi_size)) }, |
| 2282 | | }); |
| 2283 | | }, |
| 2284 | | .ptr_stack_offset => { |
| 2285 | | const reg = try self.copyToTmpRegister(ty, mcv); |
| 2286 | | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); |
| 2287 | | }, |
| 2288 | | .stack_offset => |unadjusted_off| { |
| 2289 | | if (abi_size <= 8) { |
| 2290 | | const reg = try self.copyToTmpRegister(ty, mcv); |
| 2291 | | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); |
| 2292 | | } |
| 2293 | | |
| 2294 | | const regs = try self.register_manager.allocRegs(3, .{ null, null, null }, &.{ .rax, .rcx }); |
| 2295 | | const addr_reg = regs[0]; |
| 2296 | | const count_reg = regs[1]; |
| 2297 | | const tmp_reg = regs[2]; |
| 2298 | | |
| 2299 | | try self.register_manager.getReg(.rax, null); |
| 2300 | | try self.register_manager.getReg(.rcx, null); |
| 2301 | | |
| 2302 | | _ = try self.addInst(.{ |
| 2303 | | .tag = .lea, |
| 2304 | | .ops = (Mir.Ops{ |
| 2305 | | .reg1 = addr_reg.to64(), |
| 2306 | | .reg2 = .rbp, |
| 2307 | | }).encode(), |
| 2308 | | .data = .{ .imm = @bitCast(u32, -@intCast(i32, unadjusted_off + abi_size)) }, |
| 2309 | | }); |
| 2310 | | |
| 2311 | | // TODO allow for abi_size to be u64 |
| 2312 | | try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) }); |
| 2313 | | try self.genInlineMemcpy( |
| 2314 | | @bitCast(u32, -@intCast(i32, stack_offset + abi_size)), |
| 2315 | | .rsp, |
| 2316 | | addr_reg.to64(), |
| 2317 | | count_reg.to64(), |
| 2318 | | tmp_reg.to8(), |
| 2319 | | ); |
| 2320 | | }, |
| 2321 | | else => return self.fail("TODO implement args on stack for {}", .{mcv}), |
| 2322 | | } |
| 2323 | | } |
| 2324 | | |
| 2325 | 2211 | fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2326 | 2212 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 2327 | 2213 | const callee = pl_op.operand; |
| ... | ... | @@ -2338,12 +2224,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2338 | 2224 | var info = try self.resolveCallingConventionValues(fn_ty); |
| 2339 | 2225 | defer info.deinit(self); |
| 2340 | 2226 | |
| 2341 | | var count: usize = info.args.len; |
| 2342 | 2227 | var stack_adjustment: u32 = 0; |
| 2343 | | while (count > 0) : (count -= 1) { |
| 2344 | | const arg_i = count - 1; |
| 2228 | for (args) |arg, arg_i| { |
| 2345 | 2229 | const mc_arg = info.args[arg_i]; |
| 2346 | | const arg = args[arg_i]; |
| 2347 | 2230 | const arg_ty = self.air.typeOf(arg); |
| 2348 | 2231 | const arg_mcv = try self.resolveInst(args[arg_i]); |
| 2349 | 2232 | // Here we do not use setRegOrMem even though the logic is similar, because |
| ... | ... | @@ -2355,9 +2238,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2355 | 2238 | try self.genSetReg(arg_ty, reg, arg_mcv); |
| 2356 | 2239 | }, |
| 2357 | 2240 | .stack_offset => |off| { |
| 2358 | | const abi_size = arg_ty.abiSize(self.target.*); |
| 2241 | const abi_size = @intCast(u32, arg_ty.abiSize(self.target.*)); |
| 2359 | 2242 | try self.genSetStackArg(arg_ty, off, arg_mcv); |
| 2360 | | stack_adjustment += @intCast(u32, abi_size); |
| 2243 | stack_adjustment += abi_size; |
| 2361 | 2244 | }, |
| 2362 | 2245 | .ptr_stack_offset => { |
| 2363 | 2246 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); |
| ... | ... | @@ -3269,7 +3152,65 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { |
| 3269 | 3152 | } |
| 3270 | 3153 | } |
| 3271 | 3154 | |
| 3272 | | fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { |
| 3155 | fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerError!void { |
| 3156 | const abi_size = ty.abiSize(self.target.*); |
| 3157 | switch (mcv) { |
| 3158 | .dead => unreachable, |
| 3159 | .ptr_embedded_in_code => unreachable, |
| 3160 | .unreach, .none => return, |
| 3161 | .register => |reg| { |
| 3162 | _ = try self.addInst(.{ |
| 3163 | .tag = .mov, |
| 3164 | .ops = (Mir.Ops{ |
| 3165 | .reg1 = .rsp, |
| 3166 | .reg2 = registerAlias(reg, @intCast(u32, abi_size)), |
| 3167 | .flags = 0b10, |
| 3168 | }).encode(), |
| 3169 | .data = .{ .imm = @bitCast(u32, -(stack_offset + @intCast(i32, abi_size))) }, |
| 3170 | }); |
| 3171 | }, |
| 3172 | .ptr_stack_offset => { |
| 3173 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 3174 | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); |
| 3175 | }, |
| 3176 | .stack_offset => |unadjusted_off| { |
| 3177 | if (abi_size <= 8) { |
| 3178 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 3179 | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); |
| 3180 | } |
| 3181 | |
| 3182 | const regs = try self.register_manager.allocRegs(3, .{ null, null, null }, &.{ .rax, .rcx }); |
| 3183 | const addr_reg = regs[0]; |
| 3184 | const count_reg = regs[1]; |
| 3185 | const tmp_reg = regs[2]; |
| 3186 | |
| 3187 | try self.register_manager.getReg(.rax, null); |
| 3188 | try self.register_manager.getReg(.rcx, null); |
| 3189 | |
| 3190 | _ = try self.addInst(.{ |
| 3191 | .tag = .lea, |
| 3192 | .ops = (Mir.Ops{ |
| 3193 | .reg1 = addr_reg.to64(), |
| 3194 | .reg2 = .rbp, |
| 3195 | }).encode(), |
| 3196 | .data = .{ .imm = @bitCast(u32, -(unadjusted_off + @intCast(i32, abi_size))) }, |
| 3197 | }); |
| 3198 | |
| 3199 | // TODO allow for abi_size to be u64 |
| 3200 | try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) }); |
| 3201 | try self.genInlineMemcpy( |
| 3202 | -(stack_offset + @intCast(i32, abi_size)), |
| 3203 | .rsp, |
| 3204 | addr_reg.to64(), |
| 3205 | count_reg.to64(), |
| 3206 | tmp_reg.to8(), |
| 3207 | ); |
| 3208 | }, |
| 3209 | else => return self.fail("TODO implement args on stack for {}", .{mcv}), |
| 3210 | } |
| 3211 | } |
| 3212 | |
| 3213 | fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerError!void { |
| 3273 | 3214 | switch (mcv) { |
| 3274 | 3215 | .dead => unreachable, |
| 3275 | 3216 | .ptr_embedded_in_code => unreachable, |
| ... | ... | @@ -3296,7 +3237,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3296 | 3237 | }, |
| 3297 | 3238 | .immediate => |x_big| { |
| 3298 | 3239 | const abi_size = ty.abiSize(self.target.*); |
| 3299 | | const adj_off = stack_offset + abi_size; |
| 3240 | const adj_off = stack_offset + @intCast(i32, abi_size); |
| 3300 | 3241 | if (adj_off > 128) { |
| 3301 | 3242 | return self.fail("TODO implement set stack variable with large stack offset", .{}); |
| 3302 | 3243 | } |
| ... | ... | @@ -3306,7 +3247,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3306 | 3247 | // offset from rbp, which is at the top of the stack frame. |
| 3307 | 3248 | // mov [rbp+offset], immediate |
| 3308 | 3249 | const payload = try self.addExtra(Mir.ImmPair{ |
| 3309 | | .dest_off = @bitCast(u32, -@intCast(i32, adj_off)), |
| 3250 | .dest_off = @bitCast(u32, -adj_off), |
| 3310 | 3251 | .operand = @truncate(u32, x_big), |
| 3311 | 3252 | }); |
| 3312 | 3253 | _ = try self.addInst(.{ |
| ... | ... | @@ -3326,7 +3267,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3326 | 3267 | 8 => { |
| 3327 | 3268 | // We have a positive stack offset value but we want a twos complement negative |
| 3328 | 3269 | // offset from rbp, which is at the top of the stack frame. |
| 3329 | | const negative_offset = -@intCast(i32, adj_off); |
| 3270 | const negative_offset = -adj_off; |
| 3330 | 3271 | |
| 3331 | 3272 | // 64 bit write to memory would take two mov's anyways so we |
| 3332 | 3273 | // insted just use two 32 bit writes to avoid register allocation |
| ... | ... | @@ -3369,7 +3310,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3369 | 3310 | return self.fail("stack offset too large", .{}); |
| 3370 | 3311 | } |
| 3371 | 3312 | const abi_size = ty.abiSize(self.target.*); |
| 3372 | | const adj_off = stack_offset + abi_size; |
| 3313 | const adj_off = stack_offset + @intCast(i32, abi_size); |
| 3373 | 3314 | _ = try self.addInst(.{ |
| 3374 | 3315 | .tag = .mov, |
| 3375 | 3316 | .ops = (Mir.Ops{ |
| ... | ... | @@ -3377,7 +3318,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3377 | 3318 | .reg2 = registerAlias(reg, @intCast(u32, abi_size)), |
| 3378 | 3319 | .flags = 0b10, |
| 3379 | 3320 | }).encode(), |
| 3380 | | .data = .{ .imm = @bitCast(u32, -@intCast(i32, adj_off)) }, |
| 3321 | .data = .{ .imm = @bitCast(u32, -adj_off) }, |
| 3381 | 3322 | }); |
| 3382 | 3323 | }, |
| 3383 | 3324 | .memory, .embedded_in_code => { |
| ... | ... | @@ -3403,7 +3344,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3403 | 3344 | return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); |
| 3404 | 3345 | } |
| 3405 | 3346 | |
| 3406 | | const regs = try self.register_manager.allocRegs(3, .{ null, null, null }, &.{ .rax, .rcx }); |
| 3347 | const regs = try self.register_manager.allocRegs(3, .{ null, null, null }, &.{ .rax, .rcx, .rbp }); |
| 3407 | 3348 | const addr_reg = regs[0]; |
| 3408 | 3349 | const count_reg = regs[1]; |
| 3409 | 3350 | const tmp_reg = regs[2]; |
| ... | ... | @@ -3417,14 +3358,14 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3417 | 3358 | .reg1 = addr_reg.to64(), |
| 3418 | 3359 | .reg2 = .rbp, |
| 3419 | 3360 | }).encode(), |
| 3420 | | .data = .{ .imm = @bitCast(u32, -@intCast(i32, off + abi_size)) }, |
| 3361 | .data = .{ .imm = @bitCast(u32, -(off + @intCast(i32, abi_size))) }, |
| 3421 | 3362 | }); |
| 3422 | 3363 | |
| 3423 | 3364 | // TODO allow for abi_size to be u64 |
| 3424 | 3365 | try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) }); |
| 3425 | 3366 | |
| 3426 | 3367 | return self.genInlineMemcpy( |
| 3427 | | @bitCast(u32, -@intCast(i32, stack_offset + abi_size)), |
| 3368 | -(stack_offset + @intCast(i32, abi_size)), |
| 3428 | 3369 | .rbp, |
| 3429 | 3370 | addr_reg.to64(), |
| 3430 | 3371 | count_reg.to64(), |
| ... | ... | @@ -3436,7 +3377,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3436 | 3377 | |
| 3437 | 3378 | fn genInlineMemcpy( |
| 3438 | 3379 | self: *Self, |
| 3439 | | stack_offset: u32, |
| 3380 | stack_offset: i32, |
| 3440 | 3381 | stack_reg: Register, |
| 3441 | 3382 | addr_reg: Register, |
| 3442 | 3383 | count_reg: Register, |
| ... | ... | @@ -3494,7 +3435,7 @@ fn genInlineMemcpy( |
| 3494 | 3435 | .reg1 = stack_reg, |
| 3495 | 3436 | .reg2 = tmp_reg.to8(), |
| 3496 | 3437 | }).encode(), |
| 3497 | | .data = .{ .imm = stack_offset }, |
| 3438 | .data = .{ .imm = @bitCast(u32, stack_offset) }, |
| 3498 | 3439 | }); |
| 3499 | 3440 | |
| 3500 | 3441 | // add rcx, 1 |
| ... | ... | @@ -3535,14 +3476,14 @@ fn genInlineMemcpy( |
| 3535 | 3476 | try self.performReloc(loop_reloc); |
| 3536 | 3477 | } |
| 3537 | 3478 | |
| 3538 | | fn genInlineMemset(self: *Self, ty: Type, stack_offset: u32, value: MCValue) InnerError!void { |
| 3479 | fn genInlineMemset(self: *Self, ty: Type, stack_offset: i32, value: MCValue) InnerError!void { |
| 3539 | 3480 | try self.register_manager.getReg(.rax, null); |
| 3540 | 3481 | const abi_size = ty.abiSize(self.target.*); |
| 3541 | | const adj_off = stack_offset + abi_size; |
| 3482 | const adj_off = stack_offset + @intCast(i32, abi_size); |
| 3542 | 3483 | if (adj_off > 128) { |
| 3543 | 3484 | return self.fail("TODO inline memset with large stack offset", .{}); |
| 3544 | 3485 | } |
| 3545 | | const negative_offset = @bitCast(u32, -@intCast(i32, adj_off)); |
| 3486 | const negative_offset = @bitCast(u32, -adj_off); |
| 3546 | 3487 | |
| 3547 | 3488 | // We are actually counting `abi_size` bytes; however, we reuse the index register |
| 3548 | 3489 | // as both the counter and offset scaler, hence we need to subtract one from `abi_size` |
| ... | ... | @@ -3633,7 +3574,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3633 | 3574 | const ptr_abi_size = ty.abiSize(self.target.*); |
| 3634 | 3575 | const elem_ty = ty.childType(); |
| 3635 | 3576 | const elem_abi_size = elem_ty.abiSize(self.target.*); |
| 3636 | | const off = unadjusted_off + elem_abi_size; |
| 3577 | const off = unadjusted_off + @intCast(i32, elem_abi_size); |
| 3637 | 3578 | if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { |
| 3638 | 3579 | return self.fail("stack offset too large", .{}); |
| 3639 | 3580 | } |
| ... | ... | @@ -3643,7 +3584,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3643 | 3584 | .reg1 = registerAlias(reg, @intCast(u32, ptr_abi_size)), |
| 3644 | 3585 | .reg2 = .rbp, |
| 3645 | 3586 | }).encode(), |
| 3646 | | .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) }, |
| 3587 | .data = .{ .imm = @bitCast(u32, -off) }, |
| 3647 | 3588 | }); |
| 3648 | 3589 | }, |
| 3649 | 3590 | .ptr_embedded_in_code => unreachable, |
| ... | ... | @@ -3830,7 +3771,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3830 | 3771 | }, |
| 3831 | 3772 | .stack_offset => |unadjusted_off| { |
| 3832 | 3773 | const abi_size = ty.abiSize(self.target.*); |
| 3833 | | const off = unadjusted_off + abi_size; |
| 3774 | const off = unadjusted_off + @intCast(i32, abi_size); |
| 3834 | 3775 | if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { |
| 3835 | 3776 | return self.fail("stack offset too large", .{}); |
| 3836 | 3777 | } |
| ... | ... | @@ -3841,7 +3782,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3841 | 3782 | .reg2 = .rbp, |
| 3842 | 3783 | .flags = 0b01, |
| 3843 | 3784 | }).encode(), |
| 3844 | | .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) }, |
| 3785 | .data = .{ .imm = @bitCast(u32, -off) }, |
| 3845 | 3786 | }); |
| 3846 | 3787 | }, |
| 3847 | 3788 | } |
| ... | ... | @@ -3866,7 +3807,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 3866 | 3807 | const array_ty = ptr_ty.childType(); |
| 3867 | 3808 | const array_len = array_ty.arrayLenIncludingSentinel(); |
| 3868 | 3809 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { |
| 3869 | | const stack_offset = try self.allocMem(inst, 16, 16); |
| 3810 | const stack_offset = @intCast(i32, try self.allocMem(inst, 16, 16)); |
| 3870 | 3811 | try self.genSetStack(ptr_ty, stack_offset + 8, ptr); |
| 3871 | 3812 | try self.genSetStack(Type.initTag(.u64), stack_offset, .{ .immediate = array_len }); |
| 3872 | 3813 | break :blk .{ .stack_offset = stack_offset }; |
| ... | ... | @@ -4247,6 +4188,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 4247 | 4188 | var next_stack_offset: u32 = 0; |
| 4248 | 4189 | var count: usize = param_types.len; |
| 4249 | 4190 | while (count > 0) : (count -= 1) { |
| 4191 | // for (param_types) |ty, i| { |
| 4250 | 4192 | const i = count - 1; |
| 4251 | 4193 | const ty = param_types[i]; |
| 4252 | 4194 | if (!ty.hasCodeGenBits()) { |
| ... | ... | @@ -4265,7 +4207,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 4265 | 4207 | // such as ptr and len of slices as separate registers. |
| 4266 | 4208 | // TODO: also we need to honor the C ABI for relevant types rather than passing on |
| 4267 | 4209 | // the stack here. |
| 4268 | | result.args[i] = .{ .stack_offset = next_stack_offset }; |
| 4210 | result.args[i] = .{ .stack_offset = @intCast(i32, next_stack_offset) }; |
| 4269 | 4211 | next_stack_offset += param_size; |
| 4270 | 4212 | } |
| 4271 | 4213 | } |