noway_assert(targetType != TYP_STRUCT);
// record the offset
- unsigned offset = tree->gtLclOffs;
+ unsigned offset = tree->GetLclOffs();
// We must have a stack store with GT_STORE_LCL_FLD
noway_assert(targetReg == REG_NA);
#endif // FEATURE_SIMD
// record the offset
- unsigned offset = tree->gtLclOffs;
+ unsigned offset = tree->GetLclOffs();
// We must have a stack store with GT_STORE_LCL_FLD
noway_assert(targetReg == REG_NA);
if (treeNode->OperGet() == GT_STORE_LCL_FLD)
{
- offs = treeNode->AsLclFld()->gtLclOffs;
+ offs = treeNode->AsLclFld()->GetLclOffs();
}
GenTree* op1 = treeNode->AsOp()->gtOp1;
assert(targetReg != REG_NA);
emitAttr size = emitTypeSize(targetType);
- unsigned offs = tree->gtLclOffs;
+ unsigned offs = tree->GetLclOffs();
unsigned varNum = tree->GetLclNum();
assert(varNum < compiler->lvaCount);
if (dstAddr->OperIs(GT_LCL_FLD_ADDR))
{
- assert(dstAddr->AsLclFld()->gtLclOffs <= INT32_MAX);
- dstOffset = dstAddr->AsLclFld()->gtLclOffs;
+ dstOffset = dstAddr->AsLclFld()->GetLclOffs();
}
}
if (dstAddr->OperIs(GT_LCL_FLD_ADDR))
{
- assert(dstAddr->AsLclFld()->gtLclOffs <= INT32_MAX);
- dstOffset = dstAddr->AsLclFld()->gtLclOffs;
+ dstOffset = dstAddr->AsLclFld()->GetLclOffs();
}
}
if (src->OperIs(GT_LCL_FLD))
{
- assert(src->AsLclFld()->gtLclOffs <= INT32_MAX);
- srcOffset = static_cast<int>(src->AsLclFld()->gtLclOffs);
+ srcOffset = src->AsLclFld()->GetLclOffs();
}
}
else
if (srcAddr->OperIs(GT_LCL_FLD_ADDR))
{
- assert(srcAddr->AsLclFld()->gtLclOffs <= INT32_MAX);
- srcOffset = static_cast<int>(srcAddr->AsLclFld()->gtLclOffs);
+ srcOffset = srcAddr->AsLclFld()->GetLclOffs();
}
}
}
unsigned int offset = 0;
if (srcAddr->OperGet() == GT_LCL_FLD_ADDR)
{
- offset = srcAddr->AsLclFld()->gtLclOffs;
+ offset = srcAddr->AsLclFld()->GetLclOffs();
}
GetEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, srcReg, lclNode->GetLclNum(), offset);
}
if (dstAddr->OperIs(GT_LCL_FLD_ADDR))
{
- assert(dstAddr->AsLclFld()->gtLclOffs <= INT32_MAX);
- dstOffset = static_cast<int>(dstAddr->AsLclFld()->gtLclOffs);
+ dstOffset = dstAddr->AsLclFld()->GetLclOffs();
}
}
{
if (baseNode->gtOper == GT_LCL_FLD_ADDR)
{
- offset += baseNode->AsLclFld()->gtLclOffs;
+ offset += baseNode->AsLclFld()->GetLclOffs();
}
emit->emitIns_R_S(ins, size, dst, baseNode->AsLclVarCommon()->GetLclNum(), offset);
}
if (dstAddr->OperIs(GT_LCL_FLD_ADDR))
{
- assert(dstAddr->AsLclFld()->gtLclOffs <= INT32_MAX);
- dstOffset = static_cast<int>(dstAddr->AsLclFld()->gtLclOffs);
+ dstOffset = dstAddr->AsLclFld()->GetLclOffs();
}
}
if (src->OperIs(GT_LCL_FLD))
{
- assert(src->AsLclFld()->gtLclOffs <= INT32_MAX);
- srcOffset = static_cast<int>(src->AsLclFld()->gtLclOffs);
+ srcOffset = src->AsLclFld()->GetLclOffs();
}
}
else
if (srcAddr->OperIs(GT_LCL_FLD_ADDR))
{
- assert(srcAddr->AsLclFld()->gtLclOffs <= INT32_MAX);
- srcOffset = static_cast<int>(srcAddr->AsLclFld()->gtLclOffs);
+ srcOffset = srcAddr->AsLclFld()->GetLclOffs();
}
}
}
noway_assert(targetType != TYP_STRUCT);
emitAttr size = emitTypeSize(targetType);
- unsigned offs = tree->gtLclOffs;
+ unsigned offs = tree->GetLclOffs();
unsigned varNum = tree->GetLclNum();
assert(varNum < compiler->lvaCount);
}
case GT_LCL_FLD:
- {
- GenTreeLclFld* lclField = srcNode->AsLclFld();
-
- varNum = lclField->GetLclNum();
- offset = lclField->gtLclOffs;
+ varNum = srcNode->AsLclFld()->GetLclNum();
+ offset = srcNode->AsLclFld()->GetLclOffs();
break;
- }
case GT_LCL_VAR:
{
srcLclNum = srcAddr->AsLclVarCommon()->GetLclNum();
if (srcAddr->OperGet() == GT_LCL_FLD_ADDR)
{
- srcLclOffset = srcAddr->AsLclFld()->gtLclOffs;
+ srcLclOffset = srcAddr->AsLclFld()->GetLclOffs();
}
}
comp->gtGetLclVarNameInfo(lclNum, &ilKind, &ilName, &ilNum);
if (ilName != nullptr)
{
- chars += printf("%s+%u", ilName, tree->AsLclFld()->gtLclOffs);
+ chars += printf("%s+%u", ilName, tree->AsLclFld()->GetLclOffs());
}
else
{
- chars += printf("%s%d+%u", ilKind, ilNum, tree->AsLclFld()->gtLclOffs);
+ chars += printf("%s%d+%u", ilKind, ilNum, tree->AsLclFld()->GetLclOffs());
LclVarDsc* varDsc = comp->lvaTable + lclNum;
if (comp->dumpIRLocals)
{
GenTree* op2 = nullptr);
GenTreeIntCon* gtNewIconNode(ssize_t value, var_types type = TYP_INT);
+ GenTreeIntCon* gtNewIconNode(unsigned fieldOffset, FieldSeqNode* fieldSeq);
GenTree* gtNewPhysRegNode(regNumber reg, var_types type);
GenTree* gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs = BAD_IL_OFFSET));
GenTree* gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs = BAD_IL_OFFSET));
+ GenTreeLclVar* gtNewLclVarAddrNode(unsigned lclNum, var_types type = TYP_I_IMPL);
+ GenTreeLclFld* gtNewLclFldAddrNode(unsigned lclNum,
+ unsigned lclOffs,
+ FieldSeqNode* fieldSeq,
+ var_types type = TYP_I_IMPL);
+
#ifdef FEATURE_SIMD
GenTreeSIMD* gtNewSIMDNode(
var_types type, GenTree* op1, SIMDIntrinsicID simdIntrinsicID, var_types baseType, unsigned size);
Compiler* compiler = JitTls::GetCompiler();
bool isZeroOffset = compiler->GetZeroOffsetFieldMap()->Lookup(this, &zeroFieldSeq);
- AsLclFld()->gtLclOffs = 0;
- AsLclFld()->gtFieldSeq = FieldSeqStore::NotAField();
+ AsLclFld()->SetLclOffs(0);
+ AsLclFld()->SetFieldSeq(FieldSeqStore::NotAField());
if (zeroFieldSeq != nullptr)
{
// Set the zeroFieldSeq in the GT_LCL_FLD node
- AsLclFld()->gtFieldSeq = zeroFieldSeq;
+ AsLclFld()->SetFieldSeq(zeroFieldSeq);
// and remove the annotation from the ZeroOffsetFieldMap
compiler->GetZeroOffsetFieldMap()->Remove(this);
}
else
{
loResult->SetOper(GT_LCL_FLD);
- loResult->AsLclFld()->gtLclOffs = 0;
- loResult->AsLclFld()->gtFieldSeq = FieldSeqStore::NotAField();
+ loResult->AsLclFld()->SetLclOffs(0);
+ loResult->AsLclFld()->SetFieldSeq(FieldSeqStore::NotAField());
hiResult->SetOper(GT_LCL_FLD);
- hiResult->AsLclFld()->gtLclOffs = 4;
- hiResult->AsLclFld()->gtFieldSeq = FieldSeqStore::NotAField();
+ hiResult->AsLclFld()->SetLclOffs(4);
+ hiResult->AsLclFld()->SetFieldSeq(FieldSeqStore::NotAField());
}
return FinalizeDecomposition(use, loResult, hiResult, hiResult);
GenTreeLclFld* loResult = tree->AsLclFld();
loResult->gtType = TYP_INT;
- GenTree* hiResult = m_compiler->gtNewLclFldNode(loResult->GetLclNum(), TYP_INT, loResult->gtLclOffs + 4);
+ GenTree* hiResult = m_compiler->gtNewLclFldNode(loResult->GetLclNum(), TYP_INT, loResult->GetLclOffs() + 4);
Range().InsertAfter(loResult, hiResult);
return FinalizeDecomposition(use, loResult, hiResult, hiResult);
loStore->gtFlags |= GTF_VAR_USEASG;
// Create the store for the upper half of the GT_LONG and insert it after the low store.
- GenTreeLclFld* hiStore = m_compiler->gtNewLclFldNode(loStore->GetLclNum(), TYP_INT, loStore->gtLclOffs + 4);
+ GenTreeLclFld* hiStore = m_compiler->gtNewLclFldNode(loStore->GetLclNum(), TYP_INT, loStore->GetLclOffs() + 4);
hiStore->SetOper(GT_STORE_LCL_FLD);
hiStore->gtOp1 = value->gtOp2;
hiStore->gtFlags |= (GTF_VAR_DEF | GTF_VAR_USEASG);
{
case GT_LCL_FLD:
case GT_STORE_LCL_FLD:
- {
- GenTreeLclFld* lclField = memOp->AsLclFld();
- varNum = lclField->GetLclNum();
- offset = lclField->gtLclOffs;
+ varNum = memOp->AsLclFld()->GetLclNum();
+ offset = memOp->AsLclFld()->GetLclOffs();
break;
- }
case GT_LCL_VAR:
{
return true;
case GT_LCL_FLD:
- if (op1->AsLclFld()->GetLclNum() != op2->AsLclFld()->GetLclNum() ||
- op1->AsLclFld()->gtLclOffs != op2->AsLclFld()->gtLclOffs)
+ if ((op1->AsLclFld()->GetLclNum() != op2->AsLclFld()->GetLclNum()) ||
+ (op1->AsLclFld()->GetLclOffs() != op2->AsLclFld()->GetLclOffs()))
{
break;
}
break;
case GT_LCL_FLD:
hash = genTreeHashAdd(hash, tree->AsLclFld()->GetLclNum());
- add = tree->AsLclFld()->gtLclOffs;
+ add = tree->AsLclFld()->GetLclOffs();
break;
case GT_CNS_INT:
}
break;
+ case GT_LCL_FLD_ADDR:
+ case GT_LCL_VAR_ADDR:
+ level = 1;
+ costEx = 3;
+ costSz = 3;
+ break;
+
case GT_PHI_ARG:
case GT_ARGPLACE:
level = 0;
return new (this, GT_CNS_INT) GenTreeIntCon(type, value);
}
+GenTreeIntCon* Compiler::gtNewIconNode(unsigned fieldOffset, FieldSeqNode* fieldSeq)
+{
+ GenTreeIntCon* node = new (this, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, static_cast<ssize_t>(fieldOffset));
+ node->gtFieldSeq = fieldSeq == nullptr ? FieldSeqStore::NotAField() : fieldSeq;
+ return node;
+}
+
// return a new node representing the value in a physical register
GenTree* Compiler::gtNewPhysRegNode(regNumber reg, var_types type)
{
return node;
}
+GenTreeLclVar* Compiler::gtNewLclVarAddrNode(unsigned lclNum, var_types type)
+{
+ return new (this, GT_LCL_VAR_ADDR) GenTreeLclVar(type, lclNum);
+}
+
+GenTreeLclFld* Compiler::gtNewLclFldAddrNode(unsigned lclNum, unsigned lclOffs, FieldSeqNode* fieldSeq, var_types type)
+{
+ GenTreeLclFld* node = new (this, GT_LCL_FLD_ADDR) GenTreeLclFld(type, lclNum, lclOffs);
+ node->SetFieldSeq(fieldSeq == nullptr ? FieldSeqStore::NotAField() : fieldSeq);
+ return node;
+}
+
GenTreeLclFld* Compiler::gtNewLclFldNode(unsigned lnum, var_types type, unsigned offset)
{
GenTreeLclFld* node = new (this, GT_LCL_FLD) GenTreeLclFld(type, lnum, offset);
// assert(lnum < lvaCount);
- node->gtFieldSeq = FieldSeqStore::NotAField();
+ node->SetFieldSeq(FieldSeqStore::NotAField());
return node;
}
// Remember that the LclVar node has been cloned. The flag will be set
// on 'copy' as well.
tree->gtFlags |= GTF_VAR_CLONED;
- copy = new (this, tree->gtOper) GenTreeLclFld(tree->gtOper, tree->TypeGet(), tree->AsLclFld()->GetLclNum(),
- tree->AsLclFld()->gtLclOffs);
- copy->AsLclFld()->gtFieldSeq = tree->AsLclFld()->gtFieldSeq;
+ copy = new (this, tree->OperGet())
+ GenTreeLclFld(tree->OperGet(), tree->TypeGet(), tree->AsLclFld()->GetLclNum(),
+ tree->AsLclFld()->GetLclOffs());
+ copy->AsLclFld()->SetFieldSeq(tree->AsLclFld()->GetFieldSeq());
break;
case GT_CLS_VAR:
// be set on 'copy' as well.
tree->gtFlags |= GTF_VAR_CLONED;
copy = new (this, GT_LCL_FLD)
- GenTreeLclFld(tree->TypeGet(), tree->AsLclFld()->GetLclNum(), tree->AsLclFld()->gtLclOffs);
- copy->AsLclFld()->gtFieldSeq = tree->AsLclFld()->gtFieldSeq;
- copy->gtFlags = tree->gtFlags;
+ GenTreeLclFld(tree->TypeGet(), tree->AsLclFld()->GetLclNum(), tree->AsLclFld()->GetLclOffs());
+ copy->AsLclFld()->SetFieldSeq(tree->AsLclFld()->GetFieldSeq());
+ copy->gtFlags = tree->gtFlags;
}
goto DONE;
copy = new (this, oper) GenTreeVal(oper, tree->gtType, tree->AsVal()->gtVal1);
goto DONE;
+ case GT_LCL_VAR_ADDR:
+ copy = new (this, oper) GenTreeLclVar(oper, tree->TypeGet(), tree->AsLclVar()->GetLclNum());
+ goto DONE;
+
+ case GT_LCL_FLD_ADDR:
+ copy = new (this, oper)
+ GenTreeLclFld(oper, tree->TypeGet(), tree->AsLclFld()->GetLclNum(), tree->AsLclFld()->GetLclOffs());
+ copy->AsLclFld()->SetFieldSeq(tree->AsLclFld()->GetFieldSeq());
+ goto DONE;
+
default:
NO_WAY("Cloning of node not supported");
goto DONE;
if (isLclFld)
{
- printf("[+%u]", tree->AsLclFld()->gtLclOffs);
- gtDispFieldSeq(tree->AsLclFld()->gtFieldSeq);
+ printf("[+%u]", tree->AsLclFld()->GetLclOffs());
+ gtDispFieldSeq(tree->AsLclFld()->GetFieldSeq());
}
if (varDsc->lvRegister)
unsigned lclOffset = 0;
if (addrArg->OperIsLocalField())
{
- lclOffset = addrArg->AsLclFld()->gtLclOffs;
+ lclOffset = addrArg->AsLclFld()->GetLclOffs();
}
if (lclOffset != 0)
if (OperGet() == GT_LCL_FLD)
{
// Otherwise, prepend this field to whatever we've already accumulated outside in.
- *pFldSeq = comp->GetFieldSeqStore()->Append(AsLclFld()->gtFieldSeq, *pFldSeq);
+ *pFldSeq = comp->GetFieldSeqStore()->Append(AsLclFld()->GetFieldSeq(), *pFldSeq);
}
return true;
}
if (addrArg->OperGet() == GT_LCL_FLD)
{
// Otherwise, prepend this field to whatever we've already accumulated outside in.
- *pFldSeq = comp->GetFieldSeqStore()->Append(addrArg->AsLclFld()->gtFieldSeq, *pFldSeq);
+ *pFldSeq = comp->GetFieldSeqStore()->Append(addrArg->AsLclFld()->GetFieldSeq(), *pFldSeq);
}
return true;
}
*pLclVarTree = this->AsLclVarCommon();
if (this->OperGet() == GT_LCL_FLD_ADDR)
{
- *pFldSeq = comp->GetFieldSeqStore()->Append(this->AsLclFld()->gtFieldSeq, *pFldSeq);
+ *pFldSeq = comp->GetFieldSeqStore()->Append(this->AsLclFld()->GetFieldSeq(), *pFldSeq);
}
return true;
}
void dumpLIRFlags();
#endif
+ bool TypeIs(var_types type) const
+ {
+ return gtType == type;
+ }
+
+ template <typename... T>
+ bool TypeIs(var_types type, T... rest) const
+ {
+ return TypeIs(type) || TypeIs(rest...);
+ }
+
bool OperIs(genTreeOps oper) const
{
return OperGet() == oper;
struct GenTreeLclFld : public GenTreeLclVarCommon
{
- unsigned gtLclOffs; // offset into the variable to access
-
- FieldSeqNode* gtFieldSeq; // This LclFld node represents some sequences of accesses.
+private:
+ uint16_t m_lclOffs; // offset into the variable to access
+ FieldSeqNode* m_fieldSeq; // This LclFld node represents some sequences of accesses.
- // old/FE style constructor where load/store/addr share same opcode
+public:
GenTreeLclFld(var_types type, unsigned lclNum, unsigned lclOffs)
- : GenTreeLclVarCommon(GT_LCL_FLD, type, lclNum), gtLclOffs(lclOffs), gtFieldSeq(nullptr)
+ : GenTreeLclVarCommon(GT_LCL_FLD, type, lclNum), m_lclOffs(static_cast<uint16_t>(lclOffs)), m_fieldSeq(nullptr)
{
- assert(sizeof(*this) <= s_gtNodeSizes[GT_LCL_FLD]);
+ assert(lclOffs <= UINT16_MAX);
}
GenTreeLclFld(genTreeOps oper, var_types type, unsigned lclNum, unsigned lclOffs)
- : GenTreeLclVarCommon(oper, type, lclNum), gtLclOffs(lclOffs), gtFieldSeq(nullptr)
+ : GenTreeLclVarCommon(oper, type, lclNum), m_lclOffs(static_cast<uint16_t>(lclOffs)), m_fieldSeq(nullptr)
{
- assert(sizeof(*this) <= s_gtNodeSizes[GT_LCL_FLD]);
+ assert(lclOffs <= UINT16_MAX);
}
+
+ uint16_t GetLclOffs() const
+ {
+ return m_lclOffs;
+ }
+
+ void SetLclOffs(unsigned lclOffs)
+ {
+ assert(lclOffs <= UINT16_MAX);
+ m_lclOffs = static_cast<uint16_t>(lclOffs);
+ }
+
+ FieldSeqNode* GetFieldSeq() const
+ {
+ return m_fieldSeq;
+ }
+
+ void SetFieldSeq(FieldSeqNode* fieldSeq)
+ {
+ m_fieldSeq = fieldSeq;
+ }
+
#if DEBUGGABLE_GENTREE
GenTreeLclFld() : GenTreeLclVarCommon()
{
switch (rmOp->OperGet())
{
case GT_LCL_FLD:
- {
- GenTreeLclFld* lclField = rmOp->AsLclFld();
-
- varNum = lclField->GetLclNum();
- offset = lclField->gtLclOffs;
+ varNum = rmOp->AsLclFld()->GetLclNum();
+ offset = rmOp->AsLclFld()->GetLclOffs();
break;
- }
case GT_LCL_VAR:
{
switch (op2->OperGet())
{
case GT_LCL_FLD:
- {
- GenTreeLclFld* lclField = op2->AsLclFld();
-
- varNum = lclField->GetLclNum();
- offset = lclField->AsLclFld()->gtLclOffs;
+ varNum = op2->AsLclFld()->GetLclNum();
+ offset = op2->AsLclFld()->GetLclOffs();
break;
- }
case GT_LCL_VAR:
{
switch (op2->OperGet())
{
case GT_LCL_FLD:
- {
- GenTreeLclFld* lclField = op2->AsLclFld();
-
- varNum = lclField->GetLclNum();
- offset = lclField->AsLclFld()->gtLclOffs;
+ varNum = op2->AsLclFld()->GetLclNum();
+ offset = op2->AsLclFld()->GetLclOffs();
break;
- }
case GT_LCL_VAR:
{
switch (op2->OperGet())
{
case GT_LCL_FLD:
- {
- GenTreeLclFld* lclField = op2->AsLclFld();
-
- varNum = lclField->GetLclNum();
- offset = lclField->AsLclFld()->gtLclOffs;
+ varNum = op2->AsLclFld()->GetLclNum();
+ offset = op2->AsLclFld()->GetLclOffs();
break;
- }
case GT_LCL_VAR:
{
switch (op3->OperGet())
{
case GT_LCL_FLD:
- {
- GenTreeLclFld* lclField = op3->AsLclFld();
-
- varNum = lclField->GetLclNum();
- offset = lclField->AsLclFld()->gtLclOffs;
+ varNum = op3->AsLclFld()->GetLclNum();
+ offset = op3->AsLclFld()->GetLclOffs();
break;
- }
case GT_LCL_VAR:
{
goto LCL;
case GT_LCL_FLD:
-
- offs += tree->AsLclFld()->gtLclOffs;
+ offs += tree->AsLclFld()->GetLclOffs();
goto LCL;
LCL:
case GT_LCL_FLD:
case GT_STORE_LCL_FLD:
- offs += tree->AsLclFld()->gtLclOffs;
+ offs += tree->AsLclFld()->GetLclOffs();
goto LCL;
LCL:
case GT_LCL_FLD_ADDR:
case GT_LCL_FLD:
- offs += tree->AsLclFld()->gtLclOffs;
+ offs += tree->AsLclFld()->GetLclOffs();
goto LCL;
LCL:
switch (rmOp->OperGet())
{
case GT_LCL_FLD:
- {
- GenTreeLclFld* lclField = rmOp->AsLclFld();
-
- varNum = lclField->GetLclNum();
- offset = lclField->AsLclFld()->gtLclOffs;
+ varNum = rmOp->AsLclFld()->GetLclNum();
+ offset = rmOp->AsLclFld()->GetLclOffs();
break;
- }
case GT_LCL_VAR:
{
}
}
+ if (tree->OperIsLocalAddr())
+ {
+ LclVarDsc* varDsc = lvaGetDesc(tree->AsLclVarCommon());
+ assert(varDsc->lvAddrExposed);
+ varDsc->incRefCnts(weight, this);
+ return;
+ }
+
if ((tree->gtOper != GT_LCL_VAR) && (tree->gtOper != GT_LCL_FLD))
{
return;
/* Change lclVar(lclNum) to lclFld(lclNum,padding) */
tree->ChangeOper(GT_LCL_FLD);
- tree->AsLclFld()->gtLclOffs = padding;
+ tree->AsLclFld()->SetLclOffs(padding);
}
else
{
GenTree* local;
if (thisPtr->isLclField())
{
- local = new (comp, GT_LCL_FLD)
- GenTreeLclFld(GT_LCL_FLD, thisPtr->TypeGet(), lclNum, thisPtr->AsLclFld()->gtLclOffs);
+ local = new (comp, GT_LCL_FLD) GenTreeLclFld(thisPtr->TypeGet(), lclNum, thisPtr->AsLclFld()->GetLclOffs());
}
else
{
fgArgTabEntry* prevArgTabEntry = argTable[prevInx];
assert(prevArgTabEntry->argNum < curArgTabEntry->argNum);
+ // TODO-CQ: We should also allow LCL_VAR_ADDR and LCL_FLD_ADDR here, they're
+ // side effect free leaf nodes that like constant can be evaluated at any point.
if (prevArgTabEntry->GetNode()->gtOper != GT_CNS_INT)
{
prevArgTabEntry->needTmp = true;
// (tmp.ptr=argx),(tmp.type=handle)
GenTreeLclFld* destPtrSlot = gtNewLclFldNode(tmp, TYP_I_IMPL, OFFSETOF__CORINFO_TypedReference__dataPtr);
GenTreeLclFld* destTypeSlot = gtNewLclFldNode(tmp, TYP_I_IMPL, OFFSETOF__CORINFO_TypedReference__type);
- destPtrSlot->gtFieldSeq = GetFieldSeqStore()->CreateSingleton(GetRefanyDataField());
+ destPtrSlot->SetFieldSeq(GetFieldSeqStore()->CreateSingleton(GetRefanyDataField()));
destPtrSlot->gtFlags |= GTF_VAR_DEF;
- destTypeSlot->gtFieldSeq = GetFieldSeqStore()->CreateSingleton(GetRefanyTypeField());
+ destTypeSlot->SetFieldSeq(GetFieldSeqStore()->CreateSingleton(GetRefanyTypeField()));
destTypeSlot->gtFlags |= GTF_VAR_DEF;
GenTree* asgPtrSlot = gtNewAssignNode(destPtrSlot, argx->AsOp()->gtOp1);
assert(varNum < lvaCount);
LclVarDsc* varDsc = &lvaTable[varNum];
- unsigned baseOffset = (argValue->OperGet() == GT_LCL_FLD) ? argValue->AsLclFld()->gtLclOffs : 0;
+ unsigned baseOffset = argValue->OperIs(GT_LCL_FLD) ? argValue->AsLclFld()->GetLclOffs() : 0;
unsigned lastOffset = baseOffset + structSize;
// The allocated size of our LocalVar must be at least as big as lastOffset
{
if (info.compIsVarArgs)
{
- GenTree* newTree =
- fgMorphStackArgForVarArgs(tree->AsLclFld()->GetLclNum(), tree->gtType, tree->AsLclFld()->gtLclOffs);
+ GenTree* newTree = fgMorphStackArgForVarArgs(tree->AsLclFld()->GetLclNum(), tree->TypeGet(),
+ tree->AsLclFld()->GetLclOffs());
if (newTree != nullptr)
{
if (newTree->OperIsBlk() && ((tree->gtFlags & GTF_VAR_DEF) == 0))
assert(dest->gtOper == GT_LCL_FLD);
blockWidth = genTypeSize(dest->TypeGet());
destAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, dest);
- destFldSeq = dest->AsLclFld()->gtFieldSeq;
+ destFldSeq = dest->AsLclFld()->GetFieldSeq();
}
}
else
srcLclNum = srcLclVarTree->GetLclNum();
if (rhs->OperGet() == GT_LCL_FLD)
{
- srcFldSeq = rhs->AsLclFld()->gtFieldSeq;
+ srcFldSeq = rhs->AsLclFld()->GetFieldSeq();
}
}
else if (rhs->OperIsIndir())
{
srcLclVarTree->gtFlags |= GTF_VAR_CAST;
srcLclVarTree->ChangeOper(GT_LCL_FLD);
- srcLclVarTree->gtType = destType;
- srcLclVarTree->AsLclFld()->gtFieldSeq = curFieldSeq;
- src = srcLclVarTree;
- done = true;
+ srcLclVarTree->gtType = destType;
+ srcLclVarTree->AsLclFld()->SetFieldSeq(curFieldSeq);
+ src = srcLclVarTree;
+ done = true;
}
}
}
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->AsLclFld()->SetFieldSeq(
+ GetFieldSeqStore()->Append(temp->AsLclFld()->GetFieldSeq(), fieldSeq));
temp->gtType = typ;
foldAndReturnTemp = true;
if (temp->OperGet() == GT_LCL_FLD)
{
lclFld = temp->AsLclFld();
- lclFld->gtLclOffs += (unsigned short)ival1;
- lclFld->gtFieldSeq = GetFieldSeqStore()->Append(lclFld->gtFieldSeq, fieldSeq);
+ lclFld->SetLclOffs(lclFld->GetLclOffs() + static_cast<unsigned>(ival1));
+ lclFld->SetFieldSeq(GetFieldSeqStore()->Append(lclFld->GetFieldSeq(), fieldSeq));
}
else // we have a GT_LCL_VAR
{
assert(temp->OperGet() == GT_LCL_VAR);
temp->ChangeOper(GT_LCL_FLD); // Note that this typically makes the gtFieldSeq "NotAField",
// unless there is a zero filed offset associated with 'temp'.
- lclFld = temp->AsLclFld();
- lclFld->gtLclOffs = (unsigned short)ival1;
+ lclFld = temp->AsLclFld();
+ lclFld->SetLclOffs(static_cast<unsigned>(ival1));
- if (lclFld->gtFieldSeq == FieldSeqStore::NotAField())
+ if (lclFld->GetFieldSeq() == FieldSeqStore::NotAField())
{
if (fieldSeq != nullptr)
{
// If it does represent a field, note that.
- lclFld->gtFieldSeq = fieldSeq;
+ lclFld->SetFieldSeq(fieldSeq);
}
}
else
{
// Append 'fieldSeq' to the existing one
- lclFld->gtFieldSeq = GetFieldSeqStore()->Append(lclFld->gtFieldSeq, fieldSeq);
+ lclFld->SetFieldSeq(GetFieldSeqStore()->Append(lclFld->GetFieldSeq(), fieldSeq));
}
}
temp->gtType = tree->gtType;
if (varTypeIsStruct(varDsc) && (varDsc->lvPromoted))
{
// Promoted struct
- unsigned fldOffset = tree->AsLclFld()->gtLclOffs;
+ unsigned fldOffset = tree->AsLclFld()->GetLclOffs();
unsigned fieldLclIndex = 0;
LclVarDsc* fldVarDsc = nullptr;
//
class Value
{
- GenTree* m_node;
- unsigned m_lclNum;
- unsigned m_offset;
- bool m_address;
+ GenTree* m_node;
+ FieldSeqNode* m_fieldSeq;
+ unsigned m_lclNum;
+ unsigned m_offset;
+ bool m_address;
INDEBUG(bool m_consumed;)
public:
// Produce an unknown value associated with the specified node.
Value(GenTree* node)
: m_node(node)
+ , m_fieldSeq(nullptr)
, m_lclNum(BAD_VAR_NUM)
, m_offset(0)
, m_address(false)
return m_offset;
}
+ // Get the location's field sequence.
+ FieldSeqNode* FieldSeq() const
+ {
+ return m_fieldSeq;
+ }
+
+ //------------------------------------------------------------------------
+ // Location: Produce a location value.
+ //
+ // Arguments:
+ // lclVar - a GT_LCL_VAR node that defines the location
+ //
+ // Notes:
+ // - (lclnum) => LOCATION(lclNum, 0)
+ //
+ void Location(GenTreeLclVar* lclVar)
+ {
+ assert(lclVar->OperIs(GT_LCL_VAR));
+ assert(!IsLocation() && !IsAddress());
+
+ m_lclNum = lclVar->GetLclNum();
+
+ assert(m_offset == 0);
+ assert(m_fieldSeq == nullptr);
+ }
+
+ //------------------------------------------------------------------------
+ // Location: Produce an address value from a GT_LCL_VAR_ADDR node.
+ //
+ // Arguments:
+ // lclVar - a GT_LCL_VAR_ADDR node that defines the address
+ //
+ // Notes:
+ // - (lclnum) => ADDRESS(lclNum, 0)
+ //
+ void Address(GenTreeLclVar* lclVar)
+ {
+ assert(lclVar->OperIs(GT_LCL_VAR_ADDR));
+ assert(!IsLocation() && !IsAddress());
+
+ m_lclNum = lclVar->GetLclNum();
+ m_address = true;
+
+ assert(m_offset == 0);
+ assert(m_fieldSeq == nullptr);
+ }
+
//------------------------------------------------------------------------
// Location: Produce a location value.
//
// Arguments:
- // lclNum - the local variable number
- // offset - the byte offset of the location (used for GT_LCL_FLD nodes)
+ // lclFld - a GT_LCL_FLD node that defines the location
//
// Notes:
- // - (lclnum, offset) => LOCATION(lclNum, offset)
+ // - (lclnum, lclOffs) => LOCATION(lclNum, offset)
//
- void Location(unsigned lclNum, unsigned offset = 0)
+ void Location(GenTreeLclFld* lclFld)
{
+ assert(lclFld->OperIs(GT_LCL_FLD));
assert(!IsLocation() && !IsAddress());
- m_lclNum = lclNum;
- m_offset = offset;
+ m_lclNum = lclFld->GetLclNum();
+ m_offset = lclFld->GetLclOffs();
+ m_fieldSeq = lclFld->GetFieldSeq();
+ }
+
+ //------------------------------------------------------------------------
+ // Address: Produce an address value from a LCL_FLD_ADDR node.
+ //
+ // Arguments:
+ // lclFld - a GT_LCL_FLD_ADDR node that defines the address
+ //
+ // Notes:
+ // - (lclnum, lclOffs) => ADDRESS(lclNum, offset)
+ //
+ void Address(GenTreeLclFld* lclFld)
+ {
+ assert(lclFld->OperIs(GT_LCL_FLD_ADDR));
+ assert(!IsLocation() && !IsAddress());
+
+ m_lclNum = lclFld->GetLclNum();
+ m_offset = lclFld->GetLclOffs();
+ m_fieldSeq = lclFld->GetFieldSeq();
+ m_address = true;
}
//------------------------------------------------------------------------
if (val.IsLocation())
{
- m_address = true;
- m_lclNum = val.m_lclNum;
- m_offset = val.m_offset;
+ m_address = true;
+ m_lclNum = val.m_lclNum;
+ m_offset = val.m_offset;
+ m_fieldSeq = val.m_fieldSeq;
}
INDEBUG(val.Consume();)
// Field: Produce a location value from an address value.
//
// Arguments:
- // val - the input value
- // offset - the offset to add to the existing location offset
+ // val - the input value
+ // field - the FIELD node that uses the input address value
+ // fieldSeqStore - the compiler's field sequence store
//
// Return Value:
// `true` if the value was consumed. `false` if the input value
// if the offset overflows then location is not representable, must escape
// - UNKNOWN => UNKNOWN
//
- bool Field(Value& val, unsigned offset)
+ bool Field(Value& val, GenTreeField* field, FieldSeqStore* fieldSeqStore)
{
assert(!IsLocation() && !IsAddress());
if (val.IsAddress())
{
- ClrSafeInt<unsigned> newOffset = ClrSafeInt<unsigned>(val.m_offset) + ClrSafeInt<unsigned>(offset);
+ ClrSafeInt<unsigned> newOffset =
+ ClrSafeInt<unsigned>(val.m_offset) + ClrSafeInt<unsigned>(field->gtFldOffset);
if (newOffset.IsOverflow())
{
m_lclNum = val.m_lclNum;
m_offset = newOffset.Value();
+
+ if (field->gtFldMayOverlap)
+ {
+ m_fieldSeq = FieldSeqStore::NotAField();
+ }
+ else
+ {
+ m_fieldSeq = fieldSeqStore->Append(val.m_fieldSeq, fieldSeqStore->CreateSingleton(field->gtFldHnd));
+ }
}
INDEBUG(val.Consume();)
if (val.IsAddress())
{
- m_lclNum = val.m_lclNum;
- m_offset = val.m_offset;
+ m_lclNum = val.m_lclNum;
+ m_offset = val.m_offset;
+ m_fieldSeq = val.m_fieldSeq;
}
INDEBUG(val.Consume();)
case GT_LCL_VAR:
assert(TopValue(0).Node() == node);
- TopValue(0).Location(node->AsLclVar()->GetLclNum());
+ TopValue(0).Location(node->AsLclVar());
+ break;
+
+ case GT_LCL_VAR_ADDR:
+ assert(TopValue(0).Node() == node);
+
+ TopValue(0).Address(node->AsLclVar());
break;
case GT_LCL_FLD:
assert(TopValue(0).Node() == node);
- TopValue(0).Location(node->AsLclFld()->GetLclNum(), node->AsLclFld()->gtLclOffs);
+ TopValue(0).Location(node->AsLclFld());
+ break;
+
+ case GT_LCL_FLD_ADDR:
+ assert(TopValue(0).Node() == node);
+
+ TopValue(0).Address(node->AsLclFld());
break;
case GT_ADDR:
assert(TopValue(1).Node() == node);
assert(TopValue(0).Node() == node->AsField()->gtFldObj);
- if (!TopValue(1).Field(TopValue(0), node->AsField()->gtFldOffset))
+ if (!TopValue(1).Field(TopValue(0), node->AsField(), m_compiler->GetFieldSeqStore()))
{
// Either the address comes from a location value (e.g. FIELD(IND(...)))
// or the field offset has overflowed.
}
#endif // _TARGET_64BIT_
+ // TODO-ADDR: For now use LCL_VAR_ADDR and LCL_FLD_ADDR only as call arguments and assignment sources.
+ // Other usages require more changes. For example, a tree like OBJ(ADD(ADDR(LCL_VAR), 4))
+ // could be changed to OBJ(LCL_FLD_ADDR) but then DefinesLocalAddr does not recognize
+ // LCL_FLD_ADDR (even though it does recognize LCL_VAR_ADDR).
+ if (user->OperIs(GT_CALL, GT_ASG))
+ {
+ MorphLocalAddress(val);
+ }
+
INDEBUG(val.Consume();)
}
}
//------------------------------------------------------------------------
+ // MorphLocalAddress: Change a tree that represents a local variable address
+ // to a single LCL_VAR_ADDR or LCL_FLD_ADDR node.
+ //
+ // Arguments:
+ // val - a value that represents the local address
+ //
+ void MorphLocalAddress(const Value& val)
+ {
+ assert(val.IsAddress());
+ assert(val.Node()->TypeIs(TYP_BYREF, TYP_I_IMPL));
+ assert(m_compiler->lvaVarAddrExposed(val.LclNum()));
+
+ LclVarDsc* varDsc = m_compiler->lvaGetDesc(val.LclNum());
+
+ if (varDsc->lvPromoted || varDsc->lvIsStructField || m_compiler->lvaIsImplicitByRefLocal(val.LclNum()))
+ {
+ // TODO-ADDR: For now we ignore promoted and "implict by ref" variables,
+ // they require additional changes in subsequent phases.
+ return;
+ }
+
+ GenTree* addr = val.Node();
+
+ if (val.Offset() > UINT16_MAX)
+ {
+ // The offset is too large to store in a LCL_FLD_ADDR node,
+ // use ADD(LCL_VAR_ADDR, offset) instead.
+ addr->ChangeOper(GT_ADD);
+ addr->AsOp()->gtOp1 = m_compiler->gtNewLclVarAddrNode(val.LclNum());
+ addr->AsOp()->gtOp2 = m_compiler->gtNewIconNode(val.Offset(), val.FieldSeq());
+ }
+ else if ((val.Offset() != 0) || (val.FieldSeq() != nullptr))
+ {
+ addr->ChangeOper(GT_LCL_FLD_ADDR);
+ addr->AsLclFld()->SetLclNum(val.LclNum());
+ addr->AsLclFld()->SetLclOffs(val.Offset());
+ addr->AsLclFld()->SetFieldSeq(val.FieldSeq());
+ }
+ else
+ {
+ addr->ChangeOper(GT_LCL_VAR_ADDR);
+ addr->AsLclVar()->SetLclNum(val.LclNum());
+ }
+
+ // Local address nodes never have side effects (nor any other flags, at least at this point).
+ addr->gtFlags = 0;
+
+ INDEBUG(m_stmtModified = true;)
+ }
+
+ //------------------------------------------------------------------------
// MorphStructField: Replaces a GT_FIELD based promoted/normed struct field access
// (e.g. FIELD(ADDR(LCL_VAR))) with a GT_LCL_VAR that references the struct field.
//
case GT_LCL_FLD:
{
GenTreeLclFld* lclFld = addr->AsLclFld();
- fieldSeqUpdate = GetFieldSeqStore()->Append(lclFld->gtFieldSeq, fieldSeqZero);
- lclFld->gtFieldSeq = fieldSeqUpdate;
- fieldSeqRecorded = true;
+ fieldSeqUpdate = GetFieldSeqStore()->Append(lclFld->GetFieldSeq(), fieldSeqZero);
+ lclFld->SetFieldSeq(fieldSeqUpdate);
+ fieldSeqRecorded = true;
break;
}
fieldSeqNode = addr->AsOp()->gtOp1;
GenTreeLclFld* lclFld = addr->AsOp()->gtOp1->AsLclFld();
- fieldSeqUpdate = GetFieldSeqStore()->Append(lclFld->gtFieldSeq, fieldSeqZero);
- lclFld->gtFieldSeq = fieldSeqUpdate;
- fieldSeqRecorded = true;
+ fieldSeqUpdate = GetFieldSeqStore()->Append(lclFld->GetFieldSeq(), fieldSeqZero);
+ lclFld->SetFieldSeq(fieldSeqUpdate);
+ fieldSeqRecorded = true;
}
break;
else
{
addr->SetOper(GT_LCL_FLD);
- addr->AsLclFld()->gtLclOffs = 0;
- addr->AsLclFld()->gtFieldSeq = FieldSeqStore::NotAField();
+ addr->AsLclFld()->SetLclOffs(0);
+ addr->AsLclFld()->SetFieldSeq(FieldSeqStore::NotAField());
if (((addr->gtFlags & GTF_VAR_DEF) != 0) && (genTypeSize(simdType) < genTypeSize(lclType)))
{
if (locationOp == GT_LCL_FLD)
{
- store->AsLclFld()->gtLclOffs = var->AsLclFld()->gtLclOffs;
- store->AsLclFld()->gtFieldSeq = var->AsLclFld()->gtFieldSeq;
+ store->AsLclFld()->SetLclOffs(var->AsLclFld()->GetLclOffs());
+ store->AsLclFld()->SetFieldSeq(var->AsLclFld()->GetFieldSeq());
}
copyFlags(store, var, GTF_LIVENESS_MASK);
}
else if (op1->OperIsLocalAddr())
{
- unsigned offset = (op1->OperGet() == GT_LCL_FLD_ADDR) ? op1->AsLclFld()->gtLclOffs : 0;
+ unsigned offset = op1->OperIs(GT_LCL_FLD_ADDR) ? op1->AsLclFld()->GetLclOffs() : 0;
GetEmitter()->emitIns_R_S(ins, emitTypeSize(targetType), targetReg, op1->AsLclVarCommon()->GetLclNum(),
offset);
}
offset += compiler->lvaFrameAddress(varNum, &isEBPbased);
if (op1->OperGet() == GT_LCL_FLD)
{
- offset += op1->AsLclFld()->gtLclOffs;
+ offset += op1->AsLclFld()->GetLclOffs();
}
baseReg = (isEBPbased) ? REG_EBP : REG_ESP;
}
if (treeNode->OperGet() == GT_STORE_LCL_FLD)
{
- offs = treeNode->AsLclFld()->gtLclOffs;
+ offs = treeNode->AsLclFld()->GetLclOffs();
}
GenTree* op1 = treeNode->AsOp()->gtOp1;
if (treeNode->OperGet() == GT_LCL_FLD)
{
- offs = treeNode->AsLclFld()->gtLclOffs;
+ offs = treeNode->AsLclFld()->GetLclOffs();
}
// Need an additional Xmm register that is different from targetReg to read upper 4 bytes.
{
switch (oper)
{
+ case GT_LCL_VAR_ADDR:
+ case GT_LCL_FLD_ADDR:
+ assert(lvaVarAddrExposed(tree->AsLclVarCommon()->GetLclNum()));
+ tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
+ break;
+
case GT_LCL_VAR:
{
GenTreeLclVarCommon* lcl = tree->AsLclVarCommon();
case GT_LCL_FLD:
{
GenTreeLclFld* lclFld = tree->AsLclFld();
- assert(!lvaInSsa(lclFld->GetLclNum()) || lclFld->gtFieldSeq != nullptr);
+ assert(!lvaInSsa(lclFld->GetLclNum()) || (lclFld->GetFieldSeq() != nullptr));
// If this is a (full) def, then the variable will be labeled with the new SSA number,
// which will not have a value. We skip; it will be handled by one of the assignment-like
// forms (assignment, or initBlk or copyBlk).
LclVarDsc* varDsc = &lvaTable[lclNum];
var_types indType = tree->TypeGet();
- if (lclFld->gtFieldSeq == FieldSeqStore::NotAField() || !lvaInSsa(lclFld->GetLclNum()))
+ if ((lclFld->GetFieldSeq() == FieldSeqStore::NotAField()) || !lvaInSsa(lclFld->GetLclNum()))
{
// This doesn't represent a proper field access or it's a struct
// with overlapping fields that is hard to reason about; return a new unique VN.
else
{
ValueNumPair lclVNPair = varDsc->GetPerSsaData(ssaNum)->m_vnPair;
- tree->gtVNPair = vnStore->VNPairApplySelectors(lclVNPair, lclFld->gtFieldSeq, indType);
+ tree->gtVNPair = vnStore->VNPairApplySelectors(lclVNPair, lclFld->GetFieldSeq(), indType);
}
}
}
else
{
// We should never have a null field sequence here.
- assert(lclFld->gtFieldSeq != nullptr);
- if (lclFld->gtFieldSeq == FieldSeqStore::NotAField())
+ assert(lclFld->GetFieldSeq() != nullptr);
+ if (lclFld->GetFieldSeq() == FieldSeqStore::NotAField())
{
// We don't know what field this represents. Assign a new VN to the whole variable
// (since we may be writing to an unknown portion of it.)
// (we looked in a side table above for its "def" identity). Look up that value.
ValueNumPair oldLhsVNPair =
lvaTable[lclFld->GetLclNum()].GetPerSsaData(lclFld->GetSsaNum())->m_vnPair;
- newLhsVNPair = vnStore->VNPairApplySelectorsAssign(oldLhsVNPair, lclFld->gtFieldSeq,
+ newLhsVNPair = vnStore->VNPairApplySelectorsAssign(oldLhsVNPair, lclFld->GetFieldSeq(),
rhsVNPair, // Pre-value.
lclFld->TypeGet(), compCurBB);
}
}
else if (arg->OperGet() == GT_LCL_FLD)
{
- fieldSeq = arg->AsLclFld()->gtFieldSeq;
+ fieldSeq = arg->AsLclFld()->GetFieldSeq();
if (fieldSeq == nullptr)
{
// Local field with unknown field seq -- not a precise pointer.