diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 6c353a6a238ef9..c9670fc316c3ab 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -4450,7 +4450,7 @@ bool Compiler::optNonNullAssertionProp_Ind(ASSERT_VALARG_TP assertions, GenTree* indir->gtFlags |= GTF_IND_NONFAULTING; // Set this flag to prevent reordering - indir->gtFlags |= GTF_ORDER_SIDEEFF; + indir->SetHasOrderingSideEffect(); return true; } diff --git a/src/coreclr/jit/earlyprop.cpp b/src/coreclr/jit/earlyprop.cpp index b422c79a37ba36..8233f8f521a415 100644 --- a/src/coreclr/jit/earlyprop.cpp +++ b/src/coreclr/jit/earlyprop.cpp @@ -454,7 +454,7 @@ bool Compiler::optFoldNullCheck(GenTree* tree, LocalNumberToNullCheckTreeMap* nu nullCheckTree->gtFlags &= ~(GTF_EXCEPT | GTF_DONT_CSE); // Set this flag to prevent reordering - nullCheckTree->gtFlags |= GTF_ORDER_SIDEEFF; + nullCheckTree->SetHasOrderingSideEffect(); nullCheckTree->gtFlags |= GTF_IND_NONFAULTING; if (nullCheckParent != nullptr) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index ec9ef52362968d..f0ffca4a5ef081 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -6731,7 +6731,7 @@ GenTree* GenTree::gtGetParent(GenTree*** pUse) // of the children's flags. // -bool GenTree::OperRequiresAsgFlag() +bool GenTree::OperRequiresAsgFlag() const { switch (OperGet()) { @@ -6769,8 +6769,7 @@ bool GenTree::OperRequiresAsgFlag() // OperRequiresCallFlag : Check whether the operation requires GTF_CALL flag regardless // of the children's flags. // - -bool GenTree::OperRequiresCallFlag(Compiler* comp) +bool GenTree::OperRequiresCallFlag(Compiler* comp) const { switch (gtOper) { @@ -7026,6 +7025,160 @@ bool GenTree::OperMayThrow(Compiler* comp) return OperExceptions(comp) != ExceptionSetFlags::None; } +//------------------------------------------------------------------------------ +// OperRequiresGlobRefFlag : Check whether the operation requires GTF_GLOB_REF +// flag regardless of the children's flags. +// +// Arguments: +// comp - Compiler instance +// +// Return Value: +// True if the given operator requires GTF_GLOB_REF +// +// Remarks: +// Globally visible stores and loads, as well as some equivalently modeled +// operations, require the GLOB_REF flag to be set on the node. +// +// This function is only valid after local morph when we know which locals +// are address exposed, and the flag in gtFlags is only kept valid after +// morph has run. Before local morph the property can be conservatively +// approximated for locals with lvHasLdAddrOp. +// +bool GenTree::OperRequiresGlobRefFlag(Compiler* comp) const +{ + switch (OperGet()) + { + case GT_LCL_VAR: + case GT_LCL_FLD: + case GT_STORE_LCL_VAR: + case GT_STORE_LCL_FLD: + return comp->lvaGetDesc(AsLclVarCommon())->IsAddressExposed(); + + case GT_IND: + case GT_BLK: + if (AsIndir()->IsInvariantLoad()) + { + return false; + } + FALLTHROUGH; + + case GT_STOREIND: + case GT_STORE_BLK: + case GT_STORE_DYN_BLK: + case GT_XADD: + case GT_XORR: + case GT_XAND: + case GT_XCHG: + case GT_LOCKADD: + case GT_CMPXCHG: + case GT_MEMORYBARRIER: + case GT_KEEPALIVE: + return true; + + case GT_CALL: + return AsCall()->HasSideEffects(comp, /* ignoreExceptions */ true); + + case GT_ALLOCOBJ: + return AsAllocObj()->gtHelperHasSideEffects; + +#if defined(FEATURE_HW_INTRINSICS) + case GT_HWINTRINSIC: + return AsHWIntrinsic()->OperRequiresGlobRefFlag(); +#endif // FEATURE_HW_INTRINSICS + + default: + assert(!OperRequiresCallFlag(comp) || OperIs(GT_INTRINSIC)); + assert((!OperIsIndir() || OperIs(GT_NULLCHECK)) && !OperRequiresAsgFlag()); + return false; + } +} + +//------------------------------------------------------------------------------ +// OperSupportsOrderingSideEffect : Check whether the operation supports the +// GTF_ORDER_SIDEEFF flag. +// +// Return Value: +// True if the given operator supports GTF_ORDER_SIDEEFF. +// +// Remarks: +// A node will still have this flag set if an operand has it set, even if the +// parent does not support it. This situation indicates that reordering the +// parent may be ok as long as it does not break ordering dependencies of the +// operand. +// +bool GenTree::OperSupportsOrderingSideEffect() const +{ + if (TypeIs(TYP_BYREF)) + { + // Forming byrefs may only be legal due to previous checks. + return true; + } + + switch (OperGet()) + { + case GT_BOUNDS_CHECK: + case GT_IND: + case GT_BLK: + case GT_STOREIND: + case GT_NULLCHECK: + case GT_STORE_BLK: + case GT_STORE_DYN_BLK: + case GT_XADD: + case GT_XORR: + case GT_XAND: + case GT_XCHG: + case GT_LOCKADD: + case GT_CMPXCHG: + case GT_MEMORYBARRIER: + case GT_CATCH_ARG: + return true; + default: + return false; + } +} + +//------------------------------------------------------------------------------ +// OperEffects: Compute effect flags that are relevant to this node only, +// excluding its children. +// +// Arguments: +// comp - Compiler instance +// +// Return Value: +// The effect flags. +// +GenTreeFlags GenTree::OperEffects(Compiler* comp) +{ + GenTreeFlags flags = gtFlags & GTF_ALL_EFFECT; + + if (((flags & GTF_ASG) != 0) && !OperRequiresAsgFlag()) + { + flags &= ~GTF_ASG; + } + + if (((flags & GTF_CALL) != 0) && !OperRequiresCallFlag(comp)) + { + flags &= ~GTF_CALL; + } + + if (((flags & GTF_EXCEPT) != 0) && !OperMayThrow(comp)) + { + flags &= ~GTF_EXCEPT; + } + + if (((flags & GTF_GLOB_REF) != 0) && !OperRequiresGlobRefFlag(comp)) + { + flags &= ~GTF_GLOB_REF; + } + + if (((flags & GTF_ORDER_SIDEEFF) != 0) && !OperSupportsOrderingSideEffect()) + { + flags &= ~GTF_ORDER_SIDEEFF; + } + + return flags; +} + //----------------------------------------------------------------------------------- // GetFieldCount: Return the register count for a multi-reg lclVar. // @@ -8177,7 +8330,7 @@ void Compiler::gtInitializeIndirNode(GenTreeIndir* indir, GenTreeFlags indirFlag } if ((indirFlags & GTF_IND_VOLATILE) != 0) { - indir->gtFlags |= GTF_ORDER_SIDEEFF; + indir->SetHasOrderingSideEffect(); } } @@ -25345,6 +25498,15 @@ bool GenTreeHWIntrinsic::OperRequiresCallFlag() const return false; } +//------------------------------------------------------------------------------ +// OperRequiresGlobRefFlag : Check whether the operation requires GTF_GLOB_REF +// flag regardless of the children's flags. +// +bool GenTreeHWIntrinsic::OperRequiresGlobRefFlag() const +{ + return OperIsMemoryLoad() || OperRequiresAsgFlag() || OperRequiresCallFlag(); +} + //------------------------------------------------------------------------ // GetLayout: Get the layout for this TYP_STRUCT HWI node. // diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 3605f3bcd33262..68af7c75bcc21e 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -1852,12 +1852,18 @@ struct GenTree // Returns true if it is a GT_COPY or GT_RELOAD of a multi-reg call node inline bool IsCopyOrReloadOfMultiRegCall() const; - bool OperRequiresAsgFlag(); + bool OperRequiresAsgFlag() const; - bool OperRequiresCallFlag(Compiler* comp); + bool OperRequiresCallFlag(Compiler* comp) const; - bool OperMayThrow(Compiler* comp); ExceptionSetFlags OperExceptions(Compiler* comp); + bool OperMayThrow(Compiler* comp); + + bool OperRequiresGlobRefFlag(Compiler* comp) const; + + bool OperSupportsOrderingSideEffect() const; + + GenTreeFlags OperEffects(Compiler* comp); unsigned GetScaleIndexMul(); unsigned GetScaleIndexShf(); @@ -2152,6 +2158,12 @@ struct GenTree gtFlags |= sourceFlags; } + void SetHasOrderingSideEffect() + { + assert(OperSupportsOrderingSideEffect()); + gtFlags |= GTF_ORDER_SIDEEFF; + } + inline bool IsCnsIntOrI() const; inline bool IsIntegralConst() const; @@ -6249,6 +6261,7 @@ struct GenTreeHWIntrinsic : public GenTreeJitIntrinsic bool OperRequiresAsgFlag() const; bool OperRequiresCallFlag() const; + bool OperRequiresGlobRefFlag() const; unsigned GetResultOpNumForRmwIntrinsic(GenTree* use, GenTree* op1, GenTree* op2, GenTree* op3); @@ -7177,6 +7190,14 @@ struct GenTreeIndir : public GenTreeOp return (gtFlags & GTF_IND_UNALIGNED) != 0; } + // True if this indirection is invariant. + bool IsInvariantLoad() const + { + bool isInvariant = (gtFlags & GTF_IND_INVARIANT) != 0; + assert(!isInvariant || OperIs(GT_IND, GT_BLK)); + return isInvariant; + } + #if DEBUGGABLE_GENTREE // Used only for GenTree::GetVtableForOper() GenTreeIndir() : GenTreeOp() diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index b0c2a37f7eaab5..baf481d773fa6a 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1966,7 +1966,7 @@ BasicBlock* Compiler::impPushCatchArgOnStack(BasicBlock* hndBlk, CORINFO_CLASS_H /* Mark the node as having a side-effect - i.e. cannot be * moved around since it is tied to a fixed location (EAX) */ - arg->gtFlags |= GTF_ORDER_SIDEEFF; + arg->SetHasOrderingSideEffect(); #if defined(JIT32_GCENCODER) const bool forceInsertNewBlock = isSingleBlockFilter || compStressCompile(STRESS_CATCH_ARG, 5); @@ -9823,8 +9823,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) // byref is literally 0, and since the byref // leaks out here, we need to ensure it is // nullchecked. - nullcheck->gtFlags |= GTF_ORDER_SIDEEFF; - boxPayloadAddress->gtFlags |= GTF_ORDER_SIDEEFF; + nullcheck->SetHasOrderingSideEffect(); + boxPayloadAddress->SetHasOrderingSideEffect(); GenTree* result = gtNewOperNode(GT_COMMA, TYP_BYREF, nullcheck, boxPayloadAddress); impPushOnStack(result, tiRetVal); break; diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index d340354d34ef1f..e8efd2a316980f 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -2937,8 +2937,8 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, // Add an ordering dependency between the bounds check and // forming the byref to prevent these from being reordered. The // JIT is not allowed to create arbitrary illegal byrefs. - boundsCheck->gtFlags |= GTF_ORDER_SIDEEFF; - result->gtFlags |= GTF_ORDER_SIDEEFF; + boundsCheck->SetHasOrderingSideEffect(); + result->SetHasOrderingSideEffect(); retNode = gtNewOperNode(GT_COMMA, resultType, boundsCheck, result); break; diff --git a/src/coreclr/jit/jiteh.cpp b/src/coreclr/jit/jiteh.cpp index a257ebc173502e..e9128fc26d13fc 100644 --- a/src/coreclr/jit/jiteh.cpp +++ b/src/coreclr/jit/jiteh.cpp @@ -2379,7 +2379,7 @@ bool Compiler::fgCreateFiltersForGenericExceptions() // Now we need to spill CATCH_ARG (it should be the first thing evaluated) GenTree* arg = new (this, GT_CATCH_ARG) GenTree(GT_CATCH_ARG, TYP_REF); - arg->gtFlags |= GTF_ORDER_SIDEEFF; + arg->SetHasOrderingSideEffect(); unsigned tempNum = lvaGrabTemp(false DEBUGARG("SpillCatchArg")); lvaTable[tempNum].lvType = TYP_REF; GenTree* argStore = gtNewTempStore(tempNum, arg); diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index c17c4cdd29527e..8805610d56a1a5 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -1705,7 +1705,8 @@ void Compiler::optPerformStaticOptimizations(unsigned loopNum, LoopCloneContext* JITDUMP("Updating flags on GDV guard inside hot loop. Before:\n"); DISPSTMT(stmt); - indir->gtFlags |= GTF_ORDER_SIDEEFF | GTF_IND_NONFAULTING; + indir->gtFlags |= GTF_IND_NONFAULTING; + indir->SetHasOrderingSideEffect(); indir->gtFlags &= ~GTF_EXCEPT; assert(fgNodeThreading == NodeThreading::None); gtUpdateStmtSideEffects(stmt); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 8cc25ba6b68bc1..5082308aa59070 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -4558,8 +4558,8 @@ GenTree* Compiler::fgMorphIndexAddr(GenTreeIndexAddr* indexAddr) // dependency, so make sure this dependency remains visible. Also, the // JIT is not allowed to create arbitrary byrefs, so we must make sure // the address is not reordered with the bounds check. - boundsCheck->gtFlags |= GTF_ORDER_SIDEEFF; - addr->gtFlags |= GTF_ORDER_SIDEEFF; + boundsCheck->SetHasOrderingSideEffect(); + addr->SetHasOrderingSideEffect(); tree = gtNewOperNode(GT_COMMA, tree->TypeGet(), boundsCheck, tree); fgSetRngChkTarget(boundsCheck); @@ -5116,7 +5116,7 @@ GenTree* Compiler::fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* m GenTree* lclVar = gtNewLclvNode(lclNum, objRefType); GenTree* nullchk = gtNewNullCheck(lclVar, compCurBB); - nullchk->gtFlags |= GTF_ORDER_SIDEEFF; + nullchk->SetHasOrderingSideEffect(); if (store != nullptr) { @@ -5129,10 +5129,6 @@ GenTree* Compiler::fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* m } addr = gtNewLclvNode(lclNum, objRefType); // Use "tmpLcl" to create "addr" node. - - // Ensure the creation of the byref does not get reordered with the - // null check, as that could otherwise create an illegal byref. - addr->gtFlags |= GTF_ORDER_SIDEEFF; } else { diff --git a/src/coreclr/jit/rationalize.cpp b/src/coreclr/jit/rationalize.cpp index fcb4df3129e239..56e22bb8f75cc5 100644 --- a/src/coreclr/jit/rationalize.cpp +++ b/src/coreclr/jit/rationalize.cpp @@ -342,24 +342,12 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge } else { - if (((node->gtFlags & GTF_ASG) != 0) && !node->OperRequiresAsgFlag()) - { - // Clear the GTF_ASG flag for all nodes that do not require it - node->gtFlags &= ~GTF_ASG; - } - - if (!node->IsCall()) - { - // Clear the GTF_CALL flag for all nodes but calls - node->gtFlags &= ~GTF_CALL; - } - if (node->IsValue() && use.IsDummyUse()) { node->SetUnusedValue(); } - if (node->TypeGet() == TYP_LONG) + if (node->TypeIs(TYP_LONG)) { comp->compLongUsed = true; } diff --git a/src/coreclr/jit/sideeffects.cpp b/src/coreclr/jit/sideeffects.cpp index cf0b3210e38535..3627e5fa3083b1 100644 --- a/src/coreclr/jit/sideeffects.cpp +++ b/src/coreclr/jit/sideeffects.cpp @@ -464,7 +464,7 @@ SideEffectSet::SideEffectSet(Compiler* compiler, GenTree* node) : m_sideEffectFl // void SideEffectSet::AddNode(Compiler* compiler, GenTree* node) { - m_sideEffectFlags |= (node->gtFlags & GTF_ALL_EFFECT); + m_sideEffectFlags |= node->OperEffects(compiler); m_aliasSet.AddNode(compiler, node); } @@ -571,7 +571,7 @@ bool SideEffectSet::InterferesWith(const SideEffectSet& other, bool strict) cons // bool SideEffectSet::InterferesWith(Compiler* compiler, GenTree* node, bool strict) const { - return InterferesWith((node->gtFlags & GTF_ALL_EFFECT), AliasSet::NodeInfo(compiler, node), strict); + return InterferesWith(node->OperEffects(compiler), AliasSet::NodeInfo(compiler, node), strict); } //------------------------------------------------------------------------