@@ -5461,15 +5461,43 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
54615461 // the partial byref will not point within the object, and thus not get updated correctly during a GC.
54625462 // This is mostly a risk in fully-interruptible code regions.
54635463
5464- /* Add the first element's offset */
5465-
5466- GenTree* cns = gtNewIconNode(elemOffs, TYP_I_IMPL);
5464+ // We can generate two types of trees for "addr":
5465+ //
5466+ // 1) "arrRef + (index + elemOffset)"
5467+ // 2) "(arrRef + elemOffset) + index"
5468+ //
5469+ // XArch has powerful addressing modes such as [base + index*scale + offset] so it's fine with 1),
5470+ // while for Arm we better try to make an invariant sub-tree as large as possible, which is usually
5471+ // "(arrRef + elemOffset)" and is CSE/LoopHoisting friendly => produces better codegen.
5472+ // 2) should still be safe from GC's point of view since both ADD operations are byref and point to
5473+ // within the object so GC will be able to correctly track and update them.
54675474
5468- addr = gtNewOperNode(GT_ADD, TYP_I_IMPL, addr, cns);
5475+ bool groupArrayRefWithElemOffset = false;
5476+ #ifdef TARGET_ARMARCH
5477+ groupArrayRefWithElemOffset = true;
5478+ // TODO: in some cases even on ARM we better use 1) shape because if "index" is invariant and "arrRef" is not
5479+ // we at least will be able to hoist/CSE "index + elemOffset" in some cases.
5480+ // See https://github.com/dotnet/runtime/pull/61293#issuecomment-964146497
54695481
5470- /* Add the object ref to the element's offset */
5482+ // Use 2) form only for primitive types for now - it significantly reduced number of size regressions
5483+ if (!varTypeIsIntegral(elemTyp))
5484+ {
5485+ groupArrayRefWithElemOffset = false;
5486+ }
5487+ #endif
54715488
5472- addr = gtNewOperNode(GT_ADD, TYP_BYREF, arrRef, addr);
5489+ // First element's offset
5490+ GenTree* elemOffset = gtNewIconNode(elemOffs, TYP_I_IMPL);
5491+ if (groupArrayRefWithElemOffset)
5492+ {
5493+ GenTree* basePlusOffset = gtNewOperNode(GT_ADD, TYP_BYREF, arrRef, elemOffset);
5494+ addr = gtNewOperNode(GT_ADD, TYP_BYREF, basePlusOffset, addr);
5495+ }
5496+ else
5497+ {
5498+ addr = gtNewOperNode(GT_ADD, TYP_I_IMPL, addr, elemOffset);
5499+ addr = gtNewOperNode(GT_ADD, TYP_BYREF, arrRef, addr);
5500+ }
54735501
54745502 assert(((tree->gtDebugFlags & GTF_DEBUG_NODE_LARGE) != 0) ||
54755503 (GenTree::s_gtNodeSizes[GT_IND] == TREE_NODE_SZ_SMALL));
@@ -5539,16 +5567,16 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
55395567 tree = gtNewOperNode(GT_COMMA, tree->TypeGet(), arrRefDefn, tree);
55405568 }
55415569
5542- JITDUMP("fgMorphArrayIndex (before remorph):\n");
5543- DISPTREE(tree);
5570+ JITDUMP("fgMorphArrayIndex (before remorph):\n")
5571+ DISPTREE(tree)
55445572
55455573 // Currently we morph the tree to perform some folding operations prior
55465574 // to attaching fieldSeq info and labeling constant array index contributions
55475575 //
55485576 tree = fgMorphTree(tree);
55495577
5550- JITDUMP("fgMorphArrayIndex (after remorph):\n");
5551- DISPTREE(tree);
5578+ JITDUMP("fgMorphArrayIndex (after remorph):\n")
5579+ DISPTREE(tree)
55525580
55535581 // Ideally we just want to proceed to attaching fieldSeq info and labeling the
55545582 // constant array index contributions, but the morphing operation may have changed
@@ -5562,48 +5590,66 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
55625590
55635591 if (fgIsCommaThrow(tree))
55645592 {
5565- if ((arrElem != indTree) || // A new tree node may have been created
5566- (indTree->OperGet() != GT_IND)) // The GT_IND may have been changed to a GT_CNS_INT
5593+ if ((arrElem != indTree) || // A new tree node may have been created
5594+ (! indTree->OperIs( GT_IND) )) // The GT_IND may have been changed to a GT_CNS_INT
55675595 {
55685596 return tree; // Just return the Comma-Throw, don't try to attach the fieldSeq info, etc..
55695597 }
55705598 }
55715599
55725600 assert(!fgGlobalMorph || (arrElem->gtDebugFlags & GTF_DEBUG_NODE_MORPHED));
5573- DBEXEC(fgGlobalMorph && (arrElem == tree), tree->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED);
5574-
5575- addr = arrElem->AsOp()->gtOp1;
5601+ DBEXEC(fgGlobalMorph && (arrElem == tree), tree->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED)
55765602
5577- assert( addr->TypeGet() == TYP_BYREF );
5603+ addr = arrElem->gtGetOp1( );
55785604
55795605 GenTree* cnsOff = nullptr;
5580- if (addr->OperGet() == GT_ADD)
5606+ if (addr->OperIs( GT_ADD) )
55815607 {
5582- assert(addr->TypeGet() == TYP_BYREF);
5583- assert(addr->AsOp()->gtOp1->TypeGet() == TYP_REF);
5584-
5585- addr = addr->AsOp()->gtOp2;
5586-
5587- // Look for the constant [#FirstElem] node here, or as the RHS of an ADD.
5588-
5589- if (addr->gtOper == GT_CNS_INT)
5608+ GenTree* addrOp1 = addr->gtGetOp1();
5609+ if (groupArrayRefWithElemOffset)
55905610 {
5591- cnsOff = addr;
5592- addr = nullptr;
5611+ if (addrOp1->OperIs(GT_ADD) && addrOp1->gtGetOp2()->IsCnsIntOrI())
5612+ {
5613+ assert(addrOp1->gtGetOp1()->TypeIs(TYP_REF));
5614+ cnsOff = addrOp1->gtGetOp2();
5615+ addr = addr->gtGetOp2();
5616+ // Label any constant array index contributions with #ConstantIndex and any LclVars with
5617+ // GTF_VAR_ARR_INDEX
5618+ addr->LabelIndex(this);
5619+ }
5620+ else
5621+ {
5622+ assert(addr->gtGetOp2()->IsCnsIntOrI());
5623+ cnsOff = addr->gtGetOp2();
5624+ addr = nullptr;
5625+ }
55935626 }
55945627 else
55955628 {
5596- if ((addr->OperGet() == GT_ADD) && (addr->AsOp()->gtOp2->gtOper == GT_CNS_INT))
5629+ assert(addr->TypeIs(TYP_BYREF));
5630+ assert(addr->gtGetOp1()->TypeIs(TYP_REF));
5631+ addr = addr->gtGetOp2();
5632+
5633+ // Look for the constant [#FirstElem] node here, or as the RHS of an ADD.
5634+ if (addr->IsCnsIntOrI())
55975635 {
5598- cnsOff = addr->AsOp()->gtOp2;
5599- addr = addr->AsOp()->gtOp1;
5636+ cnsOff = addr;
5637+ addr = nullptr;
5638+ }
5639+ else
5640+ {
5641+ if ((addr->OperIs(GT_ADD)) && addr->gtGetOp2()->IsCnsIntOrI())
5642+ {
5643+ cnsOff = addr->gtGetOp2();
5644+ addr = addr->gtGetOp1();
5645+ }
5646+ // Label any constant array index contributions with #ConstantIndex and any LclVars with
5647+ // GTF_VAR_ARR_INDEX
5648+ addr->LabelIndex(this);
56005649 }
5601-
5602- // Label any constant array index contributions with #ConstantIndex and any LclVars with GTF_VAR_ARR_INDEX
5603- addr->LabelIndex(this);
56045650 }
56055651 }
5606- else if (addr->OperGet() == GT_CNS_INT )
5652+ else if (addr->IsCnsIntOrI() )
56075653 {
56085654 cnsOff = addr;
56095655 }
0 commit comments