diff options
author | Pat Gavlin <pagavlin@microsoft.com> | 2017-08-30 12:41:25 -0700 |
---|---|---|
committer | Pat Gavlin <pagavlin@microsoft.com> | 2017-08-30 12:41:25 -0700 |
commit | a8a0d45684aa7dc9f58b8946600e268739e47438 (patch) | |
tree | 89333cc1f863f419473506762df0b440f87567ef /src/jit | |
parent | bec6ac10f3968a8f699aad6233657ac59df37a73 (diff) | |
download | coreclr-a8a0d45684aa7dc9f58b8946600e268739e47438.tar.gz coreclr-a8a0d45684aa7dc9f58b8946600e268739e47438.tar.bz2 coreclr-a8a0d45684aa7dc9f58b8946600e268739e47438.zip |
Revert commit bec6ac10f3968a8f699aad6233657ac59df37a73.
This restores the `GT_INDEX_ADDR` changes.
Diffstat (limited to 'src/jit')
-rw-r--r-- | src/jit/codegenarmarch.cpp | 89 | ||||
-rw-r--r-- | src/jit/codegenlinear.h | 1 | ||||
-rw-r--r-- | src/jit/codegenxarch.cpp | 85 | ||||
-rw-r--r-- | src/jit/compiler.h | 22 | ||||
-rw-r--r-- | src/jit/flowgraph.cpp | 7 | ||||
-rw-r--r-- | src/jit/gentree.cpp | 54 | ||||
-rw-r--r-- | src/jit/gentree.h | 62 | ||||
-rw-r--r-- | src/jit/gtlist.h | 2 | ||||
-rw-r--r-- | src/jit/gtstructs.h | 1 | ||||
-rw-r--r-- | src/jit/lsraarm.cpp | 6 | ||||
-rw-r--r-- | src/jit/lsraarm64.cpp | 6 | ||||
-rw-r--r-- | src/jit/lsraxarch.cpp | 25 | ||||
-rw-r--r-- | src/jit/morph.cpp | 170 |
13 files changed, 477 insertions, 53 deletions
diff --git a/src/jit/codegenarmarch.cpp b/src/jit/codegenarmarch.cpp index 9bc422f305..bf83e13a6f 100644 --- a/src/jit/codegenarmarch.cpp +++ b/src/jit/codegenarmarch.cpp @@ -185,6 +185,10 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode) genLeaInstruction(treeNode->AsAddrMode()); break; + case GT_INDEX_ADDR: + genCodeForIndexAddr(treeNode->AsIndexAddr()); + break; + case GT_IND: genCodeForIndir(treeNode->AsIndir()); break; @@ -1540,6 +1544,91 @@ void CodeGen::genCodeForLclFld(GenTreeLclFld* tree) } //------------------------------------------------------------------------ +// genCodeForIndexAddr: Produce code for a GT_INDEX_ADDR node. +// +// Arguments: +// tree - the GT_INDEX_ADDR node +// +void CodeGen::genCodeForIndexAddr(GenTreeIndexAddr* node) +{ + GenTree* const base = node->Arr(); + GenTree* const index = node->Index(); + + genConsumeReg(base); + genConsumeReg(index); + + const regNumber tmpReg = node->GetSingleTempReg(); + + // Generate the bounds check if necessary. + if ((node->gtFlags & GTF_INX_RNGCHK) != 0) + { + // Create a GT_IND(GT_LEA)) tree for the array length access and load the length into a register. + GenTreeAddrMode arrLenAddr(base->TypeGet(), base, nullptr, 0, static_cast<unsigned>(node->gtLenOffset)); + arrLenAddr.gtRegNum = REG_NA; + arrLenAddr.SetContained(); + arrLenAddr.gtNext = (GenTree*)(-1); + + GenTreeIndir arrLen = indirForm(TYP_INT, &arrLenAddr); + arrLen.gtRegNum = tmpReg; + arrLen.ClearContained(); + + getEmitter()->emitInsLoadStoreOp(ins_Load(TYP_INT), emitTypeSize(TYP_INT), arrLen.gtRegNum, &arrLen); + +#ifdef _TARGET_64BIT_ + // The CLI Spec allows an array to be indexed by either an int32 or a native int. In the case that the index + // is a native int on a 64-bit platform, we will need to widen the array length and the compare. + if (index->TypeGet() == TYP_I_IMPL) + { + // Extend the array length as needed. + getEmitter()->emitIns_R_R(ins_Move_Extend(TYP_INT, true), EA_8BYTE, arrLen.gtRegNum, arrLen.gtRegNum); + } +#endif + + // Generate the range check. + getEmitter()->emitInsBinary(INS_cmp, emitTypeSize(TYP_I_IMPL), index, &arrLen); + genJumpToThrowHlpBlk(genJumpKindForOper(GT_GE, CK_UNSIGNED), SCK_RNGCHK_FAIL, node->gtIndRngFailBB); + } + + // Compute the address of the array element. + switch (node->gtElemSize) + { + case 1: + // dest = base + index + getEmitter()->emitIns_R_R_R(INS_add, emitTypeSize(node), node->gtRegNum, base->gtRegNum, index->gtRegNum); + break; + + case 2: + case 4: + case 8: + case 16: + { + DWORD lsl; + BitScanForward(&lsl, node->gtElemSize); + + // dest = base + index * scale + genScaledAdd(emitTypeSize(node), node->gtRegNum, base->gtRegNum, index->gtRegNum, lsl); + break; + } + + default: + { + // tmp = scale + CodeGen::genSetRegToIcon(tmpReg, (ssize_t)node->gtElemSize, TYP_INT); + + // dest = base + index * tmp + getEmitter()->emitIns_R_R_R_R(INS_MULADD, emitTypeSize(node), node->gtRegNum, node->gtRegNum, + index->gtRegNum, tmpReg); + break; + } + } + + // dest = dest + elemOffs + getEmitter()->emitIns_R_R_I(INS_add, emitTypeSize(node), node->gtRegNum, node->gtRegNum, node->gtElemOffset); + + genProduceReg(node); +} + +//------------------------------------------------------------------------ // genCodeForIndir: Produce code for a GT_IND node. // // Arguments: diff --git a/src/jit/codegenlinear.h b/src/jit/codegenlinear.h index 45927db98e..40f61bce93 100644 --- a/src/jit/codegenlinear.h +++ b/src/jit/codegenlinear.h @@ -167,6 +167,7 @@ void genCodeForShiftRMW(GenTreeStoreInd* storeInd); void genCodeForCast(GenTreeOp* tree); void genCodeForLclAddr(GenTree* tree); +void genCodeForIndexAddr(GenTreeIndexAddr* tree); void genCodeForIndir(GenTreeIndir* tree); void genCodeForNegNot(GenTree* tree); void genCodeForLclVar(GenTreeLclVar* tree); diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp index a591b8a4d0..effd50e7cb 100644 --- a/src/jit/codegenxarch.cpp +++ b/src/jit/codegenxarch.cpp @@ -1787,6 +1787,10 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode) genLeaInstruction(treeNode->AsAddrMode()); break; + case GT_INDEX_ADDR: + genCodeForIndexAddr(treeNode->AsIndexAddr()); + break; + case GT_IND: genCodeForIndir(treeNode->AsIndir()); break; @@ -4495,6 +4499,87 @@ void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* tree) } //------------------------------------------------------------------------ +// genCodeForIndexAddr: Produce code for a GT_INDEX_ADDR node. +// +// Arguments: +// tree - the GT_INDEX_ADDR node +// +void CodeGen::genCodeForIndexAddr(GenTreeIndexAddr* node) +{ + GenTree* const base = node->Arr(); + GenTree* const index = node->Index(); + + genConsumeReg(base); + genConsumeReg(index); + + regNumber tmpReg = REG_NA; + + // Generate the bounds check if necessary. + if ((node->gtFlags & GTF_INX_RNGCHK) != 0) + { + // Create a GT_IND(GT_LEA)) tree for the array length access. + GenTreeAddrMode arrLenAddr(base->TypeGet(), base, nullptr, 0, node->gtLenOffset); + arrLenAddr.gtRegNum = REG_NA; + arrLenAddr.SetContained(); + arrLenAddr.gtNext = (GenTree*)(-1); + + GenTreeIndir arrLen = indirForm(TYP_INT, &arrLenAddr); + +#ifdef _TARGET_64BIT_ + // The CLI Spec allows an array to be indexed by either an int32 or a native int. In the case that the index + // is a native int on a 64-bit platform, we will need to widen the array length and the compare. + if (index->TypeGet() == TYP_I_IMPL) + { + // Load the array length into a register. + tmpReg = node->GetSingleTempReg(); + arrLen.gtRegNum = tmpReg; + arrLen.ClearContained(); + getEmitter()->emitInsLoadInd(ins_Load(TYP_INT), EA_4BYTE, arrLen.gtRegNum, &arrLen); + } + else +#endif + { + assert(varTypeIsIntegral(index->TypeGet())); + + arrLen.gtRegNum = REG_NA; + arrLen.SetContained(); + arrLen.gtNext = (GenTree*)(-1); + } + + // Generate the range check. + getEmitter()->emitInsBinary(INS_cmp, emitTypeSize(TYP_I_IMPL), index, &arrLen); + genJumpToThrowHlpBlk(EJ_jae, SCK_RNGCHK_FAIL, node->gtIndRngFailBB); + } + + // Compute the address of the array element. + switch (node->gtElemSize) + { + case 1: + case 2: + case 4: + case 8: + getEmitter()->emitIns_R_ARX(INS_lea, emitTypeSize(node), node->gtRegNum, base->gtRegNum, index->gtRegNum, + node->gtElemSize, static_cast<int>(node->gtElemOffset)); + break; + + default: + { + // Multiply the index by the element size. + // + // TODO-CQ: this should really just use `imul index, index, #gtElemSize` + tmpReg = (tmpReg == REG_NA) ? node->GetSingleTempReg() : tmpReg; + CodeGen::genSetRegToIcon(tmpReg, (ssize_t)node->gtElemSize, TYP_INT); + inst_RV_RV(INS_imul, tmpReg, index->gtRegNum); + getEmitter()->emitIns_R_ARX(INS_lea, emitTypeSize(node), node->gtRegNum, base->gtRegNum, tmpReg, 1, + static_cast<int>(node->gtElemOffset)); + break; + } + } + + genProduceReg(node); +} + +//------------------------------------------------------------------------ // genCodeForIndir: Produce code for a GT_IND node. // // Arguments: diff --git a/src/jit/compiler.h b/src/jit/compiler.h index 78b9635dce..916c96f2ee 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -4670,6 +4670,8 @@ private: void fgSetRngChkTarget(GenTreePtr tree, bool delay = true); + BasicBlock* fgSetRngChkTargetInner(SpecialCodeKind kind, bool delay, unsigned* stkDepth); + #if REARRANGE_ADDS void fgMoveOpsLeft(GenTreePtr tree); #endif @@ -9329,6 +9331,26 @@ public: return compRoot->m_arrayInfoMap; } + inline bool TryGetArrayInfo(GenTreeIndir* indir, ArrayInfo* arrayInfo) + { + if ((indir->gtFlags & GTF_IND_ARR_INDEX) == 0) + { + return false; + } + + if (indir->gtOp1->OperIs(GT_INDEX_ADDR)) + { + GenTreeIndexAddr* const indexAddr = indir->gtOp1->AsIndexAddr(); + *arrayInfo = ArrayInfo(indexAddr->gtElemType, indexAddr->gtElemSize, indexAddr->gtElemOffset, + indexAddr->gtStructElemClass); + return true; + } + + bool found = GetArrayInfoMap()->Lookup(indir, arrayInfo); + assert(found); + return true; + } + NodeToUnsignedMap* m_memorySsaMap[MemoryKindCount]; // In some cases, we want to assign intermediate SSA #'s to memory states, and know what nodes create those memory diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp index 57e5552e09..0f5f3dbe8d 100644 --- a/src/jit/flowgraph.cpp +++ b/src/jit/flowgraph.cpp @@ -18356,6 +18356,13 @@ void Compiler::fgSetTreeSeqHelper(GenTreePtr tree, bool isLIR) noway_assert(!"DYN_BLK nodes should be sequenced as a special case"); break; + case GT_INDEX_ADDR: + // Evaluate the index first, then the array address + assert((tree->gtFlags & GTF_REVERSE_OPS) != 0); + fgSetTreeSeqHelper(tree->AsIndexAddr()->Index(), isLIR); + fgSetTreeSeqHelper(tree->AsIndexAddr()->Arr(), isLIR); + break; + default: #ifdef DEBUG gtDispTree(tree); diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp index 97e0453e48..2284197ebd 100644 --- a/src/jit/gentree.cpp +++ b/src/jit/gentree.cpp @@ -299,6 +299,7 @@ void GenTree::InitNodeSize() GenTree::s_gtNodeSizes[GT_FTN_ADDR] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_BOX] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_INDEX] = TREE_NODE_SZ_LARGE; + GenTree::s_gtNodeSizes[GT_INDEX_ADDR] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_ARR_BOUNDS_CHECK] = TREE_NODE_SZ_LARGE; #ifdef FEATURE_SIMD GenTree::s_gtNodeSizes[GT_SIMD_CHK] = TREE_NODE_SZ_LARGE; @@ -1301,6 +1302,12 @@ AGAIN: return false; } break; + case GT_INDEX_ADDR: + if (op1->AsIndexAddr()->gtElemSize != op2->AsIndexAddr()->gtElemSize) + { + return false; + } + break; // For the ones below no extra argument matters for comparison. case GT_QMARK: @@ -1876,6 +1883,9 @@ AGAIN: case GT_INDEX: hash += tree->gtIndex.gtIndElemSize; break; + case GT_INDEX_ADDR: + hash += tree->AsIndexAddr()->gtElemSize; + break; case GT_ALLOCOBJ: hash = genTreeHashAdd(hash, static_cast<unsigned>( reinterpret_cast<uintptr_t>(tree->gtAllocObj.gtAllocObjClsHnd))); @@ -1938,6 +1948,7 @@ AGAIN: case GT_ARR_INDEX: case GT_QMARK: case GT_INDEX: + case GT_INDEX_ADDR: break; #ifdef FEATURE_SIMD @@ -4776,6 +4787,23 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) } break; + case GT_INDEX_ADDR: + costEx = 6; // cmp reg,reg; jae throw; mov reg, [addrmode] (not taken) + costSz = 9; // jump to cold section + + level = gtSetEvalOrder(tree->AsIndexAddr()->Index()); + costEx += tree->AsIndexAddr()->Index()->gtCostEx; + costSz += tree->AsIndexAddr()->Index()->gtCostSz; + + lvl2 = gtSetEvalOrder(tree->AsIndexAddr()->Arr()); + if (level < lvl2) + { + level = lvl2; + } + costEx += tree->AsIndexAddr()->Arr()->gtCostEx; + costSz += tree->AsIndexAddr()->Arr()->gtCostSz; + break; + default: #ifdef DEBUG if (verbose) @@ -5808,6 +5836,7 @@ bool GenTree::OperMayThrow() #ifdef FEATURE_SIMD case GT_SIMD_CHK: #endif // FEATURE_SIMD + case GT_INDEX_ADDR: return true; default: break; @@ -7411,6 +7440,19 @@ GenTreePtr Compiler::gtCloneExpr( } break; + case GT_INDEX_ADDR: + { + GenTreeIndexAddr* asIndAddr = tree->AsIndexAddr(); + + copy = new (this, GT_INDEX_ADDR) + GenTreeIndexAddr(asIndAddr->Arr(), asIndAddr->Index(), asIndAddr->gtElemType, + asIndAddr->gtStructElemClass, asIndAddr->gtElemSize, asIndAddr->gtLenOffset, + asIndAddr->gtElemOffset); + copy->AsIndexAddr()->gtIndRngFailBB = asIndAddr->gtIndRngFailBB; + copy->AsIndexAddr()->gtStkDepth = asIndAddr->gtStkDepth; + } + break; + case GT_ALLOCOBJ: { GenTreeAllocObj* asAllocObj = tree->AsAllocObj(); @@ -9579,6 +9621,7 @@ void Compiler::gtDispNode(GenTreePtr tree, IndentStack* indentStack, __in __in_z __fallthrough; case GT_INDEX: + case GT_INDEX_ADDR: if ((tree->gtFlags & (GTF_IND_VOLATILE | GTF_IND_UNALIGNED)) == 0) // We prefer printing V or U over R { @@ -15712,6 +15755,9 @@ CORINFO_CLASS_HANDLE Compiler::gtGetStructHandleIfPresent(GenTree* tree) case GT_INDEX: structHnd = tree->gtIndex.gtStructElemClass; break; + case GT_INDEX_ADDR: + structHnd = tree->AsIndexAddr()->gtStructElemClass; + break; case GT_FIELD: info.compCompHnd->getFieldType(tree->gtField.gtFldHnd, &structHnd); break; @@ -15740,12 +15786,12 @@ CORINFO_CLASS_HANDLE Compiler::gtGetStructHandleIfPresent(GenTree* tree) } else #endif - if (tree->gtFlags & GTF_IND_ARR_INDEX) { ArrayInfo arrInfo; - bool b = GetArrayInfoMap()->Lookup(tree, &arrInfo); - assert(b); - structHnd = EncodeElemType(arrInfo.m_elemType, arrInfo.m_elemStructType); + if (TryGetArrayInfo(tree->AsIndir(), &arrInfo)) + { + structHnd = EncodeElemType(arrInfo.m_elemType, arrInfo.m_elemStructType); + } } break; #ifdef FEATURE_SIMD diff --git a/src/jit/gentree.h b/src/jit/gentree.h index a5d0337f6d..2f99ce4955 100644 --- a/src/jit/gentree.h +++ b/src/jit/gentree.h @@ -4243,6 +4243,68 @@ struct GenTreeIndex : public GenTreeOp #endif }; +// gtIndexAddr: given an array object and an index, checks that the index is within the bounds of the array if +// necessary and produces the address of the value at that index of the array. +struct GenTreeIndexAddr : public GenTreeOp +{ + GenTree*& Arr() + { + return gtOp1; + } + GenTree*& Index() + { + return gtOp2; + } + + CORINFO_CLASS_HANDLE gtStructElemClass; // If the element type is a struct, this is the struct type. + + GenTree* gtIndRngFailBB; // Label to jump to for array-index-out-of-range + unsigned gtStkDepth; // Stack depth at which the jump occurs (required for fgSetRngChkTarget) + + var_types gtElemType; // The element type of the array. + unsigned gtElemSize; // size of elements in the array + unsigned gtLenOffset; // The offset from the array's base address to its length. + unsigned gtElemOffset; // The offset from the array's base address to its first element. + + GenTreeIndexAddr(GenTree* arr, + GenTree* ind, + var_types elemType, + CORINFO_CLASS_HANDLE structElemClass, + unsigned elemSize, + unsigned lenOffset, + unsigned elemOffset) + : GenTreeOp(GT_INDEX_ADDR, TYP_BYREF, arr, ind) + , gtStructElemClass(structElemClass) + , gtIndRngFailBB(nullptr) + , gtStkDepth(0) + , gtElemType(elemType) + , gtElemSize(elemSize) + , gtLenOffset(lenOffset) + , gtElemOffset(elemOffset) + { +#ifdef DEBUG + if (JitConfig.JitSkipArrayBoundCheck() == 1) + { + // Skip bounds check + } + else +#endif + { + // Do bounds check + gtFlags |= GTF_INX_RNGCHK; + } + + // REVERSE_OPS is set because we must evaluate the index before the array address. + gtFlags |= GTF_EXCEPT | GTF_GLOB_REF | GTF_REVERSE_OPS; + } + +#if DEBUGGABLE_GENTREE + GenTreeIndexAddr() : GenTreeOp() + { + } +#endif +}; + /* gtArrLen -- array length (GT_ARR_LENGTH) GT_ARR_LENGTH is used for "arr.length" */ diff --git a/src/jit/gtlist.h b/src/jit/gtlist.h index 7e78b27dd2..544023391f 100644 --- a/src/jit/gtlist.h +++ b/src/jit/gtlist.h @@ -163,6 +163,8 @@ GTNODE(QMARK , GenTreeQmark ,0,GTK_BINOP|GTK_EXOP|GTK_NOTLIR) GTNODE(COLON , GenTreeColon ,0,GTK_BINOP|GTK_NOTLIR) GTNODE(INDEX , GenTreeIndex ,0,GTK_BINOP|GTK_EXOP|GTK_NOTLIR) // SZ-array-element +GTNODE(INDEX_ADDR , GenTreeIndex ,0,GTK_BINOP|GTK_EXOP) // addr of SZ-array-element; used when + // aiming to minimize compile times. GTNODE(MKREFANY , GenTreeOp ,0,GTK_BINOP) diff --git a/src/jit/gtstructs.h b/src/jit/gtstructs.h index 1d623b71ff..34d1794f4b 100644 --- a/src/jit/gtstructs.h +++ b/src/jit/gtstructs.h @@ -71,6 +71,7 @@ GTSTRUCT_1(Colon , GT_COLON) GTSTRUCT_1(FptrVal , GT_FTN_ADDR) GTSTRUCT_1(Intrinsic , GT_INTRINSIC) GTSTRUCT_1(Index , GT_INDEX) +GTSTRUCT_1(IndexAddr , GT_INDEX_ADDR) #ifdef FEATURE_SIMD GTSTRUCT_2(BoundsChk , GT_ARR_BOUNDS_CHECK, GT_SIMD_CHK) #else // !FEATURE_SIMD diff --git a/src/jit/lsraarm.cpp b/src/jit/lsraarm.cpp index bfe25bf99b..49127052cb 100644 --- a/src/jit/lsraarm.cpp +++ b/src/jit/lsraarm.cpp @@ -769,6 +769,12 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) unreached(); } break; + + case GT_INDEX_ADDR: + info->srcCount = 2; + info->dstCount = 1; + info->internalIntCount = 1; + break; } // end switch (tree->OperGet()) // We need to be sure that we've set info->srcCount and info->dstCount appropriately diff --git a/src/jit/lsraarm64.cpp b/src/jit/lsraarm64.cpp index dc2ceb7b76..66a45192d0 100644 --- a/src/jit/lsraarm64.cpp +++ b/src/jit/lsraarm64.cpp @@ -661,6 +661,12 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) assert((tree->gtFlags & (GTF_VAR_DEF | GTF_VAR_USEASG)) == 0); info->internalIntCount = 1; break; + + case GT_INDEX_ADDR: + info->srcCount = 2; + info->dstCount = 1; + info->internalIntCount = 1; + break; } // end switch (tree->OperGet()) // We need to be sure that we've set info->srcCount and info->dstCount appropriately diff --git a/src/jit/lsraxarch.cpp b/src/jit/lsraxarch.cpp index 7455569b46..4945db32b4 100644 --- a/src/jit/lsraxarch.cpp +++ b/src/jit/lsraxarch.cpp @@ -613,6 +613,31 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) JITDUMP("Unexpected node %s in Lower.\n", GenTree::OpName(tree->OperGet())); unreached(); break; + + case GT_INDEX_ADDR: + info->srcCount = 2; + info->dstCount = 1; + + if (tree->AsIndexAddr()->Index()->TypeGet() == TYP_I_IMPL) + { + info->internalIntCount = 1; + } + else + { + switch (tree->AsIndexAddr()->gtElemSize) + { + case 1: + case 2: + case 4: + case 8: + break; + + default: + info->internalIntCount = 1; + break; + } + } + break; } // end switch (tree->OperGet()) // If op2 of a binary-op gets marked as contained, then binary-op srcCount will be 1. diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp index d7b3aae456..45a8d14afb 100644 --- a/src/jit/morph.cpp +++ b/src/jit/morph.cpp @@ -5720,76 +5720,78 @@ void Compiler::fgMoveOpsLeft(GenTreePtr tree) /*****************************************************************************/ -void Compiler::fgSetRngChkTarget(GenTreePtr tree, bool delay) +void Compiler::fgSetRngChkTarget(GenTree* tree, bool delay) { - GenTreeBoundsChk* bndsChk = nullptr; - SpecialCodeKind kind = SCK_RNGCHK_FAIL; - -#ifdef FEATURE_SIMD - if ((tree->gtOper == GT_ARR_BOUNDS_CHECK) || (tree->gtOper == GT_SIMD_CHK)) -#else // FEATURE_SIMD - if (tree->gtOper == GT_ARR_BOUNDS_CHECK) -#endif // FEATURE_SIMD + if (tree->OperIsBoundsCheck()) + { + GenTreeBoundsChk* const boundsChk = tree->AsBoundsChk(); + BasicBlock* const failBlock = fgSetRngChkTargetInner(boundsChk->gtThrowKind, delay, &boundsChk->gtStkDepth); + if (failBlock != nullptr) + { + boundsChk->gtIndRngFailBB = gtNewCodeRef(failBlock); + } + } + else if (tree->OperIs(GT_INDEX_ADDR)) { - bndsChk = tree->AsBoundsChk(); - kind = tree->gtBoundsChk.gtThrowKind; + GenTreeIndexAddr* const indexAddr = tree->AsIndexAddr(); + BasicBlock* const failBlock = fgSetRngChkTargetInner(SCK_RNGCHK_FAIL, delay, &indexAddr->gtStkDepth); + if (failBlock != nullptr) + { + indexAddr->gtIndRngFailBB = gtNewCodeRef(failBlock); + } } else { - noway_assert((tree->gtOper == GT_ARR_ELEM) || (tree->gtOper == GT_ARR_INDEX)); + noway_assert(tree->OperIs(GT_ARR_ELEM, GT_ARR_INDEX)); + fgSetRngChkTargetInner(SCK_RNGCHK_FAIL, delay, nullptr); } +} -#ifdef _TARGET_X86_ - unsigned callStkDepth = fgPtrArgCntCur; -#else - // only x86 pushes args - const unsigned callStkDepth = 0; -#endif - +BasicBlock* Compiler::fgSetRngChkTargetInner(SpecialCodeKind kind, bool delay, unsigned* stkDepth) +{ if (opts.MinOpts()) { delay = false; +#ifdef _TARGET_X86_ // we need to initialize this field - if (fgGlobalMorph && bndsChk != nullptr) + if (fgGlobalMorph && (stkDepth != nullptr)) { - bndsChk->gtStkDepth = callStkDepth; + *stkDepth = fgPtrArgCntCur; } +#endif // _TARGET_X86_ } if (!opts.compDbgCode) { if (delay || compIsForInlining()) { - /* We delay this until after loop-oriented range check - analysis. For now we merely store the current stack - level in the tree node. - */ - if (bndsChk != nullptr) +#ifdef _TARGET_X86_ + // We delay this until after loop-oriented range check analysis. For now we merely store the current stack + // level in the tree node. + if (stkDepth != nullptr) { - noway_assert(!bndsChk->gtIndRngFailBB || previousCompletedPhase >= PHASE_OPTIMIZE_LOOPS); - bndsChk->gtStkDepth = callStkDepth; + *stkDepth = fgPtrArgCntCur; } +#endif // _TARGET_X86_ } else { - /* Create/find the appropriate "range-fail" label */ - +#ifdef _TARGET_X86_ // fgPtrArgCntCur is only valid for global morph or if we walk full stmt. - noway_assert((bndsChk != nullptr) || fgGlobalMorph); - - unsigned stkDepth = (bndsChk != nullptr) ? bndsChk->gtStkDepth : callStkDepth; - - BasicBlock* rngErrBlk = fgRngChkTarget(compCurBB, stkDepth, kind); - - /* Add the label to the indirection node */ + noway_assert(fgGlobalMorph || (stkDepth != nullptr)); + const unsigned theStkDepth = fgGlobalMorph ? fgPtrArgCntCur : *stkDepth; +#else + // only x86 pushes args + const unsigned theStkDepth = 0; +#endif - if (bndsChk != nullptr) - { - bndsChk->gtIndRngFailBB = gtNewCodeRef(rngErrBlk); - } + // Create/find the appropriate "range-fail" label + return fgRngChkTarget(compCurBB, theStkDepth, kind); } } + + return nullptr; } /***************************************************************************** @@ -5844,9 +5846,6 @@ GenTreePtr Compiler::fgMorphArrayIndex(GenTreePtr tree) } #endif // FEATURE_SIMD - GenTreePtr arrRef = asIndex->Arr(); - GenTreePtr index = asIndex->Index(); - // Set up the the array length's offset into lenOffs // And the the first element's offset into elemOffs ssize_t lenOffs; @@ -5868,6 +5867,58 @@ GenTreePtr Compiler::fgMorphArrayIndex(GenTreePtr tree) elemOffs = offsetof(CORINFO_Array, u1Elems); } +#ifndef LEGACY_BACKEND + // In minopts, we expand GT_INDEX to GT_IND(GT_INDEX_ADDR) in order to minimize the size of the IR. As minopts + // compilation time is roughly proportional to the size of the IR, this helps keep compilation times down. + // Furthermore, this representation typically saves on code size in minopts w.r.t. the complete expansion + // performed when optimizing, as it does not require LclVar nodes (which are always stack loads/stores in + // minopts). + // + // When we *are* optimizing, we fully expand GT_INDEX to: + // 1. Evaluate the array address expression and store the result in a temp if the expression is complex or + // side-effecting. + // 2. Evaluate the array index expression and store the result in a temp if the expression is complex or + // side-effecting. + // 3. Perform an explicit bounds check: GT_ARR_BOUNDS_CHK(index, GT_ARR_LENGTH(array)) + // 4. Compute the address of the element that will be accessed: + // GT_ADD(GT_ADD(array, firstElementOffset), GT_MUL(index, elementSize)) + // 5. Dereference the address with a GT_IND. + // + // This expansion explicitly exposes the bounds check and the address calculation to the optimizer, which allows + // for more straightforward bounds-check removal, CSE, etc. + if (opts.MinOpts()) + { + GenTree* const array = fgMorphTree(asIndex->Arr()); + GenTree* const index = fgMorphTree(asIndex->Index()); + + GenTreeIndexAddr* const indexAddr = + new (this, GT_INDEX_ADDR) GenTreeIndexAddr(array, index, elemTyp, elemStructType, elemSize, + static_cast<unsigned>(lenOffs), static_cast<unsigned>(elemOffs)); + indexAddr->gtFlags |= (array->gtFlags | index->gtFlags) & GTF_ALL_EFFECT; + + // Mark the indirection node as needing a range check if necessary. + if ((indexAddr->gtFlags & GTF_INX_RNGCHK) != 0) + { + fgSetRngChkTarget(indexAddr); + } + + // Change `tree` into an indirection and return. + tree->ChangeOper(GT_IND); + GenTreeIndir* const indir = tree->AsIndir(); + indir->Addr() = indexAddr; + indir->gtFlags = GTF_IND_ARR_INDEX | (indexAddr->gtFlags & GTF_ALL_EFFECT); + +#ifdef DEBUG + indexAddr->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; +#endif // DEBUG + + return indir; + } +#endif // LEGACY_BACKEND + + GenTreePtr arrRef = asIndex->Arr(); + GenTreePtr index = asIndex->Index(); + bool chkd = ((tree->gtFlags & GTF_INX_RNGCHK) != 0); // if false, range checking will be disabled bool nCSE = ((tree->gtFlags & GTF_DONT_CSE) != 0); @@ -9893,6 +9944,9 @@ GenTreePtr Compiler::fgMorphGetStructAddr(GenTreePtr* pTree, CORINFO_CLASS_HANDL case GT_ARR_ELEM: addr = gtNewOperNode(GT_ADDR, TYP_BYREF, tree); break; + case GT_INDEX_ADDR: + addr = tree; + break; default: { // TODO: Consider using lvaGrabTemp and gtNewTempAssign instead, since we're @@ -9920,10 +9974,12 @@ GenTreePtr Compiler::fgMorphGetStructAddr(GenTreePtr* pTree, CORINFO_CLASS_HANDL GenTree* Compiler::fgMorphBlkNode(GenTreePtr tree, bool isDest) { - if (tree->gtOper == GT_COMMA) + GenTree* handleTree = nullptr; + GenTree* addr = nullptr; + if (tree->OperIs(GT_COMMA)) { GenTree* effectiveVal = tree->gtEffectiveVal(); - GenTree* addr = gtNewOperNode(GT_ADDR, TYP_BYREF, effectiveVal); + addr = gtNewOperNode(GT_ADDR, TYP_BYREF, effectiveVal); #ifdef DEBUG addr->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; #endif @@ -9946,13 +10002,24 @@ GenTree* Compiler::fgMorphBlkNode(GenTreePtr tree, bool isDest) lastComma->gtOp.gtOp2 = addr; addr = tree; } - var_types structType = effectiveVal->TypeGet(); + + handleTree = effectiveVal; + } + else if (tree->OperIs(GT_IND) && tree->AsIndir()->Addr()->OperIs(GT_INDEX_ADDR)) + { + handleTree = tree; + addr = tree->AsIndir()->Addr(); + } + + if (addr != nullptr) + { + var_types structType = handleTree->TypeGet(); if (structType == TYP_STRUCT) { - CORINFO_CLASS_HANDLE structHnd = gtGetStructHandleIfPresent(effectiveVal); + CORINFO_CLASS_HANDLE structHnd = gtGetStructHandleIfPresent(handleTree); if (structHnd == NO_CLASS_HANDLE) { - tree = gtNewOperNode(GT_IND, effectiveVal->TypeGet(), addr); + tree = gtNewOperNode(GT_IND, structType, addr); } else { @@ -15600,6 +15667,11 @@ GenTreePtr Compiler::fgMorphTree(GenTreePtr tree, MorphAddrContext* mac) tree->gtDynBlk.gtDynamicSize = fgMorphTree(tree->gtDynBlk.gtDynamicSize); break; + case GT_INDEX_ADDR: + tree->AsIndexAddr()->Index() = fgMorphTree(tree->AsIndexAddr()->Index()); + tree->AsIndexAddr()->Arr() = fgMorphTree(tree->AsIndexAddr()->Arr()); + break; + default: #ifdef DEBUG gtDispTree(tree); |