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
This commit is contained in:
Jakub Konka 2021-12-31 17:57:59 +01:00
parent bc12d50170
commit c7f774803a
2 changed files with 154 additions and 32 deletions

View File

@ -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.

View File

@ -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;
\\}
, "");
}
}
}