stage2: refactor handling of immediates in x86_64 backend

Fixes issues with incorrect operand sizes in a handful of cases
and allows for usage of differently sized integers in Zig sources.
This commit is contained in:
Jakub Konka 2022-01-15 18:28:39 +01:00
parent a5c7742ba6
commit 4d4bbd7624
4 changed files with 142 additions and 107 deletions

View File

@ -497,14 +497,14 @@ fn gen(self: *Self) InnerError!void {
.ops = (Mir.Ops{ .ops = (Mir.Ops{
.reg1 = .rsp, .reg1 = .rsp,
}).encode(), }).encode(),
.data = .{ .imm = @intCast(i32, aligned_stack_end) + stack_adjustment }, .data = .{ .imm = @bitCast(u32, @intCast(i32, aligned_stack_end) + stack_adjustment) },
}); });
self.mir_instructions.set(backpatch_stack_add, .{ self.mir_instructions.set(backpatch_stack_add, .{
.tag = .add, .tag = .add,
.ops = (Mir.Ops{ .ops = (Mir.Ops{
.reg1 = .rsp, .reg1 = .rsp,
}).encode(), }).encode(),
.data = .{ .imm = @intCast(i32, aligned_stack_end) + stack_adjustment }, .data = .{ .imm = @bitCast(u32, @intCast(i32, aligned_stack_end) + stack_adjustment) },
}); });
} }
} else { } else {
@ -1347,7 +1347,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
.reg2 = .rbp, .reg2 = .rbp,
.flags = 0b01, .flags = 0b01,
}).encode(), }).encode(),
.data = .{ .imm = -@intCast(i32, off + 16) }, .data = .{ .imm = @bitCast(u32, -@intCast(i32, off + 16)) },
}); });
// add addr, offset // add addr, offset
_ = try self.addInst(.{ _ = try self.addInst(.{
@ -1555,7 +1555,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) }); try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) });
return self.genInlineMemcpy( return self.genInlineMemcpy(
-@intCast(i32, off + abi_size), @bitCast(u32, -@intCast(i32, off + abi_size)),
registerAlias(addr_reg, @divExact(reg.size(), 8)), registerAlias(addr_reg, @divExact(reg.size(), 8)),
count_reg.to64(), count_reg.to64(),
tmp_reg.to8(), tmp_reg.to8(),
@ -1637,7 +1637,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
// introduce new MIR tag specifically for mov [reg + 0], imm // introduce new MIR tag specifically for mov [reg + 0], imm
const payload = try self.addExtra(Mir.ImmPair{ const payload = try self.addExtra(Mir.ImmPair{
.dest_off = 0, .dest_off = 0,
.operand = @bitCast(i32, @intCast(u32, imm)), .operand = @truncate(u32, imm),
}); });
_ = try self.addInst(.{ _ = try self.addInst(.{
.tag = .mov_mem_imm, .tag = .mov_mem_imm,
@ -1872,7 +1872,7 @@ fn genBinMathOpMir(
.ops = (Mir.Ops{ .ops = (Mir.Ops{
.reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)), .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)),
}).encode(), }).encode(),
.data = .{ .imm = @intCast(i32, imm) }, .data = .{ .imm = @truncate(u32, imm) },
}); });
}, },
.embedded_in_code, .memory => { .embedded_in_code, .memory => {
@ -1891,7 +1891,7 @@ fn genBinMathOpMir(
.reg2 = .rbp, .reg2 = .rbp,
.flags = 0b01, .flags = 0b01,
}).encode(), }).encode(),
.data = .{ .imm = -@intCast(i32, adj_off) }, .data = .{ .imm = @bitCast(u32, -@intCast(i32, adj_off)) },
}); });
}, },
.compare_flags_unsigned => { .compare_flags_unsigned => {
@ -1926,7 +1926,7 @@ fn genBinMathOpMir(
.reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)),
.flags = 0b10, .flags = 0b10,
}).encode(), }).encode(),
.data = .{ .imm = -@intCast(i32, adj_off) }, .data = .{ .imm = @bitCast(u32, -@intCast(i32, adj_off)) },
}); });
}, },
.immediate => |imm| { .immediate => |imm| {
@ -1947,8 +1947,8 @@ fn genBinMathOpMir(
else => unreachable, else => unreachable,
}; };
const payload = try self.addExtra(Mir.ImmPair{ const payload = try self.addExtra(Mir.ImmPair{
.dest_off = -@intCast(i32, adj_off), .dest_off = @bitCast(u32, -@intCast(i32, adj_off)),
.operand = @bitCast(i32, @intCast(u32, imm)), .operand = @truncate(u32, imm),
}); });
_ = try self.addInst(.{ _ = try self.addInst(.{
.tag = tag, .tag = tag,
@ -2015,7 +2015,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
.reg2 = dst_reg.to32(), .reg2 = dst_reg.to32(),
.flags = 0b10, .flags = 0b10,
}).encode(), }).encode(),
.data = .{ .imm = @intCast(i32, imm) }, .data = .{ .imm = @truncate(u32, imm) },
}); });
} else { } else {
// TODO verify we don't spill and assign to the same register as dst_mcv // TODO verify we don't spill and assign to the same register as dst_mcv
@ -2088,7 +2088,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
const mcv = self.args[arg_index]; const mcv = self.args[arg_index];
const payload = try self.addExtra(Mir.ArgDbgInfo{ const payload = try self.addExtra(Mir.ArgDbgInfo{
.air_inst = inst, .air_inst = inst,
.arg_index = @intCast(u32, arg_index), // TODO can arg_index: u32? .arg_index = @truncate(u32, arg_index), // TODO can arg_index: u32?
}); });
_ = try self.addInst(.{ _ = try self.addInst(.{
.tag = .arg_dbg_info, .tag = .arg_dbg_info,
@ -2196,7 +2196,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
.ops = (Mir.Ops{ .ops = (Mir.Ops{
.flags = 0b01, .flags = 0b01,
}).encode(), }).encode(),
.data = .{ .imm = @bitCast(i32, got_addr) }, .data = .{ .imm = @truncate(u32, got_addr) },
}); });
} else if (func_value.castTag(.extern_fn)) |_| { } else if (func_value.castTag(.extern_fn)) |_| {
return self.fail("TODO implement calling extern functions", .{}); return self.fail("TODO implement calling extern functions", .{});
@ -3121,8 +3121,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
// offset from rbp, which is at the top of the stack frame. // offset from rbp, which is at the top of the stack frame.
// mov [rbp+offset], immediate // mov [rbp+offset], immediate
const payload = try self.addExtra(Mir.ImmPair{ const payload = try self.addExtra(Mir.ImmPair{
.dest_off = -@intCast(i32, adj_off), .dest_off = @bitCast(u32, -@intCast(i32, adj_off)),
.operand = @bitCast(i32, @intCast(u32, x_big)), .operand = @truncate(u32, x_big),
}); });
_ = try self.addInst(.{ _ = try self.addInst(.{
.tag = .mov_mem_imm, .tag = .mov_mem_imm,
@ -3147,8 +3147,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
// insted just use two 32 bit writes to avoid register allocation // insted just use two 32 bit writes to avoid register allocation
{ {
const payload = try self.addExtra(Mir.ImmPair{ const payload = try self.addExtra(Mir.ImmPair{
.dest_off = negative_offset + 4, .dest_off = @bitCast(u32, negative_offset + 4),
.operand = @bitCast(i32, @truncate(u32, x_big >> 32)), .operand = @truncate(u32, x_big >> 32),
}); });
_ = try self.addInst(.{ _ = try self.addInst(.{
.tag = .mov_mem_imm, .tag = .mov_mem_imm,
@ -3161,8 +3161,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
} }
{ {
const payload = try self.addExtra(Mir.ImmPair{ const payload = try self.addExtra(Mir.ImmPair{
.dest_off = negative_offset, .dest_off = @bitCast(u32, negative_offset),
.operand = @bitCast(i32, @truncate(u32, x_big)), .operand = @truncate(u32, x_big),
}); });
_ = try self.addInst(.{ _ = try self.addInst(.{
.tag = .mov_mem_imm, .tag = .mov_mem_imm,
@ -3192,7 +3192,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
.reg2 = registerAlias(reg, @intCast(u32, abi_size)), .reg2 = registerAlias(reg, @intCast(u32, abi_size)),
.flags = 0b10, .flags = 0b10,
}).encode(), }).encode(),
.data = .{ .imm = -@intCast(i32, adj_off) }, .data = .{ .imm = @bitCast(u32, -@intCast(i32, adj_off)) },
}); });
}, },
.memory, .embedded_in_code => { .memory, .embedded_in_code => {
@ -3228,14 +3228,14 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
.reg1 = addr_reg.to64(), .reg1 = addr_reg.to64(),
.reg2 = .rbp, .reg2 = .rbp,
}).encode(), }).encode(),
.data = .{ .imm = -@intCast(i32, off + abi_size) }, .data = .{ .imm = @bitCast(u32, -@intCast(i32, off + abi_size)) },
}); });
// TODO allow for abi_size to be u64 // TODO allow for abi_size to be u64
try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) }); try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) });
return self.genInlineMemcpy( return self.genInlineMemcpy(
-@intCast(i32, stack_offset + abi_size), @bitCast(u32, -@intCast(i32, stack_offset + abi_size)),
addr_reg.to64(), addr_reg.to64(),
count_reg.to64(), count_reg.to64(),
tmp_reg.to8(), tmp_reg.to8(),
@ -3246,7 +3246,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
fn genInlineMemcpy( fn genInlineMemcpy(
self: *Self, self: *Self,
stack_offset: i32, stack_offset: u32,
addr_reg: Register, addr_reg: Register,
count_reg: Register, count_reg: Register,
tmp_reg: Register, tmp_reg: Register,
@ -3361,7 +3361,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
.reg1 = registerAlias(reg, @intCast(u32, ptr_abi_size)), .reg1 = registerAlias(reg, @intCast(u32, ptr_abi_size)),
.reg2 = .rbp, .reg2 = .rbp,
}).encode(), }).encode(),
.data = .{ .imm = -@intCast(i32, off) }, .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) },
}); });
}, },
.ptr_embedded_in_code => unreachable, .ptr_embedded_in_code => unreachable,
@ -3426,7 +3426,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
.ops = (Mir.Ops{ .ops = (Mir.Ops{
.reg1 = registerAlias(reg, @intCast(u32, abi_size)), .reg1 = registerAlias(reg, @intCast(u32, abi_size)),
}).encode(), }).encode(),
.data = .{ .imm = @intCast(i32, x) }, .data = .{ .imm = @truncate(u32, x) },
}); });
return; return;
} }
@ -3482,7 +3482,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
.reg1 = reg, .reg1 = reg,
.flags = 0b10, .flags = 0b10,
}).encode(), }).encode(),
.data = .{ .got_entry = @intCast(u32, x) }, .data = .{ .got_entry = @truncate(u32, x) },
}); });
// MOV reg, [reg] // MOV reg, [reg]
_ = try self.addInst(.{ _ = try self.addInst(.{
@ -3502,7 +3502,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
.reg1 = reg, .reg1 = reg,
.flags = 0b01, .flags = 0b01,
}).encode(), }).encode(),
.data = .{ .imm = @intCast(i32, x) }, .data = .{ .imm = @truncate(u32, x) },
}); });
} else { } else {
// If this is RAX, we can use a direct load. // If this is RAX, we can use a direct load.
@ -3561,7 +3561,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
.reg2 = .rbp, .reg2 = .rbp,
.flags = 0b01, .flags = 0b01,
}).encode(), }).encode(),
.data = .{ .imm = -@intCast(i32, off) }, .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) },
}); });
}, },
} }

View File

@ -483,17 +483,26 @@ inline fn setRexWRegister(reg: Register) bool {
}; };
} }
inline fn immOpSize(imm: i64) u8 { inline fn immOpSize(u_imm: u32) u8 {
blk: { const imm = @bitCast(i32, u_imm);
_ = math.cast(i8, imm) catch break :blk; if (math.minInt(i8) <= imm and imm <= math.maxInt(i8)) {
return 8; return 8;
} }
blk: { if (math.minInt(i16) <= imm and imm <= math.maxInt(i16)) {
_ = math.cast(i16, imm) catch break :blk;
return 16; return 16;
} }
blk: { return 32;
_ = math.cast(i32, imm) catch break :blk; }
inline fn imm64OpSize(u_imm: u64) u8 {
const imm = @bitCast(i64, u_imm);
if (math.minInt(i8) <= imm and imm <= math.maxInt(i8)) {
return 8;
}
if (math.minInt(i16) <= imm and imm <= math.maxInt(i16)) {
return 16;
}
if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) {
return 32; return 32;
} }
return 64; return 64;
@ -560,10 +569,10 @@ fn mirMovabs(isel: *Isel, inst: Mir.Inst.Index) InnerError!void {
const tag = isel.mir.instructions.items(.tag)[inst]; const tag = isel.mir.instructions.items(.tag)[inst];
assert(tag == .movabs); assert(tag == .movabs);
const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]);
const imm: i64 = if (ops.reg1.size() == 64) blk: { const imm: u64 = if (ops.reg1.size() == 64) blk: {
const payload = isel.mir.instructions.items(.data)[inst].payload; const payload = isel.mir.instructions.items(.data)[inst].payload;
const imm = isel.mir.extraData(Mir.Imm64, payload).data; const imm = isel.mir.extraData(Mir.Imm64, payload).data;
break :blk @bitCast(i64, imm.decode()); break :blk imm.decode();
} else isel.mir.instructions.items(.data)[inst].imm; } else isel.mir.instructions.items(.data)[inst].imm;
if (ops.flags == 0b00) { if (ops.flags == 0b00) {
// movabs reg, imm64 // movabs reg, imm64
@ -1233,7 +1242,7 @@ const ScaleIndex = struct {
const Memory = struct { const Memory = struct {
base: ?Register, base: ?Register,
rip: bool = false, rip: bool = false,
disp: i32, disp: u32,
ptr_size: PtrSize, ptr_size: PtrSize,
scale_index: ?ScaleIndex = null, scale_index: ?ScaleIndex = null,
@ -1283,7 +1292,7 @@ const Memory = struct {
} else { } else {
encoder.sib_baseDisp8(dst); encoder.sib_baseDisp8(dst);
} }
encoder.disp8(@intCast(i8, mem_op.disp)); encoder.disp8(@bitCast(i8, @truncate(u8, mem_op.disp)));
} else { } else {
encoder.modRm_SIBDisp32(src); encoder.modRm_SIBDisp32(src);
if (mem_op.scale_index) |si| { if (mem_op.scale_index) |si| {
@ -1291,17 +1300,17 @@ const Memory = struct {
} else { } else {
encoder.sib_baseDisp32(dst); encoder.sib_baseDisp32(dst);
} }
encoder.disp32(mem_op.disp); encoder.disp32(@bitCast(i32, mem_op.disp));
} }
} else { } else {
if (mem_op.disp == 0) { if (mem_op.disp == 0) {
encoder.modRm_indirectDisp0(src, dst); encoder.modRm_indirectDisp0(src, dst);
} else if (immOpSize(mem_op.disp) == 8) { } else if (immOpSize(mem_op.disp) == 8) {
encoder.modRm_indirectDisp8(src, dst); encoder.modRm_indirectDisp8(src, dst);
encoder.disp8(@intCast(i8, mem_op.disp)); encoder.disp8(@bitCast(i8, @truncate(u8, mem_op.disp)));
} else { } else {
encoder.modRm_indirectDisp32(src, dst); encoder.modRm_indirectDisp32(src, dst);
encoder.disp32(mem_op.disp); encoder.disp32(@bitCast(i32, mem_op.disp));
} }
} }
} else { } else {
@ -1315,16 +1324,16 @@ const Memory = struct {
encoder.sib_disp32(); encoder.sib_disp32();
} }
} }
encoder.disp32(mem_op.disp); encoder.disp32(@bitCast(i32, mem_op.disp));
} }
} }
}; };
fn encodeImm(encoder: Encoder, imm: i32, size: u64) void { fn encodeImm(encoder: Encoder, imm: u32, size: u64) void {
switch (size) { switch (size) {
8 => encoder.imm8(@intCast(i8, imm)), 8 => encoder.imm8(@bitCast(i8, @truncate(u8, imm))),
16 => encoder.imm16(@intCast(i16, imm)), 16 => encoder.imm16(@bitCast(i16, @truncate(u16, imm))),
32, 64 => encoder.imm32(imm), 32, 64 => encoder.imm32(@bitCast(i32, imm)),
else => unreachable, else => unreachable,
} }
} }
@ -1338,7 +1347,7 @@ const RegisterOrMemory = union(enum) {
} }
fn mem(ptr_size: Memory.PtrSize, args: struct { fn mem(ptr_size: Memory.PtrSize, args: struct {
disp: i32, disp: u32,
base: ?Register = null, base: ?Register = null,
scale_index: ?ScaleIndex = null, scale_index: ?ScaleIndex = null,
}) RegisterOrMemory { }) RegisterOrMemory {
@ -1352,7 +1361,7 @@ const RegisterOrMemory = union(enum) {
}; };
} }
fn rip(ptr_size: Memory.PtrSize, disp: i32) RegisterOrMemory { fn rip(ptr_size: Memory.PtrSize, disp: u32) RegisterOrMemory {
return .{ return .{
.memory = .{ .memory = .{
.base = null, .base = null,
@ -1377,12 +1386,12 @@ fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) LoweringError!void {
opc.encode(encoder); opc.encode(encoder);
} }
fn lowerToIEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) LoweringError!void { fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) LoweringError!void {
if (tag == .ret_far or tag == .ret_near) { if (tag == .ret_far or tag == .ret_near) {
const encoder = try Encoder.init(code, 3); const encoder = try Encoder.init(code, 3);
const opc = getOpCode(tag, .i, false).?; const opc = getOpCode(tag, .i, false).?;
opc.encode(encoder); opc.encode(encoder);
encoder.imm16(@intCast(i16, imm)); encoder.imm16(@bitCast(i16, @truncate(u16, imm)));
return; return;
} }
const opc = getOpCode(tag, .i, immOpSize(imm) == 8).?; const opc = getOpCode(tag, .i, immOpSize(imm) == 8).?;
@ -1410,11 +1419,11 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) LoweringError!
opc.encodeWithReg(encoder, reg); opc.encodeWithReg(encoder, reg);
} }
fn lowerToDEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) LoweringError!void { fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) LoweringError!void {
const opc = getOpCode(tag, .d, false).?; const opc = getOpCode(tag, .d, false).?;
const encoder = try Encoder.init(code, 6); const encoder = try Encoder.init(code, 6);
opc.encode(encoder); opc.encode(encoder);
encoder.imm32(imm); encoder.imm32(@bitCast(i32, imm));
} }
fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) LoweringError!void { fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) LoweringError!void {
@ -1467,19 +1476,19 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
} }
} }
fn lowerToTdEnc(tag: Tag, moffs: i64, reg: Register, code: *std.ArrayList(u8)) LoweringError!void { fn lowerToTdEnc(tag: Tag, moffs: u64, reg: Register, code: *std.ArrayList(u8)) LoweringError!void {
return lowerToTdFdEnc(tag, reg, moffs, code, true); return lowerToTdFdEnc(tag, reg, moffs, code, true);
} }
fn lowerToFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8)) LoweringError!void { fn lowerToFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8)) LoweringError!void {
return lowerToTdFdEnc(tag, reg, moffs, code, false); return lowerToTdFdEnc(tag, reg, moffs, code, false);
} }
fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8), td: bool) LoweringError!void { fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), td: bool) LoweringError!void {
if (reg.lowId() != Register.rax.lowId()) { if (reg.lowId() != Register.rax.lowId()) {
return error.RaxOperandExpected; return error.RaxOperandExpected;
} }
if (reg.size() != immOpSize(moffs)) { if (reg.size() != imm64OpSize(moffs)) {
return error.OperandSizeMismatch; return error.OperandSizeMismatch;
} }
const opc = if (td) const opc = if (td)
@ -1495,27 +1504,16 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8),
}); });
opc.encode(encoder); opc.encode(encoder);
switch (reg.size()) { switch (reg.size()) {
8 => { 8 => encoder.imm8(@bitCast(i8, @truncate(u8, moffs))),
const moffs8 = try math.cast(i8, moffs); 16 => encoder.imm16(@bitCast(i16, @truncate(u16, moffs))),
encoder.imm8(moffs8); 32 => encoder.imm32(@bitCast(i32, @truncate(u32, moffs))),
}, 64 => encoder.imm64(moffs),
16 => {
const moffs16 = try math.cast(i16, moffs);
encoder.imm16(moffs16);
},
32 => {
const moffs32 = try math.cast(i32, moffs);
encoder.imm32(moffs32);
},
64 => {
encoder.imm64(@bitCast(u64, moffs));
},
else => unreachable, else => unreachable,
} }
} }
fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) LoweringError!void { fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) LoweringError!void {
if (reg.size() != immOpSize(imm)) { if (reg.size() != imm64OpSize(imm)) {
return error.OperandSizeMismatch; return error.OperandSizeMismatch;
} }
const opc = getOpCode(tag, .oi, reg.size() == 8).?; const opc = getOpCode(tag, .oi, reg.size() == 8).?;
@ -1529,26 +1527,15 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) Low
}); });
opc.encodeWithReg(encoder, reg); opc.encodeWithReg(encoder, reg);
switch (reg.size()) { switch (reg.size()) {
8 => { 8 => encoder.imm8(@bitCast(i8, @truncate(u8, imm))),
const imm8 = try math.cast(i8, imm); 16 => encoder.imm16(@bitCast(i16, @truncate(u16, imm))),
encoder.imm8(imm8); 32 => encoder.imm32(@bitCast(i32, @truncate(u32, imm))),
}, 64 => encoder.imm64(imm),
16 => {
const imm16 = try math.cast(i16, imm);
encoder.imm16(imm16);
},
32 => {
const imm32 = try math.cast(i32, imm);
encoder.imm32(imm32);
},
64 => {
encoder.imm64(@bitCast(u64, imm));
},
else => unreachable, else => unreachable,
} }
} }
fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.ArrayList(u8)) LoweringError!void { fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) LoweringError!void {
const modrm_ext = getModRmExt(tag).?; const modrm_ext = getModRmExt(tag).?;
switch (reg_or_mem) { switch (reg_or_mem) {
.register => |dst_reg| { .register => |dst_reg| {
@ -1700,7 +1687,7 @@ fn lowerToRmiEnc(
tag: Tag, tag: Tag,
reg: Register, reg: Register,
reg_or_mem: RegisterOrMemory, reg_or_mem: RegisterOrMemory,
imm: i32, imm: u32,
code: *std.ArrayList(u8), code: *std.ArrayList(u8),
) LoweringError!void { ) LoweringError!void {
if (reg.size() == 8) { if (reg.size() == 8) {
@ -1804,7 +1791,10 @@ test "lower MI encoding" {
try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", isel.lowered(), "mov rax, 0x10"); try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", isel.lowered(), "mov rax, 0x10");
try lowerToMiEnc(.mov, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0, .base = .r11 }), 0x10, isel.code()); try lowerToMiEnc(.mov, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0, .base = .r11 }), 0x10, isel.code());
try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", isel.lowered(), "mov dword ptr [r11 + 0], 0x10"); try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", isel.lowered(), "mov dword ptr [r11 + 0], 0x10");
try lowerToMiEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ .disp = -8, .base = .rdx }), 0x10, isel.code()); try lowerToMiEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{
.disp = @bitCast(u32, @as(i32, -8)),
.base = .rdx,
}), 0x10, isel.code());
try expectEqualHexStrings("\x81\x42\xF8\x10\x00\x00\x00", isel.lowered(), "add dword ptr [rdx - 8], 0x10"); try expectEqualHexStrings("\x81\x42\xF8\x10\x00\x00\x00", isel.lowered(), "add dword ptr [rdx - 8], 0x10");
try lowerToMiEnc(.sub, RegisterOrMemory.mem(.dword_ptr, .{ try lowerToMiEnc(.sub, RegisterOrMemory.mem(.dword_ptr, .{
.disp = 0x10000000, .disp = 0x10000000,
@ -1836,15 +1826,24 @@ test "lower MI encoding" {
isel.lowered(), isel.lowered(),
"mov qword ptr [rip + 0x10], 0x10", "mov qword ptr [rip + 0x10], 0x10",
); );
try lowerToMiEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ .disp = -8, .base = .rbp }), 0x10, isel.code()); try lowerToMiEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
.disp = @bitCast(u32, @as(i32, -8)),
.base = .rbp,
}), 0x10, isel.code());
try expectEqualHexStrings( try expectEqualHexStrings(
"\x48\xc7\x45\xf8\x10\x00\x00\x00", "\x48\xc7\x45\xf8\x10\x00\x00\x00",
isel.lowered(), isel.lowered(),
"mov qword ptr [rbp - 8], 0x10", "mov qword ptr [rbp - 8], 0x10",
); );
try lowerToMiEnc(.mov, RegisterOrMemory.mem(.word_ptr, .{ .disp = -2, .base = .rbp }), 0x10, isel.code()); try lowerToMiEnc(.mov, RegisterOrMemory.mem(.word_ptr, .{
.disp = @bitCast(u32, @as(i32, -2)),
.base = .rbp,
}), 0x10, isel.code());
try expectEqualHexStrings("\x66\xC7\x45\xFE\x10\x00", isel.lowered(), "mov word ptr [rbp - 2], 0x10"); try expectEqualHexStrings("\x66\xC7\x45\xFE\x10\x00", isel.lowered(), "mov word ptr [rbp - 2], 0x10");
try lowerToMiEnc(.mov, RegisterOrMemory.mem(.byte_ptr, .{ .disp = -1, .base = .rbp }), 0x10, isel.code()); try lowerToMiEnc(.mov, RegisterOrMemory.mem(.byte_ptr, .{
.disp = @bitCast(u32, @as(i32, -1)),
.base = .rbp,
}), 0x10, isel.code());
try expectEqualHexStrings("\xC6\x45\xFF\x10", isel.lowered(), "mov byte ptr [rbp - 1], 0x10"); try expectEqualHexStrings("\xC6\x45\xFF\x10", isel.lowered(), "mov byte ptr [rbp - 1], 0x10");
try lowerToMiEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ try lowerToMiEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
.disp = 0x10000000, .disp = 0x10000000,
@ -1897,12 +1896,15 @@ test "lower RM encoding" {
isel.lowered(), isel.lowered(),
"sub r11, qword ptr [r12 + 0x10000000]", "sub r11, qword ptr [r12 + 0x10000000]",
); );
try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ .disp = -4, .base = .rbp }), isel.code()); try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{
.disp = @bitCast(u32, @as(i32, -4)),
.base = .rbp,
}), isel.code());
try expectEqualHexStrings("\x48\x8B\x45\xFC", isel.lowered(), "mov rax, qword ptr [rbp - 4]"); try expectEqualHexStrings("\x48\x8B\x45\xFC", isel.lowered(), "mov rax, qword ptr [rbp - 4]");
try lowerToRmEnc(.lea, .rax, RegisterOrMemory.rip(.qword_ptr, 0x10), isel.code()); try lowerToRmEnc(.lea, .rax, RegisterOrMemory.rip(.qword_ptr, 0x10), isel.code());
try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", isel.lowered(), "lea rax, [rip + 0x10]"); try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", isel.lowered(), "lea rax, [rip + 0x10]");
try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{
.disp = -8, .disp = @bitCast(u32, @as(i32, -8)),
.base = .rbp, .base = .rbp,
.scale_index = .{ .scale_index = .{
.scale = 0, .scale = 0,
@ -1911,7 +1913,7 @@ test "lower RM encoding" {
}), isel.code()); }), isel.code());
try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", isel.lowered(), "mov rax, qword ptr [rbp + rcx*1 - 8]"); try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", isel.lowered(), "mov rax, qword ptr [rbp + rcx*1 - 8]");
try lowerToRmEnc(.mov, .eax, RegisterOrMemory.mem(.dword_ptr, .{ try lowerToRmEnc(.mov, .eax, RegisterOrMemory.mem(.dword_ptr, .{
.disp = -4, .disp = @bitCast(u32, @as(i32, -4)),
.base = .rbp, .base = .rbp,
.scale_index = .{ .scale_index = .{
.scale = 2, .scale = 2,
@ -1920,7 +1922,7 @@ test "lower RM encoding" {
}), isel.code()); }), isel.code());
try expectEqualHexStrings("\x8B\x44\x95\xFC", isel.lowered(), "mov eax, dword ptr [rbp + rdx*4 - 4]"); try expectEqualHexStrings("\x8B\x44\x95\xFC", isel.lowered(), "mov eax, dword ptr [rbp + rdx*4 - 4]");
try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{
.disp = -8, .disp = @bitCast(u32, @as(i32, -8)),
.base = .rbp, .base = .rbp,
.scale_index = .{ .scale_index = .{
.scale = 3, .scale = 3,
@ -1929,7 +1931,7 @@ test "lower RM encoding" {
}), isel.code()); }), isel.code());
try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", isel.lowered(), "mov rax, qword ptr [rbp + rcx*8 - 8]"); try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", isel.lowered(), "mov rax, qword ptr [rbp + rcx*8 - 8]");
try lowerToRmEnc(.mov, .r8b, RegisterOrMemory.mem(.byte_ptr, .{ try lowerToRmEnc(.mov, .r8b, RegisterOrMemory.mem(.byte_ptr, .{
.disp = -24, .disp = @bitCast(u32, @as(i32, -24)),
.base = .rsi, .base = .rsi,
.scale_index = .{ .scale_index = .{
.scale = 0, .scale = 0,
@ -1953,7 +1955,10 @@ test "lower MR encoding" {
defer isel.deinit(); defer isel.deinit();
try lowerToMrEnc(.mov, RegisterOrMemory.reg(.rax), .rbx, isel.code()); try lowerToMrEnc(.mov, RegisterOrMemory.reg(.rax), .rbx, isel.code());
try expectEqualHexStrings("\x48\x89\xd8", isel.lowered(), "mov rax, rbx"); try expectEqualHexStrings("\x48\x89\xd8", isel.lowered(), "mov rax, rbx");
try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ .disp = -4, .base = .rbp }), .r11, isel.code()); try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
.disp = @bitCast(u32, @as(i32, -4)),
.base = .rbp,
}), .r11, isel.code());
try expectEqualHexStrings("\x4c\x89\x5d\xfc", isel.lowered(), "mov qword ptr [rbp - 4], r11"); try expectEqualHexStrings("\x4c\x89\x5d\xfc", isel.lowered(), "mov qword ptr [rbp - 4], r11");
try lowerToMrEnc(.add, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), .r12b, isel.code()); try lowerToMrEnc(.add, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), .r12b, isel.code());
try expectEqualHexStrings( try expectEqualHexStrings(
@ -2063,7 +2068,7 @@ test "lower RMI encoding" {
var isel = TestIsel.init(); var isel = TestIsel.init();
defer isel.deinit(); defer isel.deinit();
try lowerToRmiEnc(.imul, .rax, RegisterOrMemory.mem(.qword_ptr, .{ try lowerToRmiEnc(.imul, .rax, RegisterOrMemory.mem(.qword_ptr, .{
.disp = -8, .disp = @bitCast(u32, @as(i32, -8)),
.base = .rbp, .base = .rbp,
}), 0x10, isel.code()); }), 0x10, isel.code());
try expectEqualHexStrings( try expectEqualHexStrings(
@ -2072,12 +2077,12 @@ test "lower RMI encoding" {
"imul rax, qword ptr [rbp - 8], 0x10", "imul rax, qword ptr [rbp - 8], 0x10",
); );
try lowerToRmiEnc(.imul, .eax, RegisterOrMemory.mem(.dword_ptr, .{ try lowerToRmiEnc(.imul, .eax, RegisterOrMemory.mem(.dword_ptr, .{
.disp = -4, .disp = @bitCast(u32, @as(i32, -4)),
.base = .rbp, .base = .rbp,
}), 0x10, isel.code()); }), 0x10, isel.code());
try expectEqualHexStrings("\x69\x45\xFC\x10\x00\x00\x00", isel.lowered(), "imul eax, dword ptr [rbp - 4], 0x10"); try expectEqualHexStrings("\x69\x45\xFC\x10\x00\x00\x00", isel.lowered(), "imul eax, dword ptr [rbp - 4], 0x10");
try lowerToRmiEnc(.imul, .ax, RegisterOrMemory.mem(.word_ptr, .{ try lowerToRmiEnc(.imul, .ax, RegisterOrMemory.mem(.word_ptr, .{
.disp = -2, .disp = @bitCast(u32, @as(i32, -2)),
.base = .rbp, .base = .rbp,
}), 0x10, isel.code()); }), 0x10, isel.code());
try expectEqualHexStrings("\x66\x69\x45\xFE\x10\x00", isel.lowered(), "imul ax, word ptr [rbp - 2], 0x10"); try expectEqualHexStrings("\x66\x69\x45\xFE\x10\x00", isel.lowered(), "imul ax, word ptr [rbp - 2], 0x10");

View File

@ -302,7 +302,7 @@ pub const Inst = struct {
/// Another instruction. /// Another instruction.
inst: Index, inst: Index,
/// A 32-bit immediate value. /// A 32-bit immediate value.
imm: i32, imm: u32,
/// An extern function. /// An extern function.
/// Index into the linker's string table. /// Index into the linker's string table.
extern_fn: u32, extern_fn: u32,
@ -324,8 +324,8 @@ pub const Inst = struct {
}; };
pub const ImmPair = struct { pub const ImmPair = struct {
dest_off: i32, dest_off: u32,
operand: i32, operand: u32,
}; };
pub const Imm64 = struct { pub const Imm64 = struct {

View File

@ -1700,6 +1700,36 @@ pub fn addCases(ctx: *TestContext) !void {
\\ if (!ok) unreachable; \\ if (!ok) unreachable;
\\} \\}
, ""); , "");
case.addCompareOutput(
\\pub fn main() void {
\\ var x: u16 = undefined;
\\ set(&x);
\\ assert(x == 123);
\\}
\\
\\fn set(x: *u16) void {
\\ x.* = 123;
\\}
\\
\\fn assert(ok: bool) void {
\\ if (!ok) unreachable;
\\}
, "");
case.addCompareOutput(
\\pub fn main() void {
\\ var x: u8 = undefined;
\\ set(&x);
\\ assert(x == 123);
\\}
\\
\\fn set(x: *u8) void {
\\ x.* = 123;
\\}
\\
\\fn assert(ok: bool) void {
\\ if (!ok) unreachable;
\\}
, "");
} }
{ {