authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-20 00:45:34+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-01-20 00:45:34+01:00
log538c9e7bafd859148352e76c1d8053e1ba426a06
treea895ca5e49ef116d1ce92dc8bc7404ac5af69c07
parent4a401b20e46d779f41dae3406160265eef2d6e5b
parent7c831cc2663ba4e0ce2cf29ca4a3bd9a4a074f5c
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10635 from ziglang/stage2-x86_64-params-stack

stage2: fix passing arguments on the stack on x86_64

3 files changed, 234 insertions(+), 143 deletions(-)

src/arch/x86_64/CodeGen.zig+231-140
...@@ -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.
62exitlude_jump_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},62exitlude_jump_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
6363
64stack_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 {
182184
183const StackAllocation = struct {185const 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};
188190
...@@ -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();
288291
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,33 @@ fn gen(self: *Self) InnerError!void {...@@ -490,23 +491,33 @@ 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 // 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 }
510 } else {521 } else {
511 _ = try self.addInst(.{522 _ = try self.addInst(.{
512 .tag = .dbg_prologue_end,523 .tag = .dbg_prologue_end,
...@@ -1613,6 +1624,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -1613,6 +1624,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
16131624
1614 return self.genInlineMemcpy(1625 return self.genInlineMemcpy(
1615 @bitCast(u32, -@intCast(i32, off + abi_size)),1626 @bitCast(u32, -@intCast(i32, off + abi_size)),
1627 .rbp,
1616 registerAlias(addr_reg, @divExact(reg.size(), 8)),1628 registerAlias(addr_reg, @divExact(reg.size(), 8)),
1617 count_reg.to64(),1629 count_reg.to64(),
1618 tmp_reg.to8(),1630 tmp_reg.to8(),
...@@ -2157,9 +2169,6 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -2157,9 +2169,6 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
2157 const arg_index = self.arg_index;2169 const arg_index = self.arg_index;
2158 self.arg_index += 1;2170 self.arg_index += 1;
21592171
2160 const ty = self.air.typeOfIndex(inst);
2161 _ = ty;
2162
2163 const mcv = self.args[arg_index];2172 const mcv = self.args[arg_index];
2164 const payload = try self.addExtra(Mir.ArgDbgInfo{2173 const payload = try self.addExtra(Mir.ArgDbgInfo{
2165 .air_inst = inst,2174 .air_inst = inst,
...@@ -2173,14 +2182,68 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -2173,14 +2182,68 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
2173 if (self.liveness.isUnused(inst))2182 if (self.liveness.isUnused(inst))
2174 return self.finishAirBookkeeping();2183 return self.finishAirBookkeeping();
21752184
2176 switch (mcv) {2185 const dst_mcv: MCValue = blk: {
2177 .register => |reg| {2186 switch (mcv) {
2178 self.register_manager.getRegAssumeFree(reg.to64(), inst);2187 .register => |reg| {
2179 },2188 self.register_manager.getRegAssumeFree(reg.to64(), inst);
2180 else => {},2189 break :blk mcv;
2181 }2190 },
2191 .stack_offset => |off| {
2192 const ty = self.air.typeOfIndex(inst);
2193 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;
2241 },
2242 else => unreachable,
2243 }
2244 };
21822245
2183 return self.finishAir(inst, mcv, .{ .none, .none, .none });2246 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none });
2184}2247}
21852248
2186fn airBreakpoint(self: *Self) !void {2249fn airBreakpoint(self: *Self) !void {
...@@ -2201,6 +2264,64 @@ fn airFence(self: *Self) !void {...@@ -2201,6 +2264,64 @@ fn airFence(self: *Self) !void {
2201 //return self.finishAirBookkeeping();2264 //return self.finishAirBookkeeping();
2202}2265}
22032266
2267fn 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
2204fn airCall(self: *Self, inst: Air.Inst.Index) !void {2325fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2205 const pl_op = self.air.instructions.items(.data)[inst].pl_op;2326 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2206 const callee = pl_op.operand;2327 const callee = pl_op.operand;
...@@ -2217,43 +2338,58 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -2217,43 +2338,58 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2217 var info = try self.resolveCallingConventionValues(fn_ty);2338 var info = try self.resolveCallingConventionValues(fn_ty);
2218 defer info.deinit(self);2339 defer info.deinit(self);
22192340
2341 var count: usize = info.args.len;
2342 var stack_adjustment: u32 = 0;
2343 while (count > 0) : (count -= 1) {
2344 const arg_i = count - 1;
2345 const mc_arg = info.args[arg_i];
2346 const arg = args[arg_i];
2347 const arg_ty = self.air.typeOf(arg);
2348 const arg_mcv = try self.resolveInst(args[arg_i]);
2349 // Here we do not use setRegOrMem even though the logic is similar, because
2350 // the function call will move the stack pointer, so the offsets are different.
2351 switch (mc_arg) {
2352 .none => continue,
2353 .register => |reg| {
2354 try self.register_manager.getReg(reg, null);
2355 try self.genSetReg(arg_ty, reg, arg_mcv);
2356 },
2357 .stack_offset => |off| {
2358 const abi_size = arg_ty.abiSize(self.target.*);
2359 try self.genSetStackArg(arg_ty, off, arg_mcv);
2360 stack_adjustment += @intCast(u32, abi_size);
2361 },
2362 .ptr_stack_offset => {
2363 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2364 },
2365 .ptr_embedded_in_code => {
2366 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2367 },
2368 .undef => unreachable,
2369 .immediate => unreachable,
2370 .unreach => unreachable,
2371 .dead => unreachable,
2372 .embedded_in_code => unreachable,
2373 .memory => unreachable,
2374 .compare_flags_signed => unreachable,
2375 .compare_flags_unsigned => unreachable,
2376 }
2377 }
2378
2379 if (stack_adjustment > 0) {
2380 // Adjust the stack
2381 _ = try self.addInst(.{
2382 .tag = .sub,
2383 .ops = (Mir.Ops{
2384 .reg1 = .rsp,
2385 }).encode(),
2386 .data = .{ .imm = stack_adjustment },
2387 });
2388 }
2389
2220 // Due to incremental compilation, how function calls are generated depends2390 // Due to incremental compilation, how function calls are generated depends
2221 // on linking.2391 // on linking.
2222 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {2392 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| {2393 if (self.air.value(callee)) |func_value| {
2258 if (func_value.castTag(.function)) |func_payload| {2394 if (func_value.castTag(.function)) |func_payload| {
2259 const func = func_payload.data;2395 const func = func_payload.data;
...@@ -2292,41 +2428,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -2292,41 +2428,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2292 });2428 });
2293 }2429 }
2294 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {2430 } 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| {2431 if (self.air.value(callee)) |func_value| {
2331 if (func_value.castTag(.function)) |func_payload| {2432 if (func_value.castTag(.function)) |func_payload| {
2332 const func = func_payload.data;2433 const func = func_payload.data;
...@@ -2369,39 +2470,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -2369,39 +2470,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2369 });2470 });
2370 }2471 }
2371 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {2472 } 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| {2473 if (self.air.value(callee)) |func_value| {
2406 if (func_value.castTag(.function)) |func_payload| {2474 if (func_value.castTag(.function)) |func_payload| {
2407 try p9.seeDecl(func_payload.data.owner_decl);2475 try p9.seeDecl(func_payload.data.owner_decl);
...@@ -2433,6 +2501,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -2433,6 +2501,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2433 }2501 }
2434 } else unreachable;2502 } else unreachable;
24352503
2504 if (stack_adjustment > 0) {
2505 // Readjust the stack
2506 _ = try self.addInst(.{
2507 .tag = .add,
2508 .ops = (Mir.Ops{
2509 .reg1 = .rsp,
2510 }).encode(),
2511 .data = .{ .imm = stack_adjustment },
2512 });
2513 }
2514
2436 const result: MCValue = result: {2515 const result: MCValue = result: {
2437 switch (info.return_value) {2516 switch (info.return_value) {
2438 .register => |reg| {2517 .register => |reg| {
...@@ -3346,6 +3425,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -3346,6 +3425,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
33463425
3347 return self.genInlineMemcpy(3426 return self.genInlineMemcpy(
3348 @bitCast(u32, -@intCast(i32, stack_offset + abi_size)),3427 @bitCast(u32, -@intCast(i32, stack_offset + abi_size)),
3428 .rbp,
3349 addr_reg.to64(),3429 addr_reg.to64(),
3350 count_reg.to64(),3430 count_reg.to64(),
3351 tmp_reg.to8(),3431 tmp_reg.to8(),
...@@ -3357,6 +3437,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -3357,6 +3437,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
3357fn genInlineMemcpy(3437fn genInlineMemcpy(
3358 self: *Self,3438 self: *Self,
3359 stack_offset: u32,3439 stack_offset: u32,
3440 stack_reg: Register,
3360 addr_reg: Register,3441 addr_reg: Register,
3361 count_reg: Register,3442 count_reg: Register,
3362 tmp_reg: Register,3443 tmp_reg: Register,
...@@ -3410,7 +3491,7 @@ fn genInlineMemcpy(...@@ -3410,7 +3491,7 @@ fn genInlineMemcpy(
3410 _ = try self.addInst(.{3491 _ = try self.addInst(.{
3411 .tag = .mov_scale_dst,3492 .tag = .mov_scale_dst,
3412 .ops = (Mir.Ops{3493 .ops = (Mir.Ops{
3413 .reg1 = .rbp,3494 .reg1 = stack_reg,
3414 .reg2 = tmp_reg.to8(),3495 .reg2 = tmp_reg.to8(),
3415 }).encode(),3496 }).encode(),
3416 .data = .{ .imm = stack_offset },3497 .data = .{ .imm = stack_offset },
...@@ -4140,15 +4221,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -4140,15 +4221,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
4140 return result;4221 return result;
4141 },4222 },
4142 .Unspecified, .C => {4223 .Unspecified, .C => {
4224 // First, split into args that can be passed via registers.
4225 // This will make it easier to then push the rest of args in reverse
4226 // order on the stack.
4143 var next_int_reg: usize = 0;4227 var next_int_reg: usize = 0;
4144 var next_stack_offset: u32 = 0;4228 var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator);
41454229 defer by_reg.deinit();
4146 for (param_types) |ty, i| {4230 for (param_types) |ty, i| {
4147 if (!ty.hasCodeGenBits()) {4231 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.*));4232 const param_size = @intCast(u32, ty.abiSize(self.target.*));
4153 const pass_in_reg = switch (ty.zigTypeTag()) {4233 const pass_in_reg = switch (ty.zigTypeTag()) {
4154 .Bool => true,4234 .Bool => true,
...@@ -4158,17 +4238,27 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -4158,17 +4238,27 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
4158 else => false,4238 else => false,
4159 };4239 };
4160 if (pass_in_reg) {4240 if (pass_in_reg) {
4161 if (next_int_reg >= c_abi_int_param_regs.len) {4241 if (next_int_reg >= c_abi_int_param_regs.len) break;
4162 result.args[i] = .{ .stack_offset = next_stack_offset };4242 try by_reg.putNoClobber(i, next_int_reg);
4163 next_stack_offset += param_size;4243 next_int_reg += 1;
4164 } else {4244 }
4165 const aliased_reg = registerAlias(4245 }
4166 c_abi_int_param_regs[next_int_reg],4246
4167 param_size,4247 var next_stack_offset: u32 = 0;
4168 );4248 var count: usize = param_types.len;
4169 result.args[i] = .{ .register = aliased_reg };4249 while (count > 0) : (count -= 1) {
4170 next_int_reg += 1;4250 const i = count - 1;
4171 }4251 const ty = param_types[i];
4252 if (!ty.hasCodeGenBits()) {
4253 assert(cc != .C);
4254 result.args[i] = .{ .none = {} };
4255 continue;
4256 }
4257 const param_size = @intCast(u32, ty.abiSize(self.target.*));
4258 if (by_reg.get(i)) |int_reg| {
4259 const aliased_reg = registerAlias(c_abi_int_param_regs[int_reg], param_size);
4260 result.args[i] = .{ .register = aliased_reg };
4261 next_int_reg += 1;
4172 } else {4262 } else {
4173 // For simplicity of codegen, slices and other types are always pushed onto the stack.4263 // 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,4264 // TODO: look into optimizing this by passing things as registers sometimes,
...@@ -4179,6 +4269,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -4179,6 +4269,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
4179 next_stack_offset += param_size;4269 next_stack_offset += param_size;
4180 }4270 }
4181 }4271 }
4272
4182 result.stack_byte_count = next_stack_offset;4273 result.stack_byte_count = next_stack_offset;
4183 result.stack_align = 16;4274 result.stack_align = 16;
4184 },4275 },
test/behavior/align.zig+2-2
...@@ -119,7 +119,7 @@ fn fnWithAlignedStack() i32 {...@@ -119,7 +119,7 @@ fn fnWithAlignedStack() i32 {
119}119}
120120
121test "implicitly decreasing slice alignment" {121test "implicitly decreasing slice alignment" {
122 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;122 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
123123
124 const a: u32 align(4) = 3;124 const a: u32 align(4) = 3;
125 const b: u32 align(8) = 4;125 const b: u32 align(8) = 4;
...@@ -130,7 +130,7 @@ fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 {...@@ -130,7 +130,7 @@ fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 {
130}130}
131131
132test "specifying alignment allows pointer cast" {132test "specifying alignment allows pointer cast" {
133 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;133 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
134134
135 try testBytesAlign(0x33);135 try testBytesAlign(0x33);
136}136}
test/behavior/array.zig+1-1
...@@ -20,7 +20,7 @@ test "array to slice" {...@@ -20,7 +20,7 @@ test "array to slice" {
20}20}
2121
22test "arrays" {22test "arrays" {
23 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;23 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
2424
25 var array: [5]u32 = undefined;25 var array: [5]u32 = undefined;
2626