authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-31 20:31:51+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-12-31 20:31:51+01:00
log7c1e17a840f8ff61372f04c5a9fc6282ad22e2c9
tree2ab32f42f282c04492c4c06611dbac7dada9d577
parente7ac05e882fa4290af4a41e9cae63105bcacb283
parentc7f774803a3ecbc8d0641adde8ef0528f4a8bb8c
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10470 from ziglang/stage2-x86_64-null-non-null

stage2: impl isNull/isNonNull, genSetReg for ptr_stack_offset, loading-storing via pointer (in register)

2 files changed, 188 insertions(+), 57 deletions(-)

src/arch/x86_64/CodeGen.zig+115-37
...@@ -1143,10 +1143,24 @@ fn airShr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1143,10 +1143,24 @@ fn airShr(self: *Self, inst: Air.Inst.Index) !void {
11431143
1144fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {1144fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
1145 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1145 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1146 const result: MCValue = if (self.liveness.isUnused(inst))1146 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1147 .dead1147 const operand = try self.resolveInst(ty_op.operand);
1148 else1148 if (self.wantSafety()) {
1149 return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});1149 // TODO check for null
1150 return self.fail("TODO implement check for null in .optional_payload", .{});
1151 }
1152 const dst_mcv: MCValue = blk: {
1153 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) {
1154 break :blk operand;
1155 } else {
1156 break :blk try self.allocRegOrMem(inst, true);
1157 }
1158 };
1159 const ty = self.air.typeOf(ty_op.operand);
1160 var buf: Type.Payload.ElemType = undefined;
1161 try self.load(dst_mcv, operand, ty.optionalChild(&buf));
1162 break :result dst_mcv;
1163 };
1150 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1164 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1151}1165}
11521166
...@@ -1408,16 +1422,16 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -1408,16 +1422,16 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
1408 .compare_flags_unsigned => unreachable,1422 .compare_flags_unsigned => unreachable,
1409 .compare_flags_signed => unreachable,1423 .compare_flags_signed => unreachable,
1410 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),1424 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
1411 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),1425 .ptr_stack_offset => |off| {
1426 try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off });
1427 },
1412 .ptr_embedded_in_code => |off| {1428 .ptr_embedded_in_code => |off| {
1413 try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off });1429 try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off });
1414 },1430 },
1415 .embedded_in_code => {1431 .embedded_in_code => {
1416 return self.fail("TODO implement loading from MCValue.embedded_in_code", .{});1432 return self.fail("TODO implement loading from MCValue.embedded_in_code", .{});
1417 },1433 },
1418 .register => {1434 .register => |reg| try self.setRegOrMem(elem_ty, dst_mcv, .{ .register = reg }),
1419 return self.fail("TODO implement loading from MCValue.register for {}", .{self.target.cpu.arch});
1420 },
1421 .memory => |addr| {1435 .memory => |addr| {
1422 const reg = try self.register_manager.allocReg(null, &.{});1436 const reg = try self.register_manager.allocReg(null, &.{});
1423 try self.genSetReg(ptr_ty, reg, .{ .memory = addr });1437 try self.genSetReg(ptr_ty, reg, .{ .memory = addr });
...@@ -1479,8 +1493,8 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {...@@ -1479,8 +1493,8 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {
1479 .embedded_in_code => {1493 .embedded_in_code => {
1480 return self.fail("TODO implement storing to MCValue.embedded_in_code", .{});1494 return self.fail("TODO implement storing to MCValue.embedded_in_code", .{});
1481 },1495 },
1482 .register => {1496 .register => |reg| {
1483 return self.fail("TODO implement storing to MCValue.register", .{});1497 try self.genSetPtrReg(elem_ty, reg, value);
1484 },1498 },
1485 .memory => {1499 .memory => {
1486 return self.fail("TODO implement storing to MCValue.memory", .{});1500 return self.fail("TODO implement storing to MCValue.memory", .{});
...@@ -2371,31 +2385,30 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2371,31 +2385,30 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
2371 return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });2385 return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
2372}2386}
23732387
2374fn isNull(self: *Self, operand: MCValue) !MCValue {2388fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
2375 _ = operand;2389 try self.genBinMathOpMir(.cmp, ty, operand, MCValue{ .immediate = 0 });
2376 // Here you can specialize this instruction if it makes sense to, otherwise the default2390 return MCValue{ .compare_flags_unsigned = .eq };
2377 // will call isNonNull and invert the result.
2378 return self.fail("TODO call isNonNull and invert the result", .{});
2379}2391}
23802392
2381fn isNonNull(self: *Self, operand: MCValue) !MCValue {2393fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
2382 _ = operand;2394 const is_null_res = try self.isNull(ty, operand);
2383 // Here you can specialize this instruction if it makes sense to, otherwise the default2395 assert(is_null_res.compare_flags_unsigned == .eq);
2384 // will call isNull and invert the result.2396 return MCValue{ .compare_flags_unsigned = .neq };
2385 return self.fail("TODO call isNull and invert the result", .{});
2386}2397}
23872398
2388fn isErr(self: *Self, operand: MCValue) !MCValue {2399fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
2400 _ = ty;
2389 _ = operand;2401 _ = operand;
2390 // Here you can specialize this instruction if it makes sense to, otherwise the default2402 // Here you can specialize this instruction if it makes sense to, otherwise the default
2391 // will call isNonNull and invert the result.2403 // will call isNonErr and invert the result.
2392 return self.fail("TODO call isNonErr and invert the result", .{});2404 return self.fail("TODO call isNonErr and invert the result", .{});
2393}2405}
23942406
2395fn isNonErr(self: *Self, operand: MCValue) !MCValue {2407fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
2408 _ = ty;
2396 _ = operand;2409 _ = operand;
2397 // Here you can specialize this instruction if it makes sense to, otherwise the default2410 // Here you can specialize this instruction if it makes sense to, otherwise the default
2398 // will call isNull and invert the result.2411 // will call isErr and invert the result.
2399 return self.fail("TODO call isErr and invert the result", .{});2412 return self.fail("TODO call isErr and invert the result", .{});
2400}2413}
24012414
...@@ -2403,7 +2416,8 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {...@@ -2403,7 +2416,8 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
2403 const un_op = self.air.instructions.items(.data)[inst].un_op;2416 const un_op = self.air.instructions.items(.data)[inst].un_op;
2404 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2417 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2405 const operand = try self.resolveInst(un_op);2418 const operand = try self.resolveInst(un_op);
2406 break :result try self.isNull(operand);2419 const ty = self.air.typeOf(un_op);
2420 break :result try self.isNull(ty, operand);
2407 };2421 };
2408 return self.finishAir(inst, result, .{ un_op, .none, .none });2422 return self.finishAir(inst, result, .{ un_op, .none, .none });
2409}2423}
...@@ -2420,8 +2434,9 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2420,8 +2434,9 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
2420 break :blk try self.allocRegOrMem(inst, true);2434 break :blk try self.allocRegOrMem(inst, true);
2421 }2435 }
2422 };2436 };
2423 try self.load(operand, operand_ptr, self.air.typeOf(un_op));2437 const ptr_ty = self.air.typeOf(un_op);
2424 break :result try self.isNull(operand);2438 try self.load(operand, operand_ptr, ptr_ty);
2439 break :result try self.isNull(ptr_ty.elemType(), operand);
2425 };2440 };
2426 return self.finishAir(inst, result, .{ un_op, .none, .none });2441 return self.finishAir(inst, result, .{ un_op, .none, .none });
2427}2442}
...@@ -2430,7 +2445,8 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {...@@ -2430,7 +2445,8 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
2430 const un_op = self.air.instructions.items(.data)[inst].un_op;2445 const un_op = self.air.instructions.items(.data)[inst].un_op;
2431 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2446 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2432 const operand = try self.resolveInst(un_op);2447 const operand = try self.resolveInst(un_op);
2433 break :result try self.isNonNull(operand);2448 const ty = self.air.typeOf(un_op);
2449 break :result try self.isNonNull(ty, operand);
2434 };2450 };
2435 return self.finishAir(inst, result, .{ un_op, .none, .none });2451 return self.finishAir(inst, result, .{ un_op, .none, .none });
2436}2452}
...@@ -2447,8 +2463,9 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2447,8 +2463,9 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
2447 break :blk try self.allocRegOrMem(inst, true);2463 break :blk try self.allocRegOrMem(inst, true);
2448 }2464 }
2449 };2465 };
2450 try self.load(operand, operand_ptr, self.air.typeOf(un_op));2466 const ptr_ty = self.air.typeOf(un_op);
2451 break :result try self.isNonNull(operand);2467 try self.load(operand, operand_ptr, ptr_ty);
2468 break :result try self.isNonNull(ptr_ty.elemType(), operand);
2452 };2469 };
2453 return self.finishAir(inst, result, .{ un_op, .none, .none });2470 return self.finishAir(inst, result, .{ un_op, .none, .none });
2454}2471}
...@@ -2457,7 +2474,8 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2457,7 +2474,8 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
2457 const un_op = self.air.instructions.items(.data)[inst].un_op;2474 const un_op = self.air.instructions.items(.data)[inst].un_op;
2458 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2475 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2459 const operand = try self.resolveInst(un_op);2476 const operand = try self.resolveInst(un_op);
2460 break :result try self.isErr(operand);2477 const ty = self.air.typeOf(un_op);
2478 break :result try self.isErr(ty, operand);
2461 };2479 };
2462 return self.finishAir(inst, result, .{ un_op, .none, .none });2480 return self.finishAir(inst, result, .{ un_op, .none, .none });
2463}2481}
...@@ -2474,8 +2492,9 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2474,8 +2492,9 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
2474 break :blk try self.allocRegOrMem(inst, true);2492 break :blk try self.allocRegOrMem(inst, true);
2475 }2493 }
2476 };2494 };
2477 try self.load(operand, operand_ptr, self.air.typeOf(un_op));2495 const ptr_ty = self.air.typeOf(un_op);
2478 break :result try self.isErr(operand);2496 try self.load(operand, operand_ptr, ptr_ty);
2497 break :result try self.isErr(ptr_ty.elemType(), operand);
2479 };2498 };
2480 return self.finishAir(inst, result, .{ un_op, .none, .none });2499 return self.finishAir(inst, result, .{ un_op, .none, .none });
2481}2500}
...@@ -2484,7 +2503,8 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2484,7 +2503,8 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
2484 const un_op = self.air.instructions.items(.data)[inst].un_op;2503 const un_op = self.air.instructions.items(.data)[inst].un_op;
2485 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2504 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2486 const operand = try self.resolveInst(un_op);2505 const operand = try self.resolveInst(un_op);
2487 break :result try self.isNonErr(operand);2506 const ty = self.air.typeOf(un_op);
2507 break :result try self.isNonErr(ty, operand);
2488 };2508 };
2489 return self.finishAir(inst, result, .{ un_op, .none, .none });2509 return self.finishAir(inst, result, .{ un_op, .none, .none });
2490}2510}
...@@ -2501,8 +2521,9 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2501,8 +2521,9 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
2501 break :blk try self.allocRegOrMem(inst, true);2521 break :blk try self.allocRegOrMem(inst, true);
2502 }2522 }
2503 };2523 };
2504 try self.load(operand, operand_ptr, self.air.typeOf(un_op));2524 const ptr_ty = self.air.typeOf(un_op);
2505 break :result try self.isNonErr(operand);2525 try self.load(operand, operand_ptr, ptr_ty);
2526 break :result try self.isNonErr(ptr_ty.elemType(), operand);
2506 };2527 };
2507 return self.finishAir(inst, result, .{ un_op, .none, .none });2528 return self.finishAir(inst, result, .{ un_op, .none, .none });
2508}2529}
...@@ -2899,10 +2920,67 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -2899,10 +2920,67 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
2899 }2920 }
2900}2921}
29012922
2923/// Set pointee via pointer stored in a register.
2924/// mov [reg], value
2925fn genSetPtrReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
2926 switch (mcv) {
2927 .dead => unreachable,
2928 .unreach, .none => return, // Nothing to do.
2929 .immediate => |imm| {
2930 const abi_size = ty.abiSize(self.target.*);
2931 switch (abi_size) {
2932 1, 2, 4 => {
2933 // TODO this is wasteful!
2934 // introduce new MIR tag specifically for mov [reg + 0], imm
2935 const payload = try self.addExtra(Mir.ImmPair{
2936 .dest_off = 0,
2937 .operand = @bitCast(i32, @intCast(u32, imm)),
2938 });
2939 _ = try self.addInst(.{
2940 .tag = .mov_mem_imm,
2941 .ops = (Mir.Ops{
2942 .reg1 = reg.to64(),
2943 .flags = switch (abi_size) {
2944 1 => 0b00,
2945 2 => 0b01,
2946 4 => 0b10,
2947 else => unreachable,
2948 },
2949 }).encode(),
2950 .data = .{ .payload = payload },
2951 });
2952 },
2953 else => {
2954 return self.fail("TODO implement set pointee with immediate of ABI size {d}", .{abi_size});
2955 },
2956 }
2957 },
2958 else => |other| {
2959 return self.fail("TODO implement set pointee with {}", .{other});
2960 },
2961 }
2962}
2963
2902fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {2964fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
2903 switch (mcv) {2965 switch (mcv) {
2904 .dead => unreachable,2966 .dead => unreachable,
2905 .ptr_stack_offset => unreachable,2967 .ptr_stack_offset => |unadjusted_off| {
2968 const ptr_abi_size = ty.abiSize(self.target.*);
2969 const elem_ty = ty.childType();
2970 const elem_abi_size = elem_ty.abiSize(self.target.*);
2971 const off = unadjusted_off + elem_abi_size;
2972 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
2973 return self.fail("stack offset too large", .{});
2974 }
2975 _ = try self.addInst(.{
2976 .tag = .lea,
2977 .ops = (Mir.Ops{
2978 .reg1 = registerAlias(reg, @intCast(u32, ptr_abi_size)),
2979 .reg2 = .rbp,
2980 }).encode(),
2981 .data = .{ .imm = -@intCast(i32, off) },
2982 });
2983 },
2906 .ptr_embedded_in_code => unreachable,2984 .ptr_embedded_in_code => unreachable,
2907 .unreach, .none => return, // Nothing to do.2985 .unreach, .none => return, // Nothing to do.
2908 .undef => {2986 .undef => {
test/stage2/x86_64.zig+73-20
...@@ -1662,27 +1662,80 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1662,27 +1662,80 @@ pub fn addCases(ctx: *TestContext) !void {
1662 "",1662 "",
1663 );1663 );
1664 }1664 }
1665 }1665 {
1666 var case = ctx.exe("issue 7187: miscompilation with bool return type", target);
1667 case.addCompareOutput(
1668 \\pub fn main() void {
1669 \\ var x: usize = 1;
1670 \\ var y: bool = getFalse();
1671 \\ _ = y;
1672 \\
1673 \\ assert(x == 1);
1674 \\}
1675 \\
1676 \\fn getFalse() bool {
1677 \\ return false;
1678 \\}
1679 \\
1680 \\fn assert(ok: bool) void {
1681 \\ if (!ok) unreachable;
1682 \\}
1683 , "");
1684 }
16661685
1667 {1686 {
1668 var case = ctx.exe("issue 7187: miscompilation with bool return type", linux_x64);1687 var case = ctx.exe("load-store via pointer deref", target);
1669 case.addCompareOutput(1688 case.addCompareOutput(
1670 \\pub fn main() void {1689 \\pub fn main() void {
1671 \\ var x: usize = 1;1690 \\ var x: u32 = undefined;
1672 \\ var y: bool = getFalse();1691 \\ set(&x);
1673 \\ _ = y;1692 \\ assert(x == 123);
1674 \\1693 \\}
1675 \\ assert(x == 1);1694 \\
1676 \\}1695 \\fn set(x: *u32) void {
1677 \\1696 \\ x.* = 123;
1678 \\fn getFalse() bool {1697 \\}
1679 \\ return false;1698 \\
1680 \\}1699 \\fn assert(ok: bool) void {
1681 \\1700 \\ if (!ok) unreachable;
1682 \\fn assert(ok: bool) void {1701 \\}
1683 \\ if (!ok) unreachable;1702 , "");
1684 \\}1703 }
1685 , "");1704
1705 {
1706 var case = ctx.exe("optional payload", target);
1707 case.addCompareOutput(
1708 \\pub fn main() void {
1709 \\ var x: u32 = undefined;
1710 \\ const maybe_x = byPtr(&x);
1711 \\ assert(maybe_x != null);
1712 \\}
1713 \\
1714 \\fn byPtr(x: *u32) ?*u32 {
1715 \\ return x;
1716 \\}
1717 \\
1718 \\fn assert(ok: bool) void {
1719 \\ if (!ok) unreachable;
1720 \\}
1721 , "");
1722 case.addCompareOutput(
1723 \\pub fn main() void {
1724 \\ var x: u32 = undefined;
1725 \\ const maybe_x = byPtr(&x);
1726 \\ assert(maybe_x == null);
1727 \\}
1728 \\
1729 \\fn byPtr(x: *u32) ?*u32 {
1730 \\ _ = x;
1731 \\ return null;
1732 \\}
1733 \\
1734 \\fn assert(ok: bool) void {
1735 \\ if (!ok) unreachable;
1736 \\}
1737 , "");
1738 }
1686 }1739 }
1687}1740}
16881741