Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,8 @@ class CodeGen final : public CodeGenInterface
void genAmd64EmitterUnitTestsApx();
void genAmd64EmitterUnitTestsAvx10v2();
void genAmd64EmitterUnitTestsCCMP();
void genAmd64EmitterUnitTestsCFCMOV();
void genAmd64EmitterUnitTestsCTEST();
#endif

#endif // defined(DEBUG)
Expand Down Expand Up @@ -1652,9 +1654,11 @@ class CodeGen final : public CodeGenInterface
static insOpts ShiftOpToInsOpts(genTreeOps op);
#elif defined(TARGET_XARCH)
static instruction JumpKindToCmov(emitJumpKind condition);
#ifdef TARGET_AMD64
static instruction JumpKindToCcmp(emitJumpKind condition);
static insOpts OptsFromCFlags(insCflags flags);
#endif
#endif // TARGET_AMD64
#endif // TARGET_XARCH
void inst_JCC(GenCondition condition, BasicBlock* target);
void inst_SETCC(GenCondition condition, var_types type, regNumber dstReg);

Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/jit/codegenlinear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2751,6 +2751,14 @@ void CodeGen::genEmitterUnitTests()
{
genAmd64EmitterUnitTestsCCMP();
}
if (unitTestSectionAll || (strstr(unitTestSection, "cfcmov") != nullptr))
{
genAmd64EmitterUnitTestsCFCMOV();
}
if (unitTestSectionAll || (strstr(unitTestSection, "ctest") != nullptr))
{
genAmd64EmitterUnitTestsCTEST();
}

#elif defined(TARGET_ARM64)
if (unitTestSectionAll || (strstr(unitTestSection, "general") != nullptr))
Expand Down
192 changes: 185 additions & 7 deletions src/coreclr/jit/codegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1436,6 +1436,7 @@ instruction CodeGen::JumpKindToCmov(emitJumpKind condition)
return s_table[condition];
}

#ifdef TARGET_AMD64
//------------------------------------------------------------------------
// JumpKindToCcmp:
// Convert an emitJumpKind to the corresponding ccmp instruction.
Expand Down Expand Up @@ -1475,6 +1476,7 @@ instruction CodeGen::JumpKindToCcmp(emitJumpKind condition)
assert((condition >= EJ_NONE) && (condition < EJ_COUNT));
return s_table[condition];
}
#endif // TARGET_AMD64

//------------------------------------------------------------------------
// genCodeForCompare: Produce code for a GT_SELECT/GT_SELECTCC node.
Expand Down Expand Up @@ -8668,8 +8670,9 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
regSet.verifyRegistersUsed(killMask);
}

#ifdef TARGET_AMD64
//-----------------------------------------------------------------------------------------
// OptsFromCFlags - Convert condition flags into approxpriate insOpts.
// OptsFromCFlags - Convert condition flags into appropriate insOpts.
//
// Arguments:
// flags - The condition flags to be converted.
Expand All @@ -8679,7 +8682,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
//
// Notes:
// This function maps the condition flags (e.g., CF, ZF, SF, OF) to the appropriate
// instruction options used for setting the default flag values in extneded EVEX
// instruction options used for setting the default flag values in extended EVEX
// encoding conditional instructions.
//
insOpts CodeGen::OptsFromCFlags(insCflags flags)
Expand All @@ -8696,8 +8699,6 @@ insOpts CodeGen::OptsFromCFlags(insCflags flags)
return (insOpts)opts;
}

#ifdef TARGET_AMD64

//-----------------------------------------------------------------------------------------
// genCodeForCCMP - Generate code for a conditional compare (CCMP) node.
//
Expand Down Expand Up @@ -8736,7 +8737,17 @@ void CodeGen::genCodeForCCMP(GenTreeCCMP* ccmp)
if (op2->isContainedIntOrIImmed())
{
GenTreeIntConCommon* intConst = op2->AsIntConCommon();
emit->emitIns_R_I(ccmpIns, cmpSize, srcReg1, (int)intConst->IconValue(), opts);
if (intConst->IconValue() == 0)
{
// ctest reg, reg is 1-byte shorter encoding than ccmp reg, 0.
static_assert((FIRST_CTEST_INSTRUCTION - FIRST_CCMP_INSTRUCTION) == 32);
Comment thread
tannergooding marked this conversation as resolved.
instruction ctestIns = (instruction)(ccmpIns + FIRST_CTEST_INSTRUCTION - FIRST_CCMP_INSTRUCTION);
Comment thread
tannergooding marked this conversation as resolved.
emit->emitIns_R_R(ctestIns, cmpSize, srcReg1, srcReg1, opts);
}
Comment thread
Ruihan-Yin marked this conversation as resolved.
else
{
emit->emitIns_R_I(ccmpIns, cmpSize, srcReg1, (int)intConst->IconValue(), opts);
}
Comment thread
Ruihan-Yin marked this conversation as resolved.
}
else
{
Expand Down Expand Up @@ -9340,6 +9351,100 @@ void CodeGen::genAmd64EmitterUnitTestsAvx10v2()
theEmitter->emitIns_R_R(INS_vmovw_simd, EA_16BYTE, REG_XMM0, REG_XMM1);
}

/*****************************************************************************
* Unit tests for the CFCMOV instructions.
*/

void CodeGen::genAmd64EmitterUnitTestsCFCMOV()
{
emitter* theEmitter = GetEmitter();
genDefineTempLabel(genCreateTempLabel());

GenTreePhysReg physReg(REG_EDX);
physReg.SetRegNum(REG_EDX);
GenTreeIndir load = indirForm(TYP_INT, &physReg);

// Test all CC codes
for (uint32_t ins = FIRST_CFCMOV_INSTRUCTION; ins <= LAST_CFCMOV_INSTRUCTION; ins++)
{
theEmitter->emitIns_R_R((instruction)ins, EA_8BYTE, REG_RAX, REG_RCX, INS_OPTS_NONE);
theEmitter->emitIns_R_R((instruction)ins, EA_4BYTE, REG_RAX, REG_RCX, INS_OPTS_NONE);

theEmitter->emitIns_R_A((instruction)ins, EA_8BYTE, REG_EAX, &load, INS_OPTS_NONE);
theEmitter->emitIns_R_A((instruction)ins, EA_4BYTE, REG_EAX, &load, INS_OPTS_NONE);

theEmitter->emitIns_R_AR((instruction)ins, EA_8BYTE, REG_EAX, REG_ECX, 4);
theEmitter->emitIns_R_AR((instruction)ins, EA_4BYTE, REG_EAX, REG_ECX, 4);
Comment thread
Ruihan-Yin marked this conversation as resolved.
Comment thread
Ruihan-Yin marked this conversation as resolved.

theEmitter->emitIns_R_ARX((instruction)ins, EA_8BYTE, REG_R16, REG_R17, REG_R18, 1, 0);
theEmitter->emitIns_R_ARX((instruction)ins, EA_4BYTE, REG_R16, REG_R17, REG_R18, 1, 0);
theEmitter->emitIns_R_ARX((instruction)ins, EA_8BYTE, REG_R16, REG_R17, REG_R18, 2, 4);
theEmitter->emitIns_R_ARX((instruction)ins, EA_4BYTE, REG_R16, REG_R17, REG_R18, 2, 4);

theEmitter->emitIns_AR_R((instruction)ins, EA_8BYTE, REG_EAX, REG_ECX, 4, INS_OPTS_EVEX_nf);
theEmitter->emitIns_AR_R((instruction)ins, EA_4BYTE, REG_EAX, REG_ECX, 4, INS_OPTS_EVEX_nf);

theEmitter->emitIns_ARX_R((instruction)ins, EA_8BYTE, REG_R16, REG_R17, REG_R18, 2, 4, INS_OPTS_EVEX_nf);
theEmitter->emitIns_ARX_R((instruction)ins, EA_4BYTE, REG_R16, REG_R17, REG_R18, 2, 4, INS_OPTS_EVEX_nf);

theEmitter->emitIns_ARX_R((instruction)ins, EA_8BYTE, REG_R16, REG_R17, REG_NA, 2, 0, INS_OPTS_EVEX_nf);
theEmitter->emitIns_ARX_R((instruction)ins, EA_4BYTE, REG_R16, REG_R17, REG_NA, 2, 0, INS_OPTS_EVEX_nf);

theEmitter->emitIns_R_R_R((instruction)ins, EA_8BYTE, REG_R10, REG_EAX, REG_ECX,
(insOpts)(INS_OPTS_EVEX_nd | INS_OPTS_EVEX_nf));
theEmitter->emitIns_R_R_R((instruction)ins, EA_4BYTE, REG_R10, REG_EAX, REG_ECX,
(insOpts)(INS_OPTS_EVEX_nd | INS_OPTS_EVEX_nf));
Comment thread
tannergooding marked this conversation as resolved.
theEmitter->emitIns_R_R_AR((instruction)ins, EA_8BYTE, REG_R16, REG_R17, REG_R18, 2,
(insOpts)(INS_OPTS_EVEX_nd | INS_OPTS_EVEX_nf));
theEmitter->emitIns_R_R_AR((instruction)ins, EA_4BYTE, REG_R16, REG_R17, REG_R18, 2,
(insOpts)(INS_OPTS_EVEX_nd | INS_OPTS_EVEX_nf));
theEmitter->emitIns_R_R_A((instruction)ins, EA_8BYTE, REG_R16, REG_R17, &load,
(insOpts)(INS_OPTS_EVEX_nd | INS_OPTS_EVEX_nf));
theEmitter->emitIns_R_R_A((instruction)ins, EA_4BYTE, REG_R16, REG_R17, &load,
(insOpts)(INS_OPTS_EVEX_nd | INS_OPTS_EVEX_nf));

theEmitter->emitIns_R_R_S((instruction)ins, EA_8BYTE, REG_R10, REG_R16, 0, 0,
(insOpts)(INS_OPTS_EVEX_nd | INS_OPTS_EVEX_nf));
theEmitter->emitIns_R_R_S((instruction)ins, EA_4BYTE, REG_R10, REG_R16, 0, 0,
(insOpts)(INS_OPTS_EVEX_nd | INS_OPTS_EVEX_nf));
}

// Test all CC codes
for (uint32_t ins = INS_cmovo; ins <= INS_cmovg; ins++)
{
theEmitter->emitIns_R_R((instruction)ins, EA_8BYTE, REG_RAX, REG_RCX);
theEmitter->emitIns_R_R((instruction)ins, EA_4BYTE, REG_RAX, REG_RCX);
theEmitter->emitIns_R_R((instruction)ins, EA_8BYTE, REG_R10, REG_RCX);
theEmitter->emitIns_R_R((instruction)ins, EA_4BYTE, REG_R10, REG_RCX);
theEmitter->emitIns_R_R((instruction)ins, EA_8BYTE, REG_R16, REG_RCX);
theEmitter->emitIns_R_R((instruction)ins, EA_4BYTE, REG_R16, REG_RCX);
theEmitter->emitIns_R_AR((instruction)ins, EA_8BYTE, REG_RAX, REG_RCX, 2);
theEmitter->emitIns_R_AR((instruction)ins, EA_4BYTE, REG_RAX, REG_RCX, 2);
theEmitter->emitIns_R_AR((instruction)ins, EA_8BYTE, REG_R10, REG_RCX, 2);
theEmitter->emitIns_R_AR((instruction)ins, EA_4BYTE, REG_R10, REG_RCX, 2);
theEmitter->emitIns_R_AR((instruction)ins, EA_8BYTE, REG_R16, REG_RCX, 2);
theEmitter->emitIns_R_AR((instruction)ins, EA_4BYTE, REG_R16, REG_RCX, 2);
theEmitter->emitIns_R_S((instruction)ins, EA_8BYTE, REG_RAX, 0, 0);
theEmitter->emitIns_R_S((instruction)ins, EA_4BYTE, REG_RAX, 0, 0);
theEmitter->emitIns_R_S((instruction)ins, EA_8BYTE, REG_R10, 0, 0);
theEmitter->emitIns_R_S((instruction)ins, EA_4BYTE, REG_R10, 0, 0);
theEmitter->emitIns_R_S((instruction)ins, EA_8BYTE, REG_R16, 0, 0);
theEmitter->emitIns_R_S((instruction)ins, EA_4BYTE, REG_R16, 0, 0);
theEmitter->emitIns_R_R_R((instruction)ins, EA_8BYTE, REG_R10, REG_EAX, REG_ECX, (insOpts)(INS_OPTS_EVEX_nd));
theEmitter->emitIns_R_R_R((instruction)ins, EA_4BYTE, REG_R10, REG_EAX, REG_ECX, (insOpts)(INS_OPTS_EVEX_nd));
theEmitter->emitIns_R_R_AR((instruction)ins, EA_8BYTE, REG_R16, REG_R17, REG_R18, 2,
(insOpts)(INS_OPTS_EVEX_nd));
theEmitter->emitIns_R_R_AR((instruction)ins, EA_4BYTE, REG_R16, REG_R17, REG_R18, 2,
(insOpts)(INS_OPTS_EVEX_nd));
theEmitter->emitIns_R_R_A((instruction)ins, EA_8BYTE, REG_R16, REG_R17, &load, (insOpts)(INS_OPTS_EVEX_nd));
theEmitter->emitIns_R_R_A((instruction)ins, EA_4BYTE, REG_R16, REG_R17, &load, (insOpts)(INS_OPTS_EVEX_nd));
theEmitter->emitIns_R_R_S((instruction)ins, EA_8BYTE, REG_R17, REG_R10, 0, 0, (insOpts)(INS_OPTS_EVEX_nd));
theEmitter->emitIns_R_R_S((instruction)ins, EA_4BYTE, REG_R17, REG_R10, 0, 0, (insOpts)(INS_OPTS_EVEX_nd));
theEmitter->emitIns_R_R_S((instruction)ins, EA_8BYTE, REG_R17, REG_R16, 0, 0, (insOpts)(INS_OPTS_EVEX_nd));
theEmitter->emitIns_R_R_S((instruction)ins, EA_4BYTE, REG_R17, REG_R16, 0, 0, (insOpts)(INS_OPTS_EVEX_nd));
}
}

/*****************************************************************************
* Unit tests for the CCMP instructions.
*/
Expand Down Expand Up @@ -9368,7 +9473,7 @@ void CodeGen::genAmd64EmitterUnitTestsCCMP()
// Test all dfv
for (int i = 0; i < 16; i++)
{
theEmitter->emitIns_R_R(INS_ccmpe, EA_4BYTE, REG_RAX, REG_RCX, (insOpts)(i << INS_OPTS_EVEX_dfv_byte_offset));
theEmitter->emitIns_R_R(INS_ccmpe, EA_4BYTE, REG_RAX, REG_RCX, (insOpts)(i << INS_OPTS_EVEX_dfv_shift));
}

// ============
Expand All @@ -9390,7 +9495,7 @@ void CodeGen::genAmd64EmitterUnitTestsCCMP()
// Test all dfv
for (int i = 0; i < 16; i++)
{
theEmitter->emitIns_R_S(INS_ccmpe, EA_4BYTE, REG_RAX, 0, 0, (insOpts)(i << INS_OPTS_EVEX_dfv_byte_offset));
theEmitter->emitIns_R_S(INS_ccmpe, EA_4BYTE, REG_RAX, 0, 0, (insOpts)(i << INS_OPTS_EVEX_dfv_shift));
}

// ============
Expand Down Expand Up @@ -9418,6 +9523,79 @@ void CodeGen::genAmd64EmitterUnitTestsCCMP()
theEmitter->emitIns_R_C(INS_ccmpe, EA_4BYTE, REG_RAX, hnd, 4, INS_OPTS_EVEX_dfv_cf);
}

/*****************************************************************************
* Unit tests for the CTEST instructions.
*/
void CodeGen::genAmd64EmitterUnitTestsCTEST()
{
static_assert(FIRST_CTEST_INSTRUCTION - FIRST_CCMP_INSTRUCTION == 32);
emitter* theEmitter = GetEmitter();
genDefineTempLabel(genCreateTempLabel());
GenTreePhysReg physReg(REG_EDX);
physReg.SetRegNum(REG_EDX);
GenTreeIndir load = indirForm(TYP_INT, &physReg);

// ============
// Test RR form
// ============

// Test all sizes
theEmitter->emitIns_R_R(INS_test, EA_4BYTE, REG_EAX, REG_ECX);
theEmitter->emitIns_R_R(INS_cteste, EA_4BYTE, REG_RAX, REG_RCX, INS_OPTS_EVEX_dfv_cf);
theEmitter->emitIns_R_R(INS_cteste, EA_8BYTE, REG_RAX, REG_RCX, INS_OPTS_EVEX_dfv_cf);
theEmitter->emitIns_R_R(INS_cteste, EA_2BYTE, REG_RAX, REG_RCX, INS_OPTS_EVEX_dfv_cf);
theEmitter->emitIns_R_R(INS_cteste, EA_1BYTE, REG_RAX, REG_RCX, INS_OPTS_EVEX_dfv_cf);

// Test all CC codes
for (uint32_t ins = FIRST_CTEST_INSTRUCTION; ins <= LAST_CTEST_INSTRUCTION; ins++)
{
theEmitter->emitIns_R_R((instruction)ins, EA_4BYTE, REG_RAX, REG_RCX, INS_OPTS_EVEX_dfv_cf);
}

// Test all dfv
for (int i = 0; i < 16; i++)
{
theEmitter->emitIns_R_R(INS_cteste, EA_4BYTE, REG_RAX, REG_RCX, (insOpts)(i << INS_OPTS_EVEX_dfv_shift));
}

// ============
// Test RI form (test small and large sizes and constants)
// ============

theEmitter->emitIns_R_I(INS_test, EA_8BYTE, REG_RAX, 123);
theEmitter->emitIns_R_I(INS_cteste, EA_8BYTE, REG_RAX, 123, INS_OPTS_EVEX_dfv_cf);
theEmitter->emitIns_R_I(INS_cteste, EA_8BYTE, REG_RAX, 270, INS_OPTS_EVEX_dfv_cf);

theEmitter->emitIns_R_I(INS_test, EA_4BYTE, REG_RAX, 123);
theEmitter->emitIns_R_I(INS_cteste, EA_4BYTE, REG_RAX, 123, INS_OPTS_EVEX_dfv_cf);
theEmitter->emitIns_R_I(INS_cteste, EA_4BYTE, REG_RAX, 270, INS_OPTS_EVEX_dfv_cf);

theEmitter->emitIns_R_I(INS_test, EA_2BYTE, REG_RAX, 123);
theEmitter->emitIns_R_I(INS_cteste, EA_2BYTE, REG_RAX, 123, INS_OPTS_EVEX_dfv_cf);
theEmitter->emitIns_R_I(INS_cteste, EA_2BYTE, REG_RAX, 270, INS_OPTS_EVEX_dfv_cf);

theEmitter->emitIns_R_I(INS_test, EA_1BYTE, REG_RAX, 123);
theEmitter->emitIns_R_I(INS_cteste, EA_1BYTE, REG_RAX, 123, INS_OPTS_EVEX_dfv_cf);
theEmitter->emitIns_R_I(INS_cteste, EA_1BYTE, REG_RAX, 270, INS_OPTS_EVEX_dfv_cf);

// ============
// Test MR form (test small and large sizes)
// ============

theEmitter->emitIns_AR_R(INS_cteste, EA_1BYTE, REG_EAX, REG_ECX, 4, INS_OPTS_EVEX_dfv_cf);
theEmitter->emitIns_AR_R(INS_cteste, EA_2BYTE, REG_EAX, REG_ECX, 4, INS_OPTS_EVEX_dfv_cf);
theEmitter->emitIns_AR_R(INS_cteste, EA_4BYTE, REG_EAX, REG_ECX, 4, INS_OPTS_EVEX_dfv_cf);
theEmitter->emitIns_AR_R(INS_cteste, EA_8BYTE, REG_EAX, REG_ECX, 4, INS_OPTS_EVEX_dfv_cf);

// ============
// Test MI form
// ============

theEmitter->emitIns_I_AR(INS_cteste, EA_1BYTE, 123, REG_R18, 2, INS_OPTS_EVEX_dfv_cf);
theEmitter->emitIns_I_AR(INS_cteste, EA_2BYTE, 123, REG_R18, 2, INS_OPTS_EVEX_dfv_cf);
theEmitter->emitIns_I_AR(INS_cteste, EA_4BYTE, 123, REG_R18, 2, INS_OPTS_EVEX_dfv_cf);
theEmitter->emitIns_I_AR(INS_cteste, EA_8BYTE, 123, REG_R18, 2, INS_OPTS_EVEX_dfv_cf);
}
#endif // defined(DEBUG) && defined(TARGET_AMD64)

#ifdef PROFILING_SUPPORTED
Expand Down
Loading
Loading