temp = nullptr;
ival1 = 0;
- /* Try to Fold *(&X) into X */
- if (op1->gtOper == GT_ADDR)
+ // Don't remove a volatile GT_IND, even if the address points to a local variable.
+ if ((tree->gtFlags & GTF_IND_VOLATILE) == 0)
{
- // Can not remove a GT_ADDR if it is currently a CSE candidate.
- if (gtIsActiveCSE_Candidate(op1))
+ /* Try to Fold *(&X) into X */
+ if (op1->gtOper == GT_ADDR)
{
- break;
- }
-
- temp = op1->gtOp.gtOp1; // X
+ // Can not remove a GT_ADDR if it is currently a CSE candidate.
+ if (gtIsActiveCSE_Candidate(op1))
+ {
+ break;
+ }
- // In the test below, if they're both TYP_STRUCT, this of course does *not* mean that
- // they are the *same* struct type. In fact, they almost certainly aren't. If the
- // address has an associated field sequence, that identifies this case; go through
- // the "lcl_fld" path rather than this one.
- FieldSeqNode* addrFieldSeq = nullptr; // This is an unused out parameter below.
- if (typ == temp->TypeGet() && !GetZeroOffsetFieldMap()->Lookup(op1, &addrFieldSeq))
- {
- foldAndReturnTemp = true;
- }
- else if (temp->OperIsLocal())
- {
- unsigned lclNum = temp->gtLclVarCommon.gtLclNum;
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ temp = op1->gtOp.gtOp1; // X
- // We will try to optimize when we have a promoted struct promoted with a zero lvFldOffset
- if (varDsc->lvPromoted && (varDsc->lvFldOffset == 0))
+ // In the test below, if they're both TYP_STRUCT, this of course does *not* mean that
+ // they are the *same* struct type. In fact, they almost certainly aren't. If the
+ // address has an associated field sequence, that identifies this case; go through
+ // the "lcl_fld" path rather than this one.
+ FieldSeqNode* addrFieldSeq = nullptr; // This is an unused out parameter below.
+ if (typ == temp->TypeGet() && !GetZeroOffsetFieldMap()->Lookup(op1, &addrFieldSeq))
{
- noway_assert(varTypeIsStruct(varDsc));
+ foldAndReturnTemp = true;
+ }
+ else if (temp->OperIsLocal())
+ {
+ unsigned lclNum = temp->gtLclVarCommon.gtLclNum;
+ LclVarDsc* varDsc = &lvaTable[lclNum];
- // We will try to optimize when we have a single field struct that is being struct promoted
- if (varDsc->lvFieldCnt == 1)
+ // We will try to optimize when we have a promoted struct promoted with a zero lvFldOffset
+ if (varDsc->lvPromoted && (varDsc->lvFldOffset == 0))
{
- unsigned lclNumFld = varDsc->lvFieldLclStart;
- // just grab the promoted field
- LclVarDsc* fieldVarDsc = &lvaTable[lclNumFld];
+ noway_assert(varTypeIsStruct(varDsc));
- // Also make sure that the tree type matches the fieldVarType and that it's lvFldOffset
- // is zero
- if (fieldVarDsc->TypeGet() == typ && (fieldVarDsc->lvFldOffset == 0))
+ // We will try to optimize when we have a single field struct that is being struct promoted
+ if (varDsc->lvFieldCnt == 1)
{
- // We can just use the existing promoted field LclNum
- temp->gtLclVarCommon.SetLclNum(lclNumFld);
- temp->gtType = fieldVarDsc->TypeGet();
+ unsigned lclNumFld = varDsc->lvFieldLclStart;
+ // just grab the promoted field
+ LclVarDsc* fieldVarDsc = &lvaTable[lclNumFld];
- foldAndReturnTemp = true;
+ // Also make sure that the tree type matches the fieldVarType and that it's lvFldOffset
+ // is zero
+ if (fieldVarDsc->TypeGet() == typ && (fieldVarDsc->lvFldOffset == 0))
+ {
+ // We can just use the existing promoted field LclNum
+ temp->gtLclVarCommon.SetLclNum(lclNumFld);
+ temp->gtType = fieldVarDsc->TypeGet();
+
+ foldAndReturnTemp = true;
+ }
}
}
- }
- // If the type of the IND (typ) is a "small int", and the type of the local has the
- // same width, then we can reduce to just the local variable -- it will be
- // correctly normalized, and signed/unsigned differences won't matter.
- //
- // The below transformation cannot be applied if the local var needs to be normalized on load.
- else if (varTypeIsSmall(typ) && (genTypeSize(lvaTable[lclNum].lvType) == genTypeSize(typ)) &&
- !lvaTable[lclNum].lvNormalizeOnLoad())
- {
- tree->gtType = typ = temp->TypeGet();
- foldAndReturnTemp = true;
- }
- else if (!varTypeIsStruct(typ) && (lvaTable[lclNum].lvType == typ) &&
- !lvaTable[lclNum].lvNormalizeOnLoad())
- {
- tree->gtType = typ = temp->TypeGet();
- foldAndReturnTemp = true;
- }
- else
- {
- // Assumes that when Lookup returns "false" it will leave "fieldSeq" unmodified (i.e.
- // nullptr)
- assert(fieldSeq == nullptr);
- bool b = GetZeroOffsetFieldMap()->Lookup(op1, &fieldSeq);
- assert(b || fieldSeq == nullptr);
-
- if ((fieldSeq != nullptr) && (temp->OperGet() == GT_LCL_FLD))
+ // If the type of the IND (typ) is a "small int", and the type of the local has the
+ // same width, then we can reduce to just the local variable -- it will be
+ // correctly normalized, and signed/unsigned differences won't matter.
+ //
+ // The below transformation cannot be applied if the local var needs to be normalized on load.
+ else if (varTypeIsSmall(typ) && (genTypeSize(lvaTable[lclNum].lvType) == genTypeSize(typ)) &&
+ !lvaTable[lclNum].lvNormalizeOnLoad())
{
- // Append the field sequence, change the type.
- temp->AsLclFld()->gtFieldSeq =
- GetFieldSeqStore()->Append(temp->AsLclFld()->gtFieldSeq, fieldSeq);
- temp->gtType = typ;
-
- foldAndReturnTemp = true;
+ tree->gtType = typ = temp->TypeGet();
+ foldAndReturnTemp = true;
}
- }
- // Otherwise will will fold this into a GT_LCL_FLD below
- // where we check (temp != nullptr)
- }
- else // !temp->OperIsLocal()
- {
- // We don't try to fold away the GT_IND/GT_ADDR for this case
- temp = nullptr;
- }
- }
- else if (op1->OperGet() == GT_ADD)
- {
- /* Try to change *(&lcl + cns) into lcl[cns] to prevent materialization of &lcl */
+ else if (!varTypeIsStruct(typ) && (lvaTable[lclNum].lvType == typ) &&
+ !lvaTable[lclNum].lvNormalizeOnLoad())
+ {
+ tree->gtType = typ = temp->TypeGet();
+ foldAndReturnTemp = true;
+ }
+ else
+ {
+ // Assumes that when Lookup returns "false" it will leave "fieldSeq" unmodified (i.e.
+ // nullptr)
+ assert(fieldSeq == nullptr);
+ bool b = GetZeroOffsetFieldMap()->Lookup(op1, &fieldSeq);
+ assert(b || fieldSeq == nullptr);
- if (op1->gtOp.gtOp1->OperGet() == GT_ADDR && op1->gtOp.gtOp2->OperGet() == GT_CNS_INT &&
- (!(opts.MinOpts() || opts.compDbgCode)))
- {
- // No overflow arithmetic with pointers
- noway_assert(!op1->gtOverflow());
+ if ((fieldSeq != nullptr) && (temp->OperGet() == GT_LCL_FLD))
+ {
+ // Append the field sequence, change the type.
+ temp->AsLclFld()->gtFieldSeq =
+ GetFieldSeqStore()->Append(temp->AsLclFld()->gtFieldSeq, fieldSeq);
+ temp->gtType = typ;
- temp = op1->gtOp.gtOp1->gtOp.gtOp1;
- if (!temp->OperIsLocal())
- {
- temp = nullptr;
- break;
+ foldAndReturnTemp = true;
+ }
+ }
+ // Otherwise will will fold this into a GT_LCL_FLD below
+ // where we check (temp != nullptr)
}
-
- // Can not remove the GT_ADDR if it is currently a CSE candidate.
- if (gtIsActiveCSE_Candidate(op1->gtOp.gtOp1))
+ else // !temp->OperIsLocal()
{
- break;
+ // We don't try to fold away the GT_IND/GT_ADDR for this case
+ temp = nullptr;
}
+ }
+ else if (op1->OperGet() == GT_ADD)
+ {
+ /* Try to change *(&lcl + cns) into lcl[cns] to prevent materialization of &lcl */
- ival1 = op1->gtOp.gtOp2->gtIntCon.gtIconVal;
- fieldSeq = op1->gtOp.gtOp2->gtIntCon.gtFieldSeq;
-
- // Does the address have an associated zero-offset field sequence?
- FieldSeqNode* addrFieldSeq = nullptr;
- if (GetZeroOffsetFieldMap()->Lookup(op1->gtOp.gtOp1, &addrFieldSeq))
+ if (op1->gtOp.gtOp1->OperGet() == GT_ADDR && op1->gtOp.gtOp2->OperGet() == GT_CNS_INT &&
+ (!(opts.MinOpts() || opts.compDbgCode)))
{
- fieldSeq = GetFieldSeqStore()->Append(addrFieldSeq, fieldSeq);
- }
+ // No overflow arithmetic with pointers
+ noway_assert(!op1->gtOverflow());
- if (ival1 == 0 && typ == temp->TypeGet() && temp->TypeGet() != TYP_STRUCT)
- {
- noway_assert(!varTypeIsGC(temp->TypeGet()));
- foldAndReturnTemp = true;
- }
- else
- {
- // The emitter can't handle large offsets
- if (ival1 != (unsigned short)ival1)
+ temp = op1->gtOp.gtOp1->gtOp.gtOp1;
+ if (!temp->OperIsLocal())
{
+ temp = nullptr;
break;
}
- // The emitter can get confused by invalid offsets
- if (ival1 >= Compiler::lvaLclSize(temp->gtLclVarCommon.gtLclNum))
+ // Can not remove the GT_ADDR if it is currently a CSE candidate.
+ if (gtIsActiveCSE_Candidate(op1->gtOp.gtOp1))
{
break;
}
-#ifdef _TARGET_ARM_
- // Check for a LclVar TYP_STRUCT with misalignment on a Floating Point field
- //
- if (varTypeIsFloating(typ))
+ ival1 = op1->gtOp.gtOp2->gtIntCon.gtIconVal;
+ fieldSeq = op1->gtOp.gtOp2->gtIntCon.gtFieldSeq;
+
+ // Does the address have an associated zero-offset field sequence?
+ FieldSeqNode* addrFieldSeq = nullptr;
+ if (GetZeroOffsetFieldMap()->Lookup(op1->gtOp.gtOp1, &addrFieldSeq))
+ {
+ fieldSeq = GetFieldSeqStore()->Append(addrFieldSeq, fieldSeq);
+ }
+
+ if (ival1 == 0 && typ == temp->TypeGet() && temp->TypeGet() != TYP_STRUCT)
+ {
+ noway_assert(!varTypeIsGC(temp->TypeGet()));
+ foldAndReturnTemp = true;
+ }
+ else
{
- if ((ival1 % emitTypeSize(typ)) != 0)
+ // The emitter can't handle large offsets
+ if (ival1 != (unsigned short)ival1)
{
- tree->gtFlags |= GTF_IND_UNALIGNED;
break;
}
- }
+
+ // The emitter can get confused by invalid offsets
+ if (ival1 >= Compiler::lvaLclSize(temp->gtLclVarCommon.gtLclNum))
+ {
+ break;
+ }
+
+#ifdef _TARGET_ARM_
+ // Check for a LclVar TYP_STRUCT with misalignment on a Floating Point field
+ //
+ if (varTypeIsFloating(typ))
+ {
+ if ((ival1 % emitTypeSize(typ)) != 0)
+ {
+ tree->gtFlags |= GTF_IND_UNALIGNED;
+ break;
+ }
+ }
#endif
+ }
+ // Now we can fold this into a GT_LCL_FLD below
+ // where we check (temp != nullptr)
}
- // Now we can fold this into a GT_LCL_FLD below
- // where we check (temp != nullptr)
}
}
assert(TopValue(1).Node() == node);
assert(TopValue(0).Node() == node->gtGetOp1());
- if (!TopValue(1).Indir(TopValue(0)))
+ if ((node->gtFlags & GTF_IND_VOLATILE) != 0)
+ {
+ // Volatile indirections must not be removed so the address,
+ // if any, must be escaped.
+ EscapeValue(TopValue(0), node);
+ }
+ else if (!TopValue(1).Indir(TopValue(0)))
{
// If the address comes from another indirection (e.g. IND(IND(...))
// then we need to escape the location.