authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-18 21:41:33+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-19 20:21:00+01:00
logdf6c0067b26ca1b6798612631b26b167e9fe287a
treed91929d74928788a0db8f0626b9772ea3a5edccb
parent7c6981e0c0f2412c49457a900e5a6a4db96be2e8

stage2: fix passing arguments on the stack

* push the arguments in reverse order * add logic for pushing args of any abi size to stack - very similar to `genSetStack` however, uses `.rsp` as the base register * increment and decrement `.rsp` if we called a function with args on the stack in `airCall` * add logic for recovering args from the caller's stack in the callee

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

src/arch/x86_64/CodeGen.zig+229-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,31 @@ 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 // +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 }
510519 } else {
511520 _ = try self.addInst(.{
512521 .tag = .dbg_prologue_end,
......@@ -1613,6 +1622,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
16131622
16141623 return self.genInlineMemcpy(
16151624 @bitCast(u32, -@intCast(i32, off + abi_size)),
1625 .rbp,
16161626 registerAlias(addr_reg, @divExact(reg.size(), 8)),
16171627 count_reg.to64(),
16181628 tmp_reg.to8(),
......@@ -2157,9 +2167,6 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
21572167 const arg_index = self.arg_index;
21582168 self.arg_index += 1;
21592169
2160 const ty = self.air.typeOfIndex(inst);
2161 _ = ty;
2162
21632170 const mcv = self.args[arg_index];
21642171 const payload = try self.addExtra(Mir.ArgDbgInfo{
21652172 .air_inst = inst,
......@@ -2173,14 +2180,68 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
21732180 if (self.liveness.isUnused(inst))
21742181 return self.finishAirBookkeeping();
21752182
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 };
21822243
2183 return self.finishAir(inst, mcv, .{ .none, .none, .none });
2244 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none });
21842245}
21852246
21862247fn airBreakpoint(self: *Self) !void {
......@@ -2201,6 +2262,64 @@ fn airFence(self: *Self) !void {
22012262 //return self.finishAirBookkeeping();
22022263}
22032264
2265fn 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
22042323fn airCall(self: *Self, inst: Air.Inst.Index) !void {
22052324 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
22062325 const callee = pl_op.operand;
......@@ -2217,43 +2336,58 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
22172336 var info = try self.resolveCallingConventionValues(fn_ty);
22182337 defer info.deinit(self);
22192338
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
22202388 // Due to incremental compilation, how function calls are generated depends
22212389 // on linking.
22222390 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
22572391 if (self.air.value(callee)) |func_value| {
22582392 if (func_value.castTag(.function)) |func_payload| {
22592393 const func = func_payload.data;
......@@ -2292,41 +2426,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
22922426 });
22932427 }
22942428 } 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
23302429 if (self.air.value(callee)) |func_value| {
23312430 if (func_value.castTag(.function)) |func_payload| {
23322431 const func = func_payload.data;
......@@ -2369,39 +2468,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
23692468 });
23702469 }
23712470 } 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 }
24052471 if (self.air.value(callee)) |func_value| {
24062472 if (func_value.castTag(.function)) |func_payload| {
24072473 try p9.seeDecl(func_payload.data.owner_decl);
......@@ -2433,6 +2499,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
24332499 }
24342500 } else unreachable;
24352501
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
24362513 const result: MCValue = result: {
24372514 switch (info.return_value) {
24382515 .register => |reg| {
......@@ -3346,6 +3423,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
33463423
33473424 return self.genInlineMemcpy(
33483425 @bitCast(u32, -@intCast(i32, stack_offset + abi_size)),
3426 .rbp,
33493427 addr_reg.to64(),
33503428 count_reg.to64(),
33513429 tmp_reg.to8(),
......@@ -3357,6 +3435,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
33573435fn genInlineMemcpy(
33583436 self: *Self,
33593437 stack_offset: u32,
3438 stack_reg: Register,
33603439 addr_reg: Register,
33613440 count_reg: Register,
33623441 tmp_reg: Register,
......@@ -3410,7 +3489,7 @@ fn genInlineMemcpy(
34103489 _ = try self.addInst(.{
34113490 .tag = .mov_scale_dst,
34123491 .ops = (Mir.Ops{
3413 .reg1 = .rbp,
3492 .reg1 = stack_reg,
34143493 .reg2 = tmp_reg.to8(),
34153494 }).encode(),
34163495 .data = .{ .imm = stack_offset },
......@@ -4140,15 +4219,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
41404219 return result;
41414220 },
41424221 .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.
41434225 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();
41464228 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;
41524230 const param_size = @intCast(u32, ty.abiSize(self.target.*));
41534231 const pass_in_reg = switch (ty.zigTypeTag()) {
41544232 .Bool => true,
......@@ -4158,17 +4236,27 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
41584236 else => false,
41594237 };
41604238 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;
41724260 } else {
41734261 // For simplicity of codegen, slices and other types are always pushed onto the stack.
41744262 // TODO: look into optimizing this by passing things as registers sometimes,
......@@ -4179,6 +4267,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
41794267 next_stack_offset += param_size;
41804268 }
41814269 }
4270
41824271 result.stack_byte_count = next_stack_offset;
41834272 result.stack_align = 16;
41844273 },
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