1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
7 // This file contains enum and class definitions and related
8 // information that the jit uses to make inlining decisions.
12 // InlineCallFrequency - rough assessment of call site frequency
13 // InlineDecision - overall decision made about an inline
14 // InlineTarget - target of a particular observation
15 // InlineImpact - impact of a particular observation
16 // InlineObservation - facts observed when considering an inline
20 // InlineResult - accumulates observations, consults with policy
21 // InlineCandidateInfo - basic information needed for inlining
22 // InlArgInfo - information about a candidate's argument
23 // InlLclVarInfo - information about a candidate's local variable
24 // InlineInfo - detailed information needed for inlining
25 // InlineContext - class, remembers what inlines happened
26 // InlinePolicy - class, determines policy for inlining
27 // InlineStrategy - class, determines overall inline strategy
29 // Enums are used throughout to provide various descriptions.
31 // There are 4 sitations where inline candidacy is evaluated. In each
32 // case an InlineResult is allocated on the stack to collect
33 // information about the inline candidate. Each InlineResult refers
34 // to an InlinePolicy.
36 // 1. Importer Candidate Screen (impMarkInlineCandidate)
38 // Creates: InlineCandidateInfo
40 // During importing, the IL being imported is scanned to identify
41 // inline candidates. This happens both when the root method is being
42 // imported as well as when prospective inlines are being imported.
43 // Candidates are marked in the IL and given an InlineCandidateInfo.
45 // 2. Inlining Optimization Pass -- candidates (fgInline)
47 // Creates / Uses: InlineContext
48 // Creates: InlineInfo, InlArgInfo, InlLocalVarInfo
50 // During the inlining optimation pass, each candidate is further
51 // analyzed. Viable candidates will eventually inspire creation of an
52 // InlineInfo and a set of InlArgInfos (for call arguments) and
53 // InlLocalVarInfos (for callee locals).
55 // The analysis will also examine InlineContexts from relevant prior
56 // inlines. If the inline is successful, a new InlineContext will be
57 // created to remember this inline. In DEBUG builds, failing inlines
58 // also create InlineContexts.
60 // 3. Inlining Optimization Pass -- non-candidates (fgNoteNotInlineCandidate)
62 // Creates / Uses: InlineContext
64 // In DEBUG, the jit also searches for non-candidate calls to try
65 // and get a complete picture of the set of failed inlines.
67 // 4. Prejit suitability screen (compCompileHelper)
69 // When prejitting, each method is scanned to see if it is a viable
78 // Implementation limits
80 #ifndef LEGACY_BACKEND
81 const unsigned int MAX_INL_ARGS = 32; // does not include obj pointer
82 const unsigned int MAX_INL_LCLS = 32;
83 #else // LEGACY_BACKEND
84 const unsigned int MAX_INL_ARGS = 10; // does not include obj pointer
85 const unsigned int MAX_INL_LCLS = 8;
86 #endif // LEGACY_BACKEND
88 // Forward declarations
92 // InlineCallsiteFrequency gives a rough classification of how
93 // often a call site will be excuted at runtime.
95 enum class InlineCallsiteFrequency
98 RARE, // once in a blue moon
99 BORING, // normal call site
100 WARM, // seen during profiling
105 // InlineDecision describes the various states the jit goes through when
106 // evaluating an inline candidate. It is distinct from CorInfoInline
107 // because it must capture internal states that don't get reported back
110 enum class InlineDecision
119 // Translate a decision into a CorInfoInline for reporting back to the runtime.
121 CorInfoInline InlGetCorInfoInlineDecision(InlineDecision d);
123 // Get a string describing this InlineDecision
125 const char* InlGetDecisionString(InlineDecision d);
127 // True if this InlineDecsion describes a failing inline
129 bool InlDecisionIsFailure(InlineDecision d);
131 // True if this decision describes a successful inline
133 bool InlDecisionIsSuccess(InlineDecision d);
135 // True if this InlineDecision is a never inline decision
137 bool InlDecisionIsNever(InlineDecision d);
139 // True if this InlineDecision describes a viable candidate
141 bool InlDecisionIsCandidate(InlineDecision d);
143 // True if this InlineDecsion describes a decision
145 bool InlDecisionIsDecided(InlineDecision d);
147 // InlineTarget describes the possible targets of an inline observation.
149 enum class InlineTarget
151 CALLEE, // observation applies to all calls to this callee
152 CALLER, // observation applies to all calls made by this caller
153 CALLSITE // observation applies to a specific call site
156 // InlineImpact describe the possible impact of an inline observation.
158 enum class InlineImpact
160 FATAL, // inlining impossible, unsafe to evaluate further
161 FUNDAMENTAL, // inlining impossible for fundamental reasons, deeper exploration safe
162 LIMITATION, // inlining impossible because of jit limitations, deeper exploration safe
163 PERFORMANCE, // inlining inadvisable because of performance concerns
164 INFORMATION // policy-free observation to provide data for later decision making
167 // InlineObservation describes the set of possible inline observations.
169 enum class InlineObservation
171 #define INLINE_OBSERVATION(name, type, description, impact, scope) scope##_##name,
172 #include "inline.def"
173 #undef INLINE_OBSERVATION
178 // Sanity check the observation value
180 bool InlIsValidObservation(InlineObservation obs);
184 // Get a string describing this observation
186 const char* InlGetObservationString(InlineObservation obs);
188 // Get a string describing the target of this observation
190 const char* InlGetTargetString(InlineObservation obs);
192 // Get a string describing the impact of this observation
194 const char* InlGetImpactString(InlineObservation obs);
196 // Get the target of this observation
198 InlineTarget InlGetTarget(InlineObservation obs);
200 // Get the impact of this observation
202 InlineImpact InlGetImpact(InlineObservation obs);
204 // InlinePolicy is an abstract base class for a family of inline
210 // Factory method for getting policies
211 static InlinePolicy* GetPolicy(Compiler* compiler, bool isPrejitRoot);
213 // Obligatory virtual dtor
214 virtual ~InlinePolicy()
218 // Get the current decision
219 InlineDecision GetDecision() const
224 // Get the observation responsible for the result
225 InlineObservation GetObservation() const
227 return m_Observation;
230 // Policy observations
231 virtual void NoteSuccess() = 0;
232 virtual void NoteBool(InlineObservation obs, bool value) = 0;
233 virtual void NoteFatal(InlineObservation obs) = 0;
234 virtual void NoteInt(InlineObservation obs, int value) = 0;
236 // Optional observations. Most policies ignore these.
237 virtual void NoteContext(InlineContext* context)
241 virtual void NoteOffset(IL_OFFSETX offset)
246 // Policy determinations
247 virtual void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo) = 0;
250 virtual bool PropagateNeverToRuntime() const = 0;
253 virtual int CodeSizeEstimate() = 0;
255 #if defined(DEBUG) || defined(INLINE_DATA)
257 // Record observation for prior failure
258 virtual void NotePriorFailure(InlineObservation obs) = 0;
260 // Name of the policy
261 virtual const char* GetName() const = 0;
262 // Detailed data value dump
263 virtual void DumpData(FILE* file) const
266 // Detailed data name dump
267 virtual void DumpSchema(FILE* file) const
270 // True if this is the inline targeted by data collection
271 bool IsDataCollectionTarget()
273 return m_IsDataCollectionTarget;
276 #endif // defined(DEBUG) || defined(INLINE_DATA)
279 InlinePolicy(bool isPrejitRoot)
280 : m_Decision(InlineDecision::UNDECIDED)
281 , m_Observation(InlineObservation::CALLEE_UNUSED_INITIAL)
282 , m_IsPrejitRoot(isPrejitRoot)
283 #if defined(DEBUG) || defined(INLINE_DATA)
284 , m_IsDataCollectionTarget(false)
285 #endif // defined(DEBUG) || defined(INLINE_DATA)
292 // No copying or assignment supported
293 InlinePolicy(const InlinePolicy&) = delete;
294 InlinePolicy& operator=(const InlinePolicy&) = delete;
297 InlineDecision m_Decision;
298 InlineObservation m_Observation;
301 #if defined(DEBUG) || defined(INLINE_DATA)
303 bool m_IsDataCollectionTarget;
305 #endif // defined(DEBUG) || defined(INLINE_DATA)
308 // InlineResult summarizes what is known about the viability of a
309 // particular inline candiate.
314 // Construct a new InlineResult to help evaluate a
315 // particular call for inlining.
316 InlineResult(Compiler* compiler, GenTreeCall* call, GenTreeStmt* stmt, const char* description);
318 // Construct a new InlineResult to evaluate a particular
319 // method to see if it is inlineable.
320 InlineResult(Compiler* compiler, CORINFO_METHOD_HANDLE method, const char* description);
322 // Has the policy determined this inline should fail?
323 bool IsFailure() const
325 return InlDecisionIsFailure(m_Policy->GetDecision());
328 // Has the policy determined this inline will succeed?
329 bool IsSuccess() const
331 return InlDecisionIsSuccess(m_Policy->GetDecision());
334 // Has the policy determined this inline will fail,
335 // and that the callee should never be inlined?
338 return InlDecisionIsNever(m_Policy->GetDecision());
341 // Has the policy determined this inline attempt is still viable?
342 bool IsCandidate() const
344 return InlDecisionIsCandidate(m_Policy->GetDecision());
347 // Has the policy determined this inline attempt is still viable
348 // and is a discretionary inline?
349 bool IsDiscretionaryCandidate() const
351 bool result = InlDecisionIsCandidate(m_Policy->GetDecision()) &&
352 (m_Policy->GetObservation() == InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE);
357 // Has the policy made a determination?
358 bool IsDecided() const
360 return InlDecisionIsDecided(m_Policy->GetDecision());
363 // NoteSuccess means the all the various checks have passed and
364 // the inline can happen.
367 assert(IsCandidate());
368 m_Policy->NoteSuccess();
371 // Make a true observation, and update internal state
374 // Caller is expected to call isFailure after this to see whether
375 // more observation is desired.
376 void Note(InlineObservation obs)
378 m_Policy->NoteBool(obs, true);
381 // Make a boolean observation, and update internal state
384 // Caller is expected to call isFailure after this to see whether
385 // more observation is desired.
386 void NoteBool(InlineObservation obs, bool value)
388 m_Policy->NoteBool(obs, value);
391 // Make an observation that must lead to immediate failure.
392 void NoteFatal(InlineObservation obs)
394 m_Policy->NoteFatal(obs);
398 // Make an observation with an int value
399 void NoteInt(InlineObservation obs, int value)
401 m_Policy->NoteInt(obs, value);
404 #if defined(DEBUG) || defined(INLINE_DATA)
406 // Record observation from an earlier failure.
407 void NotePriorFailure(InlineObservation obs)
409 m_Policy->NotePriorFailure(obs);
413 #endif // defined(DEBUG) || defined(INLINE_DATA)
415 // Determine if this inline is profitable
416 void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo)
418 m_Policy->DetermineProfitability(methodInfo);
421 // Ensure details of this inlining process are appropriately
422 // reported when the result goes out of scope.
428 // The observation leading to this particular result
429 InlineObservation GetObservation() const
431 return m_Policy->GetObservation();
434 // The callee handle for this result
435 CORINFO_METHOD_HANDLE GetCallee() const
440 // The call being considered
441 GenTreeCall* GetCall() const
446 // Result that can be reported back to the runtime
447 CorInfoInline Result() const
449 return InlGetCorInfoInlineDecision(m_Policy->GetDecision());
452 // String describing the decision made
453 const char* ResultString() const
455 return InlGetDecisionString(m_Policy->GetDecision());
458 // String describing the reason for the decision
459 const char* ReasonString() const
461 return InlGetObservationString(m_Policy->GetObservation());
464 // Get the policy that evaluated this result.
465 InlinePolicy* GetPolicy() const
470 // SetReported indicates that this particular result doesn't need
471 // to be reported back to the runtime, either because the runtime
472 // already knows, or we aren't actually inlining yet.
478 // Get the InlineContext for this inline
479 InlineContext* GetInlineContext() const
481 return m_InlineContext;
485 // No copying or assignment allowed.
486 InlineResult(const InlineResult&) = delete;
487 InlineResult& operator=(const InlineResult&) = delete;
489 // Report/log/dump decision as appropriate
492 Compiler* m_RootCompiler;
493 InlinePolicy* m_Policy;
495 InlineContext* m_InlineContext;
496 CORINFO_METHOD_HANDLE m_Caller; // immediate caller's handle
497 CORINFO_METHOD_HANDLE m_Callee;
498 const char* m_Description;
502 // InlineCandidateInfo provides basic information about a particular
505 struct InlineCandidateInfo
507 DWORD dwRestrictions;
508 CORINFO_METHOD_INFO methInfo;
510 CORINFO_CLASS_HANDLE clsHandle;
512 var_types fncRetType;
513 CORINFO_METHOD_HANDLE ilCallerHandle; // the logical IL caller of this inlinee.
514 CORINFO_CONTEXT_HANDLE exactContextHnd;
515 bool exactContextNeedsRuntimeLookup;
516 CorInfoInitClassResult initClassResult;
519 // InlArgInfo describes inline candidate argument properties.
523 GenTree* argNode; // caller node for this argument
524 GenTree* argBashTmpNode; // tmp node created, if it may be replaced with actual arg
525 unsigned argTmpNum; // the argument tmp number
526 unsigned argIsUsed : 1; // is this arg used at all?
527 unsigned argIsInvariant : 1; // the argument is a constant or a local variable address
528 unsigned argIsLclVar : 1; // the argument is a local variable
529 unsigned argIsThis : 1; // the argument is the 'this' pointer
530 unsigned argHasSideEff : 1; // the argument has side effects
531 unsigned argHasGlobRef : 1; // the argument has a global ref
532 unsigned argHasCallerLocalRef : 1; // the argument value depends on an aliased caller local
533 unsigned argHasTmp : 1; // the argument will be evaluated to a temp
534 unsigned argHasLdargaOp : 1; // Is there LDARGA(s) operation on this argument?
535 unsigned argHasStargOp : 1; // Is there STARG(s) operation on this argument?
536 unsigned argIsByRefToStructLocal : 1; // Is this arg an address of a struct local or a normed struct local or a
540 // InlLclVarInfo describes inline candidate argument and local variable properties.
544 typeInfo lclVerTypeInfo;
545 var_types lclTypeInfo;
546 unsigned lclHasLdlocaOp : 1; // Is there LDLOCA(s) operation on this local?
547 unsigned lclHasStlocOp : 1; // Is there a STLOC on this local?
548 unsigned lclHasMultipleStlocOp : 1; // Is there more than one STLOC on this local
549 unsigned lclIsPinned : 1;
552 // InlineInfo provides detailed information about a particular inline candidate.
556 Compiler* InlinerCompiler; // The Compiler instance for the caller (i.e. the inliner)
557 Compiler* InlineRoot; // The Compiler instance that is the root of the inlining tree of which the owner of "this" is
560 CORINFO_METHOD_HANDLE fncHandle;
561 InlineCandidateInfo* inlineCandidateInfo;
563 InlineResult* inlineResult;
565 GenTree* retExpr; // The return expression of the inlined candidate.
566 CORINFO_CLASS_HANDLE retExprClassHnd;
567 bool retExprClassHndIsExact;
569 CORINFO_CONTEXT_HANDLE tokenLookupContextHandle; // The context handle that will be passed to
570 // impTokenLookupContextHandle in Inlinee's Compiler.
573 InlArgInfo inlArgInfo[MAX_INL_ARGS + 1];
574 int lclTmpNum[MAX_INL_LCLS]; // map local# -> temp# (-1 if unused)
575 InlLclVarInfo lclVarInfo[MAX_INL_LCLS + MAX_INL_ARGS + 1]; // type information from local sig
577 unsigned numberOfGcRefLocals; // Number of TYP_REF and TYP_BYREF locals
579 bool HasGcRefLocals() const
581 return numberOfGcRefLocals > 0;
584 bool thisDereferencedFirst;
585 unsigned typeContextArg;
588 bool hasSIMDTypeArgLocalOrReturn;
589 #endif // FEATURE_SIMD
591 GenTreeCall* iciCall; // The GT_CALL node to be inlined.
592 GenTreeStmt* iciStmt; // The statement iciCall is in.
593 BasicBlock* iciBlock; // The basic block iciStmt is in.
596 // InlineContext tracks the inline history in a method.
600 // InlineContexts form a tree with the root method as the root and
601 // inlines as children. Nested inlines are represented as granchildren
604 // Leaves in the tree represent successful inlines of leaf methods.
605 // In DEBUG builds we also keep track of failed inline attempts.
607 // During inlining, all statements in the IR refer back to the
608 // InlineContext that is responsible for those statements existing.
609 // This makes it possible to detect recursion and to keep track of the
610 // depth of each inline attempt.
614 // InlineContexts are created by InlineStrategies
615 friend class InlineStrategy;
618 #if defined(DEBUG) || defined(INLINE_DATA)
620 // Dump the full subtree, including failures
621 void Dump(unsigned indent = 0);
623 // Dump only the success subtree, with rich data
624 void DumpData(unsigned indent = 0);
626 // Dump full subtree in xml format
627 void DumpXml(FILE* file = stderr, unsigned indent = 0);
630 CORINFO_METHOD_HANDLE GetCallee() const
635 #endif // defined(DEBUG) || defined(INLINE_DATA)
637 // Get the parent context for this context.
638 InlineContext* GetParent() const
643 // Get the code pointer for this context.
644 BYTE* GetCode() const
649 // True if this context describes a successful inline.
650 bool IsSuccess() const
655 // Get the observation that supported or disqualified this inline.
656 InlineObservation GetObservation() const
658 return m_Observation;
661 // Get the IL code size for this inline.
662 unsigned GetILSize() const
667 // Get the native code size estimate for this inline.
668 unsigned GetCodeSizeEstimate() const
670 return m_CodeSizeEstimate;
673 // Get the offset of the call site
674 IL_OFFSETX GetOffset() const
679 // True if this is the root context
682 return m_Parent == nullptr;
686 InlineContext(InlineStrategy* strategy);
689 InlineStrategy* m_InlineStrategy; // overall strategy
690 InlineContext* m_Parent; // logical caller (parent)
691 InlineContext* m_Child; // first child
692 InlineContext* m_Sibling; // next child of the parent
693 BYTE* m_Code; // address of IL buffer for the method
694 unsigned m_ILSize; // size of IL buffer for the method
695 IL_OFFSETX m_Offset; // call site location within parent
696 InlineObservation m_Observation; // what lead to this inline
697 int m_CodeSizeEstimate; // in bytes * 10
698 bool m_Success; // true if this was a successful inline
700 #if defined(DEBUG) || defined(INLINE_DATA)
702 InlinePolicy* m_Policy; // policy that evaluated this inline
703 CORINFO_METHOD_HANDLE m_Callee; // handle to the method
704 unsigned m_TreeID; // ID of the GenTreeCall
705 unsigned m_Ordinal; // Ordinal number of this inline
707 #endif // defined(DEBUG) || defined(INLINE_DATA)
710 // The InlineStrategy holds the per-method persistent inline state.
711 // It is responsible for providing information that applies to
712 // multiple inlining decisions.
718 // Construct a new inline strategy.
719 InlineStrategy(Compiler* compiler);
721 // Create context for a successful inline.
722 InlineContext* NewSuccess(InlineInfo* inlineInfo);
724 // Create context for a failing inline.
725 InlineContext* NewFailure(GenTreeStmt* stmt, InlineResult* inlineResult);
727 // Compiler associated with this strategy
728 Compiler* GetCompiler() const
734 InlineContext* GetRootContext();
736 // Context for the last sucessful inline
737 // (or root if no inlines)
738 InlineContext* GetLastContext() const
740 return m_LastContext;
743 // Get IL size for maximum allowable inline
744 unsigned GetMaxInlineILSize() const
746 return m_MaxInlineSize;
749 // Get depth of maximum allowable inline
750 unsigned GetMaxInlineDepth() const
752 return m_MaxInlineDepth;
755 // Number of successful inlines into the root
756 unsigned GetInlineCount() const
758 return m_InlineCount;
761 // Return the current code size estimate for this method
762 int GetCurrentSizeEstimate() const
764 return m_CurrentSizeEstimate;
767 // Return the initial code size estimate for this method
768 int GetInitialSizeEstimate() const
770 return m_InitialSizeEstimate;
773 // Inform strategy that there's another call
779 // Inform strategy that there's a new inline candidate.
785 // Inform strategy that a candidate was assessed and determined to
787 void NoteUnprofitable()
789 m_UnprofitableCandidateCount++;
792 // Inform strategy that a candidate has passed screening
793 // and that the jit will attempt to inline.
794 void NoteAttempt(InlineResult* result);
796 // Inform strategy that jit is about to import the inlinee IL.
802 // Inform strategy about the inline decision for a prejit root
803 void NotePrejitDecision(const InlineResult& r)
805 m_PrejitRootDecision = r.GetPolicy()->GetDecision();
806 m_PrejitRootObservation = r.GetPolicy()->GetObservation();
809 // Dump csv header for inline stats to indicated file.
810 static void DumpCsvHeader(FILE* f);
812 // Dump csv data for inline stats to indicated file.
813 void DumpCsvData(FILE* f);
815 // See if an inline of this size would fit within the current jit
817 bool BudgetCheck(unsigned ilSize);
819 // Check if this method is not allowing inlines.
820 static bool IsNoInline(ICorJitInfo* info, CORINFO_METHOD_HANDLE method);
822 #if defined(DEBUG) || defined(INLINE_DATA)
824 // Dump textual description of inlines done so far.
827 // Dump data-format description of inlines done so far.
829 void DumpDataEnsurePolicyIsSet();
830 void DumpDataHeader(FILE* file);
831 void DumpDataSchema(FILE* file);
832 void DumpDataContents(FILE* file);
834 // Dump xml-formatted description of inlines
835 void DumpXml(FILE* file = stderr, unsigned indent = 0);
836 static void FinalizeXml(FILE* file = stderr);
838 // Cache for file position of this method in the inline xml
839 long GetMethodXmlFilePosition()
841 return m_MethodXmlFilePosition;
844 void SetMethodXmlFilePosition(long val)
846 m_MethodXmlFilePosition = val;
849 // Set up or access random state (for use by RandomPolicy)
850 CLRRandom* GetRandom();
852 #endif // defined(DEBUG) || defined(INLINE_DATA)
854 // Some inline limit values
857 ALWAYS_INLINE_SIZE = 16,
858 IMPLEMENTATION_MAX_INLINE_SIZE = _UI16_MAX,
859 IMPLEMENTATION_MAX_INLINE_DEPTH = 1000
863 // Create a context for the root method.
864 InlineContext* NewRoot();
866 // Accounting updates for a successful or failed inline.
867 void NoteOutcome(InlineContext* context);
869 // Cap on allowable increase in jit time due to inlining.
870 // Multiplicative, so BUDGET = 10 means up to 10x increase
877 // Estimate the jit time change because of this inline.
878 int EstimateTime(InlineContext* context);
880 // EstimateTime helpers
881 int EstimateRootTime(unsigned ilSize);
882 int EstimateInlineTime(unsigned ilSize);
884 // Estimate native code size change because of this inline.
885 int EstimateSize(InlineContext* context);
887 #if defined(DEBUG) || defined(INLINE_DATA)
888 static bool s_HasDumpedDataHeader;
889 static bool s_HasDumpedXmlHeader;
890 static CritSecObject s_XmlWriterLock;
891 #endif // defined(DEBUG) || defined(INLINE_DATA)
893 Compiler* m_Compiler;
894 InlineContext* m_RootContext;
895 InlinePolicy* m_LastSuccessfulPolicy;
896 InlineContext* m_LastContext;
897 InlineDecision m_PrejitRootDecision;
898 InlineObservation m_PrejitRootObservation;
899 unsigned m_CallCount;
900 unsigned m_CandidateCount;
901 unsigned m_AlwaysCandidateCount;
902 unsigned m_ForceCandidateCount;
903 unsigned m_DiscretionaryCandidateCount;
904 unsigned m_UnprofitableCandidateCount;
905 unsigned m_ImportCount;
906 unsigned m_InlineCount;
907 unsigned m_MaxInlineSize;
908 unsigned m_MaxInlineDepth;
909 int m_InitialTimeBudget;
910 int m_InitialTimeEstimate;
911 int m_CurrentTimeBudget;
912 int m_CurrentTimeEstimate;
913 int m_InitialSizeEstimate;
914 int m_CurrentSizeEstimate;
915 bool m_HasForceViaDiscretionary;
917 #if defined(DEBUG) || defined(INLINE_DATA)
918 long m_MethodXmlFilePosition;
920 #endif // defined(DEBUG) || defined(INLINE_DATA)