For the CopyTo methods, we need to throw ArgumentOutOfRangeException instead of IndexOutOfRangeException for the initial index value, and then ArgumentException if the number of elements exceeds the remaining elements in the array. To support this, the GenTreeBoundsChk node now has a gtThrowKind field which indicates the kind of throw block it will branch to if the check failes.
This requires new helper calls, which I added to the end of corinfo.h.
[tfs-changeset: 1499475]
CORINFO_HELP_LOOP_CLONE_CHOICE_ADDR, // Return the reference to a counter to decide to take cloned path in debug stress.
CORINFO_HELP_DEBUG_LOG_LOOP_CLONING, // Print a message that a loop cloning optimization has occurred in debug mode.
+#ifndef RYUJIT_CTPBUILD
+ CORINFO_HELP_THROW_ARGUMENTEXCEPTION, // throw ArgumentException
+ CORINFO_HELP_THROW_ARGUMENTOUTOFRANGEEXCEPTION, // throw ArgumentOutOfRangeException
+#endif
+
CORINFO_HELP_COUNT,
};
// JbTodo: This helper definition is missing it's MDIL helper counterpart.
JITHELPER1(CORINFO_HELP_DEBUG_LOG_LOOP_CLONING, JIT_DebugLogLoopCloning, CORINFO_HELP_SIG_REG_ONLY, MDIL_HELP_UNDEF)
+#ifndef RYUJIT_CTPBUILD
+ JITHELPER1(CORINFO_HELP_THROW_ARGUMENTEXCEPTION, JIT_ThrowArgumentException, CORINFO_HELP_SIG_REG_ONLY, MDIL_HELP_UNDEF)
+ JITHELPER1(CORINFO_HELP_THROW_ARGUMENTOUTOFRANGEEXCEPTION, JIT_ThrowArgumentOutOfRangeException, CORINFO_HELP_SIG_REG_ONLY, MDIL_HELP_UNDEF)
+#endif
+
#undef JITHELPER1
#undef DYNAMICJITHELPER1
#undef JITHELPER
//-------------------------------------------------------------------------
- void genJumpToThrowHlpBlk(emitJumpKind jumpKind,
- Compiler::addCodeKind codeKind,
- GenTreePtr failBlk = NULL);
+ void genJumpToThrowHlpBlk(emitJumpKind jumpKind,
+ SpecialCodeKind codeKind,
+ GenTreePtr failBlk = NULL);
void genCheckOverflow (GenTreePtr tree);
genConsumeIfReg(src2);
getEmitter()->emitInsBinary(INS_cmp, emitAttr(TYP_INT), src1, src2);
- genJumpToThrowHlpBlk(jmpKind, Compiler::ACK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
+ genJumpToThrowHlpBlk(jmpKind, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
}
if (divisorOp->IsZero())
{
- genJumpToThrowHlpBlk(EJ_je, Compiler::ACK_DIV_BY_ZERO);
+ genJumpToThrowHlpBlk(EJ_je, SCK_DIV_BY_ZERO);
// We don't need to generate the sdiv/udiv instruction
}
else
{
// Check if the divisor is zero throw a DivideByZeroException
emit->emitIns_R_I(INS_cmp, cmpSize, divisorReg, 0);
- genJumpToThrowHlpBlk(EJ_je, Compiler::ACK_DIV_BY_ZERO);
+ genJumpToThrowHlpBlk(EJ_je, SCK_DIV_BY_ZERO);
// Check if the divisor is not -1 branch to 'sdivLabel'
emit->emitIns_R_I(INS_cmp, cmpSize, divisorReg, -1);
//
emit->emitIns_R_R_R(INS_adds, cmpSize, REG_ZR, dividendReg, dividendReg);
inst_JMP(genJumpKindForOper(GT_NE, true), sdivLabel); // goto sdiv if Z flag is clear
- genJumpToThrowHlpBlk(EJ_jo, Compiler::ACK_ARITH_EXCPN); // if the V flags is set throw ArithmeticException
+ genJumpToThrowHlpBlk(EJ_jo, SCK_ARITH_EXCPN); // if the V flags is set throw ArithmeticException
}
genDefineTempLabel(sdivLabel);
if (!divisorOp->isContainedIntOrIImmed())
{
emit->emitIns_R_I(INS_cmp, cmpSize, divisorReg, 0);
- genJumpToThrowHlpBlk(EJ_je, Compiler::ACK_DIV_BY_ZERO);
+ genJumpToThrowHlpBlk(EJ_je, SCK_DIV_BY_ZERO);
}
genCodeForBinary(treeNode); // Generate the udiv instruction
getEmitter()->emitIns_R_R(INS_cmp, EA_4BYTE, src1->gtRegNum, src2->gtRegNum);
}
- genJumpToThrowHlpBlk(jmpKind, Compiler::ACK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
+ genJumpToThrowHlpBlk(jmpKind, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
}
//------------------------------------------------------------------------
tgtReg,
arrReg,
genOffsetOfMDArrayDimensionSize(elemType, rank, dim));
- genJumpToThrowHlpBlk(EJ_jae, Compiler::ACK_RNGCHK_FAIL);
+ genJumpToThrowHlpBlk(EJ_jae, SCK_RNGCHK_FAIL);
genProduceReg(arrIndex);
#else // !0
{
// We only need to check for a negative value in sourceReg
emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, 0);
- genJumpToThrowHlpBlk(EJ_jl, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jl, SCK_OVERFLOW);
if (dstType == TYP_ULONG)
{
// cast to TYP_ULONG:
{
noway_assert(typeMask != 0);
emit->emitIns_R_I(INS_tst, cmpSize, sourceReg, typeMask);
- genJumpToThrowHlpBlk(EJ_jne, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jne, SCK_OVERFLOW);
}
else
{
noway_assert((typeMin != 0) && (typeMax != 0));
emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, typeMax);
- genJumpToThrowHlpBlk(EJ_jg, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jg, SCK_OVERFLOW);
// Compare with the MIN
emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, typeMin);
- genJumpToThrowHlpBlk(EJ_jl, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jl, SCK_OVERFLOW);
}
}
ins = INS_mov;
inst_RV_IV(INS_cmp, tmpReg, expMask, EA_4BYTE);
// If exponent is all 1's, throw ArithmeticException
- genJumpToThrowHlpBlk(EJ_je, Compiler::ACK_ARITH_EXCPN);
+ genJumpToThrowHlpBlk(EJ_je, SCK_ARITH_EXCPN);
// if it is a finite value copy it to targetReg
if (treeNode->gtRegNum != op1->gtRegNum)
*/
void CodeGen::genJumpToThrowHlpBlk(emitJumpKind jumpKind,
- Compiler::addCodeKind codeKind,
+ SpecialCodeKind codeKind,
GenTreePtr failBlk)
{
if (!compiler->opts.compDbgCode)
// Jump to the block which will throw the expection
- genJumpToThrowHlpBlk(jumpKind, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(jumpKind, SCK_OVERFLOW);
}
#if FEATURE_EH_FUNCLETS
/* Generate "jae <fail_label>" */
noway_assert(oper->gtOper == GT_ARR_BOUNDS_CHECK);
- genJumpToThrowHlpBlk(EJ_jae, Compiler::ACK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
+ genJumpToThrowHlpBlk(EJ_jae, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
}
else
{
/* Generate "cmp [arrRef+LenOffs], ixv" */
inst_AT_IV(INS_cmp, EA_4BYTE, arrRef, ixv, lenOffset);
// Generate "jbe <fail_label>"
- genJumpToThrowHlpBlk(EJ_jbe, Compiler::ACK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
+ genJumpToThrowHlpBlk(EJ_jbe, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
}
else if (arrLen->IsCnsIntOrI())
{
// Both are constants; decide at compile time.
if (!(0 <= ixvFull && ixvFull < lenv))
{
- genJumpToThrowHlpBlk(EJ_jmp, Compiler::ACK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
+ genJumpToThrowHlpBlk(EJ_jmp, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
}
}
else if (!indIsInt)
{
- genJumpToThrowHlpBlk(EJ_jmp, Compiler::ACK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
+ genJumpToThrowHlpBlk(EJ_jmp, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
}
else
{
/* Generate "cmp arrLen, ixv" */
inst_RV_IV(INS_cmp, arrLen->gtRegNum, ixv, EA_4BYTE);
// Generate "jbe <fail_label>"
- genJumpToThrowHlpBlk(EJ_jbe, Compiler::ACK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
+ genJumpToThrowHlpBlk(EJ_jbe, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
}
}
compiler->eeGetArrayDataOffset(elemType) + sizeof(int) * dim);
#endif
- genJumpToThrowHlpBlk(EJ_jae, Compiler::ACK_RNGCHK_FAIL);
+ genJumpToThrowHlpBlk(EJ_jae, SCK_RNGCHK_FAIL);
if (dim == 0)
{
getEmitter()->emitIns_R_I(INS_cmp, EA_4BYTE, regTmpHi, 0);
// Jump to the block which will throw the expection
- genJumpToThrowHlpBlk(EJ_jne, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jne, SCK_OVERFLOW);
// Unlock regLo [and regHi] after generating code for the gtOverflow() case
//
instGen_Compare_Reg_To_Zero(EA_4BYTE, reg);
if (tree->gtFlags & GTF_UNSIGNED) // conv.ovf.u8.i4 (i4 > 0 and upper bits 0)
{
- genJumpToThrowHlpBlk(EJ_jl, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jl, SCK_OVERFLOW);
goto UPPER_BITS_ZERO;
}
inst_TT_IV(INS_cmp, op1, 0x00000000, 4);
}
- genJumpToThrowHlpBlk(EJ_jne, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jne, SCK_OVERFLOW);
inst_JMP(EJ_jmp, done);
// If loDWord is negative, hiDWord should be -1 (sign extended loDWord)
{
inst_TT_IV(INS_cmp, op1, 0xFFFFFFFFL, 4);
}
- genJumpToThrowHlpBlk(EJ_jne, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jne, SCK_OVERFLOW);
// Done
inst_TT_IV(INS_cmp, op1, 0, 4);
}
- genJumpToThrowHlpBlk(EJ_jne, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jne, SCK_OVERFLOW);
break;
default:
if (unsv)
{
inst_RV_IV(INS_TEST, reg, typeMask, emitActualTypeSize(baseType));
- genJumpToThrowHlpBlk(EJ_jne, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jne, SCK_OVERFLOW);
}
else
{
noway_assert(typeMin != DUMMY_INIT(~0) && typeMax != DUMMY_INIT(0));
inst_RV_IV(INS_cmp, reg, typeMax, emitActualTypeSize(baseType));
- genJumpToThrowHlpBlk(EJ_jg, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jg, SCK_OVERFLOW);
// Compare with the MIN
inst_RV_IV(INS_cmp, reg, typeMin, emitActualTypeSize(baseType));
- genJumpToThrowHlpBlk(EJ_jl, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jl, SCK_OVERFLOW);
}
genCodeForTree_DONE(tree, reg);
{
noway_assert((op2->gtFlags & GTF_UNSIGNED) == 0); // conv.ovf.u8.un should be bashed to conv.u8.un
instGen_Compare_Reg_To_Zero(EA_4BYTE, regHi); // set flags
- genJumpToThrowHlpBlk(EJ_jl, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jl, SCK_OVERFLOW);
}
/* Move the value into the target */
{
regNumber hiReg = genRegPairHi(regPair);
instGen_Compare_Reg_To_Zero(EA_4BYTE, hiReg); // set flags
- genJumpToThrowHlpBlk(EJ_jl, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jl, SCK_OVERFLOW);
}
}
goto DONE;
inst_TT_IV(INS_cmp, op1, 0, sizeof(int));
}
- genJumpToThrowHlpBlk(EJ_jl, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jl, SCK_OVERFLOW);
goto DONE;
default:
#endif //DEBUG
getEmitter()->emitInsBinary(INS_cmp, emitTypeSize(src2->TypeGet()), src1, src2);
- genJumpToThrowHlpBlk(jmpKind, Compiler::ACK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
+ genJumpToThrowHlpBlk(jmpKind, bndsChk->gtThrowKind, bndsChk->gtIndRngFailBB);
}
tgtReg,
arrReg,
genOffsetOfMDArrayDimensionSize(elemType, rank, dim));
- genJumpToThrowHlpBlk(EJ_jae, Compiler::ACK_RNGCHK_FAIL);
+ genJumpToThrowHlpBlk(EJ_jae, SCK_RNGCHK_FAIL);
genProduceReg(arrIndex);
}
{
// We only need to check for a negative value in sourceReg
inst_RV_IV(INS_cmp, sourceReg, 0, size);
- genJumpToThrowHlpBlk(EJ_jl, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jl, SCK_OVERFLOW);
if (dstType == TYP_ULONG)
{
// cast from TYP_INT to TYP_ULONG
{
inst_RV_RV(INS_mov, tmpReg, sourceReg, TYP_LONG); // Move the 64-bit value to a writeable temp reg
inst_RV_SH(INS_SHIFT_RIGHT_LOGICAL, size, tmpReg, 32); // Shift right by 32 bits
- genJumpToThrowHlpBlk(EJ_jne, Compiler::ACK_OVERFLOW); // Thow if result shift is non-zero
+ genJumpToThrowHlpBlk(EJ_jne, SCK_OVERFLOW); // Thow if result shift is non-zero
}
else
{
noway_assert(typeMask != 0);
inst_RV_IV(INS_TEST, sourceReg, typeMask, size);
- genJumpToThrowHlpBlk(EJ_jne, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jne, SCK_OVERFLOW);
}
}
else
noway_assert((typeMin != 0) && (typeMax != 0));
inst_RV_IV(INS_cmp, sourceReg, typeMax, size);
- genJumpToThrowHlpBlk(EJ_jg, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jg, SCK_OVERFLOW);
// Compare with the MIN
inst_RV_IV(INS_cmp, sourceReg, typeMin, size);
- genJumpToThrowHlpBlk(EJ_jl, Compiler::ACK_OVERFLOW);
+ genJumpToThrowHlpBlk(EJ_jl, SCK_OVERFLOW);
}
}
inst_RV_IV(INS_cmp, tmpReg, expMask, EA_4BYTE);
// If exponent is all 1's, throw ArithmeticException
- genJumpToThrowHlpBlk(EJ_je, Compiler::ACK_ARITH_EXCPN);
+ genJumpToThrowHlpBlk(EJ_je, SCK_ARITH_EXCPN);
// if it is a finite value copy it to targetReg
if (treeNode->gtRegNum != op1->gtRegNum)
bool compCanEncodePtrArgCntMax();
- BasicBlock * fgRngChkTarget (BasicBlock * block,
- unsigned stkDepth);
void fgSetRngChkTarget (GenTreePtr tree,
bool delay = true);
// range checking or explicit calls to enable GC, and so on.
//
public:
- enum addCodeKind
- {
- ACK_NONE,
- ACK_RNGCHK_FAIL, // target when range check fails
- ACK_PAUSE_EXEC, // target to stop (e.g. to allow GC)
- ACK_DIV_BY_ZERO, // target for divide by zero (Not used on X86/X64)
- ACK_ARITH_EXCPN, // target on arithmetic exception
- ACK_OVERFLOW = ACK_ARITH_EXCPN, // target on overflow
- ACK_COUNT
- };
+
struct AddCodeDsc
{
- AddCodeDsc * acdNext;
- BasicBlock * acdDstBlk; // block to which we jump
- unsigned acdData;
- addCodeKind acdKind; // what kind of a label is this?
- unsigned short acdStkLvl;
+ AddCodeDsc * acdNext;
+ BasicBlock * acdDstBlk; // block to which we jump
+ unsigned acdData;
+ SpecialCodeKind acdKind; // what kind of a special block is this?
+ unsigned short acdStkLvl;
};
private:
- static unsigned acdHelper (addCodeKind codeKind);
+ static unsigned acdHelper (SpecialCodeKind codeKind);
AddCodeDsc * fgAddCodeList;
bool fgAddCodeModf;
bool fgRngChkThrowAdded;
- AddCodeDsc * fgExcptnTargetCache[ACK_COUNT];
+ AddCodeDsc * fgExcptnTargetCache[SCK_COUNT];
+
+ BasicBlock * fgRngChkTarget (BasicBlock * block,
+ unsigned stkDepth,
+ SpecialCodeKind kind);
- BasicBlock * fgAddCodeRef (BasicBlock * srcBlk,
- unsigned refData,
- addCodeKind kind,
- unsigned stkDepth = 0);
+ BasicBlock * fgAddCodeRef (BasicBlock * srcBlk,
+ unsigned refData,
+ SpecialCodeKind kind,
+ unsigned stkDepth = 0);
public:
- AddCodeDsc * fgFindExcptnTarget(addCodeKind kind,
+ AddCodeDsc * fgFindExcptnTarget(SpecialCodeKind kind,
unsigned refData);
private:
bool fgIsCodeAdded ();
{
if (block == add->acdDstBlk)
{
- return add->acdKind == ACK_RNGCHK_FAIL ||
- add->acdKind == ACK_DIV_BY_ZERO ||
- add->acdKind == ACK_OVERFLOW;
+ return add->acdKind == SCK_RNGCHK_FAIL ||
+ add->acdKind == SCK_DIV_BY_ZERO ||
+ add->acdKind == SCK_OVERFLOW;
}
}
{
if (block == add->acdDstBlk)
{
- assert(add->acdKind == ACK_RNGCHK_FAIL ||
- add->acdKind == ACK_DIV_BY_ZERO ||
- add->acdKind == ACK_OVERFLOW);
+ assert(add->acdKind == SCK_RNGCHK_FAIL ||
+ add->acdKind == SCK_DIV_BY_ZERO ||
+ add->acdKind == SCK_OVERFLOW);
// TODO: bbTgtStkDepth is DEBUG-only.
// Should we use it regularly and avoid this search.
assert(block->bbTgtStkDepth == add->acdStkLvl);
fgAddCodeList = 0;
fgAddCodeModf = false;
- for (int i = 0; i < ACK_COUNT; i++)
+ for (int i = 0; i < SCK_COUNT; i++)
{
fgExcptnTargetCache[i] = NULL;
}
*/
/* static */
-unsigned Compiler::acdHelper(addCodeKind codeKind)
+unsigned Compiler::acdHelper(SpecialCodeKind codeKind)
{
switch (codeKind)
{
- case ACK_RNGCHK_FAIL: return CORINFO_HELP_RNGCHKFAIL;
- case ACK_DIV_BY_ZERO: return CORINFO_HELP_THROWDIVZERO;
- case ACK_ARITH_EXCPN: return CORINFO_HELP_OVERFLOW;
+ case SCK_RNGCHK_FAIL: return CORINFO_HELP_RNGCHKFAIL;
+ case SCK_DIV_BY_ZERO: return CORINFO_HELP_THROWDIVZERO;
+ case SCK_ARITH_EXCPN: return CORINFO_HELP_OVERFLOW;
default: assert(!"Bad codeKind"); return 0;
}
}
* the given kind.
*/
-BasicBlock* Compiler::fgAddCodeRef(BasicBlock* srcBlk,
- unsigned refData,
- addCodeKind kind,
- unsigned stkDepth)
+BasicBlock* Compiler::fgAddCodeRef(BasicBlock* srcBlk,
+ unsigned refData,
+ SpecialCodeKind kind,
+ unsigned stkDepth)
{
/* For debuggable code, genJumpToThrowHlpBlk() will generate the 'throw'
code inline. It has to be kept consistent with fgAddCodeRef() */
const static
BBjumpKinds jumpKinds[] =
{
- BBJ_NONE, // ACK_NONE
- BBJ_THROW, // ACK_RNGCHK_FAIL
- BBJ_ALWAYS, // ACK_PAUSE_EXEC
- BBJ_THROW, // ACK_DIV_BY_ZERO
- BBJ_THROW, // ACK_ARITH_EXCP, ACK_OVERFLOW
+ BBJ_NONE, // SCK_NONE
+ BBJ_THROW, // SCK_RNGCHK_FAIL
+ BBJ_ALWAYS, // SCK_PAUSE_EXEC
+ BBJ_THROW, // SCK_DIV_BY_ZERO
+ BBJ_THROW, // SCK_ARITH_EXCP, SCK_OVERFLOW
+ BBJ_THROW, // SCK_ARG_EXCPN
+ BBJ_THROW, // SCK_ARG_RNG_EXCPN
};
- noway_assert(sizeof(jumpKinds) == ACK_COUNT); // sanity check
+ noway_assert(sizeof(jumpKinds) == SCK_COUNT); // sanity check
/* First look for an existing entry that matches what we're looking for */
}
#endif // _TARGET_X86_
- goto DONE;
+ return add->acdDstBlk;
}
/* We have to allocate a new entry and prepend it to the list */
msgWhere = "handler";
}
- const char* msg = "";
- if (kind == ACK_RNGCHK_FAIL)
- {
- msg = " for RNGCHK_FAIL";
- }
- else if (kind == ACK_PAUSE_EXEC)
+ const char* msg;
+ switch (kind)
{
- msg = " for PAUSE_EXEC";
- }
- else if (kind == ACK_DIV_BY_ZERO)
- {
- msg = " for DIV_BY_ZERO";
- }
- else if (kind == ACK_OVERFLOW)
- {
- msg = " for OVERFLOW";
+ case SCK_RNGCHK_FAIL: msg = " for RNGCHK_FAIL"; break;
+ case SCK_PAUSE_EXEC: msg = " for PAUSE_EXEC"; break;
+ case SCK_DIV_BY_ZERO: msg = " for DIV_BY_ZERO"; break;
+ case SCK_OVERFLOW: msg = " for OVERFLOW"; break;
+ case SCK_ARG_EXCPN: msg = " for ARG_EXCPN"; break;
+ case SCK_ARG_RNG_EXCPN: msg = " for ARG_RNG_EXCPN"; break;
+ default: msg = " for ??"; break;
}
printf("\nfgAddCodeRef -"
/* Now figure out what code to insert */
GenTreeCall* tree;
- int helper;
+ int helper = CORINFO_HELP_UNDEF;
switch (kind)
{
- case ACK_RNGCHK_FAIL: helper = CORINFO_HELP_RNGCHKFAIL;
- goto ADD_HELPER_CALL;
-
- case ACK_DIV_BY_ZERO: helper = CORINFO_HELP_THROWDIVZERO;
- goto ADD_HELPER_CALL;
+ case SCK_RNGCHK_FAIL: helper = CORINFO_HELP_RNGCHKFAIL;
+ break;
- case ACK_ARITH_EXCPN: helper = CORINFO_HELP_OVERFLOW;
- noway_assert(ACK_OVERFLOW == ACK_ARITH_EXCPN);
- goto ADD_HELPER_CALL;
+ case SCK_DIV_BY_ZERO: helper = CORINFO_HELP_THROWDIVZERO;
+ break;
-ADD_HELPER_CALL:;
+ case SCK_ARITH_EXCPN: helper = CORINFO_HELP_OVERFLOW;
+ noway_assert(SCK_OVERFLOW == SCK_ARITH_EXCPN);
+ break;
- /* Add the appropriate helper call */
+#ifndef RYUJIT_CTPBUILD
+ case SCK_ARG_EXCPN: helper = CORINFO_HELP_THROW_ARGUMENTEXCEPTION;
+ break;
- tree = gtNewHelperCallNode(helper, TYP_VOID, GTF_EXCEPT);
- // There are no args here but fgMorphArgs has side effects
- // such as setting the outgoing arg area (which is necessary
- // on AMD if there are any calls).
- tree = fgMorphArgs(tree);
- break;
+ case SCK_ARG_RNG_EXCPN: helper = CORINFO_HELP_THROW_ARGUMENTOUTOFRANGEEXCEPTION;
+ break;
+#endif // !RYUJIT_CTPBUILD
-// case ACK_PAUSE_EXEC:
+// case SCK_PAUSE_EXEC:
// noway_assert(!"add code to pause exec");
default:
noway_assert(!"unexpected code addition kind");
- return NULL;
+ return nullptr;
}
- /* Store the tree in the new basic block */
+ noway_assert(helper != CORINFO_HELP_UNDEF);
+ // Add the appropriate helper call.
+ tree = gtNewHelperCallNode(helper, TYP_VOID, GTF_EXCEPT);
- fgInsertStmtAtEnd(newBlk, fgNewStmtFromTree(tree));
+ // There are no args here but fgMorphArgs has side effects
+ // such as setting the outgoing arg area (which is necessary
+ // on AMD if there are any calls).
+ tree = fgMorphArgs(tree);
-DONE:;
+ // Store the tree in the new basic block.
+
+ fgInsertStmtAtEnd(newBlk, fgNewStmtFromTree(tree));
return add->acdDstBlk;
}
* a given type of exception
*/
-Compiler::AddCodeDsc* Compiler::fgFindExcptnTarget(addCodeKind kind,
+Compiler::AddCodeDsc* Compiler::fgFindExcptnTarget(SpecialCodeKind kind,
unsigned refData)
{
if (!(fgExcptnTargetCache[kind] && // Try the cached value first
* range check is to jump to upon failure.
*/
-BasicBlock* Compiler::fgRngChkTarget(BasicBlock* block, unsigned stkDepth)
+BasicBlock* Compiler::fgRngChkTarget(BasicBlock* block, unsigned stkDepth, SpecialCodeKind kind)
{
#ifdef DEBUG
if (verbose)
/* We attach the target label to the containing try block (if any) */
noway_assert(!compIsForInlining());
- return fgAddCodeRef(block, bbThrowIndex(block), ACK_RNGCHK_FAIL, stkDepth);
+ return fgAddCodeRef(block, bbThrowIndex(block), kind, stkDepth);
}
// Sequences the tree.
case GT_SIMD_CHK:
#endif // FEATURE_SIMD
return Compare(op1->gtBoundsChk.gtArrLen, op2->gtBoundsChk.gtArrLen)
- && Compare(op1->gtBoundsChk.gtIndex, op2->gtBoundsChk.gtIndex);
+ && Compare(op1->gtBoundsChk.gtIndex, op2->gtBoundsChk.gtIndex)
+ && (op1->gtBoundsChk.gtThrowKind == op1->gtBoundsChk.gtThrowKind);
default:
assert(!"unexpected operator");
#endif // FEATURE_SIMD
hash = genTreeHashAdd(hash, gtHashValue(tree->gtBoundsChk.gtArrLen));
hash = genTreeHashAdd(hash, gtHashValue(tree->gtBoundsChk.gtIndex));
+ hash = genTreeHashAdd(hash, tree->gtBoundsChk.gtThrowKind);
break;
default:
GenTreeBoundsChk(oper,
tree->TypeGet(),
gtCloneExpr(tree->gtBoundsChk.gtArrLen, addFlags, varNum, varVal),
- gtCloneExpr(tree->gtBoundsChk.gtIndex, addFlags, varNum, varVal));
+ gtCloneExpr(tree->gtBoundsChk.gtIndex, addFlags, varNum, varVal),
+ tree->gtBoundsChk.gtThrowKind);
break;
if (lea->Index() != NULL) bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), "(i*%d)+", lea->gtScale);
bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), "%d)", lea->gtOffset);
}
+ else if (tree->gtOper == GT_ARR_BOUNDS_CHECK)
+ {
+ switch(tree->gtBoundsChk.gtThrowKind)
+ {
+ case SCK_RNGCHK_FAIL: sprintf_s(bufp, sizeof(buf), " %s_Rng", name); break;
+ case SCK_ARG_EXCPN: sprintf_s(bufp, sizeof(buf), " %s_Arg", name); break;
+ case SCK_ARG_RNG_EXCPN: sprintf_s(bufp, sizeof(buf), " %s_ArgRng", name); break;
+ default: unreached();
+ }
+ }
else if (tree->gtOverflowEx())
{
sprintf_s(bufp, sizeof(buf), " %s_ovfl%c", name, 0);
#endif // !DEBUG
#endif // !DEBUGGABLE_GENTREE
+// The SpecialCodeKind enum is used to indicate the type of special (unique)
+// target block that will be targeted by an instruction.
+// These are used by:
+// GenTreeBoundsChk nodes (SCK_RNGCHK_FAIL, SCK_ARG_EXCPN, SCK_ARG_RNG_EXCPN)
+// - these nodes have a field (gtThrowKind) to indicate which kind
+// GenTreeOps nodes, for which codegen will generate the branch
+// - it will use the appropriate kind based on the opcode, though it's not
+// clear why SCK_OVERFLOW == SCK_ARITH_EXCPN
+// SCK_PAUSE_EXEC is not currently used.
+//
+enum SpecialCodeKind
+{
+ SCK_NONE,
+ SCK_RNGCHK_FAIL, // target when range check fails
+ SCK_PAUSE_EXEC, // target to stop (e.g. to allow GC)
+ SCK_DIV_BY_ZERO, // target for divide by zero (Not used on X86/X64)
+ SCK_ARITH_EXCPN, // target on arithmetic exception
+ SCK_OVERFLOW = SCK_ARITH_EXCPN, // target on overflow
+ SCK_ARG_EXCPN, // target on ArgumentException (currently used only for SIMD intrinsics)
+ SCK_ARG_RNG_EXCPN, // target on ArgumentOutOfRangeException (currently used only for SIMD intrinsics)
+ SCK_COUNT
+};
+
/*****************************************************************************/
DECLARE_TYPED_ENUM(genTreeOps,BYTE)
#endif
};
-// This takes an array length,an index value, and the label to jump to if the index is out of range.
+// This takes:
+// - a comparison value (generally an array length),
+// - an index value, and
+// - the label to jump to if the index is out of range.
+// - the "kind" of the throw block to branch to on failure
// It generates no result.
struct GenTreeBoundsChk: public GenTree
{
- GenTreePtr gtArrLen; // An expression for the length of the array being indexed.
- GenTreePtr gtIndex; // The index expression.
+ GenTreePtr gtArrLen; // An expression for the length of the array being indexed.
+ GenTreePtr gtIndex; // The index expression.
- GenTreePtr gtIndRngFailBB; // Label to jump to for array-index-out-of-range
+ GenTreePtr gtIndRngFailBB; // Label to jump to for array-index-out-of-range
+ SpecialCodeKind gtThrowKind; // Kind of throw block to branch to on failure
/* Only out-of-ranges at same stack depth can jump to the same label (finding return address is easier)
For delayed calling of fgSetRngChkTarget() so that the
optimizer has a chance of eliminating some of the rng checks */
unsigned gtStkDepth;
- GenTreeBoundsChk(genTreeOps oper, var_types type, GenTreePtr arrLen, GenTreePtr index) :
+ GenTreeBoundsChk(genTreeOps oper, var_types type, GenTreePtr arrLen, GenTreePtr index, SpecialCodeKind kind) :
GenTree(oper, type),
gtArrLen(arrLen), gtIndex(index),
gtIndRngFailBB(NULL),
- gtStkDepth(0)
+ gtStkDepth(0),
+ gtThrowKind(kind)
{
// Effects flags propagate upwards.
gtFlags |= (arrLen->gtFlags & GTF_ALL_EFFECT);
}
if (tree->gtOverflow())
- fgAddCodeRef(compCurBB, bbThrowIndex(compCurBB), ACK_OVERFLOW, fgPtrArgCntCur);
+ fgAddCodeRef(compCurBB, bbThrowIndex(compCurBB), SCK_OVERFLOW, fgPtrArgCntCur);
return tree;
void Compiler::fgSetRngChkTarget(GenTreePtr tree,
bool delay)
{
- GenTreeBoundsChk* bndsChk = NULL;
+ GenTreeBoundsChk* bndsChk = nullptr;
+ SpecialCodeKind kind = SCK_RNGCHK_FAIL;
#ifdef FEATURE_SIMD
if ((tree->gtOper == GT_ARR_BOUNDS_CHECK) || (tree->gtOper == GT_SIMD_CHK))
#endif // FEATURE_SIMD
{
bndsChk = tree->AsBoundsChk();
+ kind = tree->gtBoundsChk.gtThrowKind;
}
else
{
unsigned stkDepth = (bndsChk != nullptr) ? bndsChk->gtStkDepth
: callStkDepth;
- BasicBlock * rngErrBlk = fgRngChkTarget(compCurBB, stkDepth);
+ BasicBlock * rngErrBlk = fgRngChkTarget(compCurBB, stkDepth, kind);
/* Add the label to the indirection node */
arrLen = gtNewCastNode(bndsChkType, arrLen, bndsChkType);
}
- GenTreeBoundsChk* arrBndsChk = new (this, GT_ARR_BOUNDS_CHECK) GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, arrLen, index);
+ GenTreeBoundsChk* arrBndsChk = new (this, GT_ARR_BOUNDS_CHECK) GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, arrLen, index, SCK_RNGCHK_FAIL);
bndsChk = arrBndsChk;
if (!varTypeIsFloating(tree->gtType))
{
// Codegen for this instruction needs to be able to throw two exceptions:
- fgAddCodeRef(compCurBB, bbThrowIndex(compCurBB), ACK_OVERFLOW, fgPtrArgCntCur);
- fgAddCodeRef(compCurBB, bbThrowIndex(compCurBB), ACK_DIV_BY_ZERO, fgPtrArgCntCur);
+ fgAddCodeRef(compCurBB, bbThrowIndex(compCurBB), SCK_OVERFLOW, fgPtrArgCntCur);
+ fgAddCodeRef(compCurBB, bbThrowIndex(compCurBB), SCK_DIV_BY_ZERO, fgPtrArgCntCur);
}
break;
case GT_UDIV:
// Codegen for this instruction needs to be able to throw one exception:
- fgAddCodeRef(compCurBB, bbThrowIndex(compCurBB), ACK_DIV_BY_ZERO, fgPtrArgCntCur);
+ fgAddCodeRef(compCurBB, bbThrowIndex(compCurBB), SCK_DIV_BY_ZERO, fgPtrArgCntCur);
break;
#endif
// Add the excptn-throwing basic block to jump to on overflow
- fgAddCodeRef(compCurBB, bbThrowIndex(compCurBB), ACK_OVERFLOW, fgPtrArgCntCur);
+ fgAddCodeRef(compCurBB, bbThrowIndex(compCurBB), SCK_OVERFLOW, fgPtrArgCntCur);
// We can't do any commutative morphing for overflow instructions
noway_assert(varTypeIsFloating(op1->TypeGet()));
- fgAddCodeRef(compCurBB, bbThrowIndex(compCurBB), ACK_ARITH_EXCPN, fgPtrArgCntCur);
+ fgAddCodeRef(compCurBB, bbThrowIndex(compCurBB), SCK_ARITH_EXCPN, fgPtrArgCntCur);
break;
case GT_IND:
inst_RV_IV(INS_cmp, reg, expMask, EA_4BYTE);
// If exponent was all 1's, we need to throw ArithExcep
- genJumpToThrowHlpBlk(EJ_je, Compiler::ACK_ARITH_EXCPN);
+ genJumpToThrowHlpBlk(EJ_je, SCK_ARITH_EXCPN);
genCodeForTreeFloat_DONE(tree, op1->gtRegNum);
}
unsigned arrayElementsCount = simdSize / genTypeSize(baseType);
checkIndexExpr = new (this, GT_CNS_INT) GenTreeIntCon(TYP_INT, indexVal + arrayElementsCount - 1);
GenTreeArrLen* arrLen = new (this, GT_ARR_LENGTH) GenTreeArrLen(TYP_INT, arrayRef, (int)offsetof(CORINFO_Array, length));
- GenTreeBoundsChk* arrBndsChk = new (this, GT_ARR_BOUNDS_CHECK) GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, arrLen, checkIndexExpr);
+ GenTreeBoundsChk* arrBndsChk = new (this, GT_ARR_BOUNDS_CHECK) GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, arrLen, checkIndexExpr, SCK_RNGCHK_FAIL);
offset += offsetof(CORINFO_Array, u1Elems);
byrefNode = gtNewOperNode(GT_COMMA, arrayRef->TypeGet(), arrBndsChk, gtCloneExpr(arrayRef));
// (This constructor takes only the zero-based arrays.)
// We will add one or two bounds checks:
// 1. If we have an index, we must do a check on that first.
- // 2. We need to generate a check for the last array element we will access.
+ // We can't combine it with the index + vectorLength check because
+ // a. It might be negative, and b. It may need to raise a different exception
+ // (captured as SCK_ARG_RNG_EXCPN for CopyTo and SCK_RNGCHK_FAIL for Init).
+ // 2. We need to generate a check (SCK_ARG_EXCPN for CopyTo and SCK_RNGCHK_FAIL for Init)
+ // for the last array element we will access.
// We'll either check against (vectorLength - 1) or (index + vectorLength - 1).
GenTree* checkIndexExpr = new (this, GT_CNS_INT) GenTreeIntCon(TYP_INT, vectorLength - 1);
if (op3 != nullptr)
{
+ SpecialCodeKind op3CheckKind;
+ if (simdIntrinsicID == SIMDIntrinsicInitArrayX)
+ {
+ op3CheckKind = SCK_RNGCHK_FAIL;
+ }
+ else
+ {
+ assert(simdIntrinsicID == SIMDIntrinsicCopyToArrayX);
+ op3CheckKind = SCK_ARG_RNG_EXCPN;
+ }
// We need to use the original expression on this, which is the first check.
GenTree* arrayRefForArgRngChk = arrayRefForArgChk;
// Then we clone the clone we just made for the next check.
}
GenTreeArrLen* arrLen = new (this, GT_ARR_LENGTH) GenTreeArrLen(TYP_INT, arrayRefForArgRngChk, (int)offsetof(CORINFO_Array, length));
- argRngChk = new (this, GT_ARR_BOUNDS_CHECK) GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, arrLen, index);
+ argRngChk = new (this, GT_ARR_BOUNDS_CHECK) GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, arrLen, index, op3CheckKind);
// Now, clone op3 to create another node for the argChk
GenTree* index2 = gtCloneExpr(op3);
assert(index != nullptr);
// Insert a bounds check for index + offset - 1.
// This must be a "normal" array.
+ SpecialCodeKind op2CheckKind;
+ if (simdIntrinsicID == SIMDIntrinsicInitArray || simdIntrinsicID == SIMDIntrinsicInitArrayX)
+ {
+ op2CheckKind = SCK_RNGCHK_FAIL;
+ }
+ else
+ {
+ op2CheckKind = SCK_ARG_EXCPN;
+ }
GenTreeArrLen* arrLen = new (this, GT_ARR_LENGTH) GenTreeArrLen(TYP_INT, arrayRefForArgChk, (int)offsetof(CORINFO_Array, length));
- GenTreeBoundsChk* argChk = new (this, GT_ARR_BOUNDS_CHECK) GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, arrLen, checkIndexExpr);
+ GenTreeBoundsChk* argChk = new (this, GT_ARR_BOUNDS_CHECK) GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, arrLen, checkIndexExpr, op2CheckKind);
// Create a GT_COMMA tree for the bounds check(s).
op2 = gtNewOperNode(GT_COMMA, op2->TypeGet(), argChk, op2);
}
GenTree* lengthNode = new (this, GT_CNS_INT) GenTreeIntCon(TYP_INT, vectorLength);
- GenTreeBoundsChk* simdChk = new (this, GT_SIMD_CHK) GenTreeBoundsChk(GT_SIMD_CHK, TYP_VOID, lengthNode, index);
+ GenTreeBoundsChk* simdChk = new (this, GT_SIMD_CHK) GenTreeBoundsChk(GT_SIMD_CHK, TYP_VOID, lengthNode, index, SCK_RNGCHK_FAIL);
// Create a GT_COMMA tree for the bounds check.
op2 = gtNewOperNode(GT_COMMA, op2->TypeGet(), simdChk, op2);
inst_RV_IV(INS_cmp, reg, expMask, EA_4BYTE);
// If exponent was all 1's, we need to throw ArithExcep
- genJumpToThrowHlpBlk(EJ_je, Compiler::ACK_ARITH_EXCPN);
+ genJumpToThrowHlpBlk(EJ_je, SCK_ARITH_EXCPN);
genUpdateLife(tree);
HCIMPLEND
/*********************************************************************/
+HCIMPL0(void, JIT_ThrowArgumentException)
+{
+ FCALL_CONTRACT;
+
+ /* Make no assumptions about the current machine state */
+ ResetCurrentContext();
+
+ FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
+
+ HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
+
+ COMPlusThrow(kArgumentException);
+
+ HELPER_METHOD_FRAME_END();
+}
+HCIMPLEND
+
+/*********************************************************************/
+HCIMPL0(void, JIT_ThrowArgumentOutOfRangeException)
+{
+ FCALL_CONTRACT;
+
+ /* Make no assumptions about the current machine state */
+ ResetCurrentContext();
+
+ FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
+
+ HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
+
+ COMPlusThrow(kArgumentOutOfRangeException);
+
+ HELPER_METHOD_FRAME_END();
+}
+HCIMPLEND
+
+/*********************************************************************/
HCIMPL0(void, JIT_Overflow)
{
FCALL_CONTRACT;