#if OPT_BOOL_OPS
unsigned char lvIsBoolean : 1; // set if variable is boolean
#endif
+ unsigned char lvSingleDef : 1; // variable has a single def
+ // before lvaMarkLocalVars: identifies ref type locals that can get type updates
+ // after lvaMarkLocalVars: identifies locals that are suitable for optAddCopies
+
#if ASSERTION_PROP
- unsigned char lvSingleDef : 1; // variable has a single def
unsigned char lvDisqualify : 1; // variable is no longer OK for add copy optimization
unsigned char lvVolatileHint : 1; // hint for AssertionProp
#endif
{
fgAdjustForAddressExposedOrWrittenThis();
}
+
+ // Now that we've seen the IL, set lvSingleDef for root method
+ // locals.
+ //
+ // We could also do this for root method arguments but single-def
+ // arguments are set by the caller and so we don't know anything
+ // about the possible values or types.
+ //
+ // For inlinees we do this over in impInlineFetchLocal and
+ // impInlineFetchArg (here args are included as we somtimes get
+ // new information about the types of inlinee args).
+ if (!isInlining)
+ {
+ const unsigned firstLcl = info.compArgsCount;
+ const unsigned lastLcl = firstLcl + info.compMethodInfo->locals.numArgs;
+ for (unsigned lclNum = firstLcl; lclNum < lastLcl; lclNum++)
+ {
+ LclVarDsc* lclDsc = lvaGetDesc(lclNum);
+ assert(lclDsc->lvSingleDef == 0);
+ // could restrict this to TYP_REF
+ lclDsc->lvSingleDef = !lclDsc->lvHasMultipleILStoreOp && !lclDsc->lvHasLdAddrOp;
+
+ if (lclDsc->lvSingleDef)
+ {
+ JITDUMP("Marked V%02u as a single def local\n", lclNum);
+ }
+ }
+ }
}
#ifdef _PREFAST_
// out we can prove the method returns a more specific type.
if (info.compRetType == TYP_REF)
{
+ lvaTable[lvaInlineeReturnSpillTemp].lvSingleDef = 1;
+ JITDUMP("Marked V%02u as a single def temp\n", lvaInlineeReturnSpillTemp);
+
CORINFO_CLASS_HANDLE retClassHnd = impInlineInfo->inlineCandidateInfo->methInfo.args.retTypeClass;
if (retClassHnd != nullptr)
{
comp->impDevirtualizeCall(call, &method, &methodFlags, &context, nullptr);
}
}
+ else if (tree->OperGet() == GT_ASG)
+ {
+ // If we're assigning to a ref typed local that has one definition,
+ // we may be able to sharpen the type for the local.
+ GenTree* lhs = tree->gtGetOp1()->gtEffectiveVal();
+
+ if ((lhs->OperGet() == GT_LCL_VAR) && (lhs->TypeGet() == TYP_REF))
+ {
+ const unsigned lclNum = lhs->gtLclVarCommon.gtLclNum;
+ LclVarDsc* lcl = comp->lvaGetDesc(lclNum);
+
+ if (lcl->lvSingleDef)
+ {
+ GenTree* rhs = tree->gtGetOp2();
+ bool isExact = false;
+ bool isNonNull = false;
+ CORINFO_CLASS_HANDLE newClass = comp->gtGetClassHandle(rhs, &isExact, &isNonNull);
+
+ if (newClass != NO_CLASS_HANDLE)
+ {
+ comp->lvaUpdateClass(lclNum, newClass, isExact);
+ }
+ }
+ }
+ }
return WALK_CONTINUE;
}
// If temp is newly introduced and a ref type, grab what type info we can.
if (isNewTemp && (lvaTable[tnum].lvType == TYP_REF))
{
+ assert(lvaTable[tnum].lvSingleDef == 0);
+ lvaTable[tnum].lvSingleDef = 1;
+ JITDUMP("Marked V%02u as a single def temp\n", tnum);
CORINFO_CLASS_HANDLE stkHnd = verCurrentState.esStack[level].seTypeInfo.GetClassHandle();
lvaSetClass(tnum, tree, stkHnd);
{
// When optimizing, use a new temp for each box operation
// since we then know the exact class of the box temp.
- impBoxTemp = lvaGrabTemp(true DEBUGARG("Single-def Box Helper"));
- lvaTable[impBoxTemp].lvType = TYP_REF;
- const bool isExact = true;
+ impBoxTemp = lvaGrabTemp(true DEBUGARG("Single-def Box Helper"));
+ lvaTable[impBoxTemp].lvType = TYP_REF;
+ lvaTable[impBoxTemp].lvSingleDef = 1;
+ JITDUMP("Marking V%02u as a single def local\n", impBoxTemp);
+ const bool isExact = true;
lvaSetClass(impBoxTemp, pResolvedToken->hClass, isExact);
}
//
// See also gtGetHelperCallClassHandle where we make the same
// determination for the helper call variants.
+ LclVarDsc* lclDsc = lvaGetDesc(tmp);
+ assert(lclDsc->lvSingleDef == 0);
+ lclDsc->lvSingleDef = 1;
+ JITDUMP("Marked V%02u as a single def temp\n", tmp);
lvaSetClass(tmp, pResolvedToken->hClass);
return gtNewLclvNode(tmp, TYP_REF);
#endif
// We should have seen a stloc in our IL prescan.
assert(lvaTable[lclNum].lvHasILStoreOp);
- const bool isSingleILStoreLocal =
- !lvaTable[lclNum].lvHasMultipleILStoreOp && !lvaTable[lclNum].lvHasLdAddrOp;
+ // Is there just one place this local is defined?
+ const bool isSingleDefLocal = lvaTable[lclNum].lvSingleDef;
// Conservative check that there is just one
// definition that reaches this store.
const bool hasSingleReachingDef = (block->bbStackDepthOnEntry() == 0);
- if (isSingleILStoreLocal && hasSingleReachingDef)
+ if (isSingleDefLocal && hasSingleReachingDef)
{
lvaUpdateClass(lclNum, op1, clsHnd);
}
// Propagate type info to the temp from the stack and the original tree
if (type == TYP_REF)
{
+ assert(lvaTable[tmpNum].lvSingleDef == 0);
+ lvaTable[tmpNum].lvSingleDef = 1;
+ JITDUMP("Marked V%02u as a single def local\n", tmpNum);
lvaSetClass(tmpNum, tree, tiRetVal.GetClassHandle());
}
}
// without exhaustive walk over all expressions.
impAssignTempGen(lclNum, op1, (unsigned)CHECK_SPILL_NONE);
+
+ assert(lvaTable[lclNum].lvSingleDef == 0);
+ lvaTable[lclNum].lvSingleDef = 1;
+ JITDUMP("Marked V%02u as a single def local\n", lclNum);
lvaSetClass(lclNum, resolvedToken.hClass, true /* is Exact */);
newObjThisPtr = gtNewLclvNode(lclNum, TYP_REF);
// signature and pass in a more precise type.
if (lclTyp == TYP_REF)
{
+ assert(lvaTable[tmpNum].lvSingleDef == 0);
+
+ lvaTable[tmpNum].lvSingleDef = !inlineeLocal.lclHasMultipleStlocOp && !inlineeLocal.lclHasLdlocaOp;
+ if (lvaTable[tmpNum].lvSingleDef)
+ {
+ JITDUMP("Marked V%02u as a single def temp\n", tmpNum);
+ }
+
lvaSetClass(tmpNum, inlineeLocal.lclVerTypeInfo.GetClassHandleForObjRef());
}
// If the arg can't be modified in the method
// body, use the type of the value, if
// known. Otherwise, use the declared type.
+ assert(lvaTable[tmpNum].lvSingleDef == 0);
+ lvaTable[tmpNum].lvSingleDef = 1;
+ JITDUMP("Marked V%02u as a single def temp\n", tmpNum);
lvaSetClass(tmpNum, argInfo.argNode, lclInfo.lclVerTypeInfo.GetClassHandleForObjRef());
}
else
if (!info.compIsStatic)
{
varDsc->lvIsParam = 1;
-#if ASSERTION_PROP
- varDsc->lvSingleDef = 1;
-#endif
-
- varDsc->lvIsPtr = 1;
+ varDsc->lvIsPtr = 1;
lvaArg0Var = info.compThisArg = varDscInfo->varNum;
noway_assert(info.compThisArg == 0);
varDsc->lvType = TYP_BYREF;
varDsc->lvIsParam = 1;
varDsc->lvIsRegArg = 1;
-#if ASSERTION_PROP
- varDsc->lvSingleDef = 1;
-#endif
+
if (hasFixedRetBuffReg())
{
varDsc->lvArgReg = theFixedRetBuffReg();
CorInfoTypeWithMod corInfoType = info.compCompHnd->getArgType(&info.compMethodInfo->args, argLst, &typeHnd);
varDsc->lvIsParam = 1;
-#if ASSERTION_PROP
- varDsc->lvSingleDef = 1;
-#endif
lvaInitVarDsc(varDsc, varDscInfo->varNum, strip(corInfoType), typeHnd, argLst, &info.compMethodInfo->args);
LclVarDsc* varDsc = varDscInfo->varDsc;
varDsc->lvIsParam = 1;
-#if ASSERTION_PROP
- varDsc->lvSingleDef = 1;
-#endif
-
- varDsc->lvType = TYP_I_IMPL;
+ varDsc->lvType = TYP_I_IMPL;
if (varDscInfo->canEnreg(TYP_I_IMPL))
{
// that other problems are fixed.
lvaSetVarAddrExposed(varDscInfo->varNum);
-#if ASSERTION_PROP
- varDsc->lvSingleDef = 1;
-#endif
-
if (varDscInfo->canEnreg(TYP_I_IMPL))
{
/* Another register argument */
// We should already have a class
assert(varDsc->lvClassHnd != nullptr);
-#if defined(DEBUG)
+ // We should only be updating classes for single-def locals.
+ assert(varDsc->lvSingleDef);
- // In general we only expect one update per local var. However if
- // a block is re-imported and that block has the only STLOC for
- // the var, we may see multiple updates. All subsequent updates
- // should agree on the type, since reimportation is triggered by
- // type mismatches for things other than ref types.
- if (varDsc->lvClassInfoUpdated)
- {
- assert(varDsc->lvClassHnd == clsHnd);
- assert(varDsc->lvClassIsExact == isExact);
- }
+ // Now see if we should update.
+ //
+ // New information may not always be "better" so do some
+ // simple analysis to decide if the update is worthwhile.
+ const bool isNewClass = (clsHnd != varDsc->lvClassHnd);
+ bool shouldUpdate = false;
- // This counts as an update, even if nothing changes.
- varDsc->lvClassInfoUpdated = true;
+ // Are we attempting to update the class? Only check this when we have
+ // an new type and the existing class is inexact... we should not be
+ // updating exact classes.
+ if (!varDsc->lvClassIsExact && isNewClass)
+ {
+ // Todo: improve this analysis by adding a new jit interface method
+ DWORD newAttrs = info.compCompHnd->getClassAttribs(clsHnd);
+ DWORD oldAttrs = info.compCompHnd->getClassAttribs(varDsc->lvClassHnd);
-#endif // defined(DEBUG)
+ // Avoid funny things with __Canon by only merging if both shared or both unshared
+ if ((newAttrs & CORINFO_FLG_SHAREDINST) == (oldAttrs & CORINFO_FLG_SHAREDINST))
+ {
+ // If we merge types and we get back the old class, the new class is more
+ // specific and we should update to it.
+ CORINFO_CLASS_HANDLE mergeClass = info.compCompHnd->mergeClasses(clsHnd, varDsc->lvClassHnd);
- // If previous type was exact, there is nothing to update. Would
- // like to verify new type is compatible but can't do this yet.
- if (varDsc->lvClassIsExact)
- {
- return;
+ if (mergeClass == varDsc->lvClassHnd)
+ {
+ shouldUpdate = true;
+ }
+ }
+ else if ((newAttrs & CORINFO_FLG_SHAREDINST) == 0)
+ {
+ // Update if we go from shared to unshared
+ shouldUpdate = true;
+ }
}
-
- // Are we updating the type?
- if (varDsc->lvClassHnd != clsHnd)
+ // Else are we attempting to update exactness?
+ else if (isExact && !varDsc->lvClassIsExact && !isNewClass)
{
- JITDUMP("\nlvaUpdateClass: Updating class for V%02i from (%p) %s to (%p) %s %s\n", varNum,
- dspPtr(varDsc->lvClassHnd), info.compCompHnd->getClassName(varDsc->lvClassHnd), dspPtr(clsHnd),
- info.compCompHnd->getClassName(clsHnd), isExact ? " [exact]" : "");
-
- varDsc->lvClassHnd = clsHnd;
- varDsc->lvClassIsExact = isExact;
- return;
+ shouldUpdate = true;
}
- // Class info matched. Are we updating exactness?
- if (isExact)
- {
- JITDUMP("\nlvaUpdateClass: Updating class for V%02i (%p) %s to be exact\n", varNum, dspPtr(varDsc->lvClassHnd),
- info.compCompHnd->getClassName(varDsc->lvClassHnd));
+ JITDUMP("\nlvaUpdateClass:%s Updating class for V%02u from (%p) %s%s to (%p) %s%s\n", varNum,
+ shouldUpdate ? "" : " NOT", dspPtr(varDsc->lvClassHnd), info.compCompHnd->getClassName(varDsc->lvClassHnd),
+ varDsc->lvClassIsExact ? " [exact]" : "", dspPtr(clsHnd), info.compCompHnd->getClassName(clsHnd),
+ isExact ? " [exact]" : "");
+ if (shouldUpdate)
+ {
+ varDsc->lvClassHnd = clsHnd;
varDsc->lvClassIsExact = isExact;
- return;
}
- // Else we have the same handle and (in)exactness as before. Do nothing.
return;
}
{
varDsc->lvSlotNum = lclNum;
}
+
+ // Set initial value for lvSingleDef for explicit and implicit
+ // argument locals as they are "defined" on entry.
+ varDsc->lvSingleDef = varDsc->lvIsParam;
}
JITDUMP("\n*** lvaComputeRefCounts -- explicit counts ***\n");