Skip to content

Commit

Permalink
Do not emit unsupported thunks for delegates with allows ref struct
Browse files Browse the repository at this point in the history
… generics (#105871)

Fixes #105029

I looked into redoing delegate thunk management to be per instance but that would be an unnecessarily big diff. Instead adding a facility to allow `ILStubMethodIL` specialize per-instance if needed and in this specialization generate a throwing method body (that should be unreachable at runtime).
  • Loading branch information
MichalStrehovsky authored Aug 5, 2024
1 parent 312d4fc commit 018fb9d
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 60 deletions.
116 changes: 58 additions & 58 deletions src/coreclr/tools/Common/TypeSystem/IL/DelegateInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,77 +134,77 @@ internal DelegateThunkCollection(DelegateInfo owningDelegate)
_closedStaticThunk = new DelegateInvokeClosedStaticThunk(owningDelegate);
_closedInstanceOverGeneric = new DelegateInvokeInstanceClosedOverGenericMethodThunk(owningDelegate);

MethodSignature delegateSignature = owningDelegate.Signature;

//
// Check whether we have an object array thunk
//
if ((owningDelegate.SupportedFeatures & DelegateFeature.ObjectArrayThunk) != 0
&& SignatureSupportsObjectArrayThunk(delegateSignature))
{
_invokeObjectArrayThunk = new DelegateInvokeObjectArrayThunk(owningDelegate);
}

//
// Check whether we have an open instance thunk
//
if ((owningDelegate.SupportedFeatures & DelegateFeature.OpenInstanceThunk) != 0
&& delegateSignature.Length > 0
&& SignatureSupportsOpenInstanceThunks(delegateSignature))
{
_openInstanceThunk = new DelegateInvokeOpenInstanceThunk(owningDelegate);
}
}

public bool SignatureSupportsOpenInstanceThunks(MethodSignature signature)
{
TypeDesc firstParam = signature[0];

switch (firstParam.Category)
{
case TypeFlags.Pointer:
case TypeFlags.FunctionPointer:
return false;

case TypeFlags.ByRef:
firstParam = ((ByRefType)firstParam).ParameterType;
return firstParam.IsSignatureVariable || firstParam.IsValueType;

case TypeFlags.Array:
case TypeFlags.SzArray:
case TypeFlags.SignatureTypeVariable:
return true;

default:
Debug.Assert(firstParam.IsDefType);
return !firstParam.IsValueType;
}
}

public bool SignatureSupportsObjectArrayThunk(MethodSignature signature)
{
// Methods that have a byref-like type in the signature cannot be invoked with the object array thunk.
// We would need to box the parameter and these can't be boxed.
// Neither can be methods that have pointers in the signature.
MethodSignature delegateSignature = owningDelegate.Signature;
bool generateObjectArrayThunk = true;
for (int i = 0; i < delegateSignature.Length; i++)
for (int i = 0; i < signature.Length; i++)
{
TypeDesc paramType = delegateSignature[i];
TypeDesc paramType = signature[i];
if (paramType.IsByRef)
paramType = ((ByRefType)paramType).ParameterType;
if (!paramType.IsSignatureVariable && paramType.IsByRefLike)
{
generateObjectArrayThunk = false;
break;
}
return false;
if (paramType.IsPointer || paramType.IsFunctionPointer)
{
generateObjectArrayThunk = false;
break;
}
return false;
}
TypeDesc returnType = delegateSignature.ReturnType;
TypeDesc returnType = signature.ReturnType;
if (returnType.IsByRef)
generateObjectArrayThunk = false;
return false;
if (!returnType.IsSignatureVariable && returnType.IsByRefLike)
generateObjectArrayThunk = false;
return false;
if (returnType.IsPointer || returnType.IsFunctionPointer)
generateObjectArrayThunk = false;

if ((owningDelegate.SupportedFeatures & DelegateFeature.ObjectArrayThunk) != 0 && generateObjectArrayThunk)
_invokeObjectArrayThunk = new DelegateInvokeObjectArrayThunk(owningDelegate);

//
// Check whether we have an open instance thunk
//
return false;

if ((owningDelegate.SupportedFeatures & DelegateFeature.OpenInstanceThunk) != 0 && delegateSignature.Length > 0)
{
TypeDesc firstParam = delegateSignature[0];

bool generateOpenInstanceMethod;

switch (firstParam.Category)
{
case TypeFlags.Pointer:
case TypeFlags.FunctionPointer:
generateOpenInstanceMethod = false;
break;

case TypeFlags.ByRef:
firstParam = ((ByRefType)firstParam).ParameterType;
generateOpenInstanceMethod = firstParam.IsSignatureVariable || firstParam.IsValueType;
break;

case TypeFlags.Array:
case TypeFlags.SzArray:
case TypeFlags.SignatureTypeVariable:
generateOpenInstanceMethod = true;
break;

default:
Debug.Assert(firstParam.IsDefType);
generateOpenInstanceMethod = !firstParam.IsValueType;
break;
}

if (generateOpenInstanceMethod)
{
_openInstanceThunk = new DelegateInvokeOpenInstanceThunk(owningDelegate);
}
}
return true;
}

public MethodDesc this[DelegateThunkKind kind]
Expand Down
10 changes: 9 additions & 1 deletion src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,15 @@ public override MethodIL GetMethodIL(MethodDesc method)
return methodIL;
}

var methodDefinitionIL = GetMethodIL(method.GetTypicalMethodDefinition());
MethodDesc typicalMethod = method.GetTypicalMethodDefinition();
if (typicalMethod is SpecializableILStubMethod specializableMethod)
{
MethodIL methodIL = specializableMethod.EmitIL(method);
if (methodIL != null)
return methodIL;
}

var methodDefinitionIL = GetMethodIL(typicalMethod);
if (methodDefinitionIL == null)
return null;
return new InstantiatedMethodIL(method, methodDefinitionIL);
Expand Down
35 changes: 34 additions & 1 deletion src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Internal.IL.Stubs
/// <summary>
/// Base class for all delegate invocation thunks.
/// </summary>
public abstract partial class DelegateThunk : ILStubMethod
public abstract partial class DelegateThunk : SpecializableILStubMethod
{
protected readonly DelegateInfo _delegateInfo;

Expand All @@ -19,6 +19,11 @@ public DelegateThunk(DelegateInfo delegateInfo)
_delegateInfo = delegateInfo;
}

public override MethodIL EmitIL(MethodDesc specializedMethod)
{
return null;
}

public sealed override TypeSystemContext Context
{
get
Expand Down Expand Up @@ -145,6 +150,20 @@ internal DelegateInvokeOpenInstanceThunk(DelegateInfo delegateInfo)
{
}

public override MethodIL EmitIL(MethodDesc specializedMethod)
{
Debug.Assert(specializedMethod.GetTypicalMethodDefinition() == this);
if (!_delegateInfo.Thunks.SignatureSupportsOpenInstanceThunks(specializedMethod.Signature))
{
var emit = new ILEmitter();
ILCodeStream codeStream = emit.NewCodeStream();
codeStream.EmitCallThrowHelper(emit, Context.GetHelperEntryPoint("ThrowHelpers", "ThrowNotSupportedException"));
return emit.Link(specializedMethod);
}

return null;
}

public override MethodIL EmitIL()
{
Debug.Assert(Signature.Length > 0);
Expand Down Expand Up @@ -466,6 +485,20 @@ internal DelegateInvokeObjectArrayThunk(DelegateInfo delegateInfo)
{
}

public override MethodIL EmitIL(MethodDesc specializedMethod)
{
Debug.Assert(specializedMethod.GetTypicalMethodDefinition() == this);
if (!_delegateInfo.Thunks.SignatureSupportsObjectArrayThunk(specializedMethod.Signature))
{
var emit = new ILEmitter();
ILCodeStream codeStream = emit.NewCodeStream();
codeStream.EmitCallThrowHelper(emit, Context.GetHelperEntryPoint("ThrowHelpers", "ThrowNotSupportedException"));
return emit.Link(specializedMethod);
}

return null;
}

public override MethodIL EmitIL()
{
// We will generate the following code:
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -829,4 +829,14 @@ public override bool HasCustomAttribute(string attributeNamespace, string attrib
return false;
}
}

public abstract partial class SpecializableILStubMethod : ILStubMethod
{
public abstract MethodIL EmitIL(MethodDesc specializedMethod);

public override bool HasCustomAttribute(string attributeNamespace, string attributeName)
{
return false;
}
}
}

0 comments on commit 018fb9d

Please sign in to comment.