| ... | ... | @@ -61,6 +61,8 @@ 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 | |
| 64 | 66 | /// Whenever there is a runtime branch, we push a Branch onto this stack, |
| 65 | 67 | /// and pop it off when the runtime branch joins. This provides an "overlay" |
| 66 | 68 | /// of the table of mappings from instructions to `MCValue` from within the branch. |
| ... | ... | @@ -182,7 +184,7 @@ const Branch = struct { |
| 182 | 184 | |
| 183 | 185 | const StackAllocation = struct { |
| 184 | 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 | 188 | size: u32, |
| 187 | 189 | }; |
| 188 | 190 | |
| ... | ... | @@ -284,6 +286,7 @@ pub fn generate( |
| 284 | 286 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 285 | 287 | defer function.mir_instructions.deinit(bin_file.allocator); |
| 286 | 288 | defer function.mir_extra.deinit(bin_file.allocator); |
| 289 | defer function.stack_args_relocs.deinit(bin_file.allocator); |
| 287 | 290 | defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit(); |
| 288 | 291 | |
| 289 | 292 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| ... | ... | @@ -459,13 +462,11 @@ fn gen(self: *Self) InnerError!void { |
| 459 | 462 | // Thus we don't need to adjust the stack for the first push instruction. However, |
| 460 | 463 | // any subsequent push of values on the stack such as when preserving registers, |
| 461 | 464 | // needs to be taken into account here. |
| 462 | | var stack_adjustment: i32 = 0; |
| 465 | var stack_adjustment: u32 = 0; |
| 463 | 466 | inline for (callee_preserved_regs) |reg, i| { |
| 464 | 467 | if (self.register_manager.isRegAllocated(reg)) { |
| 465 | 468 | callee_preserved_regs_push_data |= 1 << @intCast(u5, i); |
| 466 | | if (self.target.isDarwin()) { |
| 467 | | stack_adjustment += @divExact(reg.size(), 8); |
| 468 | | } |
| 469 | stack_adjustment += @divExact(reg.size(), 8); |
| 469 | 470 | } |
| 470 | 471 | } |
| 471 | 472 | const data = self.mir_instructions.items(.data); |
| ... | ... | @@ -490,23 +491,31 @@ fn gen(self: *Self) InnerError!void { |
| 490 | 491 | if (stack_end > math.maxInt(i32) - stack_adjustment) { |
| 491 | 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 | | if (aligned_stack_end > 0 or stack_adjustment > 0) { |
| 494 | // 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. |
| 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 | 499 | self.mir_instructions.set(backpatch_stack_sub, .{ |
| 496 | 500 | .tag = .sub, |
| 497 | 501 | .ops = (Mir.Ops{ |
| 498 | 502 | .reg1 = .rsp, |
| 499 | 503 | }).encode(), |
| 500 | | .data = .{ .imm = @bitCast(u32, @intCast(i32, aligned_stack_end) + stack_adjustment) }, |
| 504 | .data = .{ .imm = imm }, |
| 501 | 505 | }); |
| 502 | 506 | self.mir_instructions.set(backpatch_stack_add, .{ |
| 503 | 507 | .tag = .add, |
| 504 | 508 | .ops = (Mir.Ops{ |
| 505 | 509 | .reg1 = .rsp, |
| 506 | 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 | 519 | } else { |
| 511 | 520 | _ = try self.addInst(.{ |
| 512 | 521 | .tag = .dbg_prologue_end, |
| ... | ... | @@ -1613,6 +1622,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1613 | 1622 | |
| 1614 | 1623 | return self.genInlineMemcpy( |
| 1615 | 1624 | @bitCast(u32, -@intCast(i32, off + abi_size)), |
| 1625 | .rbp, |
| 1616 | 1626 | registerAlias(addr_reg, @divExact(reg.size(), 8)), |
| 1617 | 1627 | count_reg.to64(), |
| 1618 | 1628 | tmp_reg.to8(), |
| ... | ... | @@ -2157,9 +2167,6 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2157 | 2167 | const arg_index = self.arg_index; |
| 2158 | 2168 | self.arg_index += 1; |
| 2159 | 2169 | |
| 2160 | | const ty = self.air.typeOfIndex(inst); |
| 2161 | | _ = ty; |
| 2162 | | |
| 2163 | 2170 | const mcv = self.args[arg_index]; |
| 2164 | 2171 | const payload = try self.addExtra(Mir.ArgDbgInfo{ |
| 2165 | 2172 | .air_inst = inst, |
| ... | ... | @@ -2173,14 +2180,68 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2173 | 2180 | if (self.liveness.isUnused(inst)) |
| 2174 | 2181 | return self.finishAirBookkeeping(); |
| 2175 | 2182 | |
| 2176 | | switch (mcv) { |
| 2177 | | .register => |reg| { |
| 2178 | | self.register_manager.getRegAssumeFree(reg.to64(), inst); |
| 2179 | | }, |
| 2180 | | else => {}, |
| 2181 | | } |
| 2183 | const dst_mcv: MCValue = blk: { |
| 2184 | switch (mcv) { |
| 2185 | .register => |reg| { |
| 2186 | self.register_manager.getRegAssumeFree(reg.to64(), inst); |
| 2187 | break :blk mcv; |
| 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 | 2247 | fn airBreakpoint(self: *Self) !void { |
| ... | ... | @@ -2201,6 +2262,64 @@ fn airFence(self: *Self) !void { |
| 2201 | 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 | 2323 | fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2205 | 2324 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 2206 | 2325 | const callee = pl_op.operand; |
| ... | ... | @@ -2217,43 +2336,58 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2217 | 2336 | var info = try self.resolveCallingConventionValues(fn_ty); |
| 2218 | 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 | 2388 | // Due to incremental compilation, how function calls are generated depends |
| 2221 | 2389 | // on linking. |
| 2222 | 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 | 2391 | if (self.air.value(callee)) |func_value| { |
| 2258 | 2392 | if (func_value.castTag(.function)) |func_payload| { |
| 2259 | 2393 | const func = func_payload.data; |
| ... | ... | @@ -2292,41 +2426,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2292 | 2426 | }); |
| 2293 | 2427 | } |
| 2294 | 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 | 2429 | if (self.air.value(callee)) |func_value| { |
| 2331 | 2430 | if (func_value.castTag(.function)) |func_payload| { |
| 2332 | 2431 | const func = func_payload.data; |
| ... | ... | @@ -2369,39 +2468,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2369 | 2468 | }); |
| 2370 | 2469 | } |
| 2371 | 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 | 2471 | if (self.air.value(callee)) |func_value| { |
| 2406 | 2472 | if (func_value.castTag(.function)) |func_payload| { |
| 2407 | 2473 | try p9.seeDecl(func_payload.data.owner_decl); |
| ... | ... | @@ -2433,6 +2499,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2433 | 2499 | } |
| 2434 | 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 | 2513 | const result: MCValue = result: { |
| 2437 | 2514 | switch (info.return_value) { |
| 2438 | 2515 | .register => |reg| { |
| ... | ... | @@ -3346,6 +3423,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3346 | 3423 | |
| 3347 | 3424 | return self.genInlineMemcpy( |
| 3348 | 3425 | @bitCast(u32, -@intCast(i32, stack_offset + abi_size)), |
| 3426 | .rbp, |
| 3349 | 3427 | addr_reg.to64(), |
| 3350 | 3428 | count_reg.to64(), |
| 3351 | 3429 | tmp_reg.to8(), |
| ... | ... | @@ -3357,6 +3435,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3357 | 3435 | fn genInlineMemcpy( |
| 3358 | 3436 | self: *Self, |
| 3359 | 3437 | stack_offset: u32, |
| 3438 | stack_reg: Register, |
| 3360 | 3439 | addr_reg: Register, |
| 3361 | 3440 | count_reg: Register, |
| 3362 | 3441 | tmp_reg: Register, |
| ... | ... | @@ -3410,7 +3489,7 @@ fn genInlineMemcpy( |
| 3410 | 3489 | _ = try self.addInst(.{ |
| 3411 | 3490 | .tag = .mov_scale_dst, |
| 3412 | 3491 | .ops = (Mir.Ops{ |
| 3413 | | .reg1 = .rbp, |
| 3492 | .reg1 = stack_reg, |
| 3414 | 3493 | .reg2 = tmp_reg.to8(), |
| 3415 | 3494 | }).encode(), |
| 3416 | 3495 | .data = .{ .imm = stack_offset }, |
| ... | ... | @@ -4140,15 +4219,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 4140 | 4219 | return result; |
| 4141 | 4220 | }, |
| 4142 | 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 | 4225 | var next_int_reg: usize = 0; |
| 4144 | | var next_stack_offset: u32 = 0; |
| 4145 | | |
| 4226 | var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator); |
| 4227 | defer by_reg.deinit(); |
| 4146 | 4228 | for (param_types) |ty, i| { |
| 4147 | | if (!ty.hasCodeGenBits()) { |
| 4148 | | assert(cc != .C); |
| 4149 | | result.args[i] = .{ .none = {} }; |
| 4150 | | continue; |
| 4151 | | } |
| 4229 | if (!ty.hasCodeGenBits()) continue; |
| 4152 | 4230 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 4153 | 4231 | const pass_in_reg = switch (ty.zigTypeTag()) { |
| 4154 | 4232 | .Bool => true, |
| ... | ... | @@ -4158,17 +4236,27 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 4158 | 4236 | else => false, |
| 4159 | 4237 | }; |
| 4160 | 4238 | if (pass_in_reg) { |
| 4161 | | if (next_int_reg >= c_abi_int_param_regs.len) { |
| 4162 | | result.args[i] = .{ .stack_offset = next_stack_offset }; |
| 4163 | | next_stack_offset += param_size; |
| 4164 | | } else { |
| 4165 | | const aliased_reg = registerAlias( |
| 4166 | | c_abi_int_param_regs[next_int_reg], |
| 4167 | | param_size, |
| 4168 | | ); |
| 4169 | | result.args[i] = .{ .register = aliased_reg }; |
| 4170 | | next_int_reg += 1; |
| 4171 | | } |
| 4239 | if (next_int_reg >= c_abi_int_param_regs.len) break; |
| 4240 | try by_reg.putNoClobber(i, next_int_reg); |
| 4241 | next_int_reg += 1; |
| 4242 | } |
| 4243 | } |
| 4244 | |
| 4245 | var next_stack_offset: u32 = 0; |
| 4246 | var count: usize = param_types.len; |
| 4247 | while (count > 0) : (count -= 1) { |
| 4248 | const i = count - 1; |
| 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 | 4260 | } else { |
| 4173 | 4261 | // For simplicity of codegen, slices and other types are always pushed onto the stack. |
| 4174 | 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 | 4267 | next_stack_offset += param_size; |
| 4180 | 4268 | } |
| 4181 | 4269 | } |
| 4270 | |
| 4182 | 4271 | result.stack_byte_count = next_stack_offset; |
| 4183 | 4272 | result.stack_align = 16; |
| 4184 | 4273 | }, |