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 {
516516
517517 /// OP moffs, al/ax/eax/rax
518518 td,
519
520 /// OP r64, r/m64, imm32
521 rmi,
519522};
520523
521524const OpCode = union(enum) {
......@@ -651,6 +654,10 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
651654 .mov => OpCode.oneByte(if (is_one_byte) 0xa2 else 0xa3),
652655 else => null,
653656 },
657 .rmi => return switch (tag) {
658 .imul => OpCode.oneByte(if (is_one_byte) 0x6b else 0x69),
659 else => null,
660 },
654661 }
655662}
656663
......@@ -1180,6 +1187,96 @@ fn lowerToMrEnc(
11801187 }
11811188}
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
11831280fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
11841281 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
11851282 switch (ops.flags) {
......@@ -1383,22 +1480,7 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
13831480 0b00 => return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code),
13841481 0b10 => {
13851482 const imm = emit.mir.instructions.items(.data)[inst].imm;
1386 const opc: u8 = if (imm <= math.maxInt(i8)) 0x6b else 0x69;
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 }
1483 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);
14021484 },
14031485 else => return emit.fail("TODO implement imul", .{}),
14041486 }
......@@ -1842,3 +1924,12 @@ test "lower O encoding" {
18421924 try lowerToOEnc(.push, .r12w, code.buffer());
18431925 try expectEqualHexStrings("\x66\x41\x54", code.emitted(), "push r12w");
18441926}
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}