authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-23 21:14:14+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-23 21:14:14+01:00
log35fe088e0e341d06d97f1ac0f799b875c71f242a
tree1bfb258bc478c3e1b017895a897cdaad5ee6e6b4
parentdba5df64eacfbcca9727fe8596b186defb30a753

stage2: add lowering of RMI encoding

Example includes imul with 3 operands such as imul r64, r/m64, imm32.

1 files changed, 107 insertions(+), 16 deletions(-)

src/arch/x86_64/Emit.zig+107-16
...@@ -516,6 +516,9 @@ const Encoding = enum {...@@ -516,6 +516,9 @@ const Encoding = enum {
516516
517 /// OP moffs, al/ax/eax/rax517 /// OP moffs, al/ax/eax/rax
518 td,518 td,
519
520 /// OP r64, r/m64, imm32
521 rmi,
519};522};
520523
521const OpCode = union(enum) {524const OpCode = union(enum) {
...@@ -651,6 +654,10 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {...@@ -651,6 +654,10 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
651 .mov => OpCode.oneByte(if (is_one_byte) 0xa2 else 0xa3),654 .mov => OpCode.oneByte(if (is_one_byte) 0xa2 else 0xa3),
652 else => null,655 else => null,
653 },656 },
657 .rmi => return switch (tag) {
658 .imul => OpCode.oneByte(if (is_one_byte) 0x6b else 0x69),
659 else => null,
660 },
654 }661 }
655}662}
656663
...@@ -1180,6 +1187,96 @@ fn lowerToMrEnc(...@@ -1180,6 +1187,96 @@ fn lowerToMrEnc(
1180 }1187 }
1181}1188}
11821189
1190fn lowerToRmiEnc(
1191 tag: Tag,
1192 reg: Register,
1193 reg_or_mem: RegisterOrMemory,
1194 imm: i32,
1195 code: *std.ArrayList(u8),
1196) InnerError!void {
1197 const opc = getOpCode(tag, .rmi, reg.size() == 8).?;
1198 switch (reg_or_mem) {
1199 .register => |src_reg| {
1200 if (reg.size() != src_reg.size()) return error.EmitFail;
1201 const encoder = try Encoder.init(code, 7);
1202 encoder.rex(.{
1203 .w = reg.size() == 64,
1204 .r = reg.isExtended(),
1205 .b = src_reg.isExtended(),
1206 });
1207 opc.encode(encoder);
1208 encoder.modRm_direct(reg.lowId(), src_reg.lowId());
1209 switch (reg.size()) {
1210 8 => {
1211 const imm8 = try math.cast(i8, imm);
1212 encoder.imm8(imm8);
1213 },
1214 16 => {
1215 const imm16 = try math.cast(i16, imm);
1216 encoder.imm16(imm16);
1217 },
1218 32, 64 => encoder.imm32(imm),
1219 else => unreachable,
1220 }
1221 },
1222 .memory => |src_mem| {
1223 const encoder = try Encoder.init(code, 13);
1224 if (reg.size() == 16) {
1225 encoder.opcode_1byte(0x66);
1226 }
1227 if (src_mem.reg) |src_reg| {
1228 // TODO handle 32-bit base register - requires prefix 0x67
1229 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
1230 if (src_reg.size() != 64) return error.EmitFail;
1231 encoder.rex(.{
1232 .w = reg.size() == 64,
1233 .r = reg.isExtended(),
1234 .b = src_reg.isExtended(),
1235 });
1236 opc.encode(encoder);
1237 if (src_reg.lowId() == 4) {
1238 if (src_mem.disp == 0) {
1239 encoder.modRm_SIBDisp0(reg.lowId());
1240 encoder.sib_base(src_reg.lowId());
1241 } else if (immOpSize(src_mem.disp) == 8) {
1242 encoder.modRm_SIBDisp8(reg.lowId());
1243 encoder.sib_baseDisp8(src_reg.lowId());
1244 encoder.disp8(@intCast(i8, src_mem.disp));
1245 } else {
1246 encoder.modRm_SIBDisp32(reg.lowId());
1247 encoder.sib_baseDisp32(src_reg.lowId());
1248 encoder.disp32(src_mem.disp);
1249 }
1250 } else {
1251 if (src_mem.disp == 0) {
1252 encoder.modRm_indirectDisp0(reg.lowId(), src_reg.lowId());
1253 } else if (immOpSize(src_mem.disp) == 8) {
1254 encoder.modRm_indirectDisp8(reg.lowId(), src_reg.lowId());
1255 encoder.disp8(@intCast(i8, src_mem.disp));
1256 } else {
1257 encoder.modRm_indirectDisp32(reg.lowId(), src_reg.lowId());
1258 encoder.disp32(src_mem.disp);
1259 }
1260 }
1261 } else {
1262 encoder.rex(.{
1263 .w = reg.size() == 64,
1264 .r = reg.isExtended(),
1265 });
1266 opc.encode(encoder);
1267 if (src_mem.rip) {
1268 encoder.modRm_RIPDisp32(reg.lowId());
1269 } else {
1270 encoder.modRm_SIBDisp0(reg.lowId());
1271 encoder.sib_disp32();
1272 }
1273 encoder.disp32(src_mem.disp);
1274 }
1275 encoder.imm32(imm);
1276 },
1277 }
1278}
1279
1183fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {1280fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1184 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);1281 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
1185 switch (ops.flags) {1282 switch (ops.flags) {
...@@ -1383,22 +1480,7 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1383,22 +1480,7 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1383 0b00 => return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code),1480 0b00 => return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code),
1384 0b10 => {1481 0b10 => {
1385 const imm = emit.mir.instructions.items(.data)[inst].imm;1482 const imm = emit.mir.instructions.items(.data)[inst].imm;
1386 const opc: u8 = if (imm <= math.maxInt(i8)) 0x6b else 0x69;1483 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);
1387 const encoder = try Encoder.init(emit.code, 7);
1388 encoder.rex(.{
1389 .w = ops.reg1.size() == 64,
1390 .r = ops.reg1.isExtended(),
1391 .b = ops.reg1.isExtended(),
1392 });
1393 encoder.opcode_1byte(opc);
1394 encoder.modRm_direct(ops.reg1.lowId(), ops.reg2.lowId());
1395 if (imm <= math.maxInt(i8)) {
1396 encoder.imm8(@intCast(i8, imm));
1397 } else if (imm <= math.maxInt(i16)) {
1398 encoder.imm16(@intCast(i16, imm));
1399 } else {
1400 encoder.imm32(imm);
1401 }
1402 },1484 },
1403 else => return emit.fail("TODO implement imul", .{}),1485 else => return emit.fail("TODO implement imul", .{}),
1404 }1486 }
...@@ -1842,3 +1924,12 @@ test "lower O encoding" {...@@ -1842,3 +1924,12 @@ test "lower O encoding" {
1842 try lowerToOEnc(.push, .r12w, code.buffer());1924 try lowerToOEnc(.push, .r12w, code.buffer());
1843 try expectEqualHexStrings("\x66\x41\x54", code.emitted(), "push r12w");1925 try expectEqualHexStrings("\x66\x41\x54", code.emitted(), "push r12w");
1844}1926}
1927
1928test "lower RMI encoding" {
1929 var code = TestEmitCode.init();
1930 defer code.deinit();
1931 try lowerToRmiEnc(.imul, .rax, RegisterOrMemory.mem(.rbp, -8), 0x10, code.buffer());
1932 try expectEqualHexStrings("\x48\x69\x45\xF8\x10\x00\x00\x00", code.emitted(), "imul rax, [rbp - 8], 0x10");
1933 try lowerToRmiEnc(.imul, .r12, RegisterOrMemory.reg(.r12), 0x10, code.buffer());
1934 try expectEqualHexStrings("\x4D\x69\xE4\x10\x00\x00\x00", code.emitted(), "imul r12, r12, 0x10");
1935}