@@ -11642,14 +11642,10 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
1164211642 // TODO-1stClassStructs: improve this.
1164311643 if (op1->IsLocal() || (op1->TypeGet() != TYP_STRUCT))
1164411644 {
11645+ // Consider using GTF_IND_ASG_LHS here, as GTF_DONT_CSE here is used to indicate
11646+ // that this GT_IND is the LHS of an assignment
11647+ //
1164511648 op1->gtFlags |= GTF_DONT_CSE;
11646-
11647- // If the left side of the assignment is a GT_IND we mark with GTF_IND_ASG_LHS
11648- if (op1->OperIs(GT_IND))
11649- {
11650- // Mark this GT_IND as a LHS, so that we know this is a store
11651- op1->gtFlags |= GTF_IND_ASG_LHS;
11652- }
1165311649 }
1165411650 break;
1165511651
@@ -13655,21 +13651,24 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
1365513651 break;
1365613652 }
1365713653
13658- bool foldAndReturnTemp;
13659- bool isStore;
13654+ bool foldAndReturnTemp;
13655+ bool isStore;
13656+ var_types addCastToType;
1366013657
1366113658 foldAndReturnTemp = false;
13662- isStore = (tree->gtFlags & GTF_IND_ASG_LHS) == GTF_IND_ASG_LHS;
13663- temp = nullptr;
13664- ival1 = 0;
13659+ isStore = (tree->gtFlags & GTF_DONT_CSE) == GTF_DONT_CSE;
13660+ addCastToType = TYP_VOID; // TYP_VOID means we don't need to insert a cast
13661+
13662+ temp = nullptr;
13663+ ival1 = 0;
1366513664
1366613665 // Don't remove a volatile GT_IND, even if the address points to a local variable.
1366713666 if ((tree->gtFlags & GTF_IND_VOLATILE) == 0)
1366813667 {
1366913668 /* Try to Fold *(&X) into X */
1367013669 if (op1->gtOper == GT_ADDR)
1367113670 {
13672- // Can not remove a GT_ADDR if it is currently a CSE candidate.
13671+ // Cannot remove a GT_ADDR if it is currently a CSE candidate.
1367313672 if (gtIsActiveCSE_Candidate(op1))
1367413673 {
1367513674 break;
@@ -13716,7 +13715,9 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
1371613715 }
1371713716 }
1371813717 }
13719- bool matchingTypes = (lclType == typ);
13718+ bool matchingTypes = (lclType == typ);
13719+ bool matchingWidths = (genTypeSize(lclType) == genTypeSize(typ));
13720+ bool matchingFloat = (varTypeIsFloating(lclType) == varTypeIsFloating(typ));
1372013721
1372113722 // TYP_BOOL and TYP_UBYTE are also matching types
1372213723 if (!matchingTypes)
@@ -13731,34 +13732,43 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
1373113732 }
1373213733 }
1373313734
13734- // If the type of the Local Var matches the indirection type and we dont have a TYP_STRUCT,
13735- // then often we can fold the IND/ADDR and use the LCL_VAR directly
13736- if (matchingTypes && (typ != TYP_STRUCT))
13735+ // If the sizes match and we dont have a float or TYP_STRUCT,
13736+ // then we will fold the IND/ADDR and use the LCL_VAR directly
13737+ //
13738+ if (matchingWidths && matchingFloat && (typ != TYP_STRUCT))
1373713739 {
13738- // For Loads or Stores of non small types (types that don't need sign or zero extends)
13739- // we can fold the IND/ADDR and reduce to just the local variable
13740- if (!varTypeIsSmall(typ))
13741- {
13742- tree->gtType = typ = temp->TypeGet();
13743- foldAndReturnTemp = true;
13744- }
13745- else // varTypeIsSmall(typ) is true
13740+ // For some small types we might need to change to normalize loads or insert a cast here
13741+ //
13742+ if (varTypeIsSmall(typ))
1374613743 {
13747- // For Loads of small types than are not NormalizeOnLoad()
13748- // we can fold the IND/ADDR and reduce to just the local variable
13749- //
13750- // Stores of small types or when we don't normalize on load
13751- // then we need to insert a cast
13744+ // For any stores of small types, we will force loads to be normalized
13745+ // this is necessary as we need to zero/sign extend any load
13746+ // after this kind of store.
1375213747 //
13753- if (isStore || !varDsc->lvNormalizeOnLoad() )
13748+ if (isStore)
1375413749 {
1375513750 varDsc->lvForceLoadNormalize = true;
1375613751 }
13757- tree->gtType = typ = temp->TypeGet();
13758- foldAndReturnTemp = true;
13752+ // otherwise we have a load operation
13753+ //
13754+ // Do we have a non matching small type load?
13755+ // we may need to insert a cast operation
13756+ else if (!matchingTypes)
13757+ {
13758+ if (!varDsc->lvNormalizeOnLoad())
13759+ {
13760+ // Since we aren't normalizing on a loads
13761+ // we need to insert a cast here.
13762+ //
13763+ addCastToType = typ;
13764+ }
13765+ }
1375913766 }
13767+ // we will fold the IND/ADDR and reduce to just the local variable
13768+ //
13769+ tree->gtType = typ = temp->TypeGet();
13770+ foldAndReturnTemp = true;
1376013771 }
13761-
1376213772 if (!foldAndReturnTemp)
1376313773 {
1376413774 // Assumes that when Lookup returns "false" it will leave "fieldSeq" unmodified (i.e.
@@ -13949,19 +13959,38 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
1394913959 // normalization.
1395013960 if (temp->OperIs(GT_LCL_VAR))
1395113961 {
13952- #ifdef DEBUG
13953- // We clear this flag on `temp` because `fgMorphLocalVar` may assert that this bit is clear
13954- // and the node in question must have this bit set (as it has already been morphed).
13955- temp->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED;
13956- #endif // DEBUG
13957- const bool forceRemorph = true;
13958- temp = fgMorphLocalVar(temp, forceRemorph);
13959- #ifdef DEBUG
13960- // We then set this flag on `temp` because `fgMorhpLocalVar` may not set it itself, and the
13961- // caller of `fgMorphSmpOp` may assert that this flag is set on `temp` once this function
13962- // returns.
13963- temp->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
13964- #endif // DEBUG
13962+ unsigned lclNum = temp->AsLclVarCommon()->GetLclNum();
13963+ var_types lclType = lvaTable[lclNum].lvType;
13964+
13965+ // We build a new 'result' tree to return, as we want to call fgMorphTree on it
13966+ //
13967+ GenTree* result = gtNewLclvNode(lclNum, lclType);
13968+ assert(result->OperGet() == GT_LCL_VAR);
13969+
13970+ // Copy the GTF_DONT_CSE flag from the original tree to `result`
13971+ // because fgMorphLocalVar uses this flag to determine if we are loading or storing
13972+ // and will insert a cast when we are loading from a lvNormalizeOnLoad() local
13973+ //
13974+ result->gtFlags |= (tree->gtFlags & GTF_DONT_CSE);
13975+
13976+ // If the indirection node was a different small type than our local variable
13977+ // we insert a cast to that type.
13978+ //
13979+ if (addCastToType != TYP_VOID)
13980+ {
13981+ assert(varTypeIsSmall(addCastToType));
13982+
13983+ // Insert a narrowing cast to the old indirection type
13984+ //
13985+ result = gtNewCastNode(TYP_INT, result, false, addCastToType);
13986+ }
13987+
13988+ // On this path we return 'result' after calling fgMorphTree on it.
13989+ // so now we can destroy the unused'temp' node.
13990+ //
13991+ DEBUG_DESTROY_NODE(temp);
13992+
13993+ temp = fgMorphTree(result);
1396513994 }
1396613995
1396713996 return temp;
0 commit comments