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 {
11431143
11441144fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
11451145 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1146 const result: MCValue = if (self.liveness.isUnused(inst))
1147 .dead
1148 else
1149 return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});
1146 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1147 const operand = try self.resolveInst(ty_op.operand);
1148 if (self.wantSafety()) {
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 };
11501164 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
11511165}
11521166
......@@ -1408,16 +1422,16 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
14081422 .compare_flags_unsigned => unreachable,
14091423 .compare_flags_signed => unreachable,
14101424 .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 },
14121428 .ptr_embedded_in_code => |off| {
14131429 try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off });
14141430 },
14151431 .embedded_in_code => {
14161432 return self.fail("TODO implement loading from MCValue.embedded_in_code", .{});
14171433 },
1418 .register => {
1419 return self.fail("TODO implement loading from MCValue.register for {}", .{self.target.cpu.arch});
1420 },
1434 .register => |reg| try self.setRegOrMem(elem_ty, dst_mcv, .{ .register = reg }),
14211435 .memory => |addr| {
14221436 const reg = try self.register_manager.allocReg(null, &.{});
14231437 try self.genSetReg(ptr_ty, reg, .{ .memory = addr });
......@@ -1479,8 +1493,8 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {
14791493 .embedded_in_code => {
14801494 return self.fail("TODO implement storing to MCValue.embedded_in_code", .{});
14811495 },
1482 .register => {
1483 return self.fail("TODO implement storing to MCValue.register", .{});
1496 .register => |reg| {
1497 try self.genSetPtrReg(elem_ty, reg, value);
14841498 },
14851499 .memory => {
14861500 return self.fail("TODO implement storing to MCValue.memory", .{});
......@@ -2371,31 +2385,30 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
23712385 return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
23722386}
23732387
2374fn isNull(self: *Self, operand: MCValue) !MCValue {
2375 _ = operand;
2376 // Here you can specialize this instruction if it makes sense to, otherwise the default
2377 // will call isNonNull and invert the result.
2378 return self.fail("TODO call isNonNull and invert the result", .{});
2388fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
2389 try self.genBinMathOpMir(.cmp, ty, operand, MCValue{ .immediate = 0 });
2390 return MCValue{ .compare_flags_unsigned = .eq };
23792391}
23802392
2381fn isNonNull(self: *Self, operand: MCValue) !MCValue {
2382 _ = operand;
2383 // Here you can specialize this instruction if it makes sense to, otherwise the default
2384 // will call isNull and invert the result.
2385 return self.fail("TODO call isNull and invert the result", .{});
2393fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
2394 const is_null_res = try self.isNull(ty, operand);
2395 assert(is_null_res.compare_flags_unsigned == .eq);
2396 return MCValue{ .compare_flags_unsigned = .neq };
23862397}
23872398
2388fn isErr(self: *Self, operand: MCValue) !MCValue {
2399fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
2400 _ = ty;
23892401 _ = operand;
23902402 // 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.
23922404 return self.fail("TODO call isNonErr and invert the result", .{});
23932405}
23942406
2395fn isNonErr(self: *Self, operand: MCValue) !MCValue {
2407fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
2408 _ = ty;
23962409 _ = operand;
23972410 // 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.
23992412 return self.fail("TODO call isErr and invert the result", .{});
24002413}
24012414
......@@ -2403,7 +2416,8 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
24032416 const un_op = self.air.instructions.items(.data)[inst].un_op;
24042417 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
24052418 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);
24072421 };
24082422 return self.finishAir(inst, result, .{ un_op, .none, .none });
24092423}
......@@ -2420,8 +2434,9 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
24202434 break :blk try self.allocRegOrMem(inst, true);
24212435 }
24222436 };
2423 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
2424 break :result try self.isNull(operand);
2437 const ptr_ty = self.air.typeOf(un_op);
2438 try self.load(operand, operand_ptr, ptr_ty);
2439 break :result try self.isNull(ptr_ty.elemType(), operand);
24252440 };
24262441 return self.finishAir(inst, result, .{ un_op, .none, .none });
24272442}
......@@ -2430,7 +2445,8 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
24302445 const un_op = self.air.instructions.items(.data)[inst].un_op;
24312446 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
24322447 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);
24342450 };
24352451 return self.finishAir(inst, result, .{ un_op, .none, .none });
24362452}
......@@ -2447,8 +2463,9 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
24472463 break :blk try self.allocRegOrMem(inst, true);
24482464 }
24492465 };
2450 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
2451 break :result try self.isNonNull(operand);
2466 const ptr_ty = self.air.typeOf(un_op);
2467 try self.load(operand, operand_ptr, ptr_ty);
2468 break :result try self.isNonNull(ptr_ty.elemType(), operand);
24522469 };
24532470 return self.finishAir(inst, result, .{ un_op, .none, .none });
24542471}
......@@ -2457,7 +2474,8 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
24572474 const un_op = self.air.instructions.items(.data)[inst].un_op;
24582475 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
24592476 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);
24612479 };
24622480 return self.finishAir(inst, result, .{ un_op, .none, .none });
24632481}
......@@ -2474,8 +2492,9 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
24742492 break :blk try self.allocRegOrMem(inst, true);
24752493 }
24762494 };
2477 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
2478 break :result try self.isErr(operand);
2495 const ptr_ty = self.air.typeOf(un_op);
2496 try self.load(operand, operand_ptr, ptr_ty);
2497 break :result try self.isErr(ptr_ty.elemType(), operand);
24792498 };
24802499 return self.finishAir(inst, result, .{ un_op, .none, .none });
24812500}
......@@ -2484,7 +2503,8 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
24842503 const un_op = self.air.instructions.items(.data)[inst].un_op;
24852504 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
24862505 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);
24882508 };
24892509 return self.finishAir(inst, result, .{ un_op, .none, .none });
24902510}
......@@ -2501,8 +2521,9 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
25012521 break :blk try self.allocRegOrMem(inst, true);
25022522 }
25032523 };
2504 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
2505 break :result try self.isNonErr(operand);
2524 const ptr_ty = self.air.typeOf(un_op);
2525 try self.load(operand, operand_ptr, ptr_ty);
2526 break :result try self.isNonErr(ptr_ty.elemType(), operand);
25062527 };
25072528 return self.finishAir(inst, result, .{ un_op, .none, .none });
25082529}
......@@ -2899,10 +2920,67 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
28992920 }
29002921}
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
29022964fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
29032965 switch (mcv) {
29042966 .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 },
29062984 .ptr_embedded_in_code => unreachable,
29072985 .unreach, .none => return, // Nothing to do.
29082986 .undef => {
test/stage2/x86_64.zig+73-20
......@@ -1662,27 +1662,80 @@ pub fn addCases(ctx: *TestContext) !void {
16621662 "",
16631663 );
16641664 }
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 {
1668 var case = ctx.exe("issue 7187: miscompilation with bool return type", linux_x64);
1669 case.addCompareOutput(
1670 \\pub fn main() void {
1671 \\ var x: usize = 1;
1672 \\ var y: bool = getFalse();
1673 \\ _ = y;
1674 \\
1675 \\ assert(x == 1);
1676 \\}
1677 \\
1678 \\fn getFalse() bool {
1679 \\ return false;
1680 \\}
1681 \\
1682 \\fn assert(ok: bool) void {
1683 \\ if (!ok) unreachable;
1684 \\}
1685 , "");
1686 {
1687 var case = ctx.exe("load-store via pointer deref", target);
1688 case.addCompareOutput(
1689 \\pub fn main() void {
1690 \\ var x: u32 = undefined;
1691 \\ set(&x);
1692 \\ assert(x == 123);
1693 \\}
1694 \\
1695 \\fn set(x: *u32) void {
1696 \\ x.* = 123;
1697 \\}
1698 \\
1699 \\fn assert(ok: bool) void {
1700 \\ if (!ok) unreachable;
1701 \\}
1702 , "");
1703 }
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 }
16861739 }
16871740}
16881741