Skip to content

Commit 3db9d17

Browse files
authored
[release/7.0] JIT: Simplify JitDisasm matching behavior and support namespaces/generics in release (#75260)
* 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. * Update JIT-EE GUID Avoid using the same JIT-EE GUID as the main branch.
1 parent 5d71eeb commit 3db9d17

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 = { /* 6be47e5d-a92b-4d16-9280-f63df646ada4 */
47+
0x6be47e5d,
48+
0xa92b,
49+
0x4d16,
50+
{0x92, 0x80, 0xf6, 0x3d, 0xf6, 0x46, 0xad, 0xa4}
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
@@ -2537,7 +2531,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
25372531

25382532
if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_ALT_JIT))
25392533
{
2540-
if (pfAltJit->contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2534+
if (pfAltJit->contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
25412535
{
25422536
opts.altJit = true;
25432537
}
@@ -2618,7 +2612,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
26182612
//
26192613
if (compIsForImportOnly() && (!altJitConfig || opts.altJit))
26202614
{
2621-
if (JitConfig.JitImportBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2615+
if (JitConfig.JitImportBreak().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
26222616
{
26232617
assert(!"JitImportBreak reached");
26242618
}
@@ -2633,7 +2627,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
26332627
//
26342628
if (!compIsForInlining())
26352629
{
2636-
if (JitConfig.JitDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2630+
if (JitConfig.JitDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
26372631
{
26382632
verboseDump = true;
26392633
}
@@ -2868,32 +2862,32 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
28682862
opts.dspOrder = true;
28692863
}
28702864

2871-
if (JitConfig.JitGCDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2865+
if (JitConfig.JitGCDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
28722866
{
28732867
opts.dspGCtbls = true;
28742868
}
28752869

2876-
if (JitConfig.JitDisasm().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2870+
if (JitConfig.JitDisasm().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
28772871
{
28782872
opts.disAsm = true;
28792873
}
28802874

2881-
if (JitConfig.JitDisasm().contains("SPILLED", nullptr, nullptr))
2875+
if (JitConfig.JitDisasmSpilled())
28822876
{
28832877
opts.disAsmSpilled = true;
28842878
}
28852879

2886-
if (JitConfig.JitUnwindDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2880+
if (JitConfig.JitUnwindDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
28872881
{
28882882
opts.dspUnwind = true;
28892883
}
28902884

2891-
if (JitConfig.JitEHDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2885+
if (JitConfig.JitEHDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
28922886
{
28932887
opts.dspEHTable = true;
28942888
}
28952889

2896-
if (JitConfig.JitDebugDump().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2890+
if (JitConfig.JitDebugDump().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
28972891
{
28982892
opts.dspDebugInfo = true;
28992893
}
@@ -2931,7 +2925,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
29312925
opts.compLongAddress = true;
29322926
}
29332927

2934-
if (JitConfig.JitOptRepeat().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2928+
if (JitConfig.JitOptRepeat().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
29352929
{
29362930
opts.optRepeat = true;
29372931
}
@@ -2941,7 +2935,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
29412935
// JitEarlyExpandMDArraysFilter.
29422936
if (JitConfig.JitEarlyExpandMDArrays() == 0)
29432937
{
2944-
if (JitConfig.JitEarlyExpandMDArraysFilter().contains(info.compMethodName, info.compClassName,
2938+
if (JitConfig.JitEarlyExpandMDArraysFilter().contains(info.compMethodHnd, info.compClassHnd,
29452939
&info.compMethodInfo->args))
29462940
{
29472941
opts.compJitEarlyExpandMDArrays = true;
@@ -2982,7 +2976,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
29822976
printf(""); // in our logic this causes a flush
29832977
}
29842978

2985-
if (JitConfig.JitBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2979+
if (JitConfig.JitBreak().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
29862980
{
29872981
assert(!"JitBreak reached");
29882982
}
@@ -2994,8 +2988,8 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
29942988
}
29952989

29962990
if (verbose ||
2997-
JitConfig.JitDebugBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args) ||
2998-
JitConfig.JitBreak().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
2991+
JitConfig.JitDebugBreak().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args) ||
2992+
JitConfig.JitBreak().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
29992993
{
30002994
compDebugBreak = true;
30012995
}
@@ -3014,14 +3008,9 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
30143008
s_pJitFunctionFileInitialized = true;
30153009
}
30163010
#else // DEBUG
3017-
if (!JitConfig.JitDisasm().isEmpty())
3011+
if (JitConfig.JitDisasm().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
30183012
{
3019-
const char* methodName = info.compCompHnd->getMethodName(info.compMethodHnd, nullptr);
3020-
const char* className = info.compCompHnd->getClassName(info.compClassHnd);
3021-
if (JitConfig.JitDisasm().contains(methodName, className, &info.compMethodInfo->args))
3022-
{
3023-
opts.disAsm = true;
3024-
}
3013+
opts.disAsm = true;
30253014
}
30263015
#endif // !DEBUG
30273016

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

31983187
// JitNoProcedureSplitting will always disable procedure splitting.
3199-
if (JitConfig.JitNoProcedureSplitting().contains(info.compMethodName, info.compClassName,
3188+
if (JitConfig.JitNoProcedureSplitting().contains(info.compMethodHnd, info.compClassHnd,
32003189
&info.compMethodInfo->args))
32013190
{
32023191
opts.compProcedureSplitting = false;
32033192
}
32043193
//
32053194
// JitNoProcedureSplittingEH will disable procedure splitting in functions with EH.
3206-
if (JitConfig.JitNoProcedureSplittingEH().contains(info.compMethodName, info.compClassName,
3195+
if (JitConfig.JitNoProcedureSplittingEH().contains(info.compMethodHnd, info.compClassHnd,
32073196
&info.compMethodInfo->args))
32083197
{
32093198
opts.compProcedureSplittingEH = false;
@@ -3319,7 +3308,7 @@ bool Compiler::compJitHaltMethod()
33193308
/* This method returns true when we use an INS_BREAKPOINT to allow us to step into the generated native code */
33203309
/* Note that this these two "Jit" environment variables also work for ngen images */
33213310

3322-
if (JitConfig.JitHalt().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
3311+
if (JitConfig.JitHalt().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
33233312
{
33243313
return true;
33253314
}
@@ -3423,7 +3412,7 @@ bool Compiler::compStressCompileHelper(compStressArea stressArea, unsigned weigh
34233412
}
34243413

34253414
if (!JitConfig.JitStressOnly().isEmpty() &&
3426-
!JitConfig.JitStressOnly().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
3415+
!JitConfig.JitStressOnly().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
34273416
{
34283417
return false;
34293418
}
@@ -3706,7 +3695,7 @@ void Compiler::compSetOptimizationLevel()
37063695

37073696
if (!theMinOptsValue)
37083697
{
3709-
if (JitConfig.JitMinOptsName().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
3698+
if (JitConfig.JitMinOptsName().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
37103699
{
37113700
theMinOptsValue = true;
37123701
}
@@ -4210,8 +4199,7 @@ const char* Compiler::compGetStressMessage() const
42104199
{
42114200
// Or is it excluded via name?
42124201
if (!JitConfig.JitStressOnly().isEmpty() ||
4213-
!JitConfig.JitStressOnly().contains(info.compMethodName, info.compClassName,
4214-
&info.compMethodInfo->args))
4202+
!JitConfig.JitStressOnly().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
42154203
{
42164204
// Not excluded -- stress can happen
42174205
stressMessage = " JitStress";
@@ -5159,7 +5147,8 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
51595147
#ifdef DEBUG
51605148
const char* fullName = info.compFullName;
51615149
#else
5162-
const char* fullName = eeGetMethodFullName(info.compMethodHnd);
5150+
const char* fullName =
5151+
eeGetMethodFullName(info.compMethodHnd, /* includeReturnType */ false, /* includeThisSpecifier */ false);
51635152
#endif
51645153

51655154
char debugPart[128] = {0};
@@ -5522,13 +5511,13 @@ bool Compiler::skipMethod()
55225511
return true;
55235512
}
55245513

5525-
if (JitConfig.JitExclude().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
5514+
if (JitConfig.JitExclude().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
55265515
{
55275516
return true;
55285517
}
55295518

55305519
if (!JitConfig.JitInclude().isEmpty() &&
5531-
!JitConfig.JitInclude().contains(info.compMethodName, info.compClassName, &info.compMethodInfo->args))
5520+
!JitConfig.JitInclude().contains(info.compMethodHnd, info.compClassHnd, &info.compMethodInfo->args))
55325521
{
55335522
return true;
55345523
}
@@ -5834,9 +5823,7 @@ int Compiler::compCompile(CORINFO_MODULE_HANDLE classPtr,
58345823
{
58355824
impTokenLookupContextHandle = impInlineInfo->tokenLookupContextHandle;
58365825

5837-
assert(impInlineInfo->inlineCandidateInfo->clsHandle == info.compCompHnd->getMethodClass(info.compMethodHnd));
5838-
info.compClassHnd = impInlineInfo->inlineCandidateInfo->clsHandle;
5839-
5826+
assert(impInlineInfo->inlineCandidateInfo->clsHandle == info.compClassHnd);
58405827
assert(impInlineInfo->inlineCandidateInfo->clsAttr == info.compCompHnd->getClassAttribs(info.compClassHnd));
58415828
// printf("%x != %x\n", impInlineInfo->inlineCandidateInfo->clsAttr,
58425829
// info.compCompHnd->getClassAttribs(info.compClassHnd));
@@ -5846,7 +5833,6 @@ int Compiler::compCompile(CORINFO_MODULE_HANDLE classPtr,
58465833
{
58475834
impTokenLookupContextHandle = METHOD_BEING_COMPILED_CONTEXT();
58485835

5849-
info.compClassHnd = info.compCompHnd->getMethodClass(info.compMethodHnd);
58505836
info.compClassAttr = info.compCompHnd->getClassAttribs(info.compClassHnd);
58515837
}
58525838

0 commit comments

Comments
 (0)