stage2,x64: impl lowering of shift ops in Emit

This commit is contained in:
Jakub Konka 2022-02-07 20:11:42 +01:00
parent 7a9b9df80e
commit becbf446d3
4 changed files with 203 additions and 48 deletions

View File

@ -2037,17 +2037,42 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
break :result MCValue{ .stack_offset = stack_offset };
},
.register => |reg| {
// 1. Shift by struct_field_offset.
// 2. Mask with reg.size() - struct_field_size
// 3. Return in register
// TODO check if register can be re-used
self.register_manager.freezeRegs(&.{reg});
defer self.register_manager.unfreezeRegs(&.{reg});
const dst_mcv = blk: {
if (self.reuseOperand(inst, operand, 0, mcv)) {
break :blk mcv;
} else {
const dst_mcv = try self.copyToNewRegister(inst, Type.usize, .{ .register = reg.to64() });
break :blk dst_mcv;
}
};
// TODO shift here
// Shift by struct_field_offset.
const shift_amount = @intCast(u8, struct_field_offset * 8);
if (shift_amount > 0) {
if (shift_amount == 1) {
_ = try self.addInst(.{
.tag = .shr,
.ops = (Mir.Ops{
.reg1 = dst_mcv.register,
}).encode(),
.data = undefined,
});
} else {
_ = try self.addInst(.{
.tag = .shr,
.ops = (Mir.Ops{
.reg1 = dst_mcv.register,
.flags = 0b10,
}).encode(),
.data = .{ .imm = shift_amount },
});
}
}
// Mask with reg.size() - struct_field_size
const mask_shift = @intCast(u6, (64 - struct_field_ty.bitSize(self.target.*)));
const mask = (~@as(u64, 0)) >> mask_shift;
try self.genBinMathOpMir(.@"and", Type.usize, dst_mcv, .{ .immediate = mask });

View File

@ -133,6 +133,11 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
.lea => try emit.mirLea(inst),
.lea_pie => try emit.mirLeaPie(inst),
.shl => try emit.mirShift(.shl, inst),
.sal => try emit.mirShift(.sal, inst),
.shr => try emit.mirShift(.shr, inst),
.sar => try emit.mirShift(.sar, inst),
.imul_complex => try emit.mirIMulComplex(inst),
.push => try emit.mirPushPop(.push, inst),
@ -653,6 +658,31 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
return lowerToFdEnc(.mov, ops.reg1, imm, emit.code);
}
fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
switch (ops.flags) {
0b00 => {
// sal reg1, 1
// M1
return lowerToM1Enc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
},
0b01 => {
// sal reg1, .cl
// MC
return lowerToMcEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
},
0b10 => {
// sal reg1, imm8
// MI
const imm = @truncate(u8, emit.mir.instructions.items(.data)[inst].imm);
return lowerToMiImm8Enc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code);
},
0b11 => {
return emit.fail("TODO unused variant: SHIFT reg1, 0b11", .{});
},
}
}
fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
const tag = emit.mir.instructions.items(.tag)[inst];
assert(tag == .imul_complex);
@ -743,13 +773,13 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
emit.code,
);
const end_offset = emit.code.items.len;
const sym_index = emit.mir.instructions.items(.data)[inst].linker_sym_index;
if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
const reloc_type = switch (ops.flags) {
0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT),
0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}),
};
const sym_index = emit.mir.instructions.items(.data)[inst].linker_sym_index;
if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
const decl = macho_file.active_decl.?;
try decl.link.macho.relocs.append(emit.bin_file.allocator, .{
.offset = @intCast(u32, end_offset - 4),
@ -1064,6 +1094,10 @@ const Tag = enum {
setng,
setnle,
setg,
shl,
sal,
shr,
sar,
fn isSetCC(tag: Tag) bool {
return switch (tag) {
@ -1119,9 +1153,18 @@ const Encoding = enum {
/// OP imm32
i,
/// OP r/m64, 1
m1,
/// OP r/m64, .cl
mc,
/// OP r/m64, imm32
mi,
/// OP r/m64, imm8
mi8,
/// OP r/m64, r64
mr,
@ -1230,12 +1273,25 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
.ret_far => OpCode.oneByte(0xca),
else => null,
},
.m1 => return switch (tag) {
.shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd0 else 0xd1),
else => null,
},
.mc => return switch (tag) {
.shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd2 else 0xd3),
else => null,
},
.mi => return switch (tag) {
.adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(if (is_one_byte) 0x80 else 0x81),
.mov => OpCode.oneByte(if (is_one_byte) 0xc6 else 0xc7),
.@"test" => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),
else => null,
},
.mi8 => return switch (tag) {
.adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(0x83),
.shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xc0 else 0xc1),
else => null,
},
.mr => return switch (tag) {
.adc => OpCode.oneByte(if (is_one_byte) 0x10 else 0x11),
.add => OpCode.oneByte(if (is_one_byte) 0x00 else 0x01),
@ -1331,6 +1387,11 @@ inline fn getModRmExt(tag: Tag) ?u3 {
.setnle,
.setg,
=> 0x0,
.shl,
.sal,
=> 0x4,
.shr => 0x5,
.sar => 0x7,
else => null,
};
}
@ -1528,8 +1589,8 @@ fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
encoder.imm32(@bitCast(i32, imm));
}
fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void {
const opc = getOpCode(tag, .m, false).?;
fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *std.ArrayList(u8)) InnerError!void {
const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?;
const modrm_ext = getModRmExt(tag).?;
switch (reg_or_mem) {
.register => |reg| {
@ -1537,11 +1598,9 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
if (reg.size() == 16) {
encoder.prefix16BitMode();
}
const wide = if (tag == .jmp_near) false else setRexWRegister(reg);
encoder.rex(.{
.w = switch (reg) {
.ah, .bh, .ch, .dh => true,
else => false,
},
.w = wide,
.b = reg.isExtended(),
});
opc.encode(encoder);
@ -1553,8 +1612,9 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
encoder.prefix16BitMode();
}
if (mem_op.base) |base| {
const wide = if (tag == .jmp_near) false else mem_op.ptr_size == .qword_ptr;
encoder.rex(.{
.w = false,
.w = wide,
.b = base.isExtended(),
});
}
@ -1564,6 +1624,18 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
}
}
fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void {
return lowerToMxEnc(tag, reg_or_mem, .m, code);
}
fn lowerToM1Enc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void {
return lowerToMxEnc(tag, reg_or_mem, .m1, code);
}
fn lowerToMcEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void {
return lowerToMxEnc(tag, reg_or_mem, .mc, code);
}
fn lowerToTdEnc(tag: Tag, moffs: u64, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
return lowerToTdFdEnc(tag, reg, moffs, code, true);
}
@ -1614,9 +1686,15 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) Inn
}
}
fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
fn lowerToMiXEnc(
tag: Tag,
reg_or_mem: RegisterOrMemory,
imm: u32,
enc: Encoding,
code: *std.ArrayList(u8),
) InnerError!void {
const modrm_ext = getModRmExt(tag).?;
const opc = getOpCode(tag, .mi, reg_or_mem.size() == 8).?;
const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?;
switch (reg_or_mem) {
.register => |dst_reg| {
const encoder = try Encoder.init(code, 7);
@ -1632,7 +1710,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.Arr
});
opc.encode(encoder);
encoder.modRm_direct(modrm_ext, dst_reg.lowId());
encodeImm(encoder, imm, dst_reg.size());
encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_reg.size());
},
.memory => |dst_mem| {
const encoder = try Encoder.init(code, 12);
@ -1651,11 +1729,19 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.Arr
}
opc.encode(encoder);
dst_mem.encode(encoder, modrm_ext);
encodeImm(encoder, imm, dst_mem.ptr_size.size());
encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_mem.ptr_size.size());
},
}
}
fn lowerToMiImm8Enc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u8, code: *std.ArrayList(u8)) InnerError!void {
return lowerToMiXEnc(tag, reg_or_mem, imm, .mi8, code);
}
fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
return lowerToMiXEnc(tag, reg_or_mem, imm, .mi, code);
}
fn lowerToRmEnc(
tag: Tag,
reg: Register,
@ -1902,6 +1988,9 @@ test "lower MI encoding" {
emit.lowered(),
"mov qword ptr [rcx*2 + 0x10000000], 0x10",
);
try lowerToMiImm8Enc(.add, RegisterOrMemory.reg(.rax), 0x10, emit.code());
try expectEqualHexStrings("\x48\x83\xC0\x10", emit.lowered(), "add rax, 0x10");
}
test "lower RM encoding" {
@ -2100,6 +2189,41 @@ test "lower M encoding" {
try expectEqualHexStrings("\x41\x0F\x97\xC3", emit.lowered(), "seta r11b");
}
test "lower M1 and MC encodings" {
var emit = TestEmit.init();
defer emit.deinit();
try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12), emit.code());
try expectEqualHexStrings("\x49\xD1\xE4", emit.lowered(), "sal r12, 1");
try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12d), emit.code());
try expectEqualHexStrings("\x41\xD1\xE4", emit.lowered(), "sal r12d, 1");
try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12w), emit.code());
try expectEqualHexStrings("\x66\x41\xD1\xE4", emit.lowered(), "sal r12w, 1");
try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12b), emit.code());
try expectEqualHexStrings("\x41\xD0\xE4", emit.lowered(), "sal r12b, 1");
try lowerToM1Enc(.sal, RegisterOrMemory.reg(.rax), emit.code());
try expectEqualHexStrings("\x48\xD1\xE0", emit.lowered(), "sal rax, 1");
try lowerToM1Enc(.sal, RegisterOrMemory.reg(.eax), emit.code());
try expectEqualHexStrings("\xD1\xE0", emit.lowered(), "sal eax, 1");
try lowerToM1Enc(.sal, RegisterOrMemory.mem(.qword_ptr, .{
.disp = @bitCast(u32, @as(i32, -0x10)),
.base = .rbp,
}), emit.code());
try expectEqualHexStrings("\x48\xD1\x65\xF0", emit.lowered(), "sal qword ptr [rbp - 0x10], 1");
try lowerToM1Enc(.sal, RegisterOrMemory.mem(.dword_ptr, .{
.disp = @bitCast(u32, @as(i32, -0x10)),
.base = .rbp,
}), emit.code());
try expectEqualHexStrings("\xD1\x65\xF0", emit.lowered(), "sal dword ptr [rbp - 0x10], 1");
try lowerToMcEnc(.shr, RegisterOrMemory.reg(.r12), emit.code());
try expectEqualHexStrings("\x49\xD3\xEC", emit.lowered(), "shr r12, cl");
try lowerToMcEnc(.shr, RegisterOrMemory.reg(.rax), emit.code());
try expectEqualHexStrings("\x48\xD3\xE8", emit.lowered(), "shr rax, cl");
try lowerToMcEnc(.sar, RegisterOrMemory.reg(.rsi), emit.code());
try expectEqualHexStrings("\x48\xD3\xFE", emit.lowered(), "sar rsi, cl");
}
test "lower O encoding" {
var emit = TestEmit.init();
defer emit.deinit();

View File

@ -142,30 +142,6 @@ pub const Inst = struct {
rcr_scale_dst,
rcr_scale_imm,
rcr_mem_index_imm,
shl,
shl_mem_imm,
shl_scale_src,
shl_scale_dst,
shl_scale_imm,
shl_mem_index_imm,
sal,
sal_mem_imm,
sal_scale_src,
sal_scale_dst,
sal_scale_imm,
sal_mem_index_imm,
shr,
shr_mem_imm,
shr_scale_src,
shr_scale_dst,
shr_scale_imm,
shr_mem_index_imm,
sar,
sar_mem_imm,
sar_scale_src,
sar_scale_dst,
sar_scale_imm,
sar_mem_index_imm,
sbb,
sbb_mem_imm,
sbb_scale_src,
@ -212,6 +188,37 @@ pub const Inst = struct {
/// * `Data` contains `linker_sym_index`
lea_pie,
/// ops flags: form:
/// 0b00 reg1, 1
/// 0b01 reg1, .cl
/// 0b10 reg1, imm8
/// Notes:
/// * If flags == 0b10, uses `imm`.
shl,
shl_mem_imm,
shl_scale_src,
shl_scale_dst,
shl_scale_imm,
shl_mem_index_imm,
sal,
sal_mem_imm,
sal_scale_src,
sal_scale_dst,
sal_scale_imm,
sal_mem_index_imm,
shr,
shr_mem_imm,
shr_scale_src,
shr_scale_dst,
shr_scale_imm,
shr_mem_index_imm,
sar,
sar_mem_imm,
sar_scale_src,
sar_scale_dst,
sar_scale_imm,
sar_mem_index_imm,
/// ops flags: form:
/// 0bX0 reg1
/// 0bX1 [reg1 + imm32]

View File

@ -372,7 +372,6 @@ pub fn generateSymbol(
return Result{ .appended = {} };
},
.Struct => {
// TODO debug info
const struct_obj = typed_value.ty.castTag(.@"struct").?.data;
if (struct_obj.layout == .Packed) {
return Result{