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,
6161/// which is a relative jump, based on the address following the reloc.
6262exitlude_jump_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
6363
64stack_args_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
65
6466/// Whenever there is a runtime branch, we push a Branch onto this stack,
6567/// and pop it off when the runtime branch joins. This provides an "overlay"
6668/// of the table of mappings from instructions to `MCValue` from within the branch.
......@@ -182,7 +184,7 @@ const Branch = struct {
182184
183185const StackAllocation = struct {
184186 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.*)
186188 size: u32,
187189};
188190
......@@ -284,6 +286,7 @@ pub fn generate(
284286 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
285287 defer function.mir_instructions.deinit(bin_file.allocator);
286288 defer function.mir_extra.deinit(bin_file.allocator);
289 defer function.stack_args_relocs.deinit(bin_file.allocator);
287290 defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit();
288291
289292 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
......@@ -459,13 +462,11 @@ fn gen(self: *Self) InnerError!void {
459462 // Thus we don't need to adjust the stack for the first push instruction. However,
460463 // any subsequent push of values on the stack such as when preserving registers,
461464 // needs to be taken into account here.
462 var stack_adjustment: i32 = 0;
465 var stack_adjustment: u32 = 0;
463466 inline for (callee_preserved_regs) |reg, i| {
464467 if (self.register_manager.isRegAllocated(reg)) {
465468 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);
469470 }
470471 }
471472 const data = self.mir_instructions.items(.data);
......@@ -490,23 +491,33 @@ fn gen(self: *Self) InnerError!void {
490491 if (stack_end > math.maxInt(i32) - stack_adjustment) {
491492 return self.failSymbol("too much stack used in call parameters", .{});
492493 }
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;
495499 self.mir_instructions.set(backpatch_stack_sub, .{
496500 .tag = .sub,
497501 .ops = (Mir.Ops{
498502 .reg1 = .rsp,
499503 }).encode(),
500 .data = .{ .imm = @bitCast(u32, @intCast(i32, aligned_stack_end) + stack_adjustment) },
504 .data = .{ .imm = imm },
501505 });
502506 self.mir_instructions.set(backpatch_stack_add, .{
503507 .tag = .add,
504508 .ops = (Mir.Ops{
505509 .reg1 = .rsp,
506510 }).encode(),
507 .data = .{ .imm = @bitCast(u32, @intCast(i32, aligned_stack_end) + stack_adjustment) },
511 .data = .{ .imm = imm },
508512 });
509513 }
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 }
510521 } else {
511522 _ = try self.addInst(.{
512523 .tag = .dbg_prologue_end,
......@@ -1613,6 +1624,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
16131624
16141625 return self.genInlineMemcpy(
16151626 @bitCast(u32, -@intCast(i32, off + abi_size)),
1627 .rbp,
16161628 registerAlias(addr_reg, @divExact(reg.size(), 8)),
16171629 count_reg.to64(),
16181630 tmp_reg.to8(),
......@@ -2157,9 +2169,6 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
21572169 const arg_index = self.arg_index;
21582170 self.arg_index += 1;
21592171
2160 const ty = self.air.typeOfIndex(inst);
2161 _ = ty;
2162
21632172 const mcv = self.args[arg_index];
21642173 const payload = try self.addExtra(Mir.ArgDbgInfo{
21652174 .air_inst = inst,
......@@ -2173,14 +2182,68 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
21732182 if (self.liveness.isUnused(inst))
21742183 return self.finishAirBookkeeping();
21752184
2176 switch (mcv) {
2177 .register => |reg| {
2178 self.register_manager.getRegAssumeFree(reg.to64(), inst);
2179 },
2180 else => {},
2181 }
2185 const dst_mcv: MCValue = blk: {
2186 switch (mcv) {
2187 .register => |reg| {
2188 self.register_manager.getRegAssumeFree(reg.to64(), inst);
2189 break :blk mcv;
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 });
21842247}
21852248
21862249fn airBreakpoint(self: *Self) !void {
......@@ -2201,6 +2264,64 @@ fn airFence(self: *Self) !void {
22012264 //return self.finishAirBookkeeping();
22022265}
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
22042325fn airCall(self: *Self, inst: Air.Inst.Index) !void {
22052326 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
22062327 const callee = pl_op.operand;
......@@ -2217,43 +2338,58 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
22172338 var info = try self.resolveCallingConventionValues(fn_ty);
22182339 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
22202390 // Due to incremental compilation, how function calls are generated depends
22212391 // on linking.
22222392 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
22572393 if (self.air.value(callee)) |func_value| {
22582394 if (func_value.castTag(.function)) |func_payload| {
22592395 const func = func_payload.data;
......@@ -2292,41 +2428,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
22922428 });
22932429 }
22942430 } 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
23302431 if (self.air.value(callee)) |func_value| {
23312432 if (func_value.castTag(.function)) |func_payload| {
23322433 const func = func_payload.data;
......@@ -2369,39 +2470,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
23692470 });
23702471 }
23712472 } 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 }
24052473 if (self.air.value(callee)) |func_value| {
24062474 if (func_value.castTag(.function)) |func_payload| {
24072475 try p9.seeDecl(func_payload.data.owner_decl);
......@@ -2433,6 +2501,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
24332501 }
24342502 } 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
24362515 const result: MCValue = result: {
24372516 switch (info.return_value) {
24382517 .register => |reg| {
......@@ -3346,6 +3425,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
33463425
33473426 return self.genInlineMemcpy(
33483427 @bitCast(u32, -@intCast(i32, stack_offset + abi_size)),
3428 .rbp,
33493429 addr_reg.to64(),
33503430 count_reg.to64(),
33513431 tmp_reg.to8(),
......@@ -3357,6 +3437,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
33573437fn genInlineMemcpy(
33583438 self: *Self,
33593439 stack_offset: u32,
3440 stack_reg: Register,
33603441 addr_reg: Register,
33613442 count_reg: Register,
33623443 tmp_reg: Register,
......@@ -3410,7 +3491,7 @@ fn genInlineMemcpy(
34103491 _ = try self.addInst(.{
34113492 .tag = .mov_scale_dst,
34123493 .ops = (Mir.Ops{
3413 .reg1 = .rbp,
3494 .reg1 = stack_reg,
34143495 .reg2 = tmp_reg.to8(),
34153496 }).encode(),
34163497 .data = .{ .imm = stack_offset },
......@@ -4140,15 +4221,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
41404221 return result;
41414222 },
41424223 .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.
41434227 var next_int_reg: usize = 0;
4144 var next_stack_offset: u32 = 0;
4145
4228 var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator);
4229 defer by_reg.deinit();
41464230 for (param_types) |ty, i| {
4147 if (!ty.hasCodeGenBits()) {
4148 assert(cc != .C);
4149 result.args[i] = .{ .none = {} };
4150 continue;
4151 }
4231 if (!ty.hasCodeGenBits()) continue;
41524232 const param_size = @intCast(u32, ty.abiSize(self.target.*));
41534233 const pass_in_reg = switch (ty.zigTypeTag()) {
41544234 .Bool => true,
......@@ -4158,17 +4238,27 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
41584238 else => false,
41594239 };
41604240 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 }
4241 if (next_int_reg >= c_abi_int_param_regs.len) break;
4242 try by_reg.putNoClobber(i, next_int_reg);
4243 next_int_reg += 1;
4244 }
4245 }
4246
4247 var next_stack_offset: u32 = 0;
4248 var count: usize = param_types.len;
4249 while (count > 0) : (count -= 1) {
4250 const i = count - 1;
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;
41724262 } else {
41734263 // For simplicity of codegen, slices and other types are always pushed onto the stack.
41744264 // TODO: look into optimizing this by passing things as registers sometimes,
......@@ -4179,6 +4269,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
41794269 next_stack_offset += param_size;
41804270 }
41814271 }
4272
41824273 result.stack_byte_count = next_stack_offset;
41834274 result.stack_align = 16;
41844275 },
test/behavior/align.zig+2-2
......@@ -119,7 +119,7 @@ fn fnWithAlignedStack() i32 {
119119}
120120
121121test "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
124124 const a: u32 align(4) = 3;
125125 const b: u32 align(8) = 4;
......@@ -130,7 +130,7 @@ fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 {
130130}
131131
132132test "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
135135 try testBytesAlign(0x33);
136136}
test/behavior/array.zig+1-1
......@@ -20,7 +20,7 @@ test "array to slice" {
2020}
2121
2222test "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
2525 var array: [5]u32 = undefined;
2626