| ... | ... | @@ -516,6 +516,9 @@ const Encoding = enum { |
| 516 | 516 | |
| 517 | 517 | /// OP moffs, al/ax/eax/rax |
| 518 | 518 | td, |
| 519 | |
| 520 | /// OP r64, r/m64, imm32 |
| 521 | rmi, |
| 519 | 522 | }; |
| 520 | 523 | |
| 521 | 524 | const OpCode = union(enum) { |
| ... | ... | @@ -651,6 +654,10 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { |
| 651 | 654 | .mov => OpCode.oneByte(if (is_one_byte) 0xa2 else 0xa3), |
| 652 | 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 | } |
| 656 | 663 | |
| ... | ... | @@ -1180,6 +1187,96 @@ fn lowerToMrEnc( |
| 1180 | 1187 | } |
| 1181 | 1188 | } |
| 1182 | 1189 | |
| 1190 | fn 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 | |
| 1183 | 1280 | fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 1184 | 1281 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); |
| 1185 | 1282 | switch (ops.flags) { |
| ... | ... | @@ -1383,22 +1480,7 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1383 | 1480 | 0b00 => return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code), |
| 1384 | 1481 | 0b10 => { |
| 1385 | 1482 | 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); |
| 1402 | 1484 | }, |
| 1403 | 1485 | else => return emit.fail("TODO implement imul", .{}), |
| 1404 | 1486 | } |
| ... | ... | @@ -1842,3 +1924,12 @@ test "lower O encoding" { |
| 1842 | 1924 | try lowerToOEnc(.push, .r12w, code.buffer()); |
| 1843 | 1925 | try expectEqualHexStrings("\x66\x41\x54", code.emitted(), "push r12w"); |
| 1844 | 1926 | } |
| 1927 | |
| 1928 | test "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 | } |