Fix method and basic block flags used by early opts.
Add missing BBF_HAS_NULLCHECK in loop test transformation.
This change had no diffs in frameworks and benchmark.
Add missing OMF_HAS_ARRAYREF and BBF_HAS_IDX_LEN in many places.
This change had diffs in frameworks and benchmark since the optimization
replacing GT_ARRLENGTH with a constant fires much more often.
Fix inlining to not lose basic block flags when the inlinee basic block
has a ret expr but no statements.
Change propGetType condition in optDoEarlyPropForFunc to check for OMF_HAS_NEWARRAY
in addition to OMF_HAS_NEWOBJ.
Check that methods and basic blocks that have calls to object allocation
helpers have OMF_HAS_NEWOBJ and BBF_HAS_NEWOBJ set.
Check that methods and basic blocks that have calls to array allocation
helpers have OMF_HAS_NEWARRAY and BBF_HAS_NEWARRAY set.
Check that methods and basic blocks that have GT_NULLCHECK nodes
helpers have OMF_HAS_NULLCHECK and BBF_HAS_NULLCHECK set.
Check that methods and basic blocks that access MethodTable
via GT_IND on a ref have OMF_HAS_VTABLEREF and BBF_HAS_VTABLEREF set.
Check that methods and basic blocks that have GT_ARRLENGTH
have OMF_HAS_ARRAYREF and BBF_HAS_IDX_LEN set.
Fix fgOptWhileLoop that could lose BBF_HAS_NEWOBJ and BBF_HAS_NEWARRAY.
Add asserts to make sure we don't have diverging codegen
in debug/checked/release because of early opts.
Add BBF_HAS_VTABREF to BBF_COMPACT_UPD and BBF_SPLIT_GAINED.
#define BBF_COMPACT_UPD \
(BBF_CHANGED | BBF_GC_SAFE_POINT | BBF_HAS_JMP | BBF_NEEDS_GCPOLL | BBF_HAS_IDX_LEN | BBF_BACKWARD_JUMP | \
- BBF_HAS_NEWARRAY | BBF_HAS_NEWOBJ | BBF_HAS_NULLCHECK)
+ BBF_HAS_NEWARRAY | BBF_HAS_NEWOBJ | BBF_HAS_NULLCHECK | BBF_HAS_VTABREF)
// Flags a block should not have had before it is split.
#define BBF_SPLIT_GAINED \
(BBF_DONT_REMOVE | BBF_HAS_LABEL | BBF_HAS_JMP | BBF_BACKWARD_JUMP | BBF_HAS_IDX_LEN | BBF_HAS_NEWARRAY | \
- BBF_PROF_WEIGHT | BBF_HAS_NEWOBJ | BBF_KEEP_BBJ_ALWAYS | BBF_CLONED_FINALLY_END | BBF_HAS_NULLCHECK)
+ BBF_PROF_WEIGHT | BBF_HAS_NEWOBJ | BBF_KEEP_BBJ_ALWAYS | BBF_CLONED_FINALLY_END | BBF_HAS_NULLCHECK | \
+ BBF_HAS_VTABREF)
#ifndef __GNUC__ // GCC doesn't like C_ASSERT at global scope
static_assert_no_msg((BBF_SPLIT_NONEXIST & BBF_SPLIT_LOST) == 0);
GenTree* gtNewIndexRef(var_types typ, GenTree* arrayOp, GenTree* indexOp);
- GenTreeArrLen* gtNewArrLen(var_types typ, GenTree* arrayOp, int lenOffset);
+ GenTreeArrLen* gtNewArrLen(var_types typ, GenTree* arrayOp, int lenOffset, BasicBlock* block);
GenTree* gtNewIndir(var_types typ, GenTree* addr);
typedef JitHashTable<unsigned, JitSmallPrimitiveKeyFuncs<unsigned>, GenTree*> LocalNumberToNullCheckTreeMap;
bool gtIsVtableRef(GenTree* tree);
- GenTree* getArrayLengthFromAllocation(GenTree* tree);
- GenTree* getObjectHandleNodeFromAllocation(GenTree* tree);
+ GenTree* getArrayLengthFromAllocation(GenTree* tree DEBUGARG(BasicBlock* block));
+ GenTree* getObjectHandleNodeFromAllocation(GenTree* tree DEBUGARG(BasicBlock* block));
GenTree* optPropGetValueRec(unsigned lclNum, unsigned ssaNum, optPropKind valueKind, int walkDepth);
GenTree* optPropGetValue(unsigned lclNum, unsigned ssaNum, optPropKind valueKind);
GenTree* optEarlyPropRewriteTree(GenTree* tree, LocalNumberToNullCheckTreeMap* nullCheckMap);
unsigned nullCheckLclNum,
bool isInsideTry,
bool checkSideEffectSummary);
+#if DEBUG
+ void optCheckFlagsAreSet(unsigned methodFlag,
+ const char* methodFlagStr,
+ unsigned bbFlag,
+ const char* bbFlagStr,
+ GenTree* tree,
+ BasicBlock* basicBlock);
+#endif
#if ASSERTION_PROP
/**************************************************************************
// typ - Type of the node
// arrayOp - Array node
// lenOffset - Offset of the length field
+// block - Basic block that will contain the result
//
// Return Value:
// New GT_ARR_LENGTH node
-inline GenTreeArrLen* Compiler::gtNewArrLen(var_types typ, GenTree* arrayOp, int lenOffset)
+inline GenTreeArrLen* Compiler::gtNewArrLen(var_types typ, GenTree* arrayOp, int lenOffset, BasicBlock* block)
{
GenTreeArrLen* arrLen = new (this, GT_ARR_LENGTH) GenTreeArrLen(typ, arrayOp, lenOffset);
static_assert_no_msg(GTF_ARRLEN_NONFAULTING == GTF_IND_NONFAULTING);
arrLen->SetIndirExceptionFlags(this);
+ if (block != nullptr)
+ {
+ block->bbFlags |= BBF_HAS_IDX_LEN;
+ }
+ optMethodFlags |= OMF_HAS_ARRAYREF;
return arrLen;
}
bool Compiler::optDoEarlyPropForFunc()
{
bool propArrayLen = (optMethodFlags & OMF_HAS_NEWARRAY) && (optMethodFlags & OMF_HAS_ARRAYREF);
- bool propGetType = (optMethodFlags & OMF_HAS_NEWOBJ) && (optMethodFlags & OMF_HAS_VTABLEREF);
+ bool propGetType = (optMethodFlags & (OMF_HAS_NEWOBJ | OMF_HAS_NEWARRAY)) && (optMethodFlags & OMF_HAS_VTABLEREF);
bool propNullCheck = (optMethodFlags & OMF_HAS_NULLCHECK) != 0;
return propArrayLen || propGetType || propNullCheck;
}
//
// Arguments:
// tree - The array allocation helper call.
+// block - tree's basic block.
//
// Return Value:
// Return the array length node.
-GenTree* Compiler::getArrayLengthFromAllocation(GenTree* tree)
+GenTree* Compiler::getArrayLengthFromAllocation(GenTree* tree DEBUGARG(BasicBlock* block))
{
assert(tree != nullptr);
call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_VC) ||
call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_ALIGN8))
{
+#ifdef DEBUG
+ optCheckFlagsAreSet(OMF_HAS_NEWARRAY, "OMF_HAS_NEWARRAY", BBF_HAS_NEWARRAY, "BBF_HAS_NEWARRAY", tree,
+ block);
+#endif
// This is an array allocation site. Grab the array length node.
return gtArgEntryByArgNum(call, 1)->GetNode();
}
//
// Arguments:
// tree - The object allocation helper call.
+// block - tree's basic block.
//
// Return Value:
// Return the object type handle node.
-GenTree* Compiler::getObjectHandleNodeFromAllocation(GenTree* tree)
+GenTree* Compiler::getObjectHandleNodeFromAllocation(GenTree* tree DEBUGARG(BasicBlock* block))
{
assert(tree != nullptr);
if (call->gtCallType == CT_HELPER)
{
- if (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWFAST) ||
- call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST) ||
- call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST_FINALIZE) ||
- call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST_ALIGN8) ||
- call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST_ALIGN8_VC) ||
- call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST_ALIGN8_FINALIZE) ||
- call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_DIRECT) ||
- call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_OBJ) ||
- call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_VC) ||
- call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_ALIGN8))
+ bool hasNewObj = call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWFAST) ||
+ call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST) ||
+ call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST_FINALIZE) ||
+ call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST_ALIGN8) ||
+ call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST_ALIGN8_VC) ||
+ call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWSFAST_ALIGN8_FINALIZE);
+
+ bool hasNewArr = call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_DIRECT) ||
+ call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_OBJ) ||
+ call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_VC) ||
+ call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_ALIGN8);
+
+#ifdef DEBUG
+ if (hasNewObj)
+ {
+ optCheckFlagsAreSet(OMF_HAS_NEWOBJ, "OMF_HAS_NEWOBJ", BBF_HAS_NEWOBJ, "BBF_HAS_NEWOBJ", tree, block);
+ }
+
+ if (hasNewArr)
+ {
+ optCheckFlagsAreSet(OMF_HAS_NEWARRAY, "OMF_HAS_NEWARRAY", BBF_HAS_NEWARRAY, "BBF_HAS_NEWARRAY", tree,
+ block);
+ }
+#endif // DEBUG
+
+ if (hasNewObj || hasNewArr)
{
// This is an object allocation site. Return the runtime type handle node.
fgArgTabEntry* argTabEntry = gtArgEntryByArgNum(call, 0);
return nullptr;
}
+#ifdef DEBUG
+//-----------------------------------------------------------------------------
+// optCheckFlagsAreSet: Check that the method flag and the basic block flag are set.
+//
+// Arguments:
+// methodFlag - The method flag to check.
+// methodFlagStr - String representation of the method flag.
+// bbFlag - The basic block flag to check.
+// bbFlagStr - String representation of the basic block flag.
+// tree - Tree that makes the flags required.
+// basicBlock - The basic block to check the flag on.
+
+void Compiler::optCheckFlagsAreSet(unsigned methodFlag,
+ const char* methodFlagStr,
+ unsigned bbFlag,
+ const char* bbFlagStr,
+ GenTree* tree,
+ BasicBlock* basicBlock)
+{
+ if ((optMethodFlags & methodFlag) == 0)
+ {
+ printf("%s is not set on optMethodFlags but is required because of the following tree\n", methodFlagStr);
+ gtDispTree(tree);
+ assert(false);
+ }
+
+ if ((basicBlock->bbFlags & bbFlag) == 0)
+ {
+ printf("%s is not set on " FMT_BB " but is required because of the following tree \n", bbFlagStr,
+ compCurBB->bbNum);
+ gtDispTree(tree);
+ assert(false);
+ }
+}
+#endif
+
//------------------------------------------------------------------------------------------
// optEarlyProp: The entry point of the early value propagation.
//
{
printf("*************** In optEarlyProp()\n");
}
-#endif
-
- assert(fgSsaPassesCompleted == 1);
-
+#else
if (!optDoEarlyPropForFunc())
{
return;
}
+#endif
+
+ assert(fgSsaPassesCompleted == 1);
for (BasicBlock* block = fgFirstBB; block != nullptr; block = block->bbNext)
{
+#ifndef DEBUG
if (!optDoEarlyPropForBlock(block))
{
continue;
}
+#endif
compCurBB = block;
// Update the evaluation order and the statement info if the stmt has been rewritten.
if (isRewritten)
{
+ // Make sure the transformation happens in debug, check, and release build.
+ assert(optDoEarlyPropForFunc() && optDoEarlyPropForBlock(block));
gtSetStmtInfo(stmt);
fgSetStmtSeq(stmt);
}
}
if (!objectRefPtr->OperIsScalarLocal() || !lvaInSsa(objectRefPtr->AsLclVarCommon()->GetLclNum()))
-
{
return nullptr;
}
+#ifdef DEBUG
+ else
+ {
+ if (propKind == optPropKind::OPK_ARRAYLEN)
+ {
+ optCheckFlagsAreSet(OMF_HAS_ARRAYREF, "OMF_HAS_ARRAYREF", BBF_HAS_IDX_LEN, "BBF_HAS_IDX_LEN", tree,
+ compCurBB);
+ }
+ else
+ {
+ optCheckFlagsAreSet(OMF_HAS_VTABLEREF, "OMF_HAS_VTABLEREF", BBF_HAS_VTABREF, "BBF_HAS_VTABREF", tree,
+ compCurBB);
+ }
+ }
+#endif
unsigned lclNum = objectRefPtr->AsLclVarCommon()->GetLclNum();
unsigned ssaNum = objectRefPtr->AsLclVarCommon()->GetSsaNum();
}
// Track along the use-def chain to get the array length
- GenTreeOp* ssaDefAsg = lvaTable[lclNum].GetPerSsaData(ssaNum)->GetAssignment();
+ LclSsaVarDsc* ssaVarDsc = lvaTable[lclNum].GetPerSsaData(ssaNum);
+ GenTreeOp* ssaDefAsg = ssaVarDsc->GetAssignment();
if (ssaDefAsg == nullptr)
{
{
if (valueKind == optPropKind::OPK_ARRAYLEN)
{
- value = getArrayLengthFromAllocation(treeRhs);
+ value = getArrayLengthFromAllocation(treeRhs DEBUGARG(ssaVarDsc->GetBlock()));
if (value != nullptr)
{
if (!value->IsCnsIntOrI())
}
else if (valueKind == optPropKind::OPK_OBJ_GETTYPE)
{
- value = getObjectHandleNodeFromAllocation(treeRhs);
+ value = getObjectHandleNodeFromAllocation(treeRhs DEBUGARG(ssaVarDsc->GetBlock()));
if (value != nullptr)
{
- if (!value->IsCnsIntOrI())
- {
- // Leave out non-constant-sized array
- value = nullptr;
- }
+ assert(value->IsCnsIntOrI());
}
}
}
void Compiler::optFoldNullCheck(GenTree* tree, LocalNumberToNullCheckTreeMap* nullCheckMap)
{
+#ifdef DEBUG
+ if (tree->OperGet() == GT_NULLCHECK)
+ {
+ optCheckFlagsAreSet(OMF_HAS_NULLCHECK, "OMF_HAS_NULLCHECK", BBF_HAS_NULLCHECK, "BBF_HAS_NULLCHECK", tree,
+ compCurBB);
+ }
+#else
if ((compCurBB->bbFlags & BBF_HAS_NULLCHECK) == 0)
{
return;
}
+#endif
GenTree* nullCheckTree = optFindNullCheckToFold(tree, nullCheckMap);
GenTree* nullCheckParent = nullptr;
if ((nullCheckTree != nullptr) && optIsNullCheckFoldingLegal(tree, nullCheckTree, &nullCheckParent, &nullCheckStmt))
{
#ifdef DEBUG
+ // Make sure the transformation happens in debug, check, and release build.
+ assert(optDoEarlyPropForFunc() && optDoEarlyPropForBlock(compCurBB) &&
+ (compCurBB->bbFlags & BBF_HAS_NULLCHECK) != 0);
if (verbose)
{
printf("optEarlyProp Marking a null check for removal\n");
if (InlineeCompiler->fgFirstBB->bbStmtList != nullptr)
{
stmtAfter = fgInsertStmtListAfter(iciBlock, stmtAfter, InlineeCompiler->fgFirstBB->firstStmt());
-
- // Copy inlinee bbFlags to caller bbFlags.
- const unsigned __int64 inlineeBlockFlags = InlineeCompiler->fgFirstBB->bbFlags;
- noway_assert((inlineeBlockFlags & BBF_HAS_JMP) == 0);
- noway_assert((inlineeBlockFlags & BBF_KEEP_BBJ_ALWAYS) == 0);
- iciBlock->bbFlags |= inlineeBlockFlags;
}
+ // Copy inlinee bbFlags to caller bbFlags.
+ const unsigned __int64 inlineeBlockFlags = InlineeCompiler->fgFirstBB->bbFlags;
+ noway_assert((inlineeBlockFlags & BBF_HAS_JMP) == 0);
+ noway_assert((inlineeBlockFlags & BBF_KEEP_BBJ_ALWAYS) == 0);
+ iciBlock->bbFlags |= inlineeBlockFlags;
+
#ifdef DEBUG
if (verbose)
{
#endif // DEBUG
// Replace the call with the return expression
iciCall->ReplaceWith(pInlineInfo->retExpr, this);
+
+ if (bottomBlock != nullptr)
+ {
+ bottomBlock->bbFlags |= InlineeCompiler->fgLastBB->bbFlags & BBF_SPLIT_GAINED;
+ }
}
//
break;
case GT_ARR_LENGTH:
- copy = gtNewArrLen(tree->TypeGet(), tree->AsOp()->gtOp1, tree->AsArrLen()->ArrLenOffset());
+ copy = gtNewArrLen(tree->TypeGet(), tree->AsOp()->gtOp1, tree->AsArrLen()->ArrLenOffset(), nullptr);
break;
case GT_ARR_INDEX:
op1 = impPopStack().val;
if (opts.OptimizationEnabled())
{
- GenTreeArrLen* arrLen = gtNewArrLen(TYP_INT, op1, OFFSETOF__CORINFO_String__stringLen);
+ GenTreeArrLen* arrLen = gtNewArrLen(TYP_INT, op1, OFFSETOF__CORINFO_String__stringLen, compCurBB);
op1 = arrLen;
}
else
if (opts.OptimizationEnabled())
{
/* Use GT_ARR_LENGTH operator so rng check opts see this */
- GenTreeArrLen* arrLen = gtNewArrLen(TYP_INT, op1, OFFSETOF__CORINFO_Array__length);
-
- /* Mark the block as containing a length expression */
-
- if (op1->gtOper == GT_LCL_VAR)
- {
- block->bbFlags |= BBF_HAS_IDX_LEN;
- }
+ GenTreeArrLen* arrLen = gtNewArrLen(TYP_INT, op1, OFFSETOF__CORINFO_Array__length, block);
op1 = arrLen;
}
//
// Arguments:
// comp Compiler instance to allocate trees
+// bb Basic block of the new tree
//
// Return Values:
// Returns the gen tree representation for arrLen or MD Array node as defined by
// This tree produces GT_INDEX node, the caller is supposed to morph it appropriately
// so it can be codegen'ed.
//
-GenTree* LC_Array::ToGenTree(Compiler* comp)
+GenTree* LC_Array::ToGenTree(Compiler* comp, BasicBlock* bb)
{
// If jagged array
if (type == Jagged)
// If asked for arrlen invoke arr length operator.
if (oper == ArrLen)
{
- GenTree* arrLen = comp->gtNewArrLen(TYP_INT, arr, OFFSETOF__CORINFO_Array__length);
+ GenTree* arrLen = comp->gtNewArrLen(TYP_INT, arr, OFFSETOF__CORINFO_Array__length, bb);
return arrLen;
}
else
//
// Arguments:
// comp Compiler instance to allocate trees
+// bb Basic block of the new tree
//
// Return Values:
// Returns the gen tree representation for either a constant or a variable or an arrLen operation
// defined by the "type" member
//
-GenTree* LC_Ident::ToGenTree(Compiler* comp)
+GenTree* LC_Ident::ToGenTree(Compiler* comp, BasicBlock* bb)
{
// Convert to GenTree nodes.
switch (type)
case Var:
return comp->gtNewLclvNode(constant, comp->lvaTable[constant].lvType);
case ArrLen:
- return arrLen.ToGenTree(comp);
+ return arrLen.ToGenTree(comp, bb);
case Null:
return comp->gtNewIconNode(0, TYP_REF);
default:
//
// Arguments:
// comp Compiler instance to allocate trees
+// bb Basic block of the new tree
//
// Return Values:
// Returns the gen tree representation for either a constant or a variable or an arrLen operation
// defined by the "type" member
//
-GenTree* LC_Expr::ToGenTree(Compiler* comp)
+GenTree* LC_Expr::ToGenTree(Compiler* comp, BasicBlock* bb)
{
// Convert to GenTree nodes.
switch (type)
{
case Ident:
- return ident.ToGenTree(comp);
+ return ident.ToGenTree(comp, bb);
default:
assert(!"Could not convert LC_Expr to GenTree");
unreached();
//
// Arguments:
// comp Compiler instance to allocate trees
+// bb Basic block of the new tree
//
// Return Values:
// Returns the gen tree representation for the conditional operator on lhs and rhs trees
//
-GenTree* LC_Condition::ToGenTree(Compiler* comp)
+GenTree* LC_Condition::ToGenTree(Compiler* comp, BasicBlock* bb)
{
- GenTree* op1Tree = op1.ToGenTree(comp);
- GenTree* op2Tree = op2.ToGenTree(comp);
+ GenTree* op1Tree = op1.ToGenTree(comp, bb);
+ GenTree* op2Tree = op2.ToGenTree(comp, bb);
assert(genTypeSize(genActualType(op1Tree->TypeGet())) == genTypeSize(genActualType(op2Tree->TypeGet())));
return comp->gtNewOperNode(oper, TYP_INT, op1Tree, op2Tree);
}
noway_assert(conds.Size() > 0);
// Get the first condition.
- GenTree* cond = conds[0].ToGenTree(comp);
+ GenTree* cond = conds[0].ToGenTree(comp, block);
for (unsigned i = 1; i < conds.Size(); ++i)
{
// Append all conditions using AND operator.
- cond = comp->gtNewOperNode(GT_AND, TYP_INT, cond, conds[i].ToGenTree(comp));
+ cond = comp->gtNewOperNode(GT_AND, TYP_INT, cond, conds[i].ToGenTree(comp, block));
}
// Add "cond == 0" node
}
// Get a tree representation for this symbolic a.length
- GenTree* ToGenTree(Compiler* comp);
+ GenTree* ToGenTree(Compiler* comp, BasicBlock* bb);
};
/**
}
// Convert this symbolic representation into a tree node.
- GenTree* ToGenTree(Compiler* comp);
+ GenTree* ToGenTree(Compiler* comp, BasicBlock* bb);
};
/**
}
// Convert LC_Expr into a tree node.
- GenTree* ToGenTree(Compiler* comp);
+ GenTree* ToGenTree(Compiler* comp, BasicBlock* bb);
};
/**
}
// Convert this conditional operation into a GenTree.
- GenTree* ToGenTree(Compiler* comp);
+ GenTree* ToGenTree(Compiler* comp, BasicBlock* bb);
};
/**
}
#endif // _TARGET_64BIT_
- GenTree* arrLen = gtNewArrLen(TYP_INT, arrRef, (int)lenOffs);
+ GenTree* arrLen = gtNewArrLen(TYP_INT, arrRef, (int)lenOffs, compCurBB);
if (bndsChkType != TYP_INT)
{
}
// Flag the block that received the copy as potentially having an array/vtable
- // reference if the block copied from did; this is a conservative guess.
- if (auto copyFlags = bTest->bbFlags & (BBF_HAS_VTABREF | BBF_HAS_IDX_LEN))
+ // reference, nullcheck, object/array allocation if the block copied from did;
+ // this is a conservative guess.
+ if (auto copyFlags = bTest->bbFlags &
+ (BBF_HAS_VTABREF | BBF_HAS_IDX_LEN | BBF_HAS_NULLCHECK | BBF_HAS_NEWOBJ | BBF_HAS_NEWARRAY))
{
block->bbFlags |= copyFlags;
}
// = indexVal + arrayElementsCount - 1
unsigned arrayElementsCount = simdSize / genTypeSize(baseType);
checkIndexExpr = new (this, GT_CNS_INT) GenTreeIntCon(TYP_INT, indexVal + arrayElementsCount - 1);
- GenTreeArrLen* arrLen = gtNewArrLen(TYP_INT, arrayRef, (int)OFFSETOF__CORINFO_Array__length);
+ GenTreeArrLen* arrLen = gtNewArrLen(TYP_INT, arrayRef, (int)OFFSETOF__CORINFO_Array__length, compCurBB);
GenTreeBoundsChk* arrBndsChk = new (this, GT_ARR_BOUNDS_CHECK)
GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, checkIndexExpr, arrLen, SCK_RNGCHK_FAIL);
}
GenTreeArrLen* arrLen =
- gtNewArrLen(TYP_INT, arrayRefForArgRngChk, (int)OFFSETOF__CORINFO_Array__length);
+ gtNewArrLen(TYP_INT, arrayRefForArgRngChk, (int)OFFSETOF__CORINFO_Array__length, compCurBB);
argRngChk = new (this, GT_ARR_BOUNDS_CHECK)
GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, index, arrLen, op3CheckKind);
// Now, clone op3 to create another node for the argChk
{
op2CheckKind = SCK_ARG_EXCPN;
}
- GenTreeArrLen* arrLen = gtNewArrLen(TYP_INT, arrayRefForArgChk, (int)OFFSETOF__CORINFO_Array__length);
+ GenTreeArrLen* arrLen =
+ gtNewArrLen(TYP_INT, arrayRefForArgChk, (int)OFFSETOF__CORINFO_Array__length, compCurBB);
GenTreeBoundsChk* argChk = new (this, GT_ARR_BOUNDS_CHECK)
GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, checkIndexExpr, arrLen, op2CheckKind);