| 1876 | } |
| 1877 | |
| 1878 | void OpDispatchBuilder::MULX(OpcodeArgs) { |
| 1879 | // RDX is the implied source operand in the instruction |
| 1880 | const auto OpSize = OpSizeFromSrc(Op); |
| 1881 | |
| 1882 | // Src1 can be a memory operand, so ensure we constrain to the |
| 1883 | // absolute width of the access in that scenario. |
| 1884 | const auto GPRSize = GetGPROpSize(); |
| 1885 | const auto Src1Size = Op->Src[1].IsGPR() ? GPRSize : OpSize; |
| 1886 | |
| 1887 | Ref Src1 = LoadSource_WithOpSize(GPRClass, Op, Op->Src[1], Src1Size, Op->Flags); |
| 1888 | Ref Src2 = LoadGPRRegister(X86State::REG_RDX, GPRSize); |
| 1889 | |
| 1890 | // As per the Intel Software Development Manual, if the destination and |
| 1891 | // first operand correspond to the same register, then the result |
| 1892 | // will be the high half of the multiplication result. |
| 1893 | if (Op->Dest.Data.GPR.GPR == Op->Src[0].Data.GPR.GPR) { |
| 1894 | Ref ResultHi = _UMulH(OpSize, Src1, Src2); |
| 1895 | StoreResult(GPRClass, Op, Op->Dest, ResultHi, OpSize::iInvalid); |
| 1896 | } else { |
| 1897 | Ref ResultLo = _UMul(OpSize, Src1, Src2); |
| 1898 | Ref ResultHi = _UMulH(OpSize, Src1, Src2); |
| 1899 | |
| 1900 | StoreResult(GPRClass, Op, Op->Src[0], ResultLo, OpSize::iInvalid); |
| 1901 | StoreResult(GPRClass, Op, Op->Dest, ResultHi, OpSize::iInvalid); |
| 1902 | } |
| 1903 | } |
| 1904 | |
| 1905 | void OpDispatchBuilder::PDEP(OpcodeArgs) { |
| 1906 | LOGMAN_THROW_A_FMT(Op->InstSize >= 4, "No masking needed"); |