diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index e593b92c6b7da6..d070b815f4f29d 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -735,7 +735,8 @@ class FgStack SLOT_UNKNOWN = 0, SLOT_CONSTANT = 1, SLOT_ARRAYLEN = 2, - SLOT_ARGUMENT = 3 + SLOT_EXACTCLS = 3, + SLOT_ARGUMENT = 4, }; void Clear() @@ -754,6 +755,10 @@ class FgStack { Push(SLOT_ARRAYLEN); } + void PushExactClass() + { + Push(SLOT_EXACTCLS); + } void PushArgument(unsigned arg) { Push((FgSlot)(SLOT_ARGUMENT + arg)); @@ -790,6 +795,10 @@ class FgStack { return value == SLOT_ARRAYLEN; } + static bool IsExactClass(FgSlot value) + { + return value == SLOT_EXACTCLS; + } static bool IsArgument(FgSlot value) { return value >= SLOT_ARGUMENT; @@ -891,6 +900,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed int prefixFlags = 0; bool preciseScan = makeInlineObservations && compInlineResult->GetPolicy()->RequiresPreciseScan(); const bool resolveTokens = preciseScan; + bool exactSigRet = true; // Track offsets where IL instructions begin in DEBUG builds. Used to // validate debug info generated by the JIT. @@ -939,6 +949,16 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed compInlineResult->Note(InlineObservation::CALLEE_BEGIN_OPCODE_SCAN); } + if (preciseScan) + { + CORINFO_SIG_INFO sig; + eeGetMethodSig(info.compMethodHnd, &sig); + if (sig.retType == CORINFO_TYPE_CLASS) + { + exactSigRet = info.compCompHnd->isExactType(sig.retTypeClass); + } + } + CORINFO_RESOLVED_TOKEN resolvedToken; OPCODE opcode = CEE_NOP; @@ -1053,6 +1073,13 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed } } } + + if (preciseScan && FgStack::IsExactClass(pushedStack.Top())) + { + // Box a struct results in an exact class + handled = true; + } + break; } @@ -1112,12 +1139,14 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed CORINFO_METHOD_HANDLE methodHnd = nullptr; bool isIntrinsic = false; NamedIntrinsic ni = NI_Illegal; + CORINFO_SIG_INFO methodSig = {}; if (resolveTokens) { impResolveToken(codeAddr, &resolvedToken, CORINFO_TOKENKIND_Method); methodHnd = resolvedToken.hMethod; isIntrinsic = eeIsIntrinsic(methodHnd); + eeGetMethodSig(methodHnd, &methodSig); } if (isIntrinsic) @@ -1577,6 +1606,16 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed } } } + else if (resolveTokens && !exactSigRet && methodSig.retType != CORINFO_TYPE_VOID) + { + if (methodSig.retType != CORINFO_TYPE_CLASS || + info.compCompHnd->isExactType(methodSig.retTypeClass)) + { + // The method returns an object of an exact class. + pushedStack.PushExactClass(); + handled = true; + } + } if ((codeAddr < codeEndp - sz) && (OPCODE)getU1LittleEndian(codeAddr + sz) == CEE_RET) { @@ -1594,6 +1633,19 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed } break; + case CEE_NEWARR: + case CEE_NEWOBJ: + case CEE_INITOBJ: + { + if (preciseScan) + { + // A newobj/newarr/initobj always returns an object of an exact class. + pushedStack.PushExactClass(); + handled = true; + } + break; + } + case CEE_LDIND_I1: case CEE_LDIND_U1: case CEE_LDIND_I2: @@ -2408,6 +2460,10 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed break; case CEE_RET: + if (makeInlineObservations && !exactSigRet && FgStack::IsExactClass(pushedStack.Top())) + { + compInlineResult->Note(InlineObservation::CALLEE_MORE_DERIVED_RETURNS); + } retBlocks++; break; diff --git a/src/coreclr/jit/inline.def b/src/coreclr/jit/inline.def index 2b045ad5d20009..0e5c102b0f2398 100644 --- a/src/coreclr/jit/inline.def +++ b/src/coreclr/jit/inline.def @@ -108,6 +108,7 @@ INLINE_OBSERVATION(NUMBER_OF_BASIC_BLOCKS, int, "number of basic blocks", INLINE_OBSERVATION(NUMBER_OF_LOCALS, int, "number of locals", INFORMATION, CALLEE) INLINE_OBSERVATION(RANDOM_ACCEPT, bool, "random accept", INFORMATION, CALLEE) INLINE_OBSERVATION(UNBOX_ARG, int, "callee unboxes arg", INFORMATION, CALLEE) +INLINE_OBSERVATION(MORE_DERIVED_RETURNS, bool, "returns more derived type", INFORMATION, CALLEE) INLINE_OBSERVATION(UNSUPPORTED_OPCODE, bool, "unsupported opcode", INFORMATION, CALLEE) // ------ Caller Correctness ------- diff --git a/src/coreclr/jit/inlinepolicy.cpp b/src/coreclr/jit/inlinepolicy.cpp index d22676a62a3ea2..a66aeeefa72c05 100644 --- a/src/coreclr/jit/inlinepolicy.cpp +++ b/src/coreclr/jit/inlinepolicy.cpp @@ -328,6 +328,10 @@ void DefaultPolicy::NoteBool(InlineObservation obs, bool value) m_ArgFeedsIsKnownConst = true; break; + case InlineObservation::CALLEE_MORE_DERIVED_RETURNS: + m_CalleeReturnsDerivedType = true; + break; + case InlineObservation::CALLEE_UNSUPPORTED_OPCODE: propagate = true; break; @@ -714,6 +718,14 @@ double DefaultPolicy::DetermineMultiplier() JITDUMP("\nmultiplier in instance constructors increased to %g.", multiplier); } + // Bump up the multiplier for methods that return a more derived type + + if (m_CalleeReturnsDerivedType) + { + multiplier += 2; + JITDUMP("\nmultiplier in methods that return a more derived type increased to %g.", multiplier); + } + // Bump up the multiplier for methods in promotable struct if (m_IsFromPromotableValueClass) @@ -1042,6 +1054,7 @@ void DefaultPolicy::OnDumpXml(FILE* file, unsigned indent) const XATTR_B(m_IsNoReturn) XATTR_B(m_IsNoReturnKnown) XATTR_B(m_InsideThrowBlock) + XATTR_B(m_CalleeReturnsDerivedType) } #endif @@ -1526,6 +1539,12 @@ double ExtendedDefaultPolicy::DetermineMultiplier() JITDUMP("\nmultiplier in instance constructors increased to %g.", multiplier); } + if (m_CalleeReturnsDerivedType) + { + multiplier += 2.0; + JITDUMP("\nmultiplier in methods that return a more derived type increased to %g.", multiplier); + } + if (m_IsFromValueClass) { multiplier += 3.0; @@ -2708,6 +2727,7 @@ void DiscretionaryPolicy::DumpSchema(FILE* file) const fprintf(file, ",CalleeDoesNotReturn"); fprintf(file, ",CalleeHasGCStruct"); fprintf(file, ",CallsiteDepth"); + fprintf(file, ",CalleeReturnsDerivedType"); } //------------------------------------------------------------------------ @@ -2792,6 +2812,7 @@ void DiscretionaryPolicy::DumpData(FILE* file) const fprintf(file, ",%u", m_IsNoReturn ? 1 : 0); fprintf(file, ",%u", m_CalleeHasGCStruct ? 1 : 0); fprintf(file, ",%u", m_CallsiteDepth); + fprintf(file, ",%u", m_CalleeReturnsDerivedType ? 1 : 0); } #endif // defined(DEBUG) diff --git a/src/coreclr/jit/inlinepolicy.h b/src/coreclr/jit/inlinepolicy.h index d08fbf7b32309c..c4ccf60e2f9073 100644 --- a/src/coreclr/jit/inlinepolicy.h +++ b/src/coreclr/jit/inlinepolicy.h @@ -113,6 +113,7 @@ class DefaultPolicy : public LegalPolicy , m_ConstArgFeedsIsKnownConst(false) , m_ArgFeedsIsKnownConst(false) , m_InsideThrowBlock(false) + , m_CalleeReturnsDerivedType(false) { // empty } @@ -189,6 +190,7 @@ class DefaultPolicy : public LegalPolicy bool m_ConstArgFeedsIsKnownConst : 1; bool m_ArgFeedsIsKnownConst : 1; bool m_InsideThrowBlock : 1; + bool m_CalleeReturnsDerivedType : 1; }; // ExtendedDefaultPolicy is a slightly more aggressive variant of