diff options
author | Andy Ayers <andya@microsoft.com> | 2016-03-08 18:02:56 -0800 |
---|---|---|
committer | Andy Ayers <andya@microsoft.com> | 2016-03-08 18:02:56 -0800 |
commit | d2ab1fbd40343e01fcb71fb9793ee7c681eeb1de (patch) | |
tree | 4750d1658892573be1dc1f45df88aa1c45df09be | |
parent | 770f74904b900d77853c3e94370bc36fa3e97340 (diff) | |
parent | 1876230e5098541c150f2893644fda413484426c (diff) | |
download | coreclr-d2ab1fbd40343e01fcb71fb9793ee7c681eeb1de.tar.gz coreclr-d2ab1fbd40343e01fcb71fb9793ee7c681eeb1de.tar.bz2 coreclr-d2ab1fbd40343e01fcb71fb9793ee7c681eeb1de.zip |
Merge pull request #3574 from AndyAyersMS/InlineRefactorMoveStateMachineMerge
Inline refactoring: move state machine into LegacyPolicy
-rw-r--r-- | src/jit/compiler.cpp | 6 | ||||
-rw-r--r-- | src/jit/compiler.h | 3 | ||||
-rw-r--r-- | src/jit/flowgraph.cpp | 209 | ||||
-rw-r--r-- | src/jit/inline.def | 4 | ||||
-rw-r--r-- | src/jit/inline.h | 14 | ||||
-rw-r--r-- | src/jit/inlinepolicy.cpp | 94 | ||||
-rw-r--r-- | src/jit/inlinepolicy.h | 34 | ||||
-rw-r--r-- | src/jit/morph.cpp | 5 |
8 files changed, 213 insertions, 156 deletions
diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp index ea120565a0..2c61e8983b 100644 --- a/src/jit/compiler.cpp +++ b/src/jit/compiler.cpp @@ -4860,9 +4860,9 @@ int Compiler::compCompileHelper (CORINFO_MODULE_HANDLE clas // do the profitability screening. if (prejitResult.isCandidate()) { - // Only needed if the inline is discretionary (not forced) - // and the size is over the always threshold. - if (!forceInline && (methodInfo->ILCodeSize > ALWAYS_INLINE_SIZE)) + // Only needed if the inline is discretionary. + InlineObservation obs = prejitResult.getObservation(); + if (obs == InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE) { // We should have run the CodeSeq state machine // and got the native size estimate. diff --git a/src/jit/compiler.h b/src/jit/compiler.h index 7c4b8f5552..f1e947bd8c 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -8568,9 +8568,6 @@ public: int compNativeSizeEstimate; // The estimated native size of this method. -#ifdef DEBUG - CodeSeqSM fgCodeSeqSm; // The code sequence state machine used in the inliner. -#endif private: #ifdef FEATURE_JIT_METHOD_PERF JitTimer* pCompJitTimer; // Timer data structure (by phases) for current compilation. diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp index bd397ab30d..f8ba54e6f9 100644 --- a/src/jit/flowgraph.cpp +++ b/src/jit/flowgraph.cpp @@ -4182,90 +4182,53 @@ private: unsigned slot1; unsigned depth; }; +//------------------------------------------------------------------------ +// fgFindJumpTargets: walk the IL stream, determining jump target offsets +// +// Arguments: +// codeAddr - base address of the IL code buffer +// codeSize - number of bytes in the IL code buffer +// jumpTarget - [OUT] xxx yuuu +// +// Notes: +// +// If inlining or prejitting the root, this method also makes +// various observations about the method that factor into inline +// decisions. It sets `compNativeSizeEstimate` as a side effect. +// +// May throw an exception if the IL is malformed. -void Compiler::fgFindJumpTargets(const BYTE * codeAddr, - IL_OFFSET codeSize, - BYTE * jumpTarget) +void Compiler::fgFindJumpTargets(const BYTE* codeAddr, + IL_OFFSET codeSize, + BYTE* jumpTarget) { - - const BYTE * codeBegp = codeAddr; - const BYTE * codeEndp = codeAddr + codeSize; - unsigned varNum; - - bool seenJump = false; - - OPCODE opcode; - - var_types varType = DUMMY_INIT(TYP_UNDEF); // TYP_ type - typeInfo ti; // Verifier type. - -#ifndef DEBUG - CodeSeqSM LocalSm; -#endif - - CodeSeqSM * pSm = NULL; - - SM_OPCODE smOpcode = DUMMY_INIT((SM_OPCODE)0); - - unsigned ldStCount = 0; //Number of load/store instructions. - - // Keep track of constants and args on the stack. - fgStack pushedStack; - - // Observe force inline state and code size. - bool isForceInline = (info.compFlags & CORINFO_FLG_FORCEINLINE) != 0; + const BYTE* codeBegp = codeAddr; + const BYTE* codeEndp = codeAddr + codeSize; + unsigned varNum; + bool seenJump = false; + var_types varType = DUMMY_INIT(TYP_UNDEF); // TYP_ type + typeInfo ti; // Verifier type. + bool typeIsNormed = false; + unsigned ldStCount = 0; // Number of load/store instructions. + fgStack pushedStack; // Keep track of constants and args on the stack. + const bool isForceInline = (info.compFlags & CORINFO_FLG_FORCEINLINE) != 0; if (compInlineResult != nullptr) { + // Observe force inline state and code size. compInlineResult->noteBool(InlineObservation::CALLEE_IS_FORCE_INLINE, isForceInline); compInlineResult->noteInt(InlineObservation::CALLEE_IL_CODE_SIZE, codeSize); - } - - // Determine whether to start the state machine to estimate the size of the - // native code for this method. - bool useSm = false; - if ((codeSize > ALWAYS_INLINE_SIZE) && !isForceInline) - { - // The size of the native code for this method matters for inlining - // decisions. - - if (compIsForInlining()) - { - // This method is being compiled as an inline candidate and will be - // rejected if its native code is too large. - - useSm = true; - } - else if ((opts.eeFlags & CORJIT_FLG_PREJIT) && - (codeSize <= impInlineSize)) - { - // This method isn't an inline candidate yet, but it's small enough - // to be considered for one in the future. Keep track of the - // estimated native code size; it will be used elsewhere later. - - useSm = true; - } - } - - if (useSm) - { - -#ifdef DEBUG - pSm = &fgCodeSeqSm; -#else - pSm = &LocalSm; -#endif - pSm->Start(this); + // note that we're starting to look at the opcodes. + compInlineResult->note(InlineObservation::CALLEE_BEGIN_OPCODE_SCAN); } while (codeAddr < codeEndp) { - unsigned sz; - - opcode = (OPCODE) getU1LittleEndian(codeAddr); + OPCODE opcode = (OPCODE) getU1LittleEndian(codeAddr); codeAddr += sizeof(__int8); opts.instrCount++; + typeIsNormed = false; DECODE_OPCODE: @@ -4274,6 +4237,7 @@ DECODE_OPCODE: if (opcode >= CEE_COUNT) BADCODE3("Illegal opcode", ": %02X", (int) opcode); + // --- leave ldstcount for now, though it can be moved --- if ((opcode >= CEE_LDARG_0 && opcode <= CEE_STLOC_S) || (opcode >= CEE_LDARG && opcode <= CEE_STLOC)) { @@ -4299,12 +4263,7 @@ DECODE_OPCODE: ++ldStCount; } - sz = opcodeSizes[opcode]; - - if (pSm) - { - smOpcode = CodeSeqSM::MapToSMOpcode(opcode); - } + unsigned sz = opcodeSizes[opcode]; switch (opcode) { @@ -4703,11 +4662,8 @@ ADDR_TAKEN: varNum = compMapILargNum(varNum); // account for possible hidden param } - if (pSm) - { - varType = (var_types)lvaTable[varNum].lvType; - ti = lvaTable[varNum].lvVerTypeInfo; - } + varType = (var_types)lvaTable[varNum].lvType; + ti = lvaTable[varNum].lvVerTypeInfo; if (!varTypeIsStruct(&lvaTable[varNum]) && // We will put structs in the stack anyway // And changing the addrTaken of a local @@ -4744,21 +4700,7 @@ ADDR_TAKEN: } } // compIsForInlining() - if (pSm && - ti.IsValueClass() && - !varTypeIsStruct(varType)) - { -#ifdef DEBUG - if (verbose) - printf("Loading address of normed V%02u at IL offset [0x%x]\n", varNum, codeAddr-codeBegp-1); -#endif - - if (smOpcode == SM_LDARGA_S) - smOpcode = SM_LDARGA_S_NORMED; - else if (smOpcode == SM_LDLOCA_S) - smOpcode = SM_LDLOCA_S_NORMED; - } - + typeIsNormed = ti.IsValueClass() && !varTypeIsStruct(varType); break; ARG_WRITE: @@ -4803,17 +4745,14 @@ ARG_WRITE: _SkipCodeAddrAdjustment: ; - if (pSm) + if (compInlineResult != nullptr) { - noway_assert(smOpcode<SM_COUNT); - noway_assert(smOpcode != SM_PREFIX_N); - - pSm->Run(smOpcode DEBUGARG(0)); + InlineObservation obs = typeIsNormed ? + InlineObservation::CALLEE_OPCODE_NORMED : InlineObservation::CALLEE_OPCODE; + compInlineResult->noteInt(obs, opcode); } - } - if (codeAddr != codeEndp) { TOO_FAR: @@ -4837,45 +4776,51 @@ TOO_FAR: } } - if (pSm) + if (compInlineResult != nullptr) { - pSm->End(); - noway_assert(pSm->NativeSize != NATIVE_SIZE_INVALID); - compNativeSizeEstimate = pSm->NativeSize; + compInlineResult->note(InlineObservation::CALLEE_END_OPCODE_SCAN); -#ifdef DEBUG - if (verbose) + // If we were estimating native code size, grab that data now. + if (compInlineResult->hasNativeSizeEstimate()) { - printf("\n\ncompNativeSizeEstimate=%d\n", compNativeSizeEstimate); - } -#endif + compNativeSizeEstimate = compInlineResult->determineNativeSizeEstimate(); + noway_assert(compNativeSizeEstimate != NATIVE_SIZE_INVALID); + JITDUMP("\n\ncompNativeSizeEstimate=%d\n", compNativeSizeEstimate); - if (compIsForInlining()) - { - // If the inlining decision was obvious from the size of the IL, - // it should have been made earlier. - noway_assert(codeSize > ALWAYS_INLINE_SIZE && codeSize <= impInlineSize); + // If we're inlining, use the size estimate to do further + // screening. + // + // If we're in the prejit-root case we do something + // similar as part of the prejit screen over in + // compCompileHelper. + if (compIsForInlining()) + { + // If the inlining decision was obvious from the size of the IL, + // it should have been made earlier. + InlineObservation obs = compInlineResult->getObservation(); + noway_assert(obs == InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE); - // Make an inlining decision based on the estimated native size. - int callsiteNativeSizeEstimate = impEstimateCallsiteNativeSize(&impInlineInfo->inlineCandidateInfo->methInfo); + // Make an inlining decision based on the estimated native size. + int callsiteNativeSizeEstimate = impEstimateCallsiteNativeSize(&impInlineInfo->inlineCandidateInfo->methInfo); - impCanInlineNative(callsiteNativeSizeEstimate, - compNativeSizeEstimate, - impInlineInfo, - compInlineResult); + impCanInlineNative(callsiteNativeSizeEstimate, + compNativeSizeEstimate, + impInlineInfo, + compInlineResult); - if (compInlineResult->isFailure()) - { -#ifdef DEBUG - if (verbose) + if (compInlineResult->isFailure()) { - printf("\n\nInline expansion aborted because of impCanInlineNative: %s %s\n", - compInlineResult->resultString(), - compInlineResult->reasonString()); - } +#ifdef DEBUG + if (verbose) + { + printf("\n\nInline expansion aborted because of impCanInlineNative: %s %s\n", + compInlineResult->resultString(), + compInlineResult->reasonString()); + } #endif - return; + return; + } } } } diff --git a/src/jit/inline.def b/src/jit/inline.def index 19a5147ce4..9d2b07afb5 100644 --- a/src/jit/inline.def +++ b/src/jit/inline.def @@ -73,8 +73,10 @@ INLINE_OBSERVATION(TOO_MUCH_IL, bool, "too many il bytes", INLINE_OBSERVATION(ARG_FEEDS_CONSTANT_TEST, bool, "argument feeds constant test", INFORMATION, CALLEE) INLINE_OBSERVATION(ARG_FEEDS_RANGE_CHECK, bool, "argument feeds range check", INFORMATION, CALLEE) +INLINE_OBSERVATION(BEGIN_OPCODE_SCAN, bool, "prepare to look at opcodes", INFORMATION, CALLEE) INLINE_OBSERVATION(BELOW_ALWAYS_INLINE_SIZE, bool, "below ALWAYS_INLINE size", INFORMATION, CALLEE) INLINE_OBSERVATION(CLASS_PROMOTABLE, bool, "promotable value class", INFORMATION, CALLEE) +INLINE_OBSERVATION(END_OPCODE_SCAN, bool, "done looking at opcodes", INFORMATION, CALLEE) INLINE_OBSERVATION(HAS_SIMD, bool, "has SIMD arg, local, or ret", INFORMATION, CALLEE) INLINE_OBSERVATION(HAS_SWITCH, bool, "has switch", INFORMATION, CALLEE) INLINE_OBSERVATION(IL_CODE_SIZE, int, "number of bytes of IL", INFORMATION, CALLEE) @@ -85,6 +87,8 @@ INLINE_OBSERVATION(IS_MOSTLY_LOAD_STORE, bool, "method is mostly load/sto INLINE_OBSERVATION(LOOKS_LIKE_WRAPPER, bool, "thin wrapper around a call", INFORMATION, CALLEE) INLINE_OBSERVATION(MAXSTACK, int, "maxstack", INFORMATION, CALLEE) INLINE_OBSERVATION(NATIVE_SIZE_ESTIMATE, double, "native size estimate", INFORMATION, CALLEE) +INLINE_OBSERVATION(OPCODE, int, "next opcode in IL stream", INFORMATION, CALLEE) +INLINE_OBSERVATION(OPCODE_NORMED, int, "next opcode in IL stream", INFORMATION, CALLEE) INLINE_OBSERVATION(NUMBER_OF_ARGUMENTS, int, "number of arguments", INFORMATION, CALLEE) INLINE_OBSERVATION(NUMBER_OF_BASIC_BLOCKS, int, "number of basic blocks", INFORMATION, CALLEE) INLINE_OBSERVATION(NUMBER_OF_LOCALS, int, "number of locals", INFORMATION, CALLEE) diff --git a/src/jit/inline.h b/src/jit/inline.h index 625e1ea7ed..9a5a7bcf7d 100644 --- a/src/jit/inline.h +++ b/src/jit/inline.h @@ -228,6 +228,8 @@ public: // Policy determinations virtual double determineMultiplier() = 0; + virtual bool hasNativeSizeEstimate() = 0; + virtual int determineNativeSizeEstimate() = 0; // Policy policies virtual bool propagateNeverToRuntime() const = 0; @@ -363,6 +365,18 @@ public: return inlPolicy->determineMultiplier(); } + // Is there a native size estimate? + bool hasNativeSizeEstimate() + { + return inlPolicy->hasNativeSizeEstimate(); + } + + // Determine the native size estimate for this inline + int determineNativeSizeEstimate() + { + return inlPolicy->determineNativeSizeEstimate(); + } + // Ensure details of this inlining process are appropriately // reported when the result goes out of scope. ~InlineResult() diff --git a/src/jit/inlinepolicy.cpp b/src/jit/inlinepolicy.cpp index ceace2deca..fd8dd82f09 100644 --- a/src/jit/inlinepolicy.cpp +++ b/src/jit/inlinepolicy.cpp @@ -8,6 +8,7 @@ #endif #include "inlinepolicy.h" +#include "sm.h" //------------------------------------------------------------------------ // getPolicy: Factory method for getting an InlinePolicy @@ -41,12 +42,11 @@ void LegacyPolicy::noteSuccess() } //------------------------------------------------------------------------ -// note: handle an observation with non-fatal impact +// noteBool: handle a boolean observation with non-fatal impact // // Arguments: // obs - the current obsevation // value - the value of the observation - void LegacyPolicy::noteBool(InlineObservation obs, bool value) { // Check the impact @@ -103,6 +103,39 @@ void LegacyPolicy::noteBool(InlineObservation obs, bool value) // Passed the profitability screen. Update candidacy. setCandidate(obs); break; + case InlineObservation::CALLEE_BEGIN_OPCODE_SCAN: + { + // Set up the state machine, if this inline is + // discretionary and is still a candidate. + if (inlDecisionIsCandidate(inlDecision) + && (inlObservation == InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE)) + { + // Better not have a state machine already. + assert(inlStateMachine == nullptr); + inlStateMachine = new (inlCompiler, CMK_Inlining) CodeSeqSM; + inlStateMachine->Start(inlCompiler); + } + break; + } + + case InlineObservation::CALLEE_END_OPCODE_SCAN: + { + // We only expect to see this observation once, so the + // native size estimate should have its initial value. + assert(inlNativeSizeEstimate == NATIVE_SIZE_INVALID); + + // If we were using the state machine, get it to kick + // out a size estimate. + if (inlStateMachine != nullptr) + { + inlStateMachine->End(); + inlNativeSizeEstimate = inlStateMachine->NativeSize; + assert(inlNativeSizeEstimate != NATIVE_SIZE_INVALID); + } + + break; + } + default: // Ignore the remainder for now break; @@ -174,11 +207,11 @@ void LegacyPolicy::noteInt(InlineObservation obs, int value) { assert(inlIsForceInlineKnown); assert(value != 0); - unsigned ilByteSize = static_cast<unsigned>(value); + inlCodeSize = static_cast<unsigned>(value); // Now that we know size and forceinline state, // update candidacy. - if (ilByteSize <= ALWAYS_INLINE_SIZE) + if (inlCodeSize <= ALWAYS_INLINE_SIZE) { // Candidate based on small size setCandidate(InlineObservation::CALLEE_BELOW_ALWAYS_INLINE_SIZE); @@ -188,7 +221,7 @@ void LegacyPolicy::noteInt(InlineObservation obs, int value) // Candidate based on force inline setCandidate(InlineObservation::CALLEE_IS_FORCE_INLINE); } - else if (ilByteSize <= inlCompiler->getImpInlineSize()) + else if (inlCodeSize <= inlCompiler->getImpInlineSize()) { // Candidate, pending profitability evaluation setCandidate(InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE); @@ -202,6 +235,32 @@ void LegacyPolicy::noteInt(InlineObservation obs, int value) break; } + case InlineObservation::CALLEE_OPCODE_NORMED: + case InlineObservation::CALLEE_OPCODE: + { + if (inlStateMachine != nullptr) + { + OPCODE opcode = static_cast<OPCODE>(value); + SM_OPCODE smOpcode = CodeSeqSM::MapToSMOpcode(opcode); + noway_assert(smOpcode < SM_COUNT); + noway_assert(smOpcode != SM_PREFIX_N); + if (obs == InlineObservation::CALLEE_OPCODE_NORMED) + { + if (smOpcode == SM_LDARGA_S) + { + smOpcode = SM_LDARGA_S_NORMED; + } + else if (smOpcode == SM_LDLOCA_S) + { + smOpcode = SM_LDLOCA_S_NORMED; + } + } + + inlStateMachine->Run(smOpcode DEBUGARG(0)); + } + break; + } + default: // Ignore all other information break; @@ -401,3 +460,28 @@ double LegacyPolicy::determineMultiplier() return multiplier; } + +//------------------------------------------------------------------------ +// hasNativeCodeSizeEstimate: did this policy estimate native code size +// +// Notes: +// Temporary scaffolding for refactoring work. + +bool LegacyPolicy::hasNativeSizeEstimate() +{ + return (inlStateMachine != nullptr); +} + +//------------------------------------------------------------------------ +// determineNativeCodeSizeEstimate: return estimated native code size for +// this inline candidate. +// +// Notes: +// Uses the results of a state machine model for discretionary +// candidates. Should not be needed for forcded or always +// candidates. + +int LegacyPolicy::determineNativeSizeEstimate() +{ + return inlNativeSizeEstimate; +} diff --git a/src/jit/inlinepolicy.h b/src/jit/inlinepolicy.h index 4f831a1e77..45faba9085 100644 --- a/src/jit/inlinepolicy.h +++ b/src/jit/inlinepolicy.h @@ -17,6 +17,8 @@ #include "jit.h" #include "inline.h" +class CodeSeqSM; + // LegacyPolicy implements the inlining policy used by the jit in its // initial release. // @@ -39,6 +41,9 @@ public: LegacyPolicy(Compiler* compiler, bool isPrejitRoot) : InlinePolicy(isPrejitRoot) , inlCompiler(compiler) + , inlStateMachine(nullptr) + , inlCodeSize(0) + , inlNativeSizeEstimate(NATIVE_SIZE_INVALID) , inlIsForceInline(false) , inlIsForceInlineKnown(false) , inlIsInstanceCtor(false) @@ -55,13 +60,15 @@ public: // Policy observations void noteSuccess() override; - void noteFatal(InlineObservation obs) override; void noteBool(InlineObservation obs, bool value) override; + void noteFatal(InlineObservation obs) override; void noteInt(InlineObservation obs, int value) override; void noteDouble(InlineObservation obs, double value) override; // Policy determinations double determineMultiplier() override; + int determineNativeSizeEstimate() override; + bool hasNativeSizeEstimate() override; // Policy policies bool propagateNeverToRuntime() const override { return true; } @@ -82,17 +89,20 @@ private: const unsigned MAX_BASIC_BLOCKS = 5; // Data members - Compiler* inlCompiler; - bool inlIsForceInline :1; - bool inlIsForceInlineKnown :1; - bool inlIsInstanceCtor :1; - bool inlIsFromPromotableValueClass :1; - bool inlHasSimd :1; - bool inlLooksLikeWrapperMethod :1; - bool inlArgFeedsConstantTest :1; - bool inlMethodIsMostlyLoadStore :1; - bool inlArgFeedsRangeCheck :1; - bool inlConstantFeedsConstantTest :1; + Compiler* inlCompiler; + CodeSeqSM* inlStateMachine; + unsigned inlCodeSize; + int inlNativeSizeEstimate; + bool inlIsForceInline :1; + bool inlIsForceInlineKnown :1; + bool inlIsInstanceCtor :1; + bool inlIsFromPromotableValueClass :1; + bool inlHasSimd :1; + bool inlLooksLikeWrapperMethod :1; + bool inlArgFeedsConstantTest :1; + bool inlMethodIsMostlyLoadStore :1; + bool inlArgFeedsRangeCheck :1; + bool inlConstantFeedsConstantTest :1; }; #endif // _INLINE_POLICY_H_ diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp index 3858a20d25..cacbb909fb 100644 --- a/src/jit/morph.cpp +++ b/src/jit/morph.cpp @@ -5658,7 +5658,10 @@ void Compiler::fgMorphCallInline(GenTreeCall* call, InlineResult* inlineR void Compiler::fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result) { - if (lvaCount >= MAX_LV_NUM_COUNT_FOR_INLINING) + // Don't expect any surprises here. + assert(result->isCandidate()); + + if (lvaCount >= MAX_LV_NUM_COUNT_FOR_INLINING) { // For now, attributing this to call site, though it's really // more of a budget issue (lvaCount currently includes all |