| ... | @@ -516,6 +516,9 @@ const Encoding = enum { | ... | @@ -516,6 +516,9 @@ const Encoding = enum { |
| 516 | | 516 | |
| 517 | /// OP moffs, al/ax/eax/rax | 517 | /// OP moffs, al/ax/eax/rax |
| 518 | td, | 518 | td, |
| | 519 | |
| | 520 | /// OP r64, r/m64, imm32 |
| | 521 | rmi, |
| 519 | }; | 522 | }; |
| 520 | | 523 | |
| 521 | const OpCode = union(enum) { | 524 | const 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 | } |
| 656 | | 663 | |
| ... | @@ -1180,6 +1187,96 @@ fn lowerToMrEnc( | ... | @@ -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 | fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 1280 | fn 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 | |
| | 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 | } |