authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-31 17:57:59+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-31 18:10:28+01:00
logc7f774803a3ecbc8d0641adde8ef0528f4a8bb8c
tree2ab32f42f282c04492c4c06611dbac7dada9d577
parentbc12d50170bdbe8d6c38207baf22b78865b48595

stage2: implement loading-storing via pointer (in register)

* load address (pointer) to a stack variable in a register via `lea` instruction * store value on the stack via a pointer stored in a register via `mov [reg], imm` instruction * the lowerings naturally are handled automatically by Mir -> Isel layer * add initial (without safety) implementation of `.optional_payload` * add matching stage2 test cases

2 files changed, 154 insertions(+), 32 deletions(-)

src/arch/x86_64/CodeGen.zig+81-12
......@@ -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", .{});
......@@ -2906,11 +2920,66 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
29062920 }
29072921}
29082922
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
29092964fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
29102965 switch (mcv) {
29112966 .dead => unreachable,
2912 .ptr_stack_offset => |off| {
2913 return self.genSetReg(ty.elemType(), reg, .{ .stack_offset = off });
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 });
29142983 },
29152984 .ptr_embedded_in_code => unreachable,
29162985 .unreach, .none => return, // Nothing to do.
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