Commit c7f774803a

Jakub Konka <kubkon@jakubkonka.com>
2021-12-31 17:57:59
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
1 parent bc12d50
Changed files (2)
src
arch
test
stage2
src/arch/x86_64/CodeGen.zig
@@ -1143,10 +1143,24 @@ fn airShr(self: *Self, inst: Air.Inst.Index) !void {
 
 fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
-    const result: MCValue = if (self.liveness.isUnused(inst))
-        .dead
-    else
-        return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});
+    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
+        const operand = try self.resolveInst(ty_op.operand);
+        if (self.wantSafety()) {
+            // TODO check for null
+            return self.fail("TODO implement check for null in .optional_payload", .{});
+        }
+        const dst_mcv: MCValue = blk: {
+            if (self.reuseOperand(inst, ty_op.operand, 0, operand)) {
+                break :blk operand;
+            } else {
+                break :blk try self.allocRegOrMem(inst, true);
+            }
+        };
+        const ty = self.air.typeOf(ty_op.operand);
+        var buf: Type.Payload.ElemType = undefined;
+        try self.load(dst_mcv, operand, ty.optionalChild(&buf));
+        break :result dst_mcv;
+    };
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
@@ -1408,16 +1422,16 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
         .compare_flags_unsigned => unreachable,
         .compare_flags_signed => unreachable,
         .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
-        .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
+        .ptr_stack_offset => |off| {
+            try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off });
+        },
         .ptr_embedded_in_code => |off| {
             try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off });
         },
         .embedded_in_code => {
             return self.fail("TODO implement loading from MCValue.embedded_in_code", .{});
         },
-        .register => {
-            return self.fail("TODO implement loading from MCValue.register for {}", .{self.target.cpu.arch});
-        },
+        .register => |reg| try self.setRegOrMem(elem_ty, dst_mcv, .{ .register = reg }),
         .memory => |addr| {
             const reg = try self.register_manager.allocReg(null, &.{});
             try self.genSetReg(ptr_ty, reg, .{ .memory = addr });
@@ -1479,8 +1493,8 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {
         .embedded_in_code => {
             return self.fail("TODO implement storing to MCValue.embedded_in_code", .{});
         },
-        .register => {
-            return self.fail("TODO implement storing to MCValue.register", .{});
+        .register => |reg| {
+            try self.genSetPtrReg(elem_ty, reg, value);
         },
         .memory => {
             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
     }
 }
 
+/// Set pointee via pointer stored in a register.
+/// mov [reg], value
+fn genSetPtrReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
+    switch (mcv) {
+        .dead => unreachable,
+        .unreach, .none => return, // Nothing to do.
+        .immediate => |imm| {
+            const abi_size = ty.abiSize(self.target.*);
+            switch (abi_size) {
+                1, 2, 4 => {
+                    // TODO this is wasteful!
+                    // introduce new MIR tag specifically for mov [reg + 0], imm
+                    const payload = try self.addExtra(Mir.ImmPair{
+                        .dest_off = 0,
+                        .operand = @bitCast(i32, @intCast(u32, imm)),
+                    });
+                    _ = try self.addInst(.{
+                        .tag = .mov_mem_imm,
+                        .ops = (Mir.Ops{
+                            .reg1 = reg.to64(),
+                            .flags = switch (abi_size) {
+                                1 => 0b00,
+                                2 => 0b01,
+                                4 => 0b10,
+                                else => unreachable,
+                            },
+                        }).encode(),
+                        .data = .{ .payload = payload },
+                    });
+                },
+                else => {
+                    return self.fail("TODO implement set pointee with immediate of ABI size {d}", .{abi_size});
+                },
+            }
+        },
+        else => |other| {
+            return self.fail("TODO implement set pointee with {}", .{other});
+        },
+    }
+}
+
 fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
     switch (mcv) {
         .dead => unreachable,
-        .ptr_stack_offset => |off| {
-            return self.genSetReg(ty.elemType(), reg, .{ .stack_offset = off });
+        .ptr_stack_offset => |unadjusted_off| {
+            const ptr_abi_size = ty.abiSize(self.target.*);
+            const elem_ty = ty.childType();
+            const elem_abi_size = elem_ty.abiSize(self.target.*);
+            const off = unadjusted_off + elem_abi_size;
+            if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
+                return self.fail("stack offset too large", .{});
+            }
+            _ = try self.addInst(.{
+                .tag = .lea,
+                .ops = (Mir.Ops{
+                    .reg1 = registerAlias(reg, @intCast(u32, ptr_abi_size)),
+                    .reg2 = .rbp,
+                }).encode(),
+                .data = .{ .imm = -@intCast(i32, off) },
+            });
         },
         .ptr_embedded_in_code => unreachable,
         .unreach, .none => return, // Nothing to do.
test/stage2/x86_64.zig
@@ -1662,27 +1662,80 @@ pub fn addCases(ctx: *TestContext) !void {
                 "",
             );
         }
-    }
+        {
+            var case = ctx.exe("issue 7187: miscompilation with bool return type", target);
+            case.addCompareOutput(
+                \\pub fn main() void {
+                \\    var x: usize = 1;
+                \\    var y: bool = getFalse();
+                \\    _ = y;
+                \\
+                \\    assert(x == 1);
+                \\}
+                \\
+                \\fn getFalse() bool {
+                \\    return false;
+                \\}
+                \\
+                \\fn assert(ok: bool) void {
+                \\    if (!ok) unreachable;
+                \\}
+            , "");
+        }
 
-    {
-        var case = ctx.exe("issue 7187: miscompilation with bool return type", linux_x64);
-        case.addCompareOutput(
-            \\pub fn main() void {
-            \\    var x: usize = 1;
-            \\    var y: bool = getFalse();
-            \\    _ = y;
-            \\
-            \\    assert(x == 1);
-            \\}
-            \\
-            \\fn getFalse() bool {
-            \\    return false;
-            \\}
-            \\
-            \\fn assert(ok: bool) void {
-            \\    if (!ok) unreachable;
-            \\}
-        , "");
+        {
+            var case = ctx.exe("load-store via pointer deref", target);
+            case.addCompareOutput(
+                \\pub fn main() void {
+                \\    var x: u32 = undefined;
+                \\    set(&x);
+                \\    assert(x == 123);
+                \\}
+                \\
+                \\fn set(x: *u32) void {
+                \\    x.* = 123;
+                \\}
+                \\
+                \\fn assert(ok: bool) void {
+                \\    if (!ok) unreachable;
+                \\}
+            , "");
+        }
+
+        {
+            var case = ctx.exe("optional payload", target);
+            case.addCompareOutput(
+                \\pub fn main() void {
+                \\    var x: u32 = undefined;
+                \\    const maybe_x = byPtr(&x);
+                \\    assert(maybe_x != null);
+                \\}
+                \\
+                \\fn byPtr(x: *u32) ?*u32 {
+                \\    return x;
+                \\}
+                \\
+                \\fn assert(ok: bool) void {
+                \\    if (!ok) unreachable;
+                \\}
+            , "");
+            case.addCompareOutput(
+                \\pub fn main() void {
+                \\    var x: u32 = undefined;
+                \\    const maybe_x = byPtr(&x);
+                \\    assert(maybe_x == null);
+                \\}
+                \\
+                \\fn byPtr(x: *u32) ?*u32 {
+                \\    _ = x;
+                \\    return null;
+                \\}
+                \\
+                \\fn assert(ok: bool) void {
+                \\    if (!ok) unreachable;
+                \\}
+            , "");
+        }
     }
 }