/* * Copyright (c) 2017 Trail of Bits, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once namespace { template ALWAYS_INLINE static void WriteFlagsIncDec(State &state, T lhs, T rhs, T res) { FLAG_PF = ParityFlag(res); FLAG_AF = AuxCarryFlag(lhs, rhs, res); FLAG_ZF = ZeroFlag(res, lhs, rhs); FLAG_SF = SignFlag(res, lhs, rhs); FLAG_OF = Overflow::Flag(lhs, rhs, res); } template ALWAYS_INLINE static void WriteFlagsAddSub(State &state, T lhs, T rhs, T res) { FLAG_CF = Carry::Flag(lhs, rhs, res); WriteFlagsIncDec(state, lhs, rhs, res); } template DEF_SEM(ADD, D dst, S1 src1, S2 src2) { auto lhs = Read(src1); auto rhs = Read(src2); auto sum = UAdd(lhs, rhs); WriteZExt(dst, sum); WriteFlagsAddSub(state, lhs, rhs, sum); return memory; } template DEF_SEM(ADDPS, D dst, S1 src1, S2 src2) { FWriteV32(dst, FAddV32(FReadV32(src1), FReadV32(src2))); return memory; } template DEF_SEM(ADDPD, D dst, S1 src1, S2 src2) { FWriteV64(dst, FAddV64(FReadV64(src1), FReadV64(src2))); return memory; } template DEF_SEM(ADDSS, D dst, S1 src1, S2 src2) { auto lhs = FReadV32(src1); auto rhs = FReadV32(src2); auto sum = FAdd(FExtractV32(lhs, 0), FExtractV32(rhs, 0)); auto res = FInsertV32(lhs, 0, sum); FWriteV32(dst, res); // SSE: Writes to XMM, AVX: Zero-extends XMM. return memory; } template DEF_SEM(ADDSD, D dst, S1 src1, S2 src2) { auto lhs = FReadV64(src1); auto rhs = FReadV64(src2); auto sum = FAdd(FExtractV64(lhs, 0), FExtractV64(rhs, 0)); auto res = FInsertV64(lhs, 0, sum); FWriteV64(dst, res); // SSE: Writes to XMM, AVX: Zero-extends XMM. return memory; } } // namespace DEF_ISEL(ADD_MEMb_IMMb_80r0) = ADD; DEF_ISEL(ADD_GPR8_IMMb_80r0) = ADD; DEF_ISEL_MnW_Mn_In(ADD_MEMv_IMMz, ADD); DEF_ISEL_RnW_Rn_In(ADD_GPRv_IMMz, ADD); DEF_ISEL(ADD_MEMb_IMMb_82r0) = ADD; DEF_ISEL(ADD_GPR8_IMMb_82r0) = ADD; DEF_ISEL_MnW_Mn_In(ADD_MEMv_IMMb, ADD); DEF_ISEL_RnW_Rn_In(ADD_GPRv_IMMb, ADD); DEF_ISEL(ADD_MEMb_GPR8) = ADD; DEF_ISEL(ADD_GPR8_GPR8_00) = ADD; DEF_ISEL_MnW_Mn_Rn(ADD_MEMv_GPRv, ADD); DEF_ISEL_RnW_Rn_Rn(ADD_GPRv_GPRv_01, ADD); DEF_ISEL(ADD_GPR8_MEMb) = ADD; DEF_ISEL(ADD_GPR8_GPR8_02) = ADD; DEF_ISEL_RnW_Rn_Mn(ADD_GPRv_MEMv, ADD); DEF_ISEL_RnW_Rn_Rn(ADD_GPRv_GPRv_03, ADD); DEF_ISEL(ADD_AL_IMMb) = ADD; DEF_ISEL_RnW_Rn_In(ADD_OrAX_IMMz, ADD); DEF_ISEL(ADDPS_XMMps_MEMps) = ADDPS; DEF_ISEL(ADDPS_XMMps_XMMps) = ADDPS; IF_AVX(DEF_ISEL(VADDPS_XMMdq_XMMdq_MEMdq) = ADDPS;) IF_AVX(DEF_ISEL(VADDPS_XMMdq_XMMdq_XMMdq) = ADDPS;) IF_AVX(DEF_ISEL(VADDPS_YMMqq_YMMqq_MEMqq) = ADDPS;) IF_AVX(DEF_ISEL(VADDPS_YMMqq_YMMqq_YMMqq) = ADDPS;) DEF_ISEL(ADDPD_XMMpd_MEMpd) = ADDPD; DEF_ISEL(ADDPD_XMMpd_XMMpd) = ADDPD; IF_AVX(DEF_ISEL(VADDPD_XMMdq_XMMdq_MEMdq) = ADDPD;) IF_AVX(DEF_ISEL(VADDPD_XMMdq_XMMdq_XMMdq) = ADDPD;) IF_AVX(DEF_ISEL(VADDPD_YMMqq_YMMqq_MEMqq) = ADDPD;) IF_AVX(DEF_ISEL(VADDPD_YMMqq_YMMqq_YMMqq) = ADDPD;) DEF_ISEL(ADDSS_XMMss_MEMss) = ADDSS; DEF_ISEL(ADDSS_XMMss_XMMss) = ADDSS; IF_AVX(DEF_ISEL(VADDSS_XMMdq_XMMdq_MEMd) = ADDSS;) IF_AVX(DEF_ISEL(VADDSS_XMMdq_XMMdq_XMMd) = ADDSS;) DEF_ISEL(ADDSD_XMMsd_MEMsd) = ADDSD; DEF_ISEL(ADDSD_XMMsd_XMMsd) = ADDSD; IF_AVX(DEF_ISEL(VADDSD_XMMdq_XMMdq_MEMq) = ADDSD;) IF_AVX(DEF_ISEL(VADDSD_XMMdq_XMMdq_XMMq) = ADDSD;) namespace { template DEF_SEM(SUB, D dst, S1 src1, S2 src2) { auto lhs = Read(src1); auto rhs = Read(src2); auto sum = USub(lhs, rhs); WriteZExt(dst, sum); WriteFlagsAddSub(state, lhs, rhs, sum); return memory; } template DEF_SEM(SUBPS, D dst, S1 src1, S2 src2) { FWriteV32(dst, FSubV32(FReadV32(src1), FReadV32(src2))); return memory; } template DEF_SEM(SUBPD, D dst, S1 src1, S2 src2) { FWriteV64(dst, FSubV64(FReadV64(src1), FReadV64(src2))); return memory; } template DEF_SEM(SUBSS, D dst, S1 src1, S2 src2) { auto lhs = FReadV32(src1); auto rhs = FReadV32(src2); auto sum = FSub(FExtractV32(lhs, 0), FExtractV32(rhs, 0)); auto res = FInsertV32(lhs, 0, sum); FWriteV32(dst, res); return memory; } template DEF_SEM(SUBSD, D dst, S1 src1, S2 src2) { auto lhs = FReadV64(src1); auto rhs = FReadV64(src2); auto sum = FSub(FExtractV64(lhs, 0), FExtractV64(rhs, 0)); auto res = FInsertV64(lhs, 0, sum); FWriteV64(dst, res); return memory; } } // namespace DEF_ISEL(SUB_MEMb_IMMb_80r5) = SUB; DEF_ISEL(SUB_GPR8_IMMb_80r5) = SUB; DEF_ISEL_MnW_Mn_In(SUB_MEMv_IMMz, SUB); DEF_ISEL_RnW_Rn_In(SUB_GPRv_IMMz, SUB); DEF_ISEL(SUB_MEMb_IMMb_82r5) = SUB; DEF_ISEL(SUB_GPR8_IMMb_82r5) = SUB; DEF_ISEL_MnW_Mn_In(SUB_MEMv_IMMb, SUB); DEF_ISEL_RnW_Rn_In(SUB_GPRv_IMMb, SUB); DEF_ISEL(SUB_MEMb_GPR8) = SUB; DEF_ISEL(SUB_GPR8_GPR8_28) = SUB; DEF_ISEL_MnW_Mn_Rn(SUB_MEMv_GPRv, SUB); DEF_ISEL_RnW_Rn_Rn(SUB_GPRv_GPRv_29, SUB); DEF_ISEL(SUB_GPR8_GPR8_2A) = SUB; DEF_ISEL(SUB_GPR8_MEMb) = SUB; DEF_ISEL_RnW_Rn_Rn(SUB_GPRv_GPRv_2B, SUB); DEF_ISEL_RnW_Rn_Mn(SUB_GPRv_MEMv, SUB); DEF_ISEL(SUB_AL_IMMb) = SUB; DEF_ISEL_RnW_Rn_In(SUB_OrAX_IMMz, SUB); DEF_ISEL(SUBPS_XMMps_MEMps) = SUBPS; DEF_ISEL(SUBPS_XMMps_XMMps) = SUBPS; IF_AVX(DEF_ISEL(VSUBPS_XMMdq_XMMdq_MEMdq) = SUBPS;) IF_AVX(DEF_ISEL(VSUBPS_XMMdq_XMMdq_XMMdq) = SUBPS;) IF_AVX(DEF_ISEL(VSUBPS_YMMqq_YMMqq_MEMqq) = SUBPS;) IF_AVX(DEF_ISEL(VSUBPS_YMMqq_YMMqq_YMMqq) = SUBPS;) DEF_ISEL(SUBPD_XMMpd_MEMpd) = SUBPD; DEF_ISEL(SUBPD_XMMpd_XMMpd) = SUBPD; IF_AVX(DEF_ISEL(VSUBPD_XMMdq_XMMdq_MEMdq) = SUBPD;) IF_AVX(DEF_ISEL(VSUBPD_XMMdq_XMMdq_XMMdq) = SUBPD;) IF_AVX(DEF_ISEL(VSUBPD_YMMqq_YMMqq_MEMqq) = SUBPD;) IF_AVX(DEF_ISEL(VSUBPD_YMMqq_YMMqq_YMMqq) = SUBPD;) DEF_ISEL(SUBSS_XMMss_MEMss) = SUBSS; DEF_ISEL(SUBSS_XMMss_XMMss) = SUBSS; IF_AVX(DEF_ISEL(VSUBSS_XMMdq_XMMdq_MEMd) = SUBSS;) IF_AVX(DEF_ISEL(VSUBSS_XMMdq_XMMdq_XMMd) = SUBSS;) DEF_ISEL(SUBSD_XMMsd_MEMsd) = SUBSD; DEF_ISEL(SUBSD_XMMsd_XMMsd) = SUBSD; IF_AVX(DEF_ISEL(VSUBSD_XMMdq_XMMdq_MEMq) = SUBSD;) IF_AVX(DEF_ISEL(VSUBSD_XMMdq_XMMdq_XMMq) = SUBSD;) namespace { template DEF_SEM(CMP, S1 src1, S2 src2) { auto lhs = Read(src1); auto rhs = Read(src2); auto sum = USub(lhs, rhs); WriteFlagsAddSub(state, lhs, rhs, sum); return memory; } } // namespace DEF_ISEL(CMP_MEMb_IMMb_80r7) = CMP; DEF_ISEL(CMP_GPR8_IMMb_80r7) = CMP; DEF_ISEL_Mn_In(CMP_MEMv_IMMz, CMP); DEF_ISEL_Rn_In(CMP_GPRv_IMMz, CMP); DEF_ISEL(CMP_MEMb_IMMb_82r7) = CMP; DEF_ISEL(CMP_GPR8_IMMb_82r7) = CMP; DEF_ISEL_Mn_In(CMP_MEMv_IMMb, CMP); DEF_ISEL_Rn_In(CMP_GPRv_IMMb, CMP); DEF_ISEL(CMP_MEMb_GPR8) = CMP; DEF_ISEL(CMP_GPR8_GPR8_38) = CMP; DEF_ISEL_Mn_In(CMP_MEMv_GPRv, CMP); DEF_ISEL_Rn_Rn(CMP_GPRv_GPRv_39, CMP); DEF_ISEL(CMP_GPR8_GPR8_3A) = CMP; DEF_ISEL(CMP_GPR8_MEMb) = CMP; DEF_ISEL_Rn_Rn(CMP_GPRv_GPRv_3B, CMP); DEF_ISEL_Rn_Mn(CMP_GPRv_MEMv, CMP); DEF_ISEL(CMP_AL_IMMb) = CMP; DEF_ISEL_Rn_In(CMP_OrAX_IMMz, CMP); namespace { template ALWAYS_INLINE static void WriteFlagsMul(State &state, T lhs, T rhs, U res, V res_trunc) { const auto new_of = Overflow::Flag(lhs, rhs, res); FLAG_CF = new_of; FLAG_PF = BUndefined(); // Technically undefined. FLAG_AF = BUndefined(); FLAG_ZF = BUndefined(); FLAG_SF = BUndefined(); FLAG_OF = new_of; } // 2-operand and 3-operand multipliers truncate their results down to their // base types. template DEF_SEM(IMUL, D dst, S1 src1, S2 src2) { auto lhs = Signed(Read(src1)); auto rhs = Signed(Read(src2)); auto lhs_wide = SExt(lhs); auto rhs_wide = SExt(rhs); auto res = SMul(lhs_wide, rhs_wide); auto res_trunc = TruncTo(res); WriteZExt(dst, res_trunc); // E.g. write to EAX can overwrite RAX. WriteFlagsMul(state, lhs, rhs, res, res_trunc); return memory; } // Unsigned multiply without affecting flags. template DEF_SEM(MULX, D dst1, D dst2, const S2 src2) { auto lhs = ZExt(Read(src2)); // Kind of tricky: in 64-bit, for a 32-bit MULX, we read RDX, but we need // to truncate it down into EDX before extending it back up to "double" its // width. auto rhs = ZExt(TruncTo(Read(REG_XDX))); auto res = UMul(lhs, rhs); auto res_high = UShr(res, ZExt(BitSizeOf(src2))); // In 64-bit, a 32-bit dest needs to zero-extend up to 64 bits because the // write version of the reg will be the 64-bit version. WriteZExt(dst1, TruncTo(res_high)); // High N bits. WriteZExt(dst2, TruncTo(res)); // Low N bits. return memory; } #define MAKE_MULxax(name, src1, dst1, dst2) \ template \ DEF_SEM(MUL##name, S2 src2) { \ auto lhs = Read(src1); \ auto rhs = Read(src2); \ auto lhs_wide = ZExt(lhs); \ auto rhs_wide = ZExt(rhs); \ auto res = UMul(lhs_wide, rhs_wide); \ auto res_trunc = Trunc(res); \ auto shift = ZExt(BitSizeOf(src2)); \ WriteZExt(dst1, res_trunc); \ WriteZExt(dst2, Trunc(UShr(res, shift))); \ WriteFlagsMul(state, lhs, rhs, res, res_trunc); \ return memory; \ } MAKE_MULxax(al, REG_AL, REG_AL, REG_AH) MAKE_MULxax(ax, REG_AX, REG_AX, REG_DX) MAKE_MULxax(eax, REG_EAX, REG_XAX, REG_XDX) IF_64BIT(MAKE_MULxax(rax, REG_RAX, REG_RAX, REG_RDX)) #undef MAKE_MULxax #define MAKE_IMULxax(name, src1, dst1, dst2) \ template \ DEF_SEM(IMUL##name, S2 src2) { \ auto lhs = Signed(Read(src1)); \ auto rhs = Signed(Read(src2)); \ auto lhs_wide = SExt(lhs); \ auto rhs_wide = SExt(rhs); \ auto res = SMul(lhs_wide, rhs_wide); \ auto res_trunc = Trunc(res); \ auto shift = ZExt(BitSizeOf(src2)); \ WriteZExt(dst1, Unsigned(res_trunc)); \ WriteZExt(dst2, Trunc(UShr(Unsigned(res), shift))); \ WriteFlagsMul(state, lhs, rhs, res, res_trunc); \ return memory; \ } MAKE_IMULxax(al, REG_AL, REG_AL, REG_AH) MAKE_IMULxax(ax, REG_AX, REG_AX, REG_DX) MAKE_IMULxax(eax, REG_EAX, REG_XAX, REG_XDX) IF_64BIT(MAKE_IMULxax(rax, REG_RAX, REG_RAX, REG_RDX)) #undef MAKE_IMULxax template DEF_SEM(MULPS, D dst, S1 src1, S2 src2) { FWriteV32(dst, FMulV32(FReadV32(src1), FReadV32(src2))); return memory; } template DEF_SEM(MULPD, D dst, S1 src1, S2 src2) { FWriteV64(dst, FMulV64(FReadV64(src1), FReadV64(src2))); return memory; } template DEF_SEM(MULSS, D dst, S1 src1, S2 src2) { auto lhs = FReadV32(src1); auto rhs = FReadV32(src2); auto mul = FMul(FExtractV32(lhs, 0), FExtractV32(rhs, 0)); auto res = FInsertV32(lhs, 0, mul); FWriteV32(dst, res); // SSE: Writes to XMM, AVX: Zero-extends XMM. return memory; } template DEF_SEM(MULSD, D dst, S1 src1, S2 src2) { auto lhs = FReadV64(src1); auto rhs = FReadV64(src2); auto mul = FMul(FExtractV64(lhs, 0), FExtractV64(rhs, 0)); auto res = FInsertV64(lhs, 0, mul); FWriteV64(dst, res); // SSE: Writes to XMM, AVX: Zero-extends XMM. return memory; } } // namespace DEF_ISEL(IMUL_MEMb) = IMULal; DEF_ISEL(IMUL_GPR8) = IMULal; DEF_ISEL(IMUL_MEMv_8) = IMULal; DEF_ISEL(IMUL_MEMv_16) = IMULax; DEF_ISEL(IMUL_MEMv_32) = IMULeax; IF_64BIT(DEF_ISEL(IMUL_MEMv_64) = IMULrax;) DEF_ISEL(IMUL_GPRv_8) = IMULal; DEF_ISEL(IMUL_GPRv_16) = IMULax; DEF_ISEL(IMUL_GPRv_32) = IMULeax; IF_64BIT(DEF_ISEL(IMUL_GPRv_64) = IMULrax;) // All dests are registers, albeit different ones from the sources. DEF_ISEL_RnW_Mn_In(IMUL_GPRv_MEMv_IMMz, IMUL); DEF_ISEL_RnW_Rn_In(IMUL_GPRv_GPRv_IMMz, IMUL); DEF_ISEL_RnW_Mn_In(IMUL_GPRv_MEMv_IMMb, IMUL); DEF_ISEL_RnW_Rn_In(IMUL_GPRv_GPRv_IMMb, IMUL); // Two-operand, but dest is a register so turns into a three-operand. DEF_ISEL_RnW_Rn_Mn(IMUL_GPRv_MEMv, IMUL); DEF_ISEL_RnW_Rn_Rn(IMUL_GPRv_GPRv, IMUL); DEF_ISEL(MUL_GPR8) = MULal; DEF_ISEL(MUL_MEMb) = MULal; DEF_ISEL(MUL_MEMv_8) = MULal; DEF_ISEL(MUL_MEMv_16) = MULax; DEF_ISEL(MUL_MEMv_32) = MULeax; IF_64BIT(DEF_ISEL(MUL_MEMv_64) = MULrax;) DEF_ISEL(MUL_GPRv_8) = MULal; DEF_ISEL(MUL_GPRv_16) = MULax; DEF_ISEL(MUL_GPRv_32) = MULeax; IF_64BIT(DEF_ISEL(MUL_GPRv_64) = MULrax;) DEF_ISEL(MULX_GPR32d_GPR32d_GPR32d) = MULX; DEF_ISEL(MULX_GPR32d_GPR32d_MEMd) = MULX; DEF_ISEL(MULX_VGPR32d_VGPR32d_VGPR32d) = MULX; DEF_ISEL(MULX_VGPR32d_VGPR32d_MEMd) = MULX; IF_64BIT(DEF_ISEL(MULX_GPR64q_GPR64q_GPR64q) = MULX;) IF_64BIT(DEF_ISEL(MULX_GPR64q_GPR64q_MEMq) = MULX;) IF_64BIT(DEF_ISEL(MULX_VGPR64q_VGPR64q_VGPR64q) = MULX;) IF_64BIT(DEF_ISEL(MULX_VGPR64q_VGPR64q_MEMq) = MULX;) DEF_ISEL(MULPS_XMMps_MEMps) = MULPS; DEF_ISEL(MULPS_XMMps_XMMps) = MULPS; IF_AVX(DEF_ISEL(VMULPS_XMMdq_XMMdq_MEMdq) = MULPS;) IF_AVX(DEF_ISEL(VMULPS_XMMdq_XMMdq_XMMdq) = MULPS;) IF_AVX(DEF_ISEL(VMULPS_YMMqq_YMMqq_MEMqq) = MULPS;) IF_AVX(DEF_ISEL(VMULPS_YMMqq_YMMqq_YMMqq) = MULPS;) DEF_ISEL(MULPD_XMMpd_MEMpd) = MULPD; DEF_ISEL(MULPD_XMMpd_XMMpd) = MULPD; IF_AVX(DEF_ISEL(VMULPD_XMMdq_XMMdq_MEMdq) = MULPD;) IF_AVX(DEF_ISEL(VMULPD_XMMdq_XMMdq_XMMdq) = MULPD;) IF_AVX(DEF_ISEL(VMULPD_YMMqq_YMMqq_MEMqq) = MULPD;) IF_AVX(DEF_ISEL(VMULPD_YMMqq_YMMqq_YMMqq) = MULPD;) DEF_ISEL(MULSS_XMMss_MEMss) = MULSS; DEF_ISEL(MULSS_XMMss_XMMss) = MULSS; IF_AVX(DEF_ISEL(VMULSS_XMMdq_XMMdq_MEMd) = MULSS;) IF_AVX(DEF_ISEL(VMULSS_XMMdq_XMMdq_XMMd) = MULSS;) DEF_ISEL(MULSD_XMMsd_MEMsd) = MULSD; DEF_ISEL(MULSD_XMMsd_XMMsd) = MULSD; IF_AVX(DEF_ISEL(VMULSD_XMMdq_XMMdq_MEMq) = MULSD;) IF_AVX(DEF_ISEL(VMULSD_XMMdq_XMMdq_XMMq) = MULSD;) namespace { // TODO(pag): Is the checking of `res` against `res_trunc` worth it? It // introduces extra control flow. #define MAKE_DIVxax(name, src1, src2, dst1, dst2) \ template \ DEF_SEM(DIV##name, S3 src3, PC next_pc) { \ auto lhs_low = ZExt(Read(src1)); \ auto lhs_high = ZExt(Read(src2)); \ auto rhs = ZExt(Read(src3)); \ auto shift = ZExt(BitSizeOf(src3)); \ auto lhs = UOr(UShl(lhs_high, shift), lhs_low); \ WriteZExt(REG_PC, Read(next_pc)); \ if (IsZero(rhs)) { \ StopFailure(); \ } else { \ auto quot = UDiv(lhs, rhs); \ auto rem = URem(lhs, rhs); \ auto quot_trunc = Trunc(quot); \ auto rem_trunc = Trunc(rem); \ if (quot != ZExt(quot_trunc)) { \ StopFailure(); \ } else { \ WriteZExt(dst1, quot_trunc); \ WriteZExt(dst2, rem_trunc); \ ClearArithFlags(); \ return memory; \ } \ } \ } MAKE_DIVxax(ax, REG_AL, REG_AH, REG_AL, REG_AH) MAKE_DIVxax(dxax, REG_AX, REG_DX, REG_AX, REG_DX) MAKE_DIVxax(edxeax, REG_EAX, REG_EDX, REG_XAX, REG_XDX) IF_64BIT(MAKE_DIVxax(rdxrax, REG_RAX, REG_RDX, REG_RAX, REG_RDX)) #undef MAKE_DIVxax // TODO(pag): Is the checking of `res` against `res_trunc` worth it? It // introduces extra control flow. #define MAKE_IDIVxax(name, src1, src2, dst1, dst2) \ template \ DEF_SEM(IDIV##name, S3 src3, PC next_pc) { \ auto lhs_low = ZExt(Read(src1)); \ auto lhs_high = ZExt(Read(src2)); \ auto rhs = SExt(Read(src3)); \ auto shift = ZExt(BitSizeOf(src3)); \ auto lhs = Signed(UOr(UShl(lhs_high, shift), lhs_low)); \ WriteZExt(REG_PC, Read(next_pc)); \ if (IsZero(rhs)) { \ StopFailure(); \ } else { \ auto quot = SDiv(lhs, rhs); \ auto rem = SRem(lhs, rhs); \ auto quot_trunc = Trunc(quot); \ auto rem_trunc = Trunc(rem); \ if (quot != SExt(quot_trunc)) { \ StopFailure(); \ } else { \ WriteZExt(dst1, Unsigned(quot_trunc)); \ WriteZExt(dst2, Unsigned(rem_trunc)); \ ClearArithFlags(); \ return memory; \ } \ } \ } MAKE_IDIVxax(ax, REG_AL, REG_AH, REG_AL, REG_AH) MAKE_IDIVxax(dxax, REG_AX, REG_DX, REG_AX, REG_DX) MAKE_IDIVxax(edxeax, REG_EAX, REG_EDX, REG_XAX, REG_XDX) IF_64BIT(MAKE_IDIVxax(rdxrax, REG_RAX, REG_RDX, REG_RAX, REG_RDX)) #undef MAKE_IDIVxax } // namespace DEF_ISEL(IDIV_MEMb) = IDIVax; DEF_ISEL(IDIV_GPR8) = IDIVax; DEF_ISEL(IDIV_MEMv_8) = IDIVax; DEF_ISEL(IDIV_MEMv_16) = IDIVdxax; DEF_ISEL(IDIV_MEMv_32) = IDIVedxeax; IF_64BIT(DEF_ISEL(IDIV_MEMv_64) = IDIVrdxrax;) DEF_ISEL(IDIV_GPRv_8) = IDIVax; DEF_ISEL(IDIV_GPRv_16) = IDIVdxax; DEF_ISEL(IDIV_GPRv_32) = IDIVedxeax; IF_64BIT(DEF_ISEL(IDIV_GPRv_64) = IDIVrdxrax;) DEF_ISEL(DIV_MEMb) = DIVax; DEF_ISEL(DIV_GPR8) = DIVax; DEF_ISEL(DIV_MEMv_8) = DIVax; DEF_ISEL(DIV_MEMv_16) = DIVdxax; DEF_ISEL(DIV_MEMv_32) = DIVedxeax; IF_64BIT(DEF_ISEL(DIV_MEMv_64) = DIVrdxrax;) DEF_ISEL(DIV_GPRv_8) = DIVax; DEF_ISEL(DIV_GPRv_16) = DIVdxax; DEF_ISEL(DIV_GPRv_32) = DIVedxeax; IF_64BIT(DEF_ISEL(DIV_GPRv_64) = DIVrdxrax;) namespace { template DEF_SEM(DIVPS, D dst, S1 src1, S2 src2) { FWriteV32(dst, FDivV32(FReadV32(src1), FReadV32(src2))); return memory; } template DEF_SEM(DIVPD, D dst, const S1 src1, const S2 src2) { FWriteV64(dst, FDivV64(FReadV64(src1), FReadV64(src2))); return memory; } template DEF_SEM(DIVSS, D dst, S1 src1, S2 src2) { auto lhs = FReadV32(src1); auto rhs = FReadV32(src2); auto quot = FDiv(FExtractV32(lhs, 0), FExtractV32(rhs, 0)); auto res = FInsertV32(lhs, 0, quot); FWriteV32(dst, res); // SSE: Writes to XMM, AVX: Zero-extends XMM. return memory; } template DEF_SEM(DIVSD, D dst, S1 src1, S2 src2) { auto lhs = FReadV64(src1); auto rhs = FReadV64(src2); auto quot = FDiv(FExtractV64(lhs, 0), FExtractV64(rhs, 0)); auto res = FInsertV64(lhs, 0, quot); FWriteV64(dst, res); // SSE: Writes to XMM, AVX: Zero-extends XMM. return memory; } } // namespace DEF_ISEL(DIVPS_XMMps_MEMps) = DIVPS; DEF_ISEL(DIVPS_XMMps_XMMps) = DIVPS; IF_AVX(DEF_ISEL(VDIVPS_XMMdq_XMMdq_MEMdq) = DIVPS;) IF_AVX(DEF_ISEL(VDIVPS_XMMdq_XMMdq_XMMdq) = DIVPS;) IF_AVX(DEF_ISEL(VDIVPS_YMMqq_YMMqq_MEMqq) = DIVPS;) IF_AVX(DEF_ISEL(VDIVPS_YMMqq_YMMqq_YMMqq) = DIVPS;) DEF_ISEL(DIVPD_XMMpd_MEMpd) = DIVPD; DEF_ISEL(DIVPD_XMMpd_XMMpd) = DIVPD; IF_AVX(DEF_ISEL(VDIVPD_XMMdq_XMMdq_MEMdq) = DIVPD;) IF_AVX(DEF_ISEL(VDIVPD_XMMdq_XMMdq_XMMdq) = DIVPD;) IF_AVX(DEF_ISEL(VDIVPD_YMMqq_YMMqq_MEMqq) = DIVPD;) IF_AVX(DEF_ISEL(VDIVPD_YMMqq_YMMqq_YMMqq) = DIVPD;) DEF_ISEL(DIVSS_XMMss_MEMss) = DIVSS; DEF_ISEL(DIVSS_XMMss_XMMss) = DIVSS; IF_AVX(DEF_ISEL(VDIVSS_XMMdq_XMMdq_MEMd) = DIVSS;) IF_AVX(DEF_ISEL(VDIVSS_XMMdq_XMMdq_XMMd) = DIVSS;) DEF_ISEL(DIVSD_XMMsd_MEMsd) = DIVSD; DEF_ISEL(DIVSD_XMMsd_XMMsd) = DIVSD; IF_AVX(DEF_ISEL(VDIVSD_XMMdq_XMMdq_MEMq) = DIVSD;) IF_AVX(DEF_ISEL(VDIVSD_XMMdq_XMMdq_XMMq) = DIVSD;) namespace { template DEF_SEM(INC, D dst, S1 src) { auto lhs = Read(src); decltype(lhs) rhs = 1; auto sum = UAdd(lhs, rhs); WriteZExt(dst, sum); WriteFlagsIncDec(state, lhs, rhs, sum); return memory; } template DEF_SEM(DEC, D dst, S1 src) { auto lhs = Read(src); auto_t(S1) rhs = 1; auto sum = USub(lhs, rhs); WriteZExt(dst, sum); WriteFlagsIncDec(state, lhs, rhs, sum); return memory; } template DEF_SEM(NEG, D dst, S1 src) { auto_t(S1) lhs = 0; auto rhs = Read(src); auto neg = UNeg(rhs); WriteZExt(dst, neg); WriteFlagsAddSub(state, lhs, rhs, neg); return memory; } } // namespace DEF_ISEL(INC_MEMb) = INC; DEF_ISEL(INC_GPR8) = INC; DEF_ISEL_MnW_Mn(INC_MEMv, INC); DEF_ISEL_RnW_Rn(INC_GPRv_FFr0, INC); DEF_ISEL_RnW_Rn(INC_GPRv_40, INC); DEF_ISEL(DEC_MEMb) = DEC; DEF_ISEL(DEC_GPR8) = DEC; DEF_ISEL_MnW_Mn(DEC_MEMv, DEC); DEF_ISEL_RnW_Rn(DEC_GPRv_FFr1, DEC); DEF_ISEL_RnW_Rn(DEC_GPRv_48, DEC); DEF_ISEL(NEG_MEMb) = NEG; DEF_ISEL(NEG_GPR8) = NEG; DEF_ISEL_MnW_Mn(NEG_MEMv, NEG); DEF_ISEL_RnW_Rn(NEG_GPRv, NEG); namespace { template ALWAYS_INLINE static bool CarryFlag(T a, T b, T ab, T c, T abc) { static_assert(std::is_unsigned::value, "Invalid specialization of `CarryFlag` for addition."); return Carry::Flag(a, b, ab) || Carry::Flag(ab, c, abc); } template DEF_SEM(ADC, D dst, S1 src1, S2 src2) { auto lhs = Read(src1); auto rhs = Read(src2); auto carry = ZExtTo(Unsigned(Read(FLAG_CF))); auto sum = UAdd(lhs, rhs); auto res = UAdd(sum, carry); WriteZExt(dst, res); Write(FLAG_CF, CarryFlag(lhs, rhs, sum, carry, res)); WriteFlagsIncDec(state, lhs, rhs, res); return memory; } template DEF_SEM(SBB, D dst, S1 src1, S2 src2) { auto lhs = Read(src1); auto rhs = Read(src2); auto borrow = ZExtTo(Unsigned(Read(FLAG_CF))); auto sum = USub(lhs, rhs); auto res = USub(sum, borrow); WriteZExt(dst, res); Write(FLAG_CF, CarryFlag(lhs, rhs, sum, borrow, res)); WriteFlagsIncDec(state, lhs, rhs, res); return memory; } } // namespace DEF_ISEL(SBB_MEMb_IMMb_80r3) = SBB; DEF_ISEL(SBB_GPR8_IMMb_80r3) = SBB; DEF_ISEL_MnW_Mn_In(SBB_MEMv_IMMz, SBB); DEF_ISEL_RnW_Rn_In(SBB_GPRv_IMMz, SBB); DEF_ISEL(SBB_MEMb_IMMb_82r3) = SBB; DEF_ISEL(SBB_GPR8_IMMb_82r3) = SBB; DEF_ISEL_MnW_Mn_In(SBB_MEMv_IMMb, SBB); DEF_ISEL_RnW_Rn_In(SBB_GPRv_IMMb, SBB); DEF_ISEL(SBB_MEMb_GPR8) = SBB; DEF_ISEL(SBB_GPR8_GPR8_18) = SBB; DEF_ISEL_MnW_Mn_Rn(SBB_MEMv_GPRv, SBB); DEF_ISEL_RnW_Rn_Rn(SBB_GPRv_GPRv_19, SBB); DEF_ISEL(SBB_GPR8_GPR8_1A) = SBB; DEF_ISEL(SBB_GPR8_MEMb) = SBB; DEF_ISEL_RnW_Rn_Rn(SBB_GPRv_GPRv_1B, SBB); DEF_ISEL_RnW_Rn_Mn(SBB_GPRv_MEMv, SBB); DEF_ISEL(SBB_AL_IMMb) = SBB; DEF_ISEL_RnW_Rn_In(SBB_OrAX_IMMz, SBB); DEF_ISEL(ADC_MEMb_IMMb_80r2) = ADC; DEF_ISEL(ADC_GPR8_IMMb_80r2) = ADC; DEF_ISEL_MnW_Mn_In(ADC_MEMv_IMMz, ADC); DEF_ISEL_RnW_Rn_In(ADC_GPRv_IMMz, ADC); DEF_ISEL(ADC_MEMb_IMMb_82r2) = ADC; DEF_ISEL(ADC_GPR8_IMMb_82r2) = ADC; DEF_ISEL_MnW_Mn_In(ADC_MEMv_IMMb, ADC); DEF_ISEL_RnW_Rn_In(ADC_GPRv_IMMb, ADC); DEF_ISEL(ADC_MEMb_GPR8) = ADC; DEF_ISEL(ADC_GPR8_GPR8_10) = ADC; DEF_ISEL_MnW_Mn_Rn(ADC_MEMv_GPRv, ADC); DEF_ISEL_RnW_Rn_Rn(ADC_GPRv_GPRv_11, ADC); DEF_ISEL(ADC_GPR8_MEMb) = ADC; DEF_ISEL(ADC_GPR8_GPR8_12) = ADC; DEF_ISEL_RnW_Rn_Mn(ADC_GPRv_MEMv, ADC); DEF_ISEL_RnW_Rn_Rn(ADC_GPRv_GPRv_13, ADC); DEF_ISEL(ADC_AL_IMMb) = ADC; DEF_ISEL_RnW_Rn_In(ADC_OrAX_IMMz, ADC);