stage2: add lowering of D encoding

Example of such encoding includes a near/far call and jmp instructions.
This commit is contained in:
Jakub Konka 2021-12-23 00:36:44 +01:00
parent 362eccf539
commit 1167e248ef

View File

@ -66,40 +66,45 @@ pub fn emitMir(emit: *Emit) InnerError!void {
const inst = @intCast(u32, index);
try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len);
switch (tag) {
.adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp, .mov => try emit.mirArith(tag, inst),
.adc => try emit.mirArith(.adc, inst),
.add => try emit.mirArith(.add, inst),
.sub => try emit.mirArith(.sub, inst),
.xor => try emit.mirArith(.xor, inst),
.@"and" => try emit.mirArith(.@"and", inst),
.@"or" => try emit.mirArith(.@"or", inst),
.sbb => try emit.mirArith(.sbb, inst),
.cmp => try emit.mirArith(.cmp, inst),
.mov => try emit.mirArith(.mov, inst),
.adc_scale_src,
.add_scale_src,
.sub_scale_src,
.xor_scale_src,
.and_scale_src,
.or_scale_src,
.sbb_scale_src,
.cmp_scale_src,
.mov_scale_src,
=> try emit.mirArithScaleSrc(tag, inst),
.adc_scale_src => try emit.mirArithScaleSrc(.adc, inst),
.add_scale_src => try emit.mirArithScaleSrc(.add, inst),
.sub_scale_src => try emit.mirArithScaleSrc(.sub, inst),
.xor_scale_src => try emit.mirArithScaleSrc(.xor, inst),
.and_scale_src => try emit.mirArithScaleSrc(.@"and", inst),
.or_scale_src => try emit.mirArithScaleSrc(.@"or", inst),
.sbb_scale_src => try emit.mirArithScaleSrc(.sbb, inst),
.cmp_scale_src => try emit.mirArithScaleSrc(.cmp, inst),
.mov_scale_src => try emit.mirArithScaleSrc(.mov, inst),
.adc_scale_dst,
.add_scale_dst,
.sub_scale_dst,
.xor_scale_dst,
.and_scale_dst,
.or_scale_dst,
.sbb_scale_dst,
.cmp_scale_dst,
.mov_scale_dst,
=> try emit.mirArithScaleDst(tag, inst),
.adc_scale_dst => try emit.mirArithScaleDst(.adc, inst),
.add_scale_dst => try emit.mirArithScaleDst(.add, inst),
.sub_scale_dst => try emit.mirArithScaleDst(.sub, inst),
.xor_scale_dst => try emit.mirArithScaleDst(.xor, inst),
.and_scale_dst => try emit.mirArithScaleDst(.@"and", inst),
.or_scale_dst => try emit.mirArithScaleDst(.@"or", inst),
.sbb_scale_dst => try emit.mirArithScaleDst(.sbb, inst),
.cmp_scale_dst => try emit.mirArithScaleDst(.cmp, inst),
.mov_scale_dst => try emit.mirArithScaleDst(.mov, inst),
.adc_scale_imm,
.add_scale_imm,
.sub_scale_imm,
.xor_scale_imm,
.and_scale_imm,
.or_scale_imm,
.sbb_scale_imm,
.cmp_scale_imm,
.mov_scale_imm,
=> try emit.mirArithScaleImm(tag, inst),
.adc_scale_imm => try emit.mirArithScaleImm(.adc, inst),
.add_scale_imm => try emit.mirArithScaleImm(.add, inst),
.sub_scale_imm => try emit.mirArithScaleImm(.sub, inst),
.xor_scale_imm => try emit.mirArithScaleImm(.xor, inst),
.and_scale_imm => try emit.mirArithScaleImm(.@"and", inst),
.or_scale_imm => try emit.mirArithScaleImm(.@"or", inst),
.sbb_scale_imm => try emit.mirArithScaleImm(.sbb, inst),
.cmp_scale_imm => try emit.mirArithScaleImm(.cmp, inst),
.mov_scale_imm => try emit.mirArithScaleImm(.mov, inst),
.movabs => try emit.mirMovabs(inst),
@ -110,7 +115,8 @@ pub fn emitMir(emit: *Emit) InnerError!void {
.push, .pop => try emit.mirPushPop(tag, inst),
.jmp, .call => try emit.mirJmpCall(tag, inst),
.jmp => try emit.mirJmpCall(.jmp_near, inst),
.call => try emit.mirJmpCall(.call_near, inst),
.cond_jmp_greater_less,
.cond_jmp_above_below,
@ -283,31 +289,24 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Mir.Inst.Tag, inst: M
}
}
fn mirJmpCall(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
const flag = @truncate(u1, ops.flags);
if (flag == 0) {
const target = emit.mir.instructions.items(.data)[inst].inst;
const opc: u8 = switch (tag) {
.jmp => 0xe9,
.call => 0xe8,
else => unreachable,
};
const source = emit.code.items.len;
const encoder = try Encoder.init(emit.code, 5);
encoder.opcode_1byte(opc);
try lowerToDEnc(tag, 0, emit.code);
try emit.relocs.append(emit.bin_file.allocator, .{
.source = source,
.target = target,
.offset = emit.code.items.len,
.offset = emit.code.items.len - 4,
.length = 5,
});
encoder.imm32(0x0);
return;
}
const modrm_ext: u3 = switch (tag) {
.jmp => 0x4,
.call => 0x2,
.jmp_near => 0x4,
.call_near => 0x2,
else => unreachable,
};
if (ops.reg1 == .none) {
@ -532,7 +531,28 @@ fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
}
}
const Tag = enum {
adc,
add,
sub,
xor,
@"and",
@"or",
sbb,
cmp,
mov,
lea,
jmp_near,
call_near,
};
const Encoding = enum {
/// OP rel32
d,
/// OP r/m64
m,
/// OP r/m64, imm32
mi,
@ -552,12 +572,21 @@ const Encoding = enum {
td,
};
inline fn getOpCode(tag: Mir.Inst.Tag, enc: Encoding) u8 {
inline fn getOpCode(tag: Tag, enc: Encoding) ?u8 {
switch (enc) {
.d => return switch (tag) {
.jmp_near => 0xe9,
.call_near => 0xe8,
else => null,
},
.m => return switch (tag) {
.jmp_near, .call_near => 0xff,
else => null,
},
.mi => return switch (tag) {
.adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => 0x81,
.mov => 0xc7,
else => unreachable,
else => null,
},
.mr => return switch (tag) {
.adc => 0x11,
@ -569,7 +598,7 @@ inline fn getOpCode(tag: Mir.Inst.Tag, enc: Encoding) u8 {
.sbb => 0x19,
.cmp => 0x39,
.mov => 0x89,
else => unreachable,
else => null,
},
.rm => return switch (tag) {
.adc => 0x13,
@ -582,24 +611,24 @@ inline fn getOpCode(tag: Mir.Inst.Tag, enc: Encoding) u8 {
.cmp => 0x3b,
.mov => 0x8b,
.lea => 0x8d,
else => unreachable,
else => null,
},
.oi => return switch (tag) {
.mov => 0xb8,
else => unreachable,
else => null,
},
.fd => return switch (tag) {
.mov => 0xa1,
else => unreachable,
else => null,
},
.td => return switch (tag) {
.mov => 0xa3,
else => unreachable,
else => null,
},
}
}
inline fn getMiModRmExt(tag: Mir.Inst.Tag) u3 {
inline fn getModRmExt(tag: Tag) u3 {
return switch (tag) {
.adc => 0x2,
.add => 0x0,
@ -610,6 +639,7 @@ inline fn getMiModRmExt(tag: Mir.Inst.Tag) u3 {
.sbb => 0x3,
.cmp => 0x7,
.mov => 0x0,
.call_near => 0x2,
else => unreachable,
};
}
@ -655,34 +685,25 @@ const RegisterOrMemory = union(enum) {
}
};
fn lowerToTdEnc(
tag: Mir.Inst.Tag,
moffs: i64,
reg: Register,
code: *std.ArrayList(u8),
) InnerError!void {
fn lowerToDEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void {
const opc = getOpCode(tag, .d).?;
const encoder = try Encoder.init(code, 5);
encoder.opcode_1byte(opc);
encoder.imm32(imm);
}
fn lowerToTdEnc(tag: Tag, moffs: i64, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
return lowerToTdFdEnc(tag, reg, moffs, code, true);
}
fn lowerToFdEnc(
tag: Mir.Inst.Tag,
reg: Register,
moffs: i64,
code: *std.ArrayList(u8),
) InnerError!void {
fn lowerToFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8)) InnerError!void {
return lowerToTdFdEnc(tag, reg, moffs, code, false);
}
fn lowerToTdFdEnc(
tag: Mir.Inst.Tag,
reg: Register,
moffs: i64,
code: *std.ArrayList(u8),
td: bool,
) InnerError!void {
fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8), td: bool) InnerError!void {
if (reg.lowId() != Register.rax.lowId()) return error.EmitFail;
if (reg.size() != immOpSize(moffs)) return error.EmitFail;
var opc = if (td) getOpCode(tag, .td) else getOpCode(tag, .fd);
var opc = if (td) getOpCode(tag, .td).? else getOpCode(tag, .fd).?;
if (reg.size() == 8) {
opc -= 1;
}
@ -714,13 +735,8 @@ fn lowerToTdFdEnc(
}
}
fn lowerToOiEnc(
tag: Mir.Inst.Tag,
reg: Register,
imm: i64,
code: *std.ArrayList(u8),
) InnerError!void {
var opc = getOpCode(tag, .oi);
fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) InnerError!void {
var opc = getOpCode(tag, .oi).?;
if (reg.size() != immOpSize(imm)) return error.EmitFail;
if (reg.size() == 8) {
opc -= 8;
@ -754,14 +770,9 @@ fn lowerToOiEnc(
}
}
fn lowerToMiEnc(
tag: Mir.Inst.Tag,
reg_or_mem: RegisterOrMemory,
imm: i32,
code: *std.ArrayList(u8),
) InnerError!void {
var opc = getOpCode(tag, .mi);
const modrm_ext = getMiModRmExt(tag);
fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.ArrayList(u8)) InnerError!void {
var opc = getOpCode(tag, .mi).?;
const modrm_ext = getModRmExt(tag);
switch (reg_or_mem) {
.register => |dst_reg| {
if (dst_reg.size() == 8) {
@ -839,12 +850,12 @@ fn lowerToMiEnc(
}
fn lowerToRmEnc(
tag: Mir.Inst.Tag,
tag: Tag,
reg: Register,
reg_or_mem: RegisterOrMemory,
code: *std.ArrayList(u8),
) InnerError!void {
var opc = getOpCode(tag, .rm);
var opc = getOpCode(tag, .rm).?;
if (reg.size() == 8) {
opc -= 1;
}
@ -909,7 +920,7 @@ fn lowerToRmEnc(
}
fn lowerToMrEnc(
tag: Mir.Inst.Tag,
tag: Tag,
reg_or_mem: RegisterOrMemory,
reg: Register,
code: *std.ArrayList(u8),
@ -920,7 +931,7 @@ fn lowerToMrEnc(
// * reg is 32bit - dword ptr
// * reg is 16bit - word ptr
// * reg is 8bit - byte ptr
var opc = getOpCode(tag, .mr);
var opc = getOpCode(tag, .mr).?;
if (reg.size() == 8) {
opc -= 1;
}
@ -982,7 +993,7 @@ fn lowerToMrEnc(
}
}
fn mirArith(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
fn mirArith(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 => {
@ -1053,11 +1064,11 @@ fn immOpSize(imm: i64) u8 {
return 64;
}
fn mirArithScaleSrc(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
const scale = ops.flags;
// OP reg1, [reg2 + scale*rcx + imm32]
var opc = getOpCode(tag, .rm);
var opc = getOpCode(tag, .rm).?;
if (ops.reg1.size() == 8) {
opc -= 1;
}
@ -1080,15 +1091,15 @@ fn mirArithScaleSrc(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE
}
}
fn mirArithScaleDst(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
const scale = ops.flags;
const imm = emit.mir.instructions.items(.data)[inst].imm;
if (ops.reg2 == .none) {
// OP [reg1 + scale*rax + 0], imm32
var opc = getOpCode(tag, .mi);
const modrm_ext = getMiModRmExt(tag);
var opc = getOpCode(tag, .mi).?;
const modrm_ext = getModRmExt(tag);
if (ops.reg1.size() == 8) {
opc -= 1;
}
@ -1111,7 +1122,7 @@ fn mirArithScaleDst(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE
}
// OP [reg1 + scale*rax + imm32], reg2
var opc = getOpCode(tag, .mr);
var opc = getOpCode(tag, .mr).?;
if (ops.reg1.size() == 8) {
opc -= 1;
}
@ -1133,16 +1144,16 @@ fn mirArithScaleDst(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE
}
}
fn mirArithScaleImm(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
const scale = ops.flags;
const payload = emit.mir.instructions.items(.data)[inst].payload;
const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;
var opc = getOpCode(tag, .mi);
var opc = getOpCode(tag, .mi).?;
if (ops.reg1.size() == 8) {
opc -= 1;
}
const modrm_ext = getMiModRmExt(tag);
const modrm_ext = getModRmExt(tag);
const encoder = try Encoder.init(emit.code, 2);
encoder.rex(.{
.w = ops.reg1.size() == 64,
@ -1230,7 +1241,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
assert(ops.flags == 0b01);
const imm = emit.mir.instructions.items(.data)[inst].imm;
return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(ops.reg2, imm), emit.code);
return lowerToRmEnc(.lea, ops.reg1, RegisterOrMemory.mem(ops.reg2, imm), emit.code);
}
fn mirLeaRip(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
@ -1272,12 +1283,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
assert(tag == .call_extern);
const n_strx = emit.mir.instructions.items(.data)[inst].extern_fn;
const offset = blk: {
const offset = @intCast(u32, emit.code.items.len + 1);
// callq
const encoder = try Encoder.init(emit.code, 5);
encoder.opcode_1byte(0xe8);
encoder.imm32(0x0);
break :blk offset;
try lowerToDEnc(.call_near, 0, emit.code);
break :blk @intCast(u32, emit.code.items.len) - 4;
};
if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
// Add relocation to the decl.