diff --git a/docs/design/coreclr/jit/viewing-jit-dumps.md b/docs/design/coreclr/jit/viewing-jit-dumps.md index cef68fdac51871..6a13d9859fd798 100644 --- a/docs/design/coreclr/jit/viewing-jit-dumps.md +++ b/docs/design/coreclr/jit/viewing-jit-dumps.md @@ -120,29 +120,59 @@ These can be set in one of three ways: ## Specifying method names -The complete syntax for specifying a single method name (for a flag that takes a method name, such as `COMPlus_JitDump`) is: - +Some environment variables such as `COMPlus_JitDump` take a set of patterns specifying method names. The matching works in the following way: +* A method set string is a space-separated list of patterns. Patterns can arbitrarily contain both '*' (match any characters) and '?' (match any 1 character). +* The string matched against depends on characters in the pattern: + + If the pattern contains a ':' character, the string matched against is prefixed by the class name and a colon + + If the pattern contains a '(' character, the string matched against is suffixed by the signature + + If the class name (part before colon) contains a '[', the class contains its generic instantiation + + If the method name (part between colon and '(') contains a '[', the method contains its generic instantiation + +In particular, the matching is done against strings of the following format which coincides with how the JIT displays method signatures (so these can be copy pasted into the environment variable). ``` -[[.]::][([)] +[ClassName[Instantiation]:]MethodName[Instantiation][()] ``` -For example +For example, consider the following: +```csharp +namespace MyNamespace +{ + public class C + { + [MethodImpl(MethodImplOptions.NoInlining)] + public void M(T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + } + } +} -``` -System.Object::ToString(System.Object) +new C().M(default, default, default, default); // compilation 1 +new C().M(default, default, default, default); // compilation 2 ``` -The namespace, class name, and argument types are optional, and if they are not present, default to a wildcard. Thus stating: +The full names of these instantiations are the following, as printed by `COMPlus_JitDisasmSummary`: ``` -Main +MyNamespace.C`2[byte,System.__Canon]:M[int,System.__Canon](byte,System.__Canon,int,System.__Canon) +MyNamespace.C`2[int,int]:M[int,int](int,int,int,int) ``` +Note that ``C`2`` here is the name put into metadata by Roslyn; the suffix is not added by RyuJIT. +For Powershell users keep in mind that backtick is the escape character and itself has to be escaped via double backtick. -will match all methods named Main from any class and any number of arguments. - -`` is a comma separated list of type names. Note that presently only the number of arguments and not the types themselves are used to distinguish methods. Thus, `Main(Foo, Bar)` and `Main(int, int)` will both match any main method with two arguments. +The following strings will match both compilations: +``` +M +*C`2:M +*C`2[*]:M[*](*) +MyNamespace.C`2:M +``` -The wildcard character `*` can be used for `` and ``. In particular `*` by itself indicates every method. +The following match only the first compilation: +``` +M[int,*Canon] +MyNamespace.C`2[byte,*]:M +M(*Canon) +``` ## Useful COMPlus variables diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 91ae75acc4f006..374fb7b8ab960f 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -2893,9 +2893,15 @@ class ICorStaticInfo CORINFO_METHOD_HANDLE hMethod ) = 0; - // this function is for debugging only. It returns the method name - // and if 'moduleName' is non-null, it sets it to something that will - // says which method (a class name, or a module name) + // This function returns the method name and if 'moduleName' is non-null, + // it sets it to something that contains the method (a class + // name, or a module name). Note that the moduleName parameter is for + // diagnostics only. + // + // The method name returned is the same as getMethodNameFromMetadata except + // in the case of functions without metadata (e.g. IL stubs), where this + // function still returns a reasonable name while getMethodNameFromMetadata + // returns null. virtual const char* getMethodName ( CORINFO_METHOD_HANDLE ftn, /* IN */ const char **moduleName /* OUT */ diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index c003b6be776b20..2447d63a10be31 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 1b9551b8-21f4-4233-9c90-f3eabd6a322b */ - 0x1b9551b8, - 0x21f4, - 0x4233, - {0x9c, 0x90, 0xf3, 0xea, 0xbd, 0x6a, 0x32, 0x2b} +constexpr GUID JITEEVersionIdentifier = { /* 6be47e5d-a92b-4d16-9280-f63df646ada4 */ + 0x6be47e5d, + 0xa92b, + 0x4d16, + {0x92, 0x80, 0xf6, 0x3d, 0xf6, 0x46, 0xad, 0xa4} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index ac7cad00b2a0b4..8ece59eefb1f8e 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -1769,6 +1769,7 @@ void Compiler::compInit(ArenaAllocator* pAlloc, info.compCompHnd = compHnd; info.compMethodHnd = methodHnd; info.compMethodInfo = methodInfo; + info.compClassHnd = compHnd->getMethodClass(methodHnd); #ifdef DEBUG bRangeAllowStress = false; @@ -1788,17 +1789,10 @@ void Compiler::compInit(ArenaAllocator* pAlloc, info.compClassName = nullptr; info.compFullName = nullptr; - const char* classNamePtr; - const char* methodName; - - methodName = eeGetMethodName(methodHnd, &classNamePtr); - unsigned len = (unsigned)roundUp(strlen(classNamePtr) + 1); - info.compClassName = getAllocator(CMK_DebugOnly).allocate(len); - info.compMethodName = methodName; - strcpy_s((char*)info.compClassName, len, classNamePtr); - - info.compFullName = eeGetMethodFullName(methodHnd); - info.compPerfScore = 0.0; + info.compMethodName = eeGetMethodName(methodHnd, nullptr); + info.compClassName = eeGetClassName(info.compClassHnd); + info.compFullName = eeGetMethodFullName(methodHnd); + info.compPerfScore = 0.0; info.compMethodSuperPMIIndex = g_jitHost->getIntConfigValue(W("SuperPMIMethodContextNumber"), -1); #endif // defined(DEBUG) || defined(LATE_DISASM) || DUMP_FLOWGRAPHS @@ -2537,7 +2531,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags) if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_ALT_JIT)) { - if (pfAltJit->contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + if (pfAltJit->contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { opts.altJit = true; } @@ -2618,7 +2612,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags) // if (compIsForImportOnly() && (!altJitConfig || opts.altJit)) { - if (JitConfig.JitImportBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + if (JitConfig.JitImportBreak().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { assert(!"JitImportBreak reached"); } @@ -2633,7 +2627,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags) // if (!compIsForInlining()) { - if (JitConfig.JitDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + if (JitConfig.JitDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { verboseDump = true; } @@ -2868,32 +2862,32 @@ void Compiler::compInitOptions(JitFlags* jitFlags) opts.dspOrder = true; } - if (JitConfig.JitGCDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + if (JitConfig.JitGCDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { opts.dspGCtbls = true; } - if (JitConfig.JitDisasm().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + if (JitConfig.JitDisasm().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { opts.disAsm = true; } - if (JitConfig.JitDisasm().contains("SPILLED", nullptr, nullptr)) + if (JitConfig.JitDisasmSpilled()) { opts.disAsmSpilled = true; } - if (JitConfig.JitUnwindDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + if (JitConfig.JitUnwindDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { opts.dspUnwind = true; } - if (JitConfig.JitEHDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + if (JitConfig.JitEHDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { opts.dspEHTable = true; } - if (JitConfig.JitDebugDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + if (JitConfig.JitDebugDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { opts.dspDebugInfo = true; } @@ -2931,7 +2925,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags) opts.compLongAddress = true; } - if (JitConfig.JitOptRepeat().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + if (JitConfig.JitOptRepeat().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { opts.optRepeat = true; } @@ -2941,7 +2935,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags) // JitEarlyExpandMDArraysFilter. if (JitConfig.JitEarlyExpandMDArrays() == 0) { - if (JitConfig.JitEarlyExpandMDArraysFilter().contains(info.compMethodName, info.compClassName, + if (JitConfig.JitEarlyExpandMDArraysFilter().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { opts.compJitEarlyExpandMDArrays = true; @@ -2982,7 +2976,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags) printf(""); // in our logic this causes a flush } - if (JitConfig.JitBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + if (JitConfig.JitBreak().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { assert(!"JitBreak reached"); } @@ -2994,8 +2988,8 @@ void Compiler::compInitOptions(JitFlags* jitFlags) } if (verbose || - JitConfig.JitDebugBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args) || - JitConfig.JitBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + JitConfig.JitDebugBreak().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args) || + JitConfig.JitBreak().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { compDebugBreak = true; } @@ -3014,14 +3008,9 @@ void Compiler::compInitOptions(JitFlags* jitFlags) s_pJitFunctionFileInitialized = true; } #else // DEBUG - if (!JitConfig.JitDisasm().isEmpty()) + if (JitConfig.JitDisasm().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { - const char* methodName = info.compCompHnd->getMethodName(info.compMethodHnd, nullptr); - const char* className = info.compCompHnd->getClassName(info.compClassHnd); - if (JitConfig.JitDisasm().contains(methodName, className, &info.compMethodInfo->args)) - { - opts.disAsm = true; - } + opts.disAsm = true; } #endif // !DEBUG @@ -3189,21 +3178,21 @@ void Compiler::compInitOptions(JitFlags* jitFlags) // JitForceProcedureSplitting is used to force procedure splitting on checked assemblies. // This is useful for debugging on a checked build. Note that we still only do procedure // splitting in the zapper. - if (JitConfig.JitForceProcedureSplitting().contains(info.compMethodName, info.compClassName, + if (JitConfig.JitForceProcedureSplitting().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { opts.compProcedureSplitting = true; } // JitNoProcedureSplitting will always disable procedure splitting. - if (JitConfig.JitNoProcedureSplitting().contains(info.compMethodName, info.compClassName, + if (JitConfig.JitNoProcedureSplitting().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { opts.compProcedureSplitting = false; } // // JitNoProcedureSplittingEH will disable procedure splitting in functions with EH. - if (JitConfig.JitNoProcedureSplittingEH().contains(info.compMethodName, info.compClassName, + if (JitConfig.JitNoProcedureSplittingEH().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { opts.compProcedureSplittingEH = false; @@ -3319,7 +3308,7 @@ bool Compiler::compJitHaltMethod() /* This method returns true when we use an INS_BREAKPOINT to allow us to step into the generated native code */ /* Note that this these two "Jit" environment variables also work for ngen images */ - if (JitConfig.JitHalt().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + if (JitConfig.JitHalt().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { return true; } @@ -3423,7 +3412,7 @@ bool Compiler::compStressCompileHelper(compStressArea stressArea, unsigned weigh } if (!JitConfig.JitStressOnly().isEmpty() && - !JitConfig.JitStressOnly().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + !JitConfig.JitStressOnly().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { return false; } @@ -3706,7 +3695,7 @@ void Compiler::compSetOptimizationLevel() if (!theMinOptsValue) { - if (JitConfig.JitMinOptsName().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + if (JitConfig.JitMinOptsName().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { theMinOptsValue = true; } @@ -4210,8 +4199,7 @@ const char* Compiler::compGetStressMessage() const { // Or is it excluded via name? if (!JitConfig.JitStressOnly().isEmpty() || - !JitConfig.JitStressOnly().contains(info.compMethodName, info.compClassName, - &info.compMethodInfo->args)) + !JitConfig.JitStressOnly().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { // Not excluded -- stress can happen stressMessage = " JitStress"; @@ -5159,7 +5147,8 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl #ifdef DEBUG const char* fullName = info.compFullName; #else - const char* fullName = eeGetMethodFullName(info.compMethodHnd); + const char* fullName = + eeGetMethodFullName(info.compMethodHnd, /* includeReturnType */ false, /* includeThisSpecifier */ false); #endif char debugPart[128] = {0}; @@ -5522,13 +5511,13 @@ bool Compiler::skipMethod() return true; } - if (JitConfig.JitExclude().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + if (JitConfig.JitExclude().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { return true; } if (!JitConfig.JitInclude().isEmpty() && - !JitConfig.JitInclude().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args)) + !JitConfig.JitInclude().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args)) { return true; } @@ -5834,9 +5823,7 @@ int Compiler::compCompile(CORINFO_MODULE_HANDLE classPtr, { impTokenLookupContextHandle = impInlineInfo->tokenLookupContextHandle; - assert(impInlineInfo->inlineCandidateInfo->clsHandle == info.compCompHnd->getMethodClass(info.compMethodHnd)); - info.compClassHnd = impInlineInfo->inlineCandidateInfo->clsHandle; - + assert(impInlineInfo->inlineCandidateInfo->clsHandle == info.compClassHnd); assert(impInlineInfo->inlineCandidateInfo->clsAttr == info.compCompHnd->getClassAttribs(info.compClassHnd)); // printf("%x != %x\n", impInlineInfo->inlineCandidateInfo->clsAttr, // info.compCompHnd->getClassAttribs(info.compClassHnd)); @@ -5846,7 +5833,6 @@ int Compiler::compCompile(CORINFO_MODULE_HANDLE classPtr, { impTokenLookupContextHandle = METHOD_BEING_COMPILED_CONTEXT(); - info.compClassHnd = info.compCompHnd->getMethodClass(info.compMethodHnd); info.compClassAttr = info.compCompHnd->getClassAttribs(info.compClassHnd); } diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 2d0bf69a28180c..b8b2dfb0581786 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7517,10 +7517,31 @@ class Compiler var_types eeGetFieldType(CORINFO_FIELD_HANDLE fldHnd, CORINFO_CLASS_HANDLE* pStructHnd = nullptr); -#if defined(DEBUG) || defined(FEATURE_JIT_METHOD_PERF) || defined(FEATURE_SIMD) || defined(TRACK_LSRA_STATS) + void eePrintJitType(class StringPrinter* printer, var_types jitType); + void eePrintType(class StringPrinter* printer, + CORINFO_CLASS_HANDLE clsHnd, + bool includeNamespaces, + bool includeInstantiation); + void eePrintTypeOrJitAlias(class StringPrinter* printer, + CORINFO_CLASS_HANDLE clsHnd, + bool includeNamespaces, + bool includeInstantiation); + void eePrintMethod(class StringPrinter* printer, + CORINFO_CLASS_HANDLE clsHnd, + CORINFO_METHOD_HANDLE methodHnd, + CORINFO_SIG_INFO* sig, + bool includeNamespaces, + bool includeClassInstantiation, + bool includeMethodInstantiation, + bool includeSignature, + bool includeReturnType, + bool includeThisSpecifier); +#if defined(DEBUG) || defined(FEATURE_JIT_METHOD_PERF) || defined(FEATURE_SIMD) || defined(TRACK_LSRA_STATS) const char* eeGetMethodName(CORINFO_METHOD_HANDLE hnd, const char** className); - const char* eeGetMethodFullName(CORINFO_METHOD_HANDLE hnd); + const char* eeGetMethodFullName(CORINFO_METHOD_HANDLE hnd, + bool includeReturnType = true, + bool includeThisSpecifier = true); unsigned compMethodHash(CORINFO_METHOD_HANDLE methodHandle); bool eeIsNativeMethod(CORINFO_METHOD_HANDLE method); @@ -7746,6 +7767,12 @@ class Compiler return eeRunWithSPMIErrorTrapImp(reinterpret_cast(function), reinterpret_cast(param)); } + template + bool eeRunFunctorWithSPMIErrorTrap(Functor f) + { + return eeRunWithSPMIErrorTrap([](Functor* pf) { (*pf)(); }, &f); + } + bool eeRunWithSPMIErrorTrapImp(void (*function)(void*), void* param); // Utility functions @@ -11267,6 +11294,46 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ +class StringPrinter +{ + CompAllocator m_alloc; + char* m_buffer; + size_t m_bufferMax; + size_t m_bufferIndex = 0; + +public: + StringPrinter(CompAllocator alloc, char* buffer = nullptr, size_t bufferMax = 0) + : m_alloc(alloc), m_buffer(buffer), m_bufferMax(bufferMax) + { + if ((m_buffer == nullptr) || (m_bufferMax == 0)) + { + m_bufferMax = 128; + m_buffer = alloc.allocate(m_bufferMax); + } + + m_buffer[0] = '\0'; + } + + size_t GetLength() + { + return m_bufferIndex; + } + + char* GetBuffer() + { + assert(m_buffer[GetLength()] == '\0'); + return m_buffer; + } + void Truncate(size_t newLength) + { + assert(newLength <= m_bufferIndex); + m_bufferIndex = newLength; + m_buffer[m_bufferIndex] = '\0'; + } + + void Printf(const char* format, ...); +}; + /***************************************************************************** * * Variables to keep track of total code amounts. diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index 7b5b2961510907..11cfc55305e97c 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -1506,25 +1506,24 @@ const char* Compiler::eeGetFieldName(CORINFO_FIELD_HANDLE field, const char** cl return param.fieldOrMethodOrClassNamePtr; } +//------------------------------------------------------------------------ +// eeGetClassName: +// Get the name (including namespace and instantiation) of a type. +// If missing information (in SPMI), then return a placeholder string. +// +// Return value: +// The name string. +// const char* Compiler::eeGetClassName(CORINFO_CLASS_HANDLE clsHnd) { - FilterSuperPMIExceptionsParam_ee_il param; - - param.pThis = this; - param.pJitInfo = &info; - param.clazz = clsHnd; - - bool success = eeRunWithSPMIErrorTrap( - [](FilterSuperPMIExceptionsParam_ee_il* pParam) { - pParam->fieldOrMethodOrClassNamePtr = pParam->pJitInfo->compCompHnd->getClassName(pParam->clazz); - }, - ¶m); - - if (!success) + StringPrinter printer(getAllocator(CMK_DebugOnly)); + if (!eeRunFunctorWithSPMIErrorTrap([&]() { eePrintType(&printer, clsHnd, true, true); })) { - param.fieldOrMethodOrClassNamePtr = "hackishClassName"; + printer.Truncate(0); + printer.Printf("hackishClassName"); } - return param.fieldOrMethodOrClassNamePtr; + + return printer.GetBuffer(); } #endif // DEBUG || FEATURE_JIT_METHOD_PERF diff --git a/src/coreclr/jit/eeinterface.cpp b/src/coreclr/jit/eeinterface.cpp index 60c685e142b35a..df3a65af43be6d 100644 --- a/src/coreclr/jit/eeinterface.cpp +++ b/src/coreclr/jit/eeinterface.cpp @@ -19,228 +19,327 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif -#if defined(DEBUG) || defined(FEATURE_JIT_METHOD_PERF) || defined(FEATURE_SIMD) +//------------------------------------------------------------------------ +// StringPrinter::Printf: +// Print a formatted string. +// +// Arguments: +// format - the format +// +void StringPrinter::Printf(const char* format, ...) +{ + va_list args; + va_start(args, format); -/*****************************************************************************/ + while (true) + { + size_t bufferLeft = m_bufferMax - m_bufferIndex; + assert(bufferLeft >= 1); // always fit null terminator + + va_list argsCopy; + va_copy(argsCopy, args); + int printed = _vsnprintf_s(m_buffer + m_bufferIndex, bufferLeft, _TRUNCATE, format, argsCopy); + va_end(argsCopy); + + if (printed < 0) + { + // buffer too small + size_t newSize = m_bufferMax * 2; + char* newBuffer = m_alloc.allocate(newSize); + memcpy(newBuffer, m_buffer, m_bufferIndex + 1); // copy null terminator too -/***************************************************************************** - * - * Filter wrapper to handle exception filtering. - * On Unix compilers don't support SEH. - */ + m_buffer = newBuffer; + m_bufferMax = newSize; + } + else + { + m_bufferIndex = m_bufferIndex + static_cast(printed); + break; + } + } + + va_end(args); +} -struct FilterSuperPMIExceptionsParam_eeinterface +#if defined(DEBUG) || defined(FEATURE_JIT_METHOD_PERF) || defined(FEATURE_SIMD) + +//------------------------------------------------------------------------ +// eePrintJitType: +// Print a JIT type. +// +// Arguments: +// printer - the printer +// jitType - the JIT type +// +void Compiler::eePrintJitType(StringPrinter* printer, var_types jitType) { - Compiler* pThis; - Compiler::Info* pJitInfo; - bool hasThis; - size_t siglength; - CORINFO_SIG_INFO sig; - CORINFO_ARG_LIST_HANDLE argLst; - CORINFO_METHOD_HANDLE hnd; - const char* returnType; - const char** pArgNames; - EXCEPTION_POINTERS exceptionPointers; -}; - -const char* Compiler::eeGetMethodFullName(CORINFO_METHOD_HANDLE hnd) + printer->Printf("%s", varTypeName(jitType)); +} + +//------------------------------------------------------------------------ +// eePrintType: +// Print a type given by a class handle. +// +// Arguments: +// printer - the printer +// clsHnd - Handle for the class +// includeNamespace - Whether to print namespaces before type names +// includeInstantiation - Whether to print the instantiation of the class +// +void Compiler::eePrintType(StringPrinter* printer, + CORINFO_CLASS_HANDLE clsHnd, + bool includeNamespace, + bool includeInstantiation) { - const char* className; - const char* methodName = eeGetMethodName(hnd, &className); - if ((eeGetHelperNum(hnd) != CORINFO_HELP_UNDEF) || eeIsNativeMethod(hnd)) + const char* namespaceName; + const char* className = info.compCompHnd->getClassNameFromMetadata(clsHnd, &namespaceName); + if (className == nullptr) { - return methodName; + namespaceName = nullptr; + className = ""; + } + + if (includeNamespace && (namespaceName != nullptr) && (namespaceName[0] != '\0')) + { + printer->Printf("%s.", namespaceName); } - FilterSuperPMIExceptionsParam_eeinterface param; - param.returnType = nullptr; - param.pThis = this; - param.hasThis = false; - param.siglength = 0; - param.hnd = hnd; - param.pJitInfo = &info; + printer->Printf("%s", className); + + if (!includeInstantiation) + { + return; + } - size_t length = 0; - unsigned i; + char pref = '['; + for (unsigned typeArgIndex = 0;; typeArgIndex++) + { + CORINFO_CLASS_HANDLE typeArg = info.compCompHnd->getTypeInstantiationArgument(clsHnd, typeArgIndex); - /* Generating the full signature is a two-pass process. First we have to walk - the components in order to assess the total size, then we allocate the buffer - and copy the elements into it. - */ + if (typeArg == NO_CLASS_HANDLE) + { + break; + } - /* Right now there is a race-condition in the EE, className can be nullptr */ + printer->Printf("%c", pref); + pref = ','; + eePrintTypeOrJitAlias(printer, typeArg, includeNamespace, true); + } - /* initialize length with length of className and '.' */ + if (pref != '[') + { + printer->Printf("]"); + } +} - if (className) +//------------------------------------------------------------------------ +// eePrintTypeOrJitAlias: +// Print a type given by a class handle. If the type is a primitive type, +// prints its JIT alias. +// +// Arguments: +// printer - the printer +// clsHnd - Handle for the class +// includeNamespace - Whether to print namespaces before type names +// includeInstantiation - Whether to print the instantiation of the class +// +void Compiler::eePrintTypeOrJitAlias(StringPrinter* printer, + CORINFO_CLASS_HANDLE clsHnd, + bool includeNamespace, + bool includeInstantiation) +{ + CorInfoType typ = info.compCompHnd->asCorInfoType(clsHnd); + if ((typ == CORINFO_TYPE_CLASS) || (typ == CORINFO_TYPE_VALUECLASS)) { - length = strlen(className) + 1; + eePrintType(printer, clsHnd, includeNamespace, includeInstantiation); } else { - assert(strlen(".") == 7); - length = 7; + eePrintJitType(printer, JitType2PreciseVarType(typ)); } +} - /* add length of methodName and opening bracket */ - length += strlen(methodName) + 1; - - bool success = eeRunWithSPMIErrorTrap( - [](FilterSuperPMIExceptionsParam_eeinterface* pParam) { - - /* figure out the signature */ - - pParam->pThis->eeGetMethodSig(pParam->hnd, &pParam->sig); +//------------------------------------------------------------------------ +// eePrintMethod: +// Print a method given by a method handle, its owning class handle and its +// signature. +// +// Arguments: +// printer - the printer +// clsHnd - Handle for the owning class, or NO_CLASS_HANDLE to not print the class. +// sig - The signature of the method. +// includeNamespaces - Whether to print namespaces before type names. +// includeClassInstantiation - Whether to print the class instantiation. Only valid when clsHnd is passed. +// includeMethodInstantiation - Whether to print the method instantiation. Requires the signature to be passed. +// includeSignature - Whether to print the signature. +// includeReturnType - Whether to include the return type at the end. +// includeThisSpecifier - Whether to include a specifier at the end for whether the method is an instance +// method. +// +void Compiler::eePrintMethod(StringPrinter* printer, + CORINFO_CLASS_HANDLE clsHnd, + CORINFO_METHOD_HANDLE methHnd, + CORINFO_SIG_INFO* sig, + bool includeNamespaces, + bool includeClassInstantiation, + bool includeMethodInstantiation, + bool includeSignature, + bool includeReturnType, + bool includeThisSpecifier) +{ + if (clsHnd != NO_CLASS_HANDLE) + { + eePrintType(printer, clsHnd, includeNamespaces, includeClassInstantiation); + printer->Printf(":"); + } - // allocate space to hold the class names for each of the parameters + const char* methName = info.compCompHnd->getMethodName(methHnd, nullptr); + printer->Printf("%s", methName); - if (pParam->sig.numArgs > 0) - { - pParam->pArgNames = - pParam->pThis->getAllocator(CMK_DebugOnly).allocate(pParam->sig.numArgs); - } - else + if (includeMethodInstantiation && (sig->sigInst.methInstCount > 0)) + { + printer->Printf("["); + for (unsigned i = 0; i < sig->sigInst.methInstCount; i++) + { + if (i > 0) { - pParam->pArgNames = nullptr; + printer->Printf(","); } - unsigned i; - pParam->argLst = pParam->sig.args; + eePrintTypeOrJitAlias(printer, sig->sigInst.methInst[i], includeNamespaces, true); + } + printer->Printf("]"); + } + + if (includeSignature) + { + printer->Printf("("); - for (i = 0; i < pParam->sig.numArgs; i++) + CORINFO_ARG_LIST_HANDLE argLst = sig->args; + for (unsigned i = 0; i < sig->numArgs; i++) + { + if (i > 0) + printer->Printf(","); + + CORINFO_CLASS_HANDLE vcClsHnd; + var_types type = JitType2PreciseVarType(strip(info.compCompHnd->getArgType(sig, argLst, &vcClsHnd))); + switch (type) { - var_types type = pParam->pThis->eeGetArgType(pParam->argLst, &pParam->sig); - switch (type) + case TYP_REF: + case TYP_STRUCT: { - case TYP_REF: - case TYP_STRUCT: + CORINFO_CLASS_HANDLE clsHnd = eeGetArgClass(sig, argLst); + // For some SIMD struct types we can get a nullptr back from eeGetArgClass on Linux/X64 + if (clsHnd != NO_CLASS_HANDLE) { - CORINFO_CLASS_HANDLE clsHnd = pParam->pThis->eeGetArgClass(&pParam->sig, pParam->argLst); - // For some SIMD struct types we can get a nullptr back from eeGetArgClass on Linux/X64 - if (clsHnd != NO_CLASS_HANDLE) - { - const char* clsName = pParam->pThis->eeGetClassName(clsHnd); - if (clsName != nullptr) - { - pParam->pArgNames[i] = clsName; - break; - } - } - } - FALLTHROUGH; - default: - pParam->pArgNames[i] = varTypeName(type); + eePrintType(printer, clsHnd, includeNamespaces, true); break; + } } - pParam->siglength += strlen(pParam->pArgNames[i]); - pParam->argLst = pParam->pJitInfo->compCompHnd->getArgNext(pParam->argLst); + + FALLTHROUGH; + default: + eePrintJitType(printer, type); + break; } - /* add ',' if there is more than one argument */ + argLst = info.compCompHnd->getArgNext(argLst); + } - if (pParam->sig.numArgs > 1) - { - pParam->siglength += (pParam->sig.numArgs - 1); - } + printer->Printf(")"); - var_types retType = JITtype2varType(pParam->sig.retType); + if (includeReturnType) + { + var_types retType = JitType2PreciseVarType(sig->retType); if (retType != TYP_VOID) { + printer->Printf(":"); switch (retType) { case TYP_REF: case TYP_STRUCT: { - CORINFO_CLASS_HANDLE clsHnd = pParam->sig.retTypeClass; + CORINFO_CLASS_HANDLE clsHnd = sig->retTypeClass; if (clsHnd != NO_CLASS_HANDLE) { - const char* clsName = pParam->pThis->eeGetClassName(clsHnd); - if (clsName != nullptr) - { - pParam->returnType = clsName; - break; - } + eePrintType(printer, clsHnd, includeNamespaces, true); + break; } } FALLTHROUGH; default: - pParam->returnType = varTypeName(retType); + eePrintJitType(printer, retType); break; } - pParam->siglength += strlen(pParam->returnType) + 1; // don't forget the delimiter ':' - } - - // Does it have a 'this' pointer? Don't count explicit this, which has the this pointer type as the first - // element of the arg type list - if (pParam->sig.hasThis() && !pParam->sig.hasExplicitThis()) - { - assert(strlen(":this") == 5); - pParam->siglength += 5; - pParam->hasThis = true; } - }, - ¶m); + } - if (!success) - { - param.siglength = 0; + // Does it have a 'this' pointer? Don't count explicit this, which has + // the this pointer type as the first element of the arg type list + if (includeThisSpecifier && sig->hasThis() && !sig->hasExplicitThis()) + { + printer->Printf(":this"); + } } +} - /* add closing bracket and null terminator */ - - length += param.siglength + 2; - - char* retName = getAllocator(CMK_DebugOnly).allocate(length); - - /* Now generate the full signature string in the allocated buffer */ - - if (className) - { - strcpy_s(retName, length, className); - strcat_s(retName, length, ":"); - } - else +//------------------------------------------------------------------------ +// eeGetMethodFullName: +// Get a string describing a method. +// +// Arguments: +// hnd - the method handle +// includeReturnType - Whether to include the return type in the string +// includeThisSpecifier - Whether to include a specifier for whether this is an instance method. +// +// Returns: +// The string. +// +const char* Compiler::eeGetMethodFullName(CORINFO_METHOD_HANDLE hnd, bool includeReturnType, bool includeThisSpecifier) +{ + const char* className; + const char* methodName = eeGetMethodName(hnd, &className); + if ((eeGetHelperNum(hnd) != CORINFO_HELP_UNDEF) || eeIsNativeMethod(hnd)) { - strcpy_s(retName, length, "."); + return methodName; } - strcat_s(retName, length, methodName); + StringPrinter p(getAllocator(CMK_DebugOnly)); + CORINFO_CLASS_HANDLE clsHnd = NO_CLASS_HANDLE; + bool success = eeRunFunctorWithSPMIErrorTrap([&]() { + clsHnd = info.compCompHnd->getMethodClass(hnd); + CORINFO_SIG_INFO sig; + eeGetMethodSig(hnd, &sig); + eePrintMethod(&p, clsHnd, hnd, &sig, + /* includeNamespaces */ true, + /* includeClassInstantiation */ true, + /* includeMethodInstantiation */ true, + /* includeSignature */ true, includeReturnType, includeThisSpecifier); - // append the signature - strcat_s(retName, length, "("); + }); - if (param.siglength > 0) + if (!success) { - param.argLst = param.sig.args; - - for (i = 0; i < param.sig.numArgs; i++) + // Try with bare minimum + p.Truncate(0); + + success = eeRunFunctorWithSPMIErrorTrap([&]() { + eePrintMethod(&p, clsHnd, hnd, + /* sig */ nullptr, + /* includeNamespaces */ true, + /* includeClassInstantiation */ false, + /* includeMethodInstantiation */ false, + /* includeSignature */ false, includeReturnType, includeThisSpecifier); + }); + + if (!success) { - var_types type = eeGetArgType(param.argLst, ¶m.sig); - strcat_s(retName, length, param.pArgNames[i]); - param.argLst = info.compCompHnd->getArgNext(param.argLst); - if (i + 1 < param.sig.numArgs) - { - strcat_s(retName, length, ","); - } + p.Truncate(0); + p.Printf("hackishClassName:hackishMethodName(?)"); } } - strcat_s(retName, length, ")"); - - if (param.returnType != nullptr) - { - strcat_s(retName, length, ":"); - strcat_s(retName, length, param.returnType); - } - - if (param.hasThis) - { - strcat_s(retName, length, ":this"); - } - - assert(strlen(retName) == (length - 1)); - - return (retName); + return p.GetBuffer(); } #endif // defined(DEBUG) || defined(FEATURE_JIT_METHOD_PERF) || defined(FEATURE_SIMD) diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index b083f7869d2633..73445b88d223da 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -467,7 +467,7 @@ FILE* Compiler::fgOpenFlowGraphFile(bool* wbDontClose, Phases phase, PhasePositi } #ifdef DEBUG - dumpFunction = JitConfig.JitDumpFg().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args); + dumpFunction = JitConfig.JitDumpFg().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args); filename = JitConfig.JitDumpFgFile(); pathname = JitConfig.JitDumpFgDir(); diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 86a42a2b87a88e..243eecf117675f 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -658,8 +658,8 @@ PhaseStatus Compiler::fgInline() } #ifdef DEBUG - fgPrintInlinedMethods = JitConfig.JitPrintInlinedMethods().contains(info.compMethodName, info.compClassName, - &info.compMethodInfo->args); + fgPrintInlinedMethods = + JitConfig.JitPrintInlinedMethods().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args); #endif // DEBUG noway_assert(fgFirstBB != nullptr); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 6fbafbcc993313..6cd927dedb619e 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -21589,8 +21589,8 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, // Optionally, print info on devirtualization Compiler* const rootCompiler = impInlineRoot(); - const bool doPrint = JitConfig.JitPrintDevirtualizedMethods().contains(rootCompiler->info.compMethodName, - rootCompiler->info.compClassName, + const bool doPrint = JitConfig.JitPrintDevirtualizedMethods().contains(rootCompiler->info.compMethodHnd, + rootCompiler->info.compClassHnd, &rootCompiler->info.compMethodInfo->args); #endif // DEBUG diff --git a/src/coreclr/jit/jitconfig.cpp b/src/coreclr/jit/jitconfig.cpp index cd65af36f106e9..7e055f0726bb93 100644 --- a/src/coreclr/jit/jitconfig.cpp +++ b/src/coreclr/jit/jitconfig.cpp @@ -36,272 +36,51 @@ void JitConfigValues::MethodSet::initialize(const WCHAR* list, ICorJitHost* host } } - const char SEP_CHAR = ' '; // character used to separate each entry - const char WILD_CHAR = '*'; // character used as the wildcard match everything - char currChar = '?'; // The current character - int nameStart = -1; // Index of the start of the current class or method name - MethodName** lastName = &m_names; // Last entry inserted into the list - bool isQuoted = false; // true while parsing inside a quote "this-is-a-quoted-region" - MethodName currentName; // Buffer used while parsing the current entry - - currentName.m_next = nullptr; - currentName.m_methodNameStart = -1; - currentName.m_methodNameLen = -1; - currentName.m_methodNameWildcardAtStart = false; - currentName.m_methodNameWildcardAtEnd = false; - currentName.m_classNameStart = -1; - currentName.m_classNameLen = -1; - currentName.m_classNameWildcardAtEnd = false; - currentName.m_numArgs = -1; - - enum State - { - NO_NAME, - CLS_NAME, - FUNC_NAME, - ARG_LIST - }; // parsing state machine - - State state = NO_NAME; - for (int i = 0; (currChar != '\0'); i++) - { - currChar = m_list[i]; + auto commitPattern = [this, host](const char* start, const char* end) { + if (end <= start) + { + return; + } + + MethodName* name = static_cast(host->allocateMemory(sizeof(MethodName))); + name->m_next = m_names; + name->m_patternStart = start; + name->m_patternEnd = end; + const char* colon = static_cast(memchr(start, ':', end - start)); + const char* startOfMethodName = colon != nullptr ? colon + 1 : start; + + const char* parens = static_cast(memchr(startOfMethodName, '(', end - startOfMethodName)); + const char* endOfMethodName = parens != nullptr ? parens : end; + name->m_methodNameContainsInstantiation = + memchr(startOfMethodName, '[', endOfMethodName - startOfMethodName) != nullptr; - switch (state) + if (colon != nullptr) { - case NO_NAME: - // skip over zero or more blanks, then expect CLS_NAME - if (currChar != SEP_CHAR) - { - nameStart = i; - state = CLS_NAME; // we have found the start of the next entry - } - break; - - case CLS_NAME: - // Check for a quoted Class Name: (i.e. "MyClass") - if (m_list[nameStart] == '"') - { - // Advance until we see the second " - // - for (; (currChar != '\0'); i++) - { - currChar = m_list[i]; - // Advance until we see the second " - if (currChar == '"') - { - break; - } - // or until we see the end of string - if (currChar == '\0') - { - break; - } - } - - // skip the initial " - nameStart++; - isQuoted = true; - } - - // A colon denotes the end of the Class name and the start of the Method name - if (currChar == ':') - { - // Record the class name - currentName.m_classNameStart = nameStart; - currentName.m_classNameLen = i - nameStart; - - // Also accept the double colon syntax as well (i.e class::method) - // - if (m_list[i + 1] == ':') - { - i++; - } - - if (isQuoted) - { - // Remove the trailing " - currentName.m_classNameLen--; - isQuoted = false; - } - - // Is the first character a wildcard? - if (m_list[currentName.m_classNameStart] == WILD_CHAR) - { - // The class name is a full wildcard; mark it as such. - currentName.m_classNameStart = -1; - currentName.m_classNameLen = -1; - } - // Is there a wildcard at the end of the class name? - // - else if (m_list[currentName.m_classNameStart + currentName.m_classNameLen - 1] == WILD_CHAR) - { - // i.e. bar*:method, will match any class that starts with "bar" - - // Remove the trailing WILD_CHAR from class name - currentName.m_classNameWildcardAtEnd = true; - currentName.m_classNameLen--; // backup for WILD_CHAR - } - - // The method name will start at the next character - nameStart = i + 1; - - // Now expect FUNC_NAME - state = FUNC_NAME; - } - else if ((currChar == '\0') || (currChar == SEP_CHAR) || (currChar == '(')) - { - // Treat this as a method name without a class name. - currentName.m_classNameStart = -1; - currentName.m_classNameLen = -1; - goto DONE_FUNC_NAME; - } - break; - - case FUNC_NAME: - // Check for a quoted method name: i.e. className:"MyFunc" - // - // Note that we may have already parsed a quoted string above in CLS_NAME, i.e. "Func": - if (!isQuoted && (m_list[nameStart] == '"')) - { - // Advance until we see the second " - // - for (; (currChar != '\0'); i++) - { - currChar = m_list[i]; - // Advance until we see the second " - if (currChar == '"') - { - break; - } - // or until we see the end of string - if (currChar == '\0') - { - break; - } - } - - // skip the initial " - nameStart++; - isQuoted = true; - } - - if ((currChar == '\0') || (currChar == SEP_CHAR) || (currChar == '(')) - { - DONE_FUNC_NAME: - assert((currChar == '\0') || (currChar == SEP_CHAR) || (currChar == '(')); - - // Record the method name - currentName.m_methodNameStart = nameStart; - currentName.m_methodNameLen = i - nameStart; - - if (isQuoted) - { - // Remove the trailing " - currentName.m_methodNameLen--; - isQuoted = false; - } - - // Is the first character a wildcard? - if (m_list[currentName.m_methodNameStart] == WILD_CHAR && currentName.m_methodNameLen == 1) - { - // The method name is a full wildcard; mark it as such. - currentName.m_methodNameStart = -1; - currentName.m_methodNameLen = -1; - } - else - { - // Is there a wildcard at the start of the method name? - if (m_list[currentName.m_methodNameStart] == WILD_CHAR) - { - // i.e. class:*foo, will match any method that ends with "foo" - // Remove the leading WILD_CHAR from method name - currentName.m_methodNameStart++; - currentName.m_methodNameLen--; - currentName.m_methodNameWildcardAtStart = true; - } - // Is there a wildcard at the end of the method name? - if (m_list[currentName.m_methodNameStart + currentName.m_methodNameLen - 1] == WILD_CHAR) - { - // i.e. class:foo*, will match any method that starts with "foo" - // Remove the trailing WILD_CHAR from method name - currentName.m_methodNameLen--; // backup for WILD_CHAR - currentName.m_methodNameWildcardAtEnd = true; - } - } - - // should we expect an ARG_LIST? - // - if (currChar == '(') - { - currentName.m_numArgs = -1; - // Expect an ARG_LIST - state = ARG_LIST; - } - else // reached the end of string or a SEP_CHAR - { - assert((currChar == '\0') || (currChar == SEP_CHAR)); - - currentName.m_numArgs = -1; - - // There isn't an ARG_LIST - goto DONE_ARG_LIST; - } - } - break; - - case ARG_LIST: - if ((currChar == '\0') || (currChar == ')')) - { - if (currentName.m_numArgs == -1) - { - currentName.m_numArgs = 0; - } - - DONE_ARG_LIST: - assert((currChar == '\0') || (currChar == SEP_CHAR) || (currChar == ')')); - - // We have parsed an entire method name; create a new entry in the list for it. - MethodName* name = static_cast(host->allocateMemory(sizeof(MethodName))); - *name = currentName; - - assert(name->m_next == nullptr); - *lastName = name; - lastName = &name->m_next; - - state = NO_NAME; - - // Skip anything after the argument list until we find the next - // separator character. Otherwise if we see "func(a,b):foo" we - // would create entries for "func(a,b)" as well as ":foo". - if (currChar == ')') - { - do - { - currChar = m_list[++i]; - } while ((currChar != '\0') && (currChar != SEP_CHAR)); - } - } - else // We are looking at the ARG_LIST - { - if ((currChar != SEP_CHAR) && (currentName.m_numArgs == -1)) - { - currentName.m_numArgs = 1; - } - - // A comma means that there is an additional arg - if (currChar == ',') - { - currentName.m_numArgs++; - } - } - break; - - default: - assert(!"Bad state"); - break; + name->m_containsClassName = true; + name->m_classNameContainsInstantiation = memchr(start, '[', colon - start) != nullptr; + } + else + { + name->m_containsClassName = false; + name->m_classNameContainsInstantiation = false; + } + + name->m_containsSignature = parens != nullptr; + m_names = name; + }; + + const char* curPatternStart = m_list; + const char* curChar; + for (curChar = curPatternStart; *curChar != '\0'; curChar++) + { + if (*curChar == ' ') + { + commitPattern(curPatternStart, curChar); + curPatternStart = curChar + 1; } } + + commitPattern(curPatternStart, curChar); } void JitConfigValues::MethodSet::destroy(ICorJitHost* host) @@ -320,108 +99,91 @@ void JitConfigValues::MethodSet::destroy(ICorJitHost* host) m_names = nullptr; } -// strstr that is length-limited, this implementation is not intended to be used on hot paths -static size_t strnstr(const char* pSrc, size_t srcSize, const char* needle, size_t needleSize) +// Quadratic string matching algorithm that supports * and ? wildcards +static bool matchGlob(const char* pattern, const char* patternEnd, const char* str) { - if (srcSize < needleSize) - { - return -1; - } + // Invariant: [patternStart..backtrackPattern) matches [stringStart..backtrackStr) + const char* backtrackPattern = nullptr; + const char* backtrackStr = nullptr; - for (size_t srcPos = 0; srcPos <= srcSize - needleSize; srcPos++) + while (true) { - if (strncmp(pSrc + srcPos, needle, needleSize) == 0) + if (pattern == patternEnd) { - return srcPos; + if (*str == '\0') + return true; + } + else if (*pattern == '*') + { + backtrackPattern = ++pattern; + backtrackStr = str; + continue; + } + else if (*str == '\0') + { + // No match since pattern needs at least one char in remaining cases. + } + else if ((*pattern == '?') || (*pattern == *str)) + { + pattern++; + str++; + continue; } - } - return -1; -} -static bool matchesName( - const char* const name, int nameLen, bool wildcardAtStart, bool wildcardAtEnd, const char* const s2) -{ - if (wildcardAtStart && (int)strnstr(s2, strlen(s2), name, nameLen) == -1) - { - return false; - } + // In this case there was no match, see if we can backtrack to a wild + // card and consume one more character from the string. + if ((backtrackPattern == nullptr) || (*backtrackStr == '\0')) + return false; - if (!wildcardAtStart && strncmp(name, s2, nameLen) != 0) - { - // 's2' must start with 'nameLen' characters of 'name' - return false; + // Consume one more character for the wildcard. + pattern = backtrackPattern; + str = ++backtrackStr; } +} - // if we don't have a wildcardAtEnd then s2 also need to be zero terminated - if (!wildcardAtEnd && (s2[nameLen] != '\0')) +bool JitConfigValues::MethodSet::contains(CORINFO_METHOD_HANDLE methodHnd, + CORINFO_CLASS_HANDLE classHnd, + CORINFO_SIG_INFO* sigInfo) const +{ + if (isEmpty()) { return false; } - // we have a successful match - return true; -} - -bool JitConfigValues::MethodSet::contains(const char* methodName, - const char* className, - CORINFO_SIG_INFO* sigInfo) const -{ - int numArgs = sigInfo != nullptr ? sigInfo->numArgs : -1; + Compiler* comp = JitTls::GetCompiler(); + char buffer[1024]; + StringPrinter printer(comp->getAllocator(CMK_DebugOnly), buffer, ArrLen(buffer)); + MethodName* prevPattern = nullptr; - // Try to match any the entries in the list. for (MethodName* name = m_names; name != nullptr; name = name->m_next) { - // If m_numArgs is valid, check for a mismatch - if (name->m_numArgs != -1 && name->m_numArgs != numArgs) - { - continue; - } - - // If m_methodNameStart is valid, check for a mismatch - if (name->m_methodNameStart != -1) + if ((prevPattern == nullptr) || (name->m_containsClassName != prevPattern->m_containsClassName) || + (name->m_classNameContainsInstantiation != prevPattern->m_classNameContainsInstantiation) || + (name->m_methodNameContainsInstantiation != prevPattern->m_methodNameContainsInstantiation) || + (name->m_containsSignature != prevPattern->m_containsSignature)) { - const char* expectedMethodName = &m_list[name->m_methodNameStart]; - if (!matchesName(expectedMethodName, name->m_methodNameLen, name->m_methodNameWildcardAtStart, - name->m_methodNameWildcardAtEnd, methodName)) - { - // C++ embeds the class name into the method name; deal with that here. - const char* colon = strchr(methodName, ':'); - if (colon != nullptr && colon[1] == ':' && - matchesName(expectedMethodName, name->m_methodNameLen, name->m_methodNameWildcardAtStart, - name->m_methodNameWildcardAtEnd, methodName)) - { - int classLen = (int)(colon - methodName); - if (name->m_classNameStart == -1 || - (classLen == name->m_classNameLen && - strncmp(&m_list[name->m_classNameStart], methodName, classLen) == 0)) - { - return true; - } - } + printer.Truncate(0); + bool success = comp->eeRunFunctorWithSPMIErrorTrap([&]() { + comp->eePrintMethod(&printer, name->m_containsClassName ? classHnd : NO_CLASS_HANDLE, methodHnd, + sigInfo, + /* includeNamespaces */ true, + /* includeClassInstantiation */ name->m_classNameContainsInstantiation, + /* includeMethodInstantiation */ name->m_methodNameContainsInstantiation, + /* includeSignature */ name->m_containsSignature, + /* includeReturnType */ false, + /* includeThis */ false); + }); + + if (!success) continue; - } - } - // If m_classNameStart is valid, check for a mismatch - if (className == nullptr || name->m_classNameStart == -1 || - matchesName(&m_list[name->m_classNameStart], name->m_classNameLen, false, false, className)) - { - return true; + prevPattern = name; } -#ifdef _DEBUG - // Maybe className doesn't include the namespace. Try to match that - const char* nsSep = strrchr(className, '.'); - if (nsSep != nullptr && nsSep != className) + if (matchGlob(name->m_patternStart, name->m_patternEnd, printer.GetBuffer())) { - const char* onlyClass = nsSep[-1] == '.' ? nsSep : &nsSep[1]; - if (matchesName(&m_list[name->m_classNameStart], name->m_classNameLen, false, - name->m_classNameWildcardAtEnd, onlyClass)) - { - return true; - } + return true; } -#endif } return false; diff --git a/src/coreclr/jit/jitconfig.h b/src/coreclr/jit/jitconfig.h index 90f3daf9f63174..e19021cd52f22b 100644 --- a/src/coreclr/jit/jitconfig.h +++ b/src/coreclr/jit/jitconfig.h @@ -7,6 +7,8 @@ #include "switches.h" struct CORINFO_SIG_INFO; +typedef struct CORINFO_CLASS_STRUCT_* CORINFO_CLASS_HANDLE; +typedef struct CORINFO_METHOD_STRUCT_* CORINFO_METHOD_HANDLE; class ICorJitHost; class JitConfigValues @@ -18,14 +20,12 @@ class JitConfigValues struct MethodName { MethodName* m_next; - int m_methodNameStart; - int m_methodNameLen; - bool m_methodNameWildcardAtStart; - bool m_methodNameWildcardAtEnd; - int m_classNameStart; - int m_classNameLen; - bool m_classNameWildcardAtEnd; - int m_numArgs; + const char* m_patternStart; + const char* m_patternEnd; + bool m_containsClassName; + bool m_classNameContainsInstantiation; + bool m_methodNameContainsInstantiation; + bool m_containsSignature; }; char* m_list; @@ -51,7 +51,7 @@ class JitConfigValues { return m_names == nullptr; } - bool contains(const char* methodName, const char* className, CORINFO_SIG_INFO* sigInfo) const; + bool contains(CORINFO_METHOD_HANDLE methodHnd, CORINFO_CLASS_HANDLE classHnd, CORINFO_SIG_INFO* sigInfo) const; }; private: diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 2c15b310b1a91f..ce7df4f88e54f6 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -190,10 +190,11 @@ CONFIG_STRING(JitDisasmAssemblies, W("JitDisasmAssemblies")) // Only show JitDis CONFIG_INTEGER(JitDisasmWithGC, W("JitDisasmWithGC"), 0) // Dump interleaved GC Info for any method disassembled. CONFIG_INTEGER(JitDisasmWithDebugInfo, W("JitDisasmWithDebugInfo"), 0) // Dump interleaved debug info for any method // disassembled. -CONFIG_METHODSET(JitDump, W("JitDump")) // Dumps trees for specified method -CONFIG_INTEGER(JitDumpTier0, W("JitDumpTier0"), 1) // Dump tier0 requests -CONFIG_INTEGER(JitDumpAtOSROffset, W("JitDumpAtOSROffset"), -1) // Only dump OSR requests for this offset -CONFIG_INTEGER(JitDumpInlinePhases, W("JitDumpInlinePhases"), 1) // Dump inline compiler phases +CONFIG_INTEGER(JitDisasmSpilled, W("JitDisasmSpilled"), 0) // Display native code when any register spilling occurs +CONFIG_METHODSET(JitDump, W("JitDump")) // Dumps trees for specified method +CONFIG_INTEGER(JitDumpTier0, W("JitDumpTier0"), 1) // Dump tier0 requests +CONFIG_INTEGER(JitDumpAtOSROffset, W("JitDumpAtOSROffset"), -1) // Only dump OSR requests for this offset +CONFIG_INTEGER(JitDumpInlinePhases, W("JitDumpInlinePhases"), 1) // Dump inline compiler phases CONFIG_METHODSET(JitEHDump, W("JitEHDump")) // Dump the EH table for the method, as reported to the VM CONFIG_METHODSET(JitExclude, W("JitExclude")) CONFIG_INTEGER(JitFakeProcedureSplitting, W("JitFakeProcedureSplitting"), 0) // Do code splitting independent of VM. diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index c1f59c124b6769..6d287dee31f44f 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -776,27 +776,25 @@ private void Get_CORINFO_SIG_INFO(MethodDesc method, CORINFO_SIG_INFO* sig, Meth if (method.IsArrayAddressMethod()) hasHiddenParameter = true; - - // We only populate sigInst for intrinsic methods because most of the time, - // JIT doesn't care what the instantiation is and this is expensive. - Instantiation owningTypeInst = method.OwningType.Instantiation; - sig->sigInst.classInstCount = (uint)owningTypeInst.Length; - if (owningTypeInst.Length != 0) - { - sig->sigInst.classInst = GetJitInstantiation(owningTypeInst); - } - - sig->sigInst.methInstCount = (uint)method.Instantiation.Length; - if (method.Instantiation.Length != 0) - { - sig->sigInst.methInst = GetJitInstantiation(method.Instantiation); - } } if (hasHiddenParameter) { sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_PARAMTYPE; } + + Instantiation owningTypeInst = method.OwningType.Instantiation; + sig->sigInst.classInstCount = (uint)owningTypeInst.Length; + if (owningTypeInst.Length != 0) + { + sig->sigInst.classInst = GetJitInstantiation(owningTypeInst); + } + + sig->sigInst.methInstCount = (uint)method.Instantiation.Length; + if (method.Instantiation.Length != 0) + { + sig->sigInst.methInst = GetJitInstantiation(method.Instantiation); + } } private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* sig, MethodILScope scope) @@ -823,7 +821,7 @@ private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* s sig->sigInst.classInst = null; // Not used by the JIT sig->sigInst.classInstCount = 0; // Not used by the JIT - sig->sigInst.methInst = null; // Not used by the JIT + sig->sigInst.methInst = null; sig->sigInst.methInstCount = (uint)signature.GenericParameterCount; sig->pSig = null; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index 503e2c42bcf1f1..ce147abc89be60 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -68,7 +68,7 @@ LWM(GetClassGClayout, DWORDLONG, Agnostic_GetClassGClayout) LWM(GetClassModuleIdForStatics, DWORDLONG, Agnostic_GetClassModuleIdForStatics) LWM(GetClassName, DWORDLONG, DWORD) LWM(GetClassNameFromMetadata, DLD, DD) -LWM(GetTypeInstantiationArgument, DWORDLONG, DWORDLONG) +LWM(GetTypeInstantiationArgument, DLD, DWORDLONG) LWM(GetClassNumInstanceFields, DWORDLONG, DWORD) LWM(GetClassSize, DWORDLONG, DWORD) LWM(GetHeapClassSize, DWORDLONG, DWORD) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index e8a883ed2d606f..fa4441812314d3 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6449,27 +6449,33 @@ const char* MethodContext::repGetClassNameFromMetadata(CORINFO_CLASS_HANDLE cls, } void MethodContext::recGetTypeInstantiationArgument(CORINFO_CLASS_HANDLE cls, - CORINFO_CLASS_HANDLE result, - unsigned index) + unsigned index, + CORINFO_CLASS_HANDLE result) { if (GetTypeInstantiationArgument == nullptr) - GetTypeInstantiationArgument = new LightWeightMap(); + GetTypeInstantiationArgument = new LightWeightMap(); - DWORDLONG key = CastHandle(cls); + DLD key; + ZeroMemory(&key, sizeof(key)); + key.A = CastHandle(cls); + key.B = index; DWORDLONG value = CastHandle(result); GetTypeInstantiationArgument->Add(key, value); DEBUG_REC(dmpGetTypeInstantiationArgument(key, value)); } -void MethodContext::dmpGetTypeInstantiationArgument(DWORDLONG key, DWORDLONG value) +void MethodContext::dmpGetTypeInstantiationArgument(DLD key, DWORDLONG value) { - printf("GetTypeInstantiationArgument key - classNonNull-%llu, value NonNull-%llu", key, value); + printf("GetTypeInstantiationArgument key - classNonNull-%llu, index-%u, value NonNull-%llu", key.A, key.B, value); GetTypeInstantiationArgument->Unlock(); } CORINFO_CLASS_HANDLE MethodContext::repGetTypeInstantiationArgument(CORINFO_CLASS_HANDLE cls, unsigned index) { CORINFO_CLASS_HANDLE result = nullptr; - DWORDLONG key = CastHandle(cls); + DLD key; + ZeroMemory(&key, sizeof(key)); + key.A = CastHandle(cls); + key.B = index; int itemIndex = -1; if (GetTypeInstantiationArgument != nullptr) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 3fc5247ef66c20..66a3b90e49a601 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -796,8 +796,8 @@ class MethodContext void dmpGetClassNameFromMetadata(DLD key, DD value); const char* repGetClassNameFromMetadata(CORINFO_CLASS_HANDLE cls, const char** namespaceName); - void recGetTypeInstantiationArgument(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result, unsigned index); - void dmpGetTypeInstantiationArgument(DWORDLONG key, DWORDLONG value); + void recGetTypeInstantiationArgument(CORINFO_CLASS_HANDLE cls, unsigned index, CORINFO_CLASS_HANDLE result); + void dmpGetTypeInstantiationArgument(DLD key, DWORDLONG value); CORINFO_CLASS_HANDLE repGetTypeInstantiationArgument(CORINFO_CLASS_HANDLE cls, unsigned index); void recAppendClassName(int nBufLenIn, diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 5d146d2de2cd77..2943db7a5376f4 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -489,9 +489,9 @@ const char* interceptor_ICJI::getClassNameFromMetadata(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE interceptor_ICJI::getTypeInstantiationArgument(CORINFO_CLASS_HANDLE cls, unsigned index) { mc->cr->AddCall("getTypeInstantiationArgument"); - CORINFO_CLASS_HANDLE temp = original_ICorJitInfo->getTypeInstantiationArgument(cls, index); - mc->recGetTypeInstantiationArgument(cls, temp, index); - return temp; + CORINFO_CLASS_HANDLE result = original_ICorJitInfo->getTypeInstantiationArgument(cls, index); + mc->recGetTypeInstantiationArgument(cls, index, result); + return result; } // Append a (possibly truncated) textual representation of the type `cls` to a preallocated buffer.