| ... | @@ -61,6 +61,8 @@ end_di_column: u32, | ... | @@ -61,6 +61,8 @@ 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 | |
| 64 | /// Whenever there is a runtime branch, we push a Branch onto this stack, | 66 | /// Whenever there is a runtime branch, we push a Branch onto this stack, |
| 65 | /// and pop it off when the runtime branch joins. This provides an "overlay" | 67 | /// and pop it off when the runtime branch joins. This provides an "overlay" |
| 66 | /// of the table of mappings from instructions to `MCValue` from within the branch. | 68 | /// of the table of mappings from instructions to `MCValue` from within the branch. |
| ... | @@ -182,7 +184,7 @@ const Branch = struct { | ... | @@ -182,7 +184,7 @@ const Branch = struct { |
| 182 | | 184 | |
| 183 | const StackAllocation = struct { | 185 | const StackAllocation = struct { |
| 184 | inst: Air.Inst.Index, | 186 | inst: Air.Inst.Index, |
| 185 | /// TODO do we need size? should be determined by inst.ty.abiSize() | 187 | /// TODO do we need size? should be determined by inst.ty.abiSize(self.target.*) |
| 186 | size: u32, | 188 | size: u32, |
| 187 | }; | 189 | }; |
| 188 | | 190 | |
| ... | @@ -284,6 +286,7 @@ pub fn generate( | ... | @@ -284,6 +286,7 @@ pub fn generate( |
| 284 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); | 286 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 285 | defer function.mir_instructions.deinit(bin_file.allocator); | 287 | defer function.mir_instructions.deinit(bin_file.allocator); |
| 286 | defer function.mir_extra.deinit(bin_file.allocator); | 288 | defer function.mir_extra.deinit(bin_file.allocator); |
| | 289 | defer function.stack_args_relocs.deinit(bin_file.allocator); |
| 287 | defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit(); | 290 | defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit(); |
| 288 | | 291 | |
| 289 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { | 292 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| ... | @@ -459,13 +462,11 @@ fn gen(self: *Self) InnerError!void { | ... | @@ -459,13 +462,11 @@ fn gen(self: *Self) InnerError!void { |
| 459 | // Thus we don't need to adjust the stack for the first push instruction. However, | 462 | // Thus we don't need to adjust the stack for the first push instruction. However, |
| 460 | // any subsequent push of values on the stack such as when preserving registers, | 463 | // any subsequent push of values on the stack such as when preserving registers, |
| 461 | // needs to be taken into account here. | 464 | // needs to be taken into account here. |
| 462 | var stack_adjustment: i32 = 0; | 465 | var stack_adjustment: u32 = 0; |
| 463 | inline for (callee_preserved_regs) |reg, i| { | 466 | inline for (callee_preserved_regs) |reg, i| { |
| 464 | if (self.register_manager.isRegAllocated(reg)) { | 467 | if (self.register_manager.isRegAllocated(reg)) { |
| 465 | callee_preserved_regs_push_data |= 1 << @intCast(u5, i); | 468 | callee_preserved_regs_push_data |= 1 << @intCast(u5, i); |
| 466 | if (self.target.isDarwin()) { | 469 | stack_adjustment += @divExact(reg.size(), 8); |
| 467 | stack_adjustment += @divExact(reg.size(), 8); | | |
| 468 | } | | |
| 469 | } | 470 | } |
| 470 | } | 471 | } |
| 471 | const data = self.mir_instructions.items(.data); | 472 | const data = self.mir_instructions.items(.data); |
| ... | @@ -490,23 +491,31 @@ fn gen(self: *Self) InnerError!void { | ... | @@ -490,23 +491,31 @@ fn gen(self: *Self) InnerError!void { |
| 490 | if (stack_end > math.maxInt(i32) - stack_adjustment) { | 491 | if (stack_end > math.maxInt(i32) - stack_adjustment) { |
| 491 | return self.failSymbol("too much stack used in call parameters", .{}); | 492 | return self.failSymbol("too much stack used in call parameters", .{}); |
| 492 | } | 493 | } |
| 493 | const aligned_stack_end = mem.alignForward(stack_end, self.stack_align); | 494 | // TODO we should reuse this mechanism to align the stack when calling any function even if |
| 494 | if (aligned_stack_end > 0 or stack_adjustment > 0) { | 495 | // 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; |
| 495 | self.mir_instructions.set(backpatch_stack_sub, .{ | 499 | self.mir_instructions.set(backpatch_stack_sub, .{ |
| 496 | .tag = .sub, | 500 | .tag = .sub, |
| 497 | .ops = (Mir.Ops{ | 501 | .ops = (Mir.Ops{ |
| 498 | .reg1 = .rsp, | 502 | .reg1 = .rsp, |
| 499 | }).encode(), | 503 | }).encode(), |
| 500 | .data = .{ .imm = @bitCast(u32, @intCast(i32, aligned_stack_end) + stack_adjustment) }, | 504 | .data = .{ .imm = imm }, |
| 501 | }); | 505 | }); |
| 502 | self.mir_instructions.set(backpatch_stack_add, .{ | 506 | self.mir_instructions.set(backpatch_stack_add, .{ |
| 503 | .tag = .add, | 507 | .tag = .add, |
| 504 | .ops = (Mir.Ops{ | 508 | .ops = (Mir.Ops{ |
| 505 | .reg1 = .rsp, | 509 | .reg1 = .rsp, |
| 506 | }).encode(), | 510 | }).encode(), |
| 507 | .data = .{ .imm = @bitCast(u32, @intCast(i32, aligned_stack_end) + stack_adjustment) }, | 511 | .data = .{ .imm = imm }, |
| 508 | }); | 512 | }); |
| 509 | } | 513 | } |
| | 514 | while (self.stack_args_relocs.popOrNull()) |index| { |
| | 515 | // +16 bytes to account for saved return address of the `call` instruction and |
| | 516 | // `push rbp`. |
| | 517 | self.mir_instructions.items(.data)[index].imm += stack_adjustment + aligned_stack_end + 16; |
| | 518 | } |
| 510 | } else { | 519 | } else { |
| 511 | _ = try self.addInst(.{ | 520 | _ = try self.addInst(.{ |
| 512 | .tag = .dbg_prologue_end, | 521 | .tag = .dbg_prologue_end, |
| ... | @@ -1613,6 +1622,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo | ... | @@ -1613,6 +1622,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1613 | | 1622 | |
| 1614 | return self.genInlineMemcpy( | 1623 | return self.genInlineMemcpy( |
| 1615 | @bitCast(u32, -@intCast(i32, off + abi_size)), | 1624 | @bitCast(u32, -@intCast(i32, off + abi_size)), |
| | 1625 | .rbp, |
| 1616 | registerAlias(addr_reg, @divExact(reg.size(), 8)), | 1626 | registerAlias(addr_reg, @divExact(reg.size(), 8)), |
| 1617 | count_reg.to64(), | 1627 | count_reg.to64(), |
| 1618 | tmp_reg.to8(), | 1628 | tmp_reg.to8(), |
| ... | @@ -2157,9 +2167,6 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2157,9 +2167,6 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2157 | const arg_index = self.arg_index; | 2167 | const arg_index = self.arg_index; |
| 2158 | self.arg_index += 1; | 2168 | self.arg_index += 1; |
| 2159 | | 2169 | |
| 2160 | const ty = self.air.typeOfIndex(inst); | | |
| 2161 | _ = ty; | | |
| 2162 | | | |
| 2163 | const mcv = self.args[arg_index]; | 2170 | const mcv = self.args[arg_index]; |
| 2164 | const payload = try self.addExtra(Mir.ArgDbgInfo{ | 2171 | const payload = try self.addExtra(Mir.ArgDbgInfo{ |
| 2165 | .air_inst = inst, | 2172 | .air_inst = inst, |
| ... | @@ -2173,14 +2180,68 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2173,14 +2180,68 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2173 | if (self.liveness.isUnused(inst)) | 2180 | if (self.liveness.isUnused(inst)) |
| 2174 | return self.finishAirBookkeeping(); | 2181 | return self.finishAirBookkeeping(); |
| 2175 | | 2182 | |
| 2176 | switch (mcv) { | 2183 | const dst_mcv: MCValue = blk: { |
| 2177 | .register => |reg| { | 2184 | switch (mcv) { |
| 2178 | self.register_manager.getRegAssumeFree(reg.to64(), inst); | 2185 | .register => |reg| { |
| 2179 | }, | 2186 | self.register_manager.getRegAssumeFree(reg.to64(), inst); |
| 2180 | else => {}, | 2187 | break :blk mcv; |
| 2181 | } | 2188 | }, |
| | 2189 | .stack_offset => |off| { |
| | 2190 | const ty = self.air.typeOfIndex(inst); |
| | 2191 | const abi_size = ty.abiSize(self.target.*); |
| | 2192 | |
| | 2193 | if (abi_size <= 8) { |
| | 2194 | const reg = try self.register_manager.allocReg(inst, &.{}); |
| | 2195 | const reloc = try self.addInst(.{ |
| | 2196 | .tag = .mov, |
| | 2197 | .ops = (Mir.Ops{ |
| | 2198 | .reg1 = registerAlias(reg, @intCast(u32, abi_size)), |
| | 2199 | .reg2 = .rsp, |
| | 2200 | .flags = 0b01, |
| | 2201 | }).encode(), |
| | 2202 | .data = .{ .imm = off }, |
| | 2203 | }); |
| | 2204 | try self.stack_args_relocs.append(self.bin_file.allocator, reloc); |
| | 2205 | break :blk .{ .register = reg }; |
| | 2206 | } |
| | 2207 | |
| | 2208 | // TODO copy ellision |
| | 2209 | const dst_mcv = try self.allocRegOrMem(inst, false); |
| | 2210 | const regs = try self.register_manager.allocRegs(3, .{ null, null, null }, &.{ .rax, .rcx }); |
| | 2211 | const addr_reg = regs[0]; |
| | 2212 | const count_reg = regs[1]; |
| | 2213 | const tmp_reg = regs[2]; |
| | 2214 | |
| | 2215 | try self.register_manager.getReg(.rax, null); |
| | 2216 | try self.register_manager.getReg(.rcx, null); |
| | 2217 | |
| | 2218 | const reloc = try self.addInst(.{ |
| | 2219 | .tag = .lea, |
| | 2220 | .ops = (Mir.Ops{ |
| | 2221 | .reg1 = addr_reg.to64(), |
| | 2222 | .reg2 = .rsp, |
| | 2223 | }).encode(), |
| | 2224 | .data = .{ .imm = off }, |
| | 2225 | }); |
| | 2226 | try self.stack_args_relocs.append(self.bin_file.allocator, reloc); |
| | 2227 | |
| | 2228 | // TODO allow for abi_size to be u64 |
| | 2229 | try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) }); |
| | 2230 | try self.genInlineMemcpy( |
| | 2231 | @bitCast(u32, -@intCast(i32, dst_mcv.stack_offset + abi_size)), |
| | 2232 | .rbp, |
| | 2233 | addr_reg.to64(), |
| | 2234 | count_reg.to64(), |
| | 2235 | tmp_reg.to8(), |
| | 2236 | ); |
| | 2237 | |
| | 2238 | break :blk dst_mcv; |
| | 2239 | }, |
| | 2240 | else => unreachable, |
| | 2241 | } |
| | 2242 | }; |
| 2182 | | 2243 | |
| 2183 | return self.finishAir(inst, mcv, .{ .none, .none, .none }); | 2244 | return self.finishAir(inst, dst_mcv, .{ .none, .none, .none }); |
| 2184 | } | 2245 | } |
| 2185 | | 2246 | |
| 2186 | fn airBreakpoint(self: *Self) !void { | 2247 | fn airBreakpoint(self: *Self) !void { |
| ... | @@ -2201,6 +2262,64 @@ fn airFence(self: *Self) !void { | ... | @@ -2201,6 +2262,64 @@ fn airFence(self: *Self) !void { |
| 2201 | //return self.finishAirBookkeeping(); | 2262 | //return self.finishAirBookkeeping(); |
| 2202 | } | 2263 | } |
| 2203 | | 2264 | |
| | 2265 | fn genSetStackArg(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { |
| | 2266 | const abi_size = ty.abiSize(self.target.*); |
| | 2267 | switch (mcv) { |
| | 2268 | .dead => unreachable, |
| | 2269 | .ptr_embedded_in_code => unreachable, |
| | 2270 | .unreach, .none => return, |
| | 2271 | .register => |reg| { |
| | 2272 | _ = try self.addInst(.{ |
| | 2273 | .tag = .mov, |
| | 2274 | .ops = (Mir.Ops{ |
| | 2275 | .reg1 = .rsp, |
| | 2276 | .reg2 = registerAlias(reg, @intCast(u32, abi_size)), |
| | 2277 | .flags = 0b10, |
| | 2278 | }).encode(), |
| | 2279 | .data = .{ .imm = @bitCast(u32, -@intCast(i32, stack_offset + abi_size)) }, |
| | 2280 | }); |
| | 2281 | }, |
| | 2282 | .ptr_stack_offset => { |
| | 2283 | const reg = try self.copyToTmpRegister(ty, mcv); |
| | 2284 | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); |
| | 2285 | }, |
| | 2286 | .stack_offset => |unadjusted_off| { |
| | 2287 | if (abi_size <= 8) { |
| | 2288 | const reg = try self.copyToTmpRegister(ty, mcv); |
| | 2289 | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); |
| | 2290 | } |
| | 2291 | |
| | 2292 | const regs = try self.register_manager.allocRegs(3, .{ null, null, null }, &.{ .rax, .rcx }); |
| | 2293 | const addr_reg = regs[0]; |
| | 2294 | const count_reg = regs[1]; |
| | 2295 | const tmp_reg = regs[2]; |
| | 2296 | |
| | 2297 | try self.register_manager.getReg(.rax, null); |
| | 2298 | try self.register_manager.getReg(.rcx, null); |
| | 2299 | |
| | 2300 | _ = try self.addInst(.{ |
| | 2301 | .tag = .lea, |
| | 2302 | .ops = (Mir.Ops{ |
| | 2303 | .reg1 = addr_reg.to64(), |
| | 2304 | .reg2 = .rbp, |
| | 2305 | }).encode(), |
| | 2306 | .data = .{ .imm = @bitCast(u32, -@intCast(i32, unadjusted_off + abi_size)) }, |
| | 2307 | }); |
| | 2308 | |
| | 2309 | // TODO allow for abi_size to be u64 |
| | 2310 | try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) }); |
| | 2311 | try self.genInlineMemcpy( |
| | 2312 | @bitCast(u32, -@intCast(i32, stack_offset + abi_size)), |
| | 2313 | .rsp, |
| | 2314 | addr_reg.to64(), |
| | 2315 | count_reg.to64(), |
| | 2316 | tmp_reg.to8(), |
| | 2317 | ); |
| | 2318 | }, |
| | 2319 | else => return self.fail("TODO implement args on stack for {}", .{mcv}), |
| | 2320 | } |
| | 2321 | } |
| | 2322 | |
| 2204 | fn airCall(self: *Self, inst: Air.Inst.Index) !void { | 2323 | fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2205 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | 2324 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 2206 | const callee = pl_op.operand; | 2325 | const callee = pl_op.operand; |
| ... | @@ -2217,43 +2336,58 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2217,43 +2336,58 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2217 | var info = try self.resolveCallingConventionValues(fn_ty); | 2336 | var info = try self.resolveCallingConventionValues(fn_ty); |
| 2218 | defer info.deinit(self); | 2337 | defer info.deinit(self); |
| 2219 | | 2338 | |
| | 2339 | var count: usize = info.args.len; |
| | 2340 | var stack_adjustment: u32 = 0; |
| | 2341 | while (count > 0) : (count -= 1) { |
| | 2342 | const arg_i = count - 1; |
| | 2343 | const mc_arg = info.args[arg_i]; |
| | 2344 | const arg = args[arg_i]; |
| | 2345 | const arg_ty = self.air.typeOf(arg); |
| | 2346 | const arg_mcv = try self.resolveInst(args[arg_i]); |
| | 2347 | // Here we do not use setRegOrMem even though the logic is similar, because |
| | 2348 | // the function call will move the stack pointer, so the offsets are different. |
| | 2349 | switch (mc_arg) { |
| | 2350 | .none => continue, |
| | 2351 | .register => |reg| { |
| | 2352 | try self.register_manager.getReg(reg, null); |
| | 2353 | try self.genSetReg(arg_ty, reg, arg_mcv); |
| | 2354 | }, |
| | 2355 | .stack_offset => |off| { |
| | 2356 | const abi_size = arg_ty.abiSize(self.target.*); |
| | 2357 | try self.genSetStackArg(arg_ty, off, arg_mcv); |
| | 2358 | stack_adjustment += @intCast(u32, abi_size); |
| | 2359 | }, |
| | 2360 | .ptr_stack_offset => { |
| | 2361 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); |
| | 2362 | }, |
| | 2363 | .ptr_embedded_in_code => { |
| | 2364 | return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); |
| | 2365 | }, |
| | 2366 | .undef => unreachable, |
| | 2367 | .immediate => unreachable, |
| | 2368 | .unreach => unreachable, |
| | 2369 | .dead => unreachable, |
| | 2370 | .embedded_in_code => unreachable, |
| | 2371 | .memory => unreachable, |
| | 2372 | .compare_flags_signed => unreachable, |
| | 2373 | .compare_flags_unsigned => unreachable, |
| | 2374 | } |
| | 2375 | } |
| | 2376 | |
| | 2377 | if (stack_adjustment > 0) { |
| | 2378 | // Adjust the stack |
| | 2379 | _ = try self.addInst(.{ |
| | 2380 | .tag = .sub, |
| | 2381 | .ops = (Mir.Ops{ |
| | 2382 | .reg1 = .rsp, |
| | 2383 | }).encode(), |
| | 2384 | .data = .{ .imm = stack_adjustment }, |
| | 2385 | }); |
| | 2386 | } |
| | 2387 | |
| 2220 | // Due to incremental compilation, how function calls are generated depends | 2388 | // Due to incremental compilation, how function calls are generated depends |
| 2221 | // on linking. | 2389 | // on linking. |
| 2222 | if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) { | 2390 | if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) { |
| 2223 | for (info.args) |mc_arg, arg_i| { | | |
| 2224 | const arg = args[arg_i]; | | |
| 2225 | const arg_ty = self.air.typeOf(arg); | | |
| 2226 | const arg_mcv = try self.resolveInst(args[arg_i]); | | |
| 2227 | // Here we do not use setRegOrMem even though the logic is similar, because | | |
| 2228 | // the function call will move the stack pointer, so the offsets are different. | | |
| 2229 | switch (mc_arg) { | | |
| 2230 | .none => continue, | | |
| 2231 | .register => |reg| { | | |
| 2232 | try self.register_manager.getReg(reg, null); | | |
| 2233 | try self.genSetReg(arg_ty, reg, arg_mcv); | | |
| 2234 | }, | | |
| 2235 | .stack_offset => |off| { | | |
| 2236 | // Here we need to emit instructions like this: | | |
| 2237 | // mov qword ptr [rsp + stack_offset], x | | |
| 2238 | try self.genSetStack(arg_ty, off, arg_mcv); | | |
| 2239 | }, | | |
| 2240 | .ptr_stack_offset => { | | |
| 2241 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); | | |
| 2242 | }, | | |
| 2243 | .ptr_embedded_in_code => { | | |
| 2244 | return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); | | |
| 2245 | }, | | |
| 2246 | .undef => unreachable, | | |
| 2247 | .immediate => unreachable, | | |
| 2248 | .unreach => unreachable, | | |
| 2249 | .dead => unreachable, | | |
| 2250 | .embedded_in_code => unreachable, | | |
| 2251 | .memory => unreachable, | | |
| 2252 | .compare_flags_signed => unreachable, | | |
| 2253 | .compare_flags_unsigned => unreachable, | | |
| 2254 | } | | |
| 2255 | } | | |
| 2256 | | | |
| 2257 | if (self.air.value(callee)) |func_value| { | 2391 | if (self.air.value(callee)) |func_value| { |
| 2258 | if (func_value.castTag(.function)) |func_payload| { | 2392 | if (func_value.castTag(.function)) |func_payload| { |
| 2259 | const func = func_payload.data; | 2393 | const func = func_payload.data; |
| ... | @@ -2292,41 +2426,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2292,41 +2426,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2292 | }); | 2426 | }); |
| 2293 | } | 2427 | } |
| 2294 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | 2428 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 2295 | for (info.args) |mc_arg, arg_i| { | | |
| 2296 | const arg = args[arg_i]; | | |
| 2297 | const arg_ty = self.air.typeOf(arg); | | |
| 2298 | const arg_mcv = try self.resolveInst(args[arg_i]); | | |
| 2299 | // Here we do not use setRegOrMem even though the logic is similar, because | | |
| 2300 | // the function call will move the stack pointer, so the offsets are different. | | |
| 2301 | switch (mc_arg) { | | |
| 2302 | .none => continue, | | |
| 2303 | .register => |reg| { | | |
| 2304 | // TODO prevent this macho if block to be generated for all archs | | |
| 2305 | try self.register_manager.getReg(reg, null); | | |
| 2306 | try self.genSetReg(arg_ty, reg, arg_mcv); | | |
| 2307 | }, | | |
| 2308 | .stack_offset => |off| { | | |
| 2309 | // Here we need to emit instructions like this: | | |
| 2310 | // mov qword ptr [rsp + stack_offset], x | | |
| 2311 | try self.genSetStack(arg_ty, off, arg_mcv); | | |
| 2312 | }, | | |
| 2313 | .ptr_stack_offset => { | | |
| 2314 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); | | |
| 2315 | }, | | |
| 2316 | .ptr_embedded_in_code => { | | |
| 2317 | return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); | | |
| 2318 | }, | | |
| 2319 | .undef => unreachable, | | |
| 2320 | .immediate => unreachable, | | |
| 2321 | .unreach => unreachable, | | |
| 2322 | .dead => unreachable, | | |
| 2323 | .embedded_in_code => unreachable, | | |
| 2324 | .memory => unreachable, | | |
| 2325 | .compare_flags_signed => unreachable, | | |
| 2326 | .compare_flags_unsigned => unreachable, | | |
| 2327 | } | | |
| 2328 | } | | |
| 2329 | | | |
| 2330 | if (self.air.value(callee)) |func_value| { | 2429 | if (self.air.value(callee)) |func_value| { |
| 2331 | if (func_value.castTag(.function)) |func_payload| { | 2430 | if (func_value.castTag(.function)) |func_payload| { |
| 2332 | const func = func_payload.data; | 2431 | const func = func_payload.data; |
| ... | @@ -2369,39 +2468,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2369,39 +2468,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2369 | }); | 2468 | }); |
| 2370 | } | 2469 | } |
| 2371 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | 2470 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 2372 | for (info.args) |mc_arg, arg_i| { | | |
| 2373 | const arg = args[arg_i]; | | |
| 2374 | const arg_ty = self.air.typeOf(arg); | | |
| 2375 | const arg_mcv = try self.resolveInst(args[arg_i]); | | |
| 2376 | // Here we do not use setRegOrMem even though the logic is similar, because | | |
| 2377 | // the function call will move the stack pointer, so the offsets are different. | | |
| 2378 | switch (mc_arg) { | | |
| 2379 | .none => continue, | | |
| 2380 | .register => |reg| { | | |
| 2381 | try self.register_manager.getReg(reg, null); | | |
| 2382 | try self.genSetReg(arg_ty, reg, arg_mcv); | | |
| 2383 | }, | | |
| 2384 | .stack_offset => |off| { | | |
| 2385 | // Here we need to emit instructions like this: | | |
| 2386 | // mov qword ptr [rsp + stack_offset], x | | |
| 2387 | try self.genSetStack(arg_ty, off, arg_mcv); | | |
| 2388 | }, | | |
| 2389 | .ptr_stack_offset => { | | |
| 2390 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); | | |
| 2391 | }, | | |
| 2392 | .ptr_embedded_in_code => { | | |
| 2393 | return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); | | |
| 2394 | }, | | |
| 2395 | .undef => unreachable, | | |
| 2396 | .immediate => unreachable, | | |
| 2397 | .unreach => unreachable, | | |
| 2398 | .dead => unreachable, | | |
| 2399 | .embedded_in_code => unreachable, | | |
| 2400 | .memory => unreachable, | | |
| 2401 | .compare_flags_signed => unreachable, | | |
| 2402 | .compare_flags_unsigned => unreachable, | | |
| 2403 | } | | |
| 2404 | } | | |
| 2405 | if (self.air.value(callee)) |func_value| { | 2471 | if (self.air.value(callee)) |func_value| { |
| 2406 | if (func_value.castTag(.function)) |func_payload| { | 2472 | if (func_value.castTag(.function)) |func_payload| { |
| 2407 | try p9.seeDecl(func_payload.data.owner_decl); | 2473 | try p9.seeDecl(func_payload.data.owner_decl); |
| ... | @@ -2433,6 +2499,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2433,6 +2499,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2433 | } | 2499 | } |
| 2434 | } else unreachable; | 2500 | } else unreachable; |
| 2435 | | 2501 | |
| | 2502 | if (stack_adjustment > 0) { |
| | 2503 | // Readjust the stack |
| | 2504 | _ = try self.addInst(.{ |
| | 2505 | .tag = .add, |
| | 2506 | .ops = (Mir.Ops{ |
| | 2507 | .reg1 = .rsp, |
| | 2508 | }).encode(), |
| | 2509 | .data = .{ .imm = stack_adjustment }, |
| | 2510 | }); |
| | 2511 | } |
| | 2512 | |
| 2436 | const result: MCValue = result: { | 2513 | const result: MCValue = result: { |
| 2437 | switch (info.return_value) { | 2514 | switch (info.return_value) { |
| 2438 | .register => |reg| { | 2515 | .register => |reg| { |
| ... | @@ -3346,6 +3423,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro | ... | @@ -3346,6 +3423,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3346 | | 3423 | |
| 3347 | return self.genInlineMemcpy( | 3424 | return self.genInlineMemcpy( |
| 3348 | @bitCast(u32, -@intCast(i32, stack_offset + abi_size)), | 3425 | @bitCast(u32, -@intCast(i32, stack_offset + abi_size)), |
| | 3426 | .rbp, |
| 3349 | addr_reg.to64(), | 3427 | addr_reg.to64(), |
| 3350 | count_reg.to64(), | 3428 | count_reg.to64(), |
| 3351 | tmp_reg.to8(), | 3429 | tmp_reg.to8(), |
| ... | @@ -3357,6 +3435,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro | ... | @@ -3357,6 +3435,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3357 | fn genInlineMemcpy( | 3435 | fn genInlineMemcpy( |
| 3358 | self: *Self, | 3436 | self: *Self, |
| 3359 | stack_offset: u32, | 3437 | stack_offset: u32, |
| | 3438 | stack_reg: Register, |
| 3360 | addr_reg: Register, | 3439 | addr_reg: Register, |
| 3361 | count_reg: Register, | 3440 | count_reg: Register, |
| 3362 | tmp_reg: Register, | 3441 | tmp_reg: Register, |
| ... | @@ -3410,7 +3489,7 @@ fn genInlineMemcpy( | ... | @@ -3410,7 +3489,7 @@ fn genInlineMemcpy( |
| 3410 | _ = try self.addInst(.{ | 3489 | _ = try self.addInst(.{ |
| 3411 | .tag = .mov_scale_dst, | 3490 | .tag = .mov_scale_dst, |
| 3412 | .ops = (Mir.Ops{ | 3491 | .ops = (Mir.Ops{ |
| 3413 | .reg1 = .rbp, | 3492 | .reg1 = stack_reg, |
| 3414 | .reg2 = tmp_reg.to8(), | 3493 | .reg2 = tmp_reg.to8(), |
| 3415 | }).encode(), | 3494 | }).encode(), |
| 3416 | .data = .{ .imm = stack_offset }, | 3495 | .data = .{ .imm = stack_offset }, |
| ... | @@ -4140,15 +4219,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -4140,15 +4219,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 4140 | return result; | 4219 | return result; |
| 4141 | }, | 4220 | }, |
| 4142 | .Unspecified, .C => { | 4221 | .Unspecified, .C => { |
| | 4222 | // First, split into args that can be passed via registers. |
| | 4223 | // This will make it easier to then push the rest of args in reverse |
| | 4224 | // order on the stack. |
| 4143 | var next_int_reg: usize = 0; | 4225 | var next_int_reg: usize = 0; |
| 4144 | var next_stack_offset: u32 = 0; | 4226 | var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator); |
| 4145 | | 4227 | defer by_reg.deinit(); |
| 4146 | for (param_types) |ty, i| { | 4228 | for (param_types) |ty, i| { |
| 4147 | if (!ty.hasCodeGenBits()) { | 4229 | if (!ty.hasCodeGenBits()) continue; |
| 4148 | assert(cc != .C); | | |
| 4149 | result.args[i] = .{ .none = {} }; | | |
| 4150 | continue; | | |
| 4151 | } | | |
| 4152 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | 4230 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 4153 | const pass_in_reg = switch (ty.zigTypeTag()) { | 4231 | const pass_in_reg = switch (ty.zigTypeTag()) { |
| 4154 | .Bool => true, | 4232 | .Bool => true, |
| ... | @@ -4158,17 +4236,27 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -4158,17 +4236,27 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 4158 | else => false, | 4236 | else => false, |
| 4159 | }; | 4237 | }; |
| 4160 | if (pass_in_reg) { | 4238 | if (pass_in_reg) { |
| 4161 | if (next_int_reg >= c_abi_int_param_regs.len) { | 4239 | if (next_int_reg >= c_abi_int_param_regs.len) break; |
| 4162 | result.args[i] = .{ .stack_offset = next_stack_offset }; | 4240 | try by_reg.putNoClobber(i, next_int_reg); |
| 4163 | next_stack_offset += param_size; | 4241 | next_int_reg += 1; |
| 4164 | } else { | 4242 | } |
| 4165 | const aliased_reg = registerAlias( | 4243 | } |
| 4166 | c_abi_int_param_regs[next_int_reg], | 4244 | |
| 4167 | param_size, | 4245 | var next_stack_offset: u32 = 0; |
| 4168 | ); | 4246 | var count: usize = param_types.len; |
| 4169 | result.args[i] = .{ .register = aliased_reg }; | 4247 | while (count > 0) : (count -= 1) { |
| 4170 | next_int_reg += 1; | 4248 | const i = count - 1; |
| 4171 | } | 4249 | const ty = param_types[i]; |
| | 4250 | if (!ty.hasCodeGenBits()) { |
| | 4251 | assert(cc != .C); |
| | 4252 | result.args[i] = .{ .none = {} }; |
| | 4253 | continue; |
| | 4254 | } |
| | 4255 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); |
| | 4256 | if (by_reg.get(i)) |int_reg| { |
| | 4257 | const aliased_reg = registerAlias(c_abi_int_param_regs[int_reg], param_size); |
| | 4258 | result.args[i] = .{ .register = aliased_reg }; |
| | 4259 | next_int_reg += 1; |
| 4172 | } else { | 4260 | } else { |
| 4173 | // For simplicity of codegen, slices and other types are always pushed onto the stack. | 4261 | // For simplicity of codegen, slices and other types are always pushed onto the stack. |
| 4174 | // TODO: look into optimizing this by passing things as registers sometimes, | 4262 | // TODO: look into optimizing this by passing things as registers sometimes, |
| ... | @@ -4179,6 +4267,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -4179,6 +4267,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 4179 | next_stack_offset += param_size; | 4267 | next_stack_offset += param_size; |
| 4180 | } | 4268 | } |
| 4181 | } | 4269 | } |
| | 4270 | |
| 4182 | result.stack_byte_count = next_stack_offset; | 4271 | result.stack_byte_count = next_stack_offset; |
| 4183 | result.stack_align = 16; | 4272 | result.stack_align = 16; |
| 4184 | }, | 4273 | }, |