Transform local indirections off of `TYP_BLK` locals; support numbering exposed LCL_F...
authorSingleAccretion <62474226+SingleAccretion@users.noreply.github.com>
Wed, 11 Jan 2023 12:10:23 +0000 (15:10 +0300)
committerGitHub <noreply@github.com>
Wed, 11 Jan 2023 12:10:23 +0000 (13:10 +0100)
src/coreclr/jit/lclmorph.cpp
src/coreclr/jit/optimizer.cpp
src/coreclr/jit/valuenum.cpp

index 7793f1d2e5348312cd3ce4d7aacd21e85f192d85..8b3119d1cfd76f010cf5c8425c4cd060d14aff6b 100644 (file)
@@ -944,20 +944,21 @@ private:
         else
         {
             // Otherwise it must be accessed through some kind of indirection. Usually this is
-            // something like IND(ADDR(LCL_VAR)), global morph will change it to GT_LCL_VAR or
-            // GT_LCL_FLD so the lclvar does not need to be address exposed.
+            // something like IND(LCL_ADDR_VAR), which we will change to LCL_VAR or LCL_FLD so
+            // the lclvar does not need to be address exposed.
             //
             // However, it is possible for the indirection to be wider than the lclvar
             // (e.g. *(long*)&int32Var) or to have a field offset that pushes the indirection
-            // past the end of the lclvar memory location. In such cases morph doesn't do
-            // anything so the lclvar needs to be address exposed.
+            // past the end of the lclvar memory location. In such cases we cannot do anything
+            // so the lclvar needs to be address exposed.
             //
             // More importantly, if the lclvar is a promoted struct field then the parent lclvar
             // also needs to be address exposed so we get dependent struct promotion. Code like
             // *(long*)&int32Var has undefined behavior and it's practically useless but reading,
             // say, 2 consecutive Int32 struct fields as Int64 has more practical value.
 
-            LclVarDsc* varDsc    = m_compiler->lvaGetDesc(val.LclNum());
+            unsigned   lclNum    = val.LclNum();
+            LclVarDsc* varDsc    = m_compiler->lvaGetDesc(lclNum);
             unsigned   indirSize = GetIndirSize(node);
             bool       isWide;
 
@@ -976,24 +977,18 @@ private:
                 {
                     isWide = true;
                 }
-                else if (varDsc->TypeGet() == TYP_STRUCT)
-                {
-                    isWide = (endOffset.Value() > varDsc->lvExactSize);
-                }
                 else
                 {
-                    // For small int types use the real type size, not the stack slot size.
-                    // Morph does manage to transform `*(int*)&byteVar` into just byteVar where
-                    // the LCL_VAR node has type TYP_INT. But such code is simply bogus and
-                    // there's no reason to attempt to optimize it. It makes more sense to
-                    // mark the variable address exposed in such circumstances.
-                    //
-                    // Same for "small" SIMD types - SIMD8/12 have 8/12 bytes, even if the
-                    // stack location may have 16 bytes.
-                    //
-                    // For TYP_BLK variables the type size is 0 so they're always address
-                    // exposed.
-                    isWide = (endOffset.Value() > genTypeSize(varDsc->TypeGet()));
+                    isWide = endOffset.Value() > m_compiler->lvaLclExactSize(lclNum);
+
+                    if (varDsc->TypeGet() == TYP_BLK)
+                    {
+                        // TODO-CQ: TYP_BLK used to always be exposed here. This is in principle not necessary, but
+                        // not doing so would require VN changes. For now, exposing gets better CQ as otherwise the
+                        // variable ends up untracked and VN treats untracked-not-exposed locals more conservatively
+                        // than exposed ones.
+                        m_compiler->lvaSetVarAddrExposed(lclNum DEBUGARG(AddressExposedReason::TOO_CONSERVATIVE));
+                    }
                 }
             }
 
index 088f7134de2e05b1479a8451936faf5cc956cf7a..50555a7c1a256a6525abad67e3e1265ab83eadb2 100644 (file)
@@ -8602,13 +8602,12 @@ bool Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
                     }
                 }
                 // Otherwise, must be local lhs form.  I should assert that.
-                else if (lhs->OperGet() == GT_LCL_VAR)
+                else if (lhs->OperIsLocal())
                 {
-                    GenTreeLclVar* lhsLcl = lhs->AsLclVar();
-                    GenTree*       rhs    = tree->AsOp()->gtOp2;
-                    ValueNum       rhsVN  = rhs->gtVNPair.GetLiberal();
+                    GenTreeLclVarCommon* lhsLcl = lhs->AsLclVarCommon();
+                    ValueNum             rhsVN  = tree->AsOp()->gtOp2->gtVNPair.GetLiberal();
                     // If we gave the RHS a value number, propagate it.
-                    if (rhsVN != ValueNumStore::NoVN)
+                    if (lhsLcl->OperIs(GT_LCL_VAR) && (rhsVN != ValueNumStore::NoVN))
                     {
                         rhsVN = vnStore->VNNormalValue(rhsVN);
                         if (lhsLcl->HasSsaName())
index 8e2c86b2d73cdd349632bf2966c9d7bc1197845a..58657015dbd2827443fe2d8ebb4446de41124eeb 100644 (file)
@@ -8851,20 +8851,31 @@ void Compiler::fgValueNumberTree(GenTree* tree)
                 // If this is a (full or partial) def we skip; it will be handled as part of the assignment.
                 if ((lclFld->gtFlags & GTF_VAR_DEF) == 0)
                 {
-                    unsigned lclNum = lclFld->GetLclNum();
+                    unsigned   lclNum = lclFld->GetLclNum();
+                    LclVarDsc* varDsc = lvaGetDesc(lclNum);
 
-                    if (!lclFld->HasSsaName())
+                    if (lclFld->HasSsaName())
                     {
-                        lclFld->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lclFld->TypeGet()));
-                    }
-                    else
-                    {
-                        LclVarDsc*   varDsc      = lvaGetDesc(lclNum);
                         ValueNumPair lclVarValue = varDsc->GetPerSsaData(lclFld->GetSsaNum())->m_vnPair;
                         lclFld->gtVNPair =
                             vnStore->VNPairForLoad(lclVarValue, lvaLclExactSize(lclNum), lclFld->TypeGet(),
                                                    lclFld->GetLclOffs(), lclFld->GetSize());
                     }
+                    else if (varDsc->IsAddressExposed())
+                    {
+                        // Address-exposed locals are part of ByrefExposed.
+                        ValueNum addrVN = vnStore->VNForFunc(TYP_BYREF, VNF_PtrToLoc, vnStore->VNForIntCon(lclNum),
+                                                             vnStore->VNForIntPtrCon(lclFld->GetLclOffs()));
+                        ValueNum loadVN = fgValueNumberByrefExposedLoad(lclFld->TypeGet(), addrVN);
+
+                        lclFld->gtVNPair.SetLiberal(loadVN);
+                        lclFld->gtVNPair.SetConservative(vnStore->VNForExpr(compCurBB, lclFld->TypeGet()));
+                    }
+                    else
+                    {
+                        // An untracked local, and other odd cases.
+                        lclFld->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lclFld->TypeGet()));
+                    }
                 }
                 else
                 {