//
bool omitCopy = call->IsTailCall();
- if (!omitCopy && fgDidEarlyLiveness)
+ if (!omitCopy && fgGlobalMorph)
{
omitCopy = !varDsc->lvPromoted && ((lcl->gtFlags & GTF_VAR_DEATH) != 0);
}
// Copy prop could allow creating another later use of lcl if there are live assertions about it.
fgKillDependentAssertions(varNum DEBUGARG(lcl));
+
+ // We may have seen previous uses of this local already,
+ // and those uses should now be marked as GTF_GLOB_REF
+ // since otherwise they could be reordered with the call.
+ // The only known issues are under stress mode and happen
+ // in the same statement, so we only handle this case currently.
+ // TODO: A more complete fix will likely entail identifying
+ // these candidates before morph and address exposing them
+ // at that point, which first requires ABI determination to
+ // be moved earlier.
+ fgRemarkGlobalUses = true;
}
JITDUMP("did not need to make outgoing copy for last use of V%02d\n", varNum);
arg->SetEarlyNode(argNode);
}
+//------------------------------------------------------------------------
+// fgMarkNewlyGlobalUses: Given a local that is newly address exposed, add
+// GTF_GLOB_REF whever necessary in the specified statement.
+//
+// Arguments:
+// stmt - The statement
+// lclNum - The local that is newly address exposed
+//
+// Notes:
+// See comment in fgMakeOutgoingStructArgCopy.
+//
+void Compiler::fgMarkGlobalUses(Statement* stmt)
+{
+ struct Visitor : GenTreeVisitor<Visitor>
+ {
+ enum
+ {
+ DoPostOrder = true,
+ };
+
+ Visitor(Compiler* comp) : GenTreeVisitor(comp)
+ {
+ }
+
+ fgWalkResult PostOrderVisit(GenTree** use, GenTree* user)
+ {
+ GenTree* node = *use;
+ if (node->OperIsLocal() && m_compiler->lvaGetDesc(node->AsLclVarCommon()->GetLclNum())->IsAddressExposed())
+ {
+ node->gtFlags |= GTF_GLOB_REF;
+ }
+
+ if (user != nullptr)
+ {
+ user->gtFlags |= node->gtFlags & GTF_GLOB_REF;
+ }
+
+ return WALK_CONTINUE;
+ }
+ };
+
+ Visitor visitor(this);
+ visitor.WalkTree(stmt->GetRootNodePointer(), nullptr);
+}
+
/*****************************************************************************
*
* A little helper used to rearrange nested commutative operations. The
void Compiler::fgMorphStmts(BasicBlock* block)
{
fgRemoveRestOfBlock = false;
+ fgRemarkGlobalUses = false;
for (Statement* const stmt : block->Statements())
{
stmt->SetRootNode(morphedTree);
+ if (fgRemarkGlobalUses)
+ {
+ fgMarkGlobalUses(stmt);
+ fgRemarkGlobalUses = false;
+ }
+
if (fgRemoveRestOfBlock)
{
continue;