Skip to content

Commit 243cf9f

Browse files
authored
JIT: Simplify JitDisasm matching behavior (#74430)
This changes how the JIT matches method names and signatures for method sets (e.g. JitDisasm). It also starts printing method instantiations for full method names and makes references to types consistent in generic instantiations and the signature. In addition it starts supporting generic instantiations in release too. To do this, most of the type printing is moved to the JIT, which also aligns the output between crossgen2 and the VM (there were subtle differences here, like spaces between generic type arguments). More importantly, we (for the most part) stop relying on JIT-EE methods that are documented to only be for debug purposes. The new behavior of the matching is the following: * The matching behavior is always string based. * The JitDisasm 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 For example, consider ``` namespace MyNamespace { public class C<T1, T2> { [MethodImpl(MethodImplOptions.NoInlining)] public void M<T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4) { } } } new C<sbyte, string>().M<int, object>(default, default, default, default); // compilation 1 new C<int, int>().M<int, int>(default, default, default, default); // compilation 2 ``` The full strings are: Before the change: ``` MyNamespace.C`2[SByte,__Canon][System.SByte,System.__Canon]:M(byte,System.__Canon,int,System.__Canon) MyNamespace.C`2[Int32,Int32][System.Int32,System.Int32]:M(int,int,int,int) ``` Notice no method instantiation and the double class instantiation, which seems like an EE bug. Also two different names are used for sbyte: System.SByte and byte. After the change the strings are: ``` 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) ``` The following strings will match both compilations: ``` M *C`2:M *C`2[*]:M[*](*) MyNamespace.C`2:M ``` The following will match only the first one: ``` M[int,*Canon] MyNamespace.C`2[byte,*]:M M(*Canon) ``` There is one significant change in behavior here, which is that I have removed the special case that allows matching class names without namespaces. In particular, today Console:WriteLine would match all overloads of System.Console.WriteLine, while after this change it will not match. However, with generalized wild cards the replacement is simple in *Console:WriteLine.
1 parent 2d1e29b commit 243cf9f

File tree

18 files changed

+585
-631
lines changed

18 files changed

+585
-631
lines changed

docs/design/coreclr/jit/viewing-jit-dumps.md

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -120,29 +120,59 @@ These can be set in one of three ways:
120120

121121
## Specifying method names
122122

123-
The complete syntax for specifying a single method name (for a flag that takes a method name, such as `COMPlus_JitDump`) is:
124-
123+
Some environment variables such as `COMPlus_JitDump` take a set of patterns specifying method names. The matching works in the following way:
124+
* A method set string is a space-separated list of patterns. Patterns can arbitrarily contain both '*' (match any characters) and '?' (match any 1 character).
125+
* The string matched against depends on characters in the pattern:
126+
+ If the pattern contains a ':' character, the string matched against is prefixed by the class name and a colon
127+
+ If the pattern contains a '(' character, the string matched against is suffixed by the signature
128+
+ If the class name (part before colon) contains a '[', the class contains its generic instantiation
129+
+ If the method name (part between colon and '(') contains a '[', the method contains its generic instantiation
130+
131+
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).
125132
```
126-
[[<Namespace>.]<ClassName>::]<MethodName>[([<types>)]
133+
[ClassName[Instantiation]:]MethodName[Instantiation][(<types>)]
127134
```
128135

129-
For example
136+
For example, consider the following:
137+
```csharp
138+
namespace MyNamespace
139+
{
140+
public class C<T1, T2>
141+
{
142+
[MethodImpl(MethodImplOptions.NoInlining)]
143+
public void M<T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
144+
{
145+
}
146+
}
147+
}
130148

131-
```
132-
System.Object::ToString(System.Object)
149+
new C<sbyte, string>().M<int, object>(default, default, default, default); // compilation 1
150+
new C<int, int>().M<int, int>(default, default, default, default); // compilation 2
133151
```
134152

135-
The namespace, class name, and argument types are optional, and if they are not present, default to a wildcard. Thus stating:
153+
The full names of these instantiations are the following, as printed by `COMPlus_JitDisasmSummary`:
136154

137155
```
138-
Main
156+
MyNamespace.C`2[byte,System.__Canon]:M[int,System.__Canon](byte,System.__Canon,int,System.__Canon)
157+
MyNamespace.C`2[int,int]:M[int,int](int,int,int,int)
139158
```
159+
Note that ``C`2`` here is the name put into metadata by Roslyn; the suffix is not added by RyuJIT.
160+
For Powershell users keep in mind that backtick is the escape character and itself has to be escaped via double backtick.
140161

141-
will match all methods named Main from any class and any number of arguments.
142-
143-
`<types>` 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.
162+
The following strings will match both compilations:
163+
```
164+
M
165+
*C`2:M
166+
*C`2[*]:M[*](*)
167+
MyNamespace.C`2:M
168+
```
144169

145-
The wildcard character `*` can be used for `<ClassName>` and `<MethodName>`. In particular `*` by itself indicates every method.
170+
The following match only the first compilation:
171+
```
172+
M[int,*Canon]
173+
MyNamespace.C`2[byte,*]:M
174+
M(*Canon)
175+
```
146176

147177
## Useful COMPlus variables
148178

src/coreclr/inc/corinfo.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2893,9 +2893,15 @@ class ICorStaticInfo
28932893
CORINFO_METHOD_HANDLE hMethod
28942894
) = 0;
28952895

2896-
// this function is for debugging only. It returns the method name
2897-
// and if 'moduleName' is non-null, it sets it to something that will
2898-
// says which method (a class name, or a module name)
2896+
// This function returns the method name and if 'moduleName' is non-null,
2897+
// it sets it to something that contains the method (a class
2898+
// name, or a module name). Note that the moduleName parameter is for
2899+
// diagnostics only.
2900+
//
2901+
// The method name returned is the same as getMethodNameFromMetadata except
2902+
// in the case of functions without metadata (e.g. IL stubs), where this
2903+
// function still returns a reasonable name while getMethodNameFromMetadata
2904+
// returns null.
28992905
virtual const char* getMethodName (
29002906
CORINFO_METHOD_HANDLE ftn, /* IN */
29012907
const char **moduleName /* OUT */

src/coreclr/inc/jiteeversionguid.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
4343
#define GUID_DEFINED
4444
#endif // !GUID_DEFINED
4545

46-
constexpr GUID JITEEVersionIdentifier = { /* 1b9551b8-21f4-4233-9c90-f3eabd6a322b */
47-
0x1b9551b8,
48-
0x21f4,
49-
0x4233,
50-
{0x9c, 0x90, 0xf3, 0xea, 0xbd, 0x6a, 0x32, 0x2b}
46+
constexpr GUID JITEEVersionIdentifier = { /* 0cd8b9d4-04f4-45a7-b16b-7f24b7c0a454 */
47+
0x0cd8b9d4,
48+
0x04f4,
49+
0x45a7,
50+
{0xb1, 0x6b, 0x7f, 0x24, 0xb7, 0xc0, 0xa4, 0x54}
5151
};
5252

5353
//////////////////////////////////////////////////////////////////////////////////////////////////////////

src/coreclr/jit/compiler.cpp

Lines changed: 33 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,6 +1769,7 @@ void Compiler::compInit(ArenaAllocator* pAlloc,
17691769
info.compCompHnd = compHnd;
17701770
info.compMethodHnd = methodHnd;
17711771
info.compMethodInfo = methodInfo;
1772+
info.compClassHnd = compHnd->getMethodClass(methodHnd);
17721773

17731774
#ifdef DEBUG
17741775
bRangeAllowStress = false;
@@ -1788,17 +1789,10 @@ void Compiler::compInit(ArenaAllocator* pAlloc,
17881789
info.compClassName = nullptr;
17891790
info.compFullName = nullptr;
17901791

1791-
const char* classNamePtr;
1792-
const char* methodName;
1793-
1794-
methodName = eeGetMethodName(methodHnd, &classNamePtr);
1795-
unsigned len = (unsigned)roundUp(strlen(classNamePtr) + 1);
1796-
info.compClassName = getAllocator(CMK_DebugOnly).allocate<char>(len);
1797-
info.compMethodName = methodName;
1798-
strcpy_s((char*)info.compClassName, len, classNamePtr);
1799-
1800-
info.compFullName = eeGetMethodFullName(methodHnd);
1801-
info.compPerfScore = 0.0;
1792+
info.compMethodName = eeGetMethodName(methodHnd, nullptr);
1793+
info.compClassName = eeGetClassName(info.compClassHnd);
1794+
info.compFullName = eeGetMethodFullName(methodHnd);
1795+
info.compPerfScore = 0.0;
18021796

18031797
info.compMethodSuperPMIIndex = g_jitHost->getIntConfigValue(W("SuperPMIMethodContextNumber"), -1);
18041798
#endif // defined(DEBUG) || defined(LATE_DISASM) || DUMP_FLOWGRAPHS
@@ -2534,7 +2528,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
25342528

25352529
if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_ALT_JIT))
25362530
{
2537-
if (pfAltJit->contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2531+
if (pfAltJit->contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
25382532
{
25392533
opts.altJit = true;
25402534
}
@@ -2615,7 +2609,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
26152609
//
26162610
if (compIsForImportOnly() && (!altJitConfig || opts.altJit))
26172611
{
2618-
if (JitConfig.JitImportBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2612+
if (JitConfig.JitImportBreak().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
26192613
{
26202614
assert(!"JitImportBreak reached");
26212615
}
@@ -2630,7 +2624,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
26302624
//
26312625
if (!compIsForInlining())
26322626
{
2633-
if (JitConfig.JitDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2627+
if (JitConfig.JitDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
26342628
{
26352629
verboseDump = true;
26362630
}
@@ -2865,32 +2859,32 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
28652859
opts.dspOrder = true;
28662860
}
28672861

2868-
if (JitConfig.JitGCDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2862+
if (JitConfig.JitGCDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
28692863
{
28702864
opts.dspGCtbls = true;
28712865
}
28722866

2873-
if (JitConfig.JitDisasm().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2867+
if (JitConfig.JitDisasm().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
28742868
{
28752869
opts.disAsm = true;
28762870
}
28772871

2878-
if (JitConfig.JitDisasm().contains("SPILLED", nullptr, nullptr))
2872+
if (JitConfig.JitDisasmSpilled())
28792873
{
28802874
opts.disAsmSpilled = true;
28812875
}
28822876

2883-
if (JitConfig.JitUnwindDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2877+
if (JitConfig.JitUnwindDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
28842878
{
28852879
opts.dspUnwind = true;
28862880
}
28872881

2888-
if (JitConfig.JitEHDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2882+
if (JitConfig.JitEHDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
28892883
{
28902884
opts.dspEHTable = true;
28912885
}
28922886

2893-
if (JitConfig.JitDebugDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2887+
if (JitConfig.JitDebugDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
28942888
{
28952889
opts.dspDebugInfo = true;
28962890
}
@@ -2928,7 +2922,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
29282922
opts.compLongAddress = true;
29292923
}
29302924

2931-
if (JitConfig.JitOptRepeat().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2925+
if (JitConfig.JitOptRepeat().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
29322926
{
29332927
opts.optRepeat = true;
29342928
}
@@ -2938,7 +2932,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
29382932
// JitEarlyExpandMDArraysFilter.
29392933
if (JitConfig.JitEarlyExpandMDArrays() == 0)
29402934
{
2941-
if (JitConfig.JitEarlyExpandMDArraysFilter().contains(info.compMethodName, info.compClassName,
2935+
if (JitConfig.JitEarlyExpandMDArraysFilter().contains(info.compMethodHnd, info.compClassHnd,
29422936
&info.compMethodInfo->args))
29432937
{
29442938
opts.compJitEarlyExpandMDArrays = true;
@@ -2979,7 +2973,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
29792973
printf(""); // in our logic this causes a flush
29802974
}
29812975

2982-
if (JitConfig.JitBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2976+
if (JitConfig.JitBreak().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
29832977
{
29842978
assert(!"JitBreak reached");
29852979
}
@@ -2991,8 +2985,8 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
29912985
}
29922986

29932987
if (verbose ||
2994-
JitConfig.JitDebugBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args) ||
2995-
JitConfig.JitBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2988+
JitConfig.JitDebugBreak().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args) ||
2989+
JitConfig.JitBreak().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
29962990
{
29972991
compDebugBreak = true;
29982992
}
@@ -3011,14 +3005,9 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
30113005
s_pJitFunctionFileInitialized = true;
30123006
}
30133007
#else // DEBUG
3014-
if (!JitConfig.JitDisasm().isEmpty())
3008+
if (JitConfig.JitDisasm().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
30153009
{
3016-
const char* methodName = info.compCompHnd->getMethodName(info.compMethodHnd, nullptr);
3017-
const char* className = info.compCompHnd->getClassName(info.compClassHnd);
3018-
if (JitConfig.JitDisasm().contains(methodName, className, &info.compMethodInfo->args))
3019-
{
3020-
opts.disAsm = true;
3021-
}
3010+
opts.disAsm = true;
30223011
}
30233012
#endif // !DEBUG
30243013

@@ -3186,21 +3175,21 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
31863175
// JitForceProcedureSplitting is used to force procedure splitting on checked assemblies.
31873176
// This is useful for debugging on a checked build. Note that we still only do procedure
31883177
// splitting in the zapper.
3189-
if (JitConfig.JitForceProcedureSplitting().contains(info.compMethodName, info.compClassName,
3178+
if (JitConfig.JitForceProcedureSplitting().contains(info.compMethodHnd, info.compClassHnd,
31903179
&info.compMethodInfo->args))
31913180
{
31923181
opts.compProcedureSplitting = true;
31933182
}
31943183

31953184
// JitNoProcedureSplitting will always disable procedure splitting.
3196-
if (JitConfig.JitNoProcedureSplitting().contains(info.compMethodName, info.compClassName,
3185+
if (JitConfig.JitNoProcedureSplitting().contains(info.compMethodHnd, info.compClassHnd,
31973186
&info.compMethodInfo->args))
31983187
{
31993188
opts.compProcedureSplitting = false;
32003189
}
32013190
//
32023191
// JitNoProcedureSplittingEH will disable procedure splitting in functions with EH.
3203-
if (JitConfig.JitNoProcedureSplittingEH().contains(info.compMethodName, info.compClassName,
3192+
if (JitConfig.JitNoProcedureSplittingEH().contains(info.compMethodHnd, info.compClassHnd,
32043193
&info.compMethodInfo->args))
32053194
{
32063195
opts.compProcedureSplittingEH = false;
@@ -3316,7 +3305,7 @@ bool Compiler::compJitHaltMethod()
33163305
/* This method returns true when we use an INS_BREAKPOINT to allow us to step into the generated native code */
33173306
/* Note that this these two "Jit" environment variables also work for ngen images */
33183307

3319-
if (JitConfig.JitHalt().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
3308+
if (JitConfig.JitHalt().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
33203309
{
33213310
return true;
33223311
}
@@ -3420,7 +3409,7 @@ bool Compiler::compStressCompileHelper(compStressArea stressArea, unsigned weigh
34203409
}
34213410

34223411
if (!JitConfig.JitStressOnly().isEmpty() &&
3423-
!JitConfig.JitStressOnly().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
3412+
!JitConfig.JitStressOnly().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
34243413
{
34253414
return false;
34263415
}
@@ -3703,7 +3692,7 @@ void Compiler::compSetOptimizationLevel()
37033692

37043693
if (!theMinOptsValue)
37053694
{
3706-
if (JitConfig.JitMinOptsName().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
3695+
if (JitConfig.JitMinOptsName().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
37073696
{
37083697
theMinOptsValue = true;
37093698
}
@@ -4207,8 +4196,7 @@ const char* Compiler::compGetStressMessage() const
42074196
{
42084197
// Or is it excluded via name?
42094198
if (!JitConfig.JitStressOnly().isEmpty() ||
4210-
!JitConfig.JitStressOnly().contains(info.compMethodName, info.compClassName,
4211-
&info.compMethodInfo->args))
4199+
!JitConfig.JitStressOnly().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
42124200
{
42134201
// Not excluded -- stress can happen
42144202
stressMessage = " JitStress";
@@ -5000,7 +4988,8 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
50004988
#ifdef DEBUG
50014989
const char* fullName = info.compFullName;
50024990
#else
5003-
const char* fullName = eeGetMethodFullName(info.compMethodHnd);
4991+
const char* fullName =
4992+
eeGetMethodFullName(info.compMethodHnd, /* includeReturnType */ false, /* includeThisSpecifier */ false);
50044993
#endif
50054994

50064995
char debugPart[128] = {0};
@@ -5375,13 +5364,13 @@ bool Compiler::skipMethod()
53755364
return true;
53765365
}
53775366

5378-
if (JitConfig.JitExclude().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
5367+
if (JitConfig.JitExclude().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
53795368
{
53805369
return true;
53815370
}
53825371

53835372
if (!JitConfig.JitInclude().isEmpty() &&
5384-
!JitConfig.JitInclude().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
5373+
!JitConfig.JitInclude().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
53855374
{
53865375
return true;
53875376
}
@@ -5687,9 +5676,7 @@ int Compiler::compCompile(CORINFO_MODULE_HANDLE classPtr,
56875676
{
56885677
impTokenLookupContextHandle = impInlineInfo->tokenLookupContextHandle;
56895678

5690-
assert(impInlineInfo->inlineCandidateInfo->clsHandle == info.compCompHnd->getMethodClass(info.compMethodHnd));
5691-
info.compClassHnd = impInlineInfo->inlineCandidateInfo->clsHandle;
5692-
5679+
assert(impInlineInfo->inlineCandidateInfo->clsHandle == info.compClassHnd);
56935680
assert(impInlineInfo->inlineCandidateInfo->clsAttr == info.compCompHnd->getClassAttribs(info.compClassHnd));
56945681
// printf("%x != %x\n", impInlineInfo->inlineCandidateInfo->clsAttr,
56955682
// info.compCompHnd->getClassAttribs(info.compClassHnd));
@@ -5699,7 +5686,6 @@ int Compiler::compCompile(CORINFO_MODULE_HANDLE classPtr,
56995686
{
57005687
impTokenLookupContextHandle = METHOD_BEING_COMPILED_CONTEXT();
57015688

5702-
info.compClassHnd = info.compCompHnd->getMethodClass(info.compMethodHnd);
57035689
info.compClassAttr = info.compCompHnd->getClassAttribs(info.compClassHnd);
57045690
}
57055691

0 commit comments

Comments
 (0)