Skip to content

Commit 1fe7d18

Browse files
Optimize multicast delegate thunk (#104222)
Alternative to #104219 for consideration. RyuJIT generates somewhat better code for the canonical `Invoke` pattern: The other PR: ``` 00007FF7AE041978 mov rcx,qword ptr [rbx+rbp*8+10h] 00007FF7AE04197D mov rax,qword ptr [rcx+8] 00007FF7AE041981 mov rdx,qword ptr [rcx+20h] 00007FF7AE041985 mov rcx,rax 00007FF7AE041988 call rdx ``` This PR: ``` 00007FF69D2B1978 mov rax,qword ptr [rbx+rbp*8+10h] 00007FF69D2B197D mov rcx,qword ptr [rax+8] 00007FF69D2B1981 call qword ptr [rax+20h] ```
1 parent 78cd82e commit 1fe7d18

File tree

2 files changed

+18
-39
lines changed

2 files changed

+18
-39
lines changed

src/coreclr/tools/Common/TypeSystem/IL/DelegateInfo.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public class DelegateInfo
2020
private readonly TypeDesc _delegateType;
2121
private readonly DelegateFeature _supportedFeatures;
2222

23-
private MethodSignature _signature;
23+
private MethodDesc _invokeMethod;
2424

2525
private MethodDesc _getThunkMethod;
2626
private DelegateThunkCollection _thunks;
@@ -71,8 +71,16 @@ public MethodSignature Signature
7171
{
7272
get
7373
{
74-
_signature ??= _delegateType.GetKnownMethod("Invoke", null).Signature;
75-
return _signature;
74+
return InvokeMethod.Signature;
75+
}
76+
}
77+
78+
public MethodDesc InvokeMethod
79+
{
80+
get
81+
{
82+
_invokeMethod ??= _delegateType.GetKnownMethod("Invoke", null);
83+
return _invokeMethod;
7684
}
7785
}
7886

src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs

Lines changed: 7 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace Internal.IL.Stubs
1212
/// </summary>
1313
public abstract partial class DelegateThunk : ILStubMethod
1414
{
15-
private DelegateInfo _delegateInfo;
15+
protected readonly DelegateInfo _delegateInfo;
1616

1717
public DelegateThunk(DelegateInfo delegateInfo)
1818
{
@@ -75,22 +75,6 @@ protected FieldDesc HelperObjectField
7575
}
7676
}
7777

78-
protected FieldDesc FirstParameterField
79-
{
80-
get
81-
{
82-
return SystemDelegateType.GetKnownField("_firstParameter");
83-
}
84-
}
85-
86-
protected FieldDesc FunctionPointerField
87-
{
88-
get
89-
{
90-
return SystemDelegateType.GetKnownField("_functionPointer");
91-
}
92-
}
93-
9478
public sealed override string DiagnosticName
9579
{
9680
get
@@ -310,7 +294,6 @@ public override MethodIL EmitIL()
310294
ILLocalVariable delegateArrayLocal = emitter.NewLocal(invocationListArrayType);
311295
ILLocalVariable invocationCountLocal = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32));
312296
ILLocalVariable iteratorLocal = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32));
313-
ILLocalVariable delegateToCallLocal = emitter.NewLocal(SystemDelegateType);
314297

315298
ILLocalVariable returnValueLocal = 0;
316299
if (!Signature.ReturnType.IsVoid)
@@ -323,11 +306,11 @@ public override MethodIL EmitIL()
323306

324307
// ldarg.0 (this pointer)
325308
// ldfld Delegate._helperObject
326-
// castclass Delegate.Wrapper[]
309+
// castclass Delegate.Wrapper[] (omitted - generate unsafe cast assuming the delegate is well-formed)
327310
// stloc delegateArrayLocal
328311
codeStream.EmitLdArg(0);
329312
codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(HelperObjectField));
330-
codeStream.Emit(ILOpcode.castclass, emitter.NewToken(invocationListArrayType));
313+
// codeStream.Emit(ILOpcode.castclass, emitter.NewToken(invocationListArrayType));
331314
codeStream.EmitStLoc(delegateArrayLocal);
332315

333316
// Fill in invocationCountLocal
@@ -354,43 +337,31 @@ public override MethodIL EmitIL()
354337

355338
// Implement as do/while loop. We only have this stub in play if we're in the multicast situation
356339
// Find the delegate to call
357-
// Delegate = delegateToCallLocal = delegateArrayLocal[iteratorLocal].Value;
340+
// delegateArrayLocal[iteratorLocal].Value
358341

359342
// ldloc delegateArrayLocal
360343
// ldloc iteratorLocal
361344
// ldelema Delegate.Wrapper
362345
// ldfld Delegate.Wrapper.Value
363-
// stloc delegateToCallLocal
364346
codeStream.EmitLdLoc(delegateArrayLocal);
365347
codeStream.EmitLdLoc(iteratorLocal);
366348
codeStream.Emit(ILOpcode.ldelema, emitter.NewToken(delegateWrapperType));
367349
codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(delegateWrapperType.GetKnownField("Value")));
368-
codeStream.EmitStLoc(delegateToCallLocal);
369350

370351
// Call the delegate
371-
// returnValueLocal = delegateToCallLocal(...);
352+
// delegateArrayLocal[iteratorLocal].Value(...)
372353

373-
// ldloc delegateToCallLocal
374-
// ldfld Delegate._firstParameter
375354
// ldarg 1, n
376-
// ldloc delegateToCallLocal
377-
// ldfld Delegate._functionPointer
378-
// calli returnValueType thiscall (all the params)
355+
// callvirt DelegateType.Invoke(...)
379356
// IF there is a return value
380357
// stloc returnValueLocal
381358

382-
codeStream.EmitLdLoc(delegateToCallLocal);
383-
codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(FirstParameterField));
384-
385359
for (int i = 0; i < Signature.Length; i++)
386360
{
387361
codeStream.EmitLdArg(i + 1);
388362
}
389363

390-
codeStream.EmitLdLoc(delegateToCallLocal);
391-
codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(FunctionPointerField));
392-
393-
codeStream.Emit(ILOpcode.calli, emitter.NewToken(Signature));
364+
codeStream.Emit(ILOpcode.callvirt, emitter.NewToken(_delegateInfo.InvokeMethod.InstantiateAsOpen()));
394365

395366
if (returnValueLocal != 0)
396367
codeStream.EmitStLoc(returnValueLocal);

0 commit comments

Comments
 (0)