Skip to content

Commit

Permalink
Optimize multicast delegate thunk (#104222)
Browse files Browse the repository at this point in the history
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]
```
  • Loading branch information
MichalStrehovsky committed Jul 3, 2024
1 parent 78cd82e commit 1fe7d18
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 39 deletions.
14 changes: 11 additions & 3 deletions src/coreclr/tools/Common/TypeSystem/IL/DelegateInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class DelegateInfo
private readonly TypeDesc _delegateType;
private readonly DelegateFeature _supportedFeatures;

private MethodSignature _signature;
private MethodDesc _invokeMethod;

private MethodDesc _getThunkMethod;
private DelegateThunkCollection _thunks;
Expand Down Expand Up @@ -71,8 +71,16 @@ public MethodSignature Signature
{
get
{
_signature ??= _delegateType.GetKnownMethod("Invoke", null).Signature;
return _signature;
return InvokeMethod.Signature;
}
}

public MethodDesc InvokeMethod
{
get
{
_invokeMethod ??= _delegateType.GetKnownMethod("Invoke", null);
return _invokeMethod;
}
}

Expand Down
43 changes: 7 additions & 36 deletions src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Internal.IL.Stubs
/// </summary>
public abstract partial class DelegateThunk : ILStubMethod
{
private DelegateInfo _delegateInfo;
protected readonly DelegateInfo _delegateInfo;

public DelegateThunk(DelegateInfo delegateInfo)
{
Expand Down Expand Up @@ -75,22 +75,6 @@ protected FieldDesc HelperObjectField
}
}

protected FieldDesc FirstParameterField
{
get
{
return SystemDelegateType.GetKnownField("_firstParameter");
}
}

protected FieldDesc FunctionPointerField
{
get
{
return SystemDelegateType.GetKnownField("_functionPointer");
}
}

public sealed override string DiagnosticName
{
get
Expand Down Expand Up @@ -310,7 +294,6 @@ public override MethodIL EmitIL()
ILLocalVariable delegateArrayLocal = emitter.NewLocal(invocationListArrayType);
ILLocalVariable invocationCountLocal = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32));
ILLocalVariable iteratorLocal = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32));
ILLocalVariable delegateToCallLocal = emitter.NewLocal(SystemDelegateType);

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

// ldarg.0 (this pointer)
// ldfld Delegate._helperObject
// castclass Delegate.Wrapper[]
// castclass Delegate.Wrapper[] (omitted - generate unsafe cast assuming the delegate is well-formed)
// stloc delegateArrayLocal
codeStream.EmitLdArg(0);
codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(HelperObjectField));
codeStream.Emit(ILOpcode.castclass, emitter.NewToken(invocationListArrayType));
// codeStream.Emit(ILOpcode.castclass, emitter.NewToken(invocationListArrayType));
codeStream.EmitStLoc(delegateArrayLocal);

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

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

// ldloc delegateArrayLocal
// ldloc iteratorLocal
// ldelema Delegate.Wrapper
// ldfld Delegate.Wrapper.Value
// stloc delegateToCallLocal
codeStream.EmitLdLoc(delegateArrayLocal);
codeStream.EmitLdLoc(iteratorLocal);
codeStream.Emit(ILOpcode.ldelema, emitter.NewToken(delegateWrapperType));
codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(delegateWrapperType.GetKnownField("Value")));
codeStream.EmitStLoc(delegateToCallLocal);

// Call the delegate
// returnValueLocal = delegateToCallLocal(...);
// delegateArrayLocal[iteratorLocal].Value(...)

// ldloc delegateToCallLocal
// ldfld Delegate._firstParameter
// ldarg 1, n
// ldloc delegateToCallLocal
// ldfld Delegate._functionPointer
// calli returnValueType thiscall (all the params)
// callvirt DelegateType.Invoke(...)
// IF there is a return value
// stloc returnValueLocal

codeStream.EmitLdLoc(delegateToCallLocal);
codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(FirstParameterField));

for (int i = 0; i < Signature.Length; i++)
{
codeStream.EmitLdArg(i + 1);
}

codeStream.EmitLdLoc(delegateToCallLocal);
codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(FunctionPointerField));

codeStream.Emit(ILOpcode.calli, emitter.NewToken(Signature));
codeStream.Emit(ILOpcode.callvirt, emitter.NewToken(_delegateInfo.InvokeMethod.InstantiateAsOpen()));

if (returnValueLocal != 0)
codeStream.EmitStLoc(returnValueLocal);
Expand Down

0 comments on commit 1fe7d18

Please sign in to comment.