Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement handling PreserveSig = false for interfaces #1206

Merged
merged 9 commits into from
Jun 8, 2021
29 changes: 21 additions & 8 deletions src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeILEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ private static Marshaller[] InitializeMarshallers(MethodDesc targetMethod, Inter
TypeDesc parameterType = (i == 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(i == 0 && isHRSwapping) condition is repeated multiple times here. Also, I do not think you should need the check for DelegateMarshallingMethodThunk here. We should make sure to set PreserveSig=true for marshalled delegates earlier (UnmanagedFunctionPointerAttribute won't allow you to specify PreserveSig=false so we do not need to worry about handling it).

I think it would be a bit more efficient and readable like this:

TypeDesc parameterType;
bool isHRSwappedRetVal = false;
if (i == 0)
{
    //first item is the return type
    parameterType = methodSig.ReturnType;
    if (!flags.PreserveSig && !parameterType.IsVoid)
    {
        // PreserveSig = false can only show up an regular forward PInvokes
        Debug.Assert(direction == MarshalDirection.Forward);
    
        parameterType = methodSig.Context.GetByRefType(parameterType);
        isHRSwappedRetVal = true;
    }
}
else
{
    parameterType = methodSig[i - 1];
}

? methodSig.ReturnType //first item is the return type
: methodSig[i - 1];
marshallers[i] = Marshaller.CreateMarshaller(parameterType,
bool isHRSwapping = !flags.PreserveSig && !parameterType.IsVoid
&& targetMethod is not DelegateMarshallingMethodThunk;
marshallers[i] = Marshaller.CreateMarshaller((i == 0 && isHRSwapping) ? methodSig.Context.GetByRefType(parameterType): parameterType,
parameterIndex,
methodSig.GetEmbeddedSignatureData(),
MarshallerType.Argument,
Expand All @@ -108,8 +110,8 @@ private static Marshaller[] InitializeMarshallers(MethodDesc targetMethod, Inter
indexOffset + parameterMetadata.Index,
flags,
parameterMetadata.In,
parameterMetadata.Out,
parameterMetadata.Return
(i == 0 && isHRSwapping) ? true : parameterMetadata.Out,
(i == 0 && isHRSwapping) ? false : parameterMetadata.Return
);
}

Expand Down Expand Up @@ -231,16 +233,15 @@ private void EmitDelegateCall(DelegateMarshallingMethodThunk delegateMethod, PIn

private void EmitPInvokeCall(PInvokeILCodeStreams ilCodeStreams)
{
if (!_flags.PreserveSig && _targetMethod.Signature.ReturnType != _targetMethod.Context.GetWellKnownType(WellKnownType.Void))
throw new NotSupportedException();

ILEmitter emitter = ilCodeStreams.Emitter;
ILCodeStream fnptrLoadStream = ilCodeStreams.FunctionPointerLoadStream;
ILCodeStream callsiteSetupCodeStream = ilCodeStreams.CallsiteSetupCodeStream;
TypeSystemContext context = _targetMethod.Context;

bool isHRSwapping = !_flags.PreserveSig && _targetMethod.Signature.ReturnType != _targetMethod.Context.GetWellKnownType(WellKnownType.Void)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

== targetMethod.Context.GetWellKnownType(WellKnownType.Void) can be just IsVoid

&& _targetMethod is not DelegateMarshallingMethodThunk;
TypeDesc nativeReturnType = _flags.PreserveSig ? _marshallers[0].NativeParameterType : context.GetWellKnownType(WellKnownType.Int32);
TypeDesc[] nativeParameterTypes = new TypeDesc[_marshallers.Length - 1];
TypeDesc[] nativeParameterTypes = new TypeDesc[isHRSwapping ? _marshallers.Length : _marshallers.Length - 1];

// if the SetLastError flag is set in DllImport, clear the error code before doing P/Invoke
if (_flags.SetLastError)
Expand All @@ -254,6 +255,11 @@ private void EmitPInvokeCall(PInvokeILCodeStreams ilCodeStreams)
nativeParameterTypes[i - 1] = _marshallers[i].NativeParameterType;
}

if (isHRSwapping)
{
nativeParameterTypes[_marshallers.Length - 1] = _marshallers[0].NativeParameterType;
}

if (!_pInvokeILEmitterConfiguration.GenerateDirectCall(_targetMethod, out _))
{
MetadataType lazyHelperType = context.GetHelperType("InteropHelpers");
Expand Down Expand Up @@ -357,11 +363,18 @@ private MethodIL EmitIL()
cleanupCodestream.BeginHandler(tryFinally);

// Marshal the arguments
for (int i = 0; i < _marshallers.Length; i++)
bool isHRSwapping = !_flags.PreserveSig && _targetMethod.Signature.ReturnType != _targetMethod.Context.GetWellKnownType(WellKnownType.Void)
&& _targetMethod is not DelegateMarshallingMethodThunk;
for (int i = isHRSwapping ? 1 : 0; i < _marshallers.Length; i++)
{
_marshallers[i].EmitMarshallingIL(pInvokeILCodeStreams);
}

if (isHRSwapping)
{
_marshallers[0].EmitMarshallingIL(pInvokeILCodeStreams);
}

// make the call
switch (_targetMethod)
{
Expand Down
25 changes: 19 additions & 6 deletions src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ protected virtual void EmitMarshalReturnValueManagedToNative()

public virtual void LoadReturnValue(ILCodeStream codeStream)
{
Debug.Assert(Return);
Debug.Assert(Return || Index == 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to replace all Index == 0 checks with a property. It would make the code much easier to understand.


switch (MarshalDirection)
{
Expand Down Expand Up @@ -653,9 +653,18 @@ protected void PropagateFromByRefArg(ILCodeStream stream, Home home)
/// </summary>
protected void PropagateToByRefArg(ILCodeStream stream, Home home)
{
stream.EmitLdArg(Index - 1);
home.LoadValue(stream);
stream.EmitStInd(ManagedType);
// If by-ref arg has index == 0 then that argument is used for HR swapping and we just return that value.
if (Index == 0)
{
// Returning result would be handled by LoadReturnValue
return;
}
else
{
stream.EmitLdArg(Index - 1);
home.LoadValue(stream);
stream.EmitStInd(ManagedType);
}
kant2002 marked this conversation as resolved.
Show resolved Hide resolved
}

protected virtual void EmitMarshalArgumentManagedToNative()
Expand Down Expand Up @@ -903,7 +912,7 @@ class BlittableValueMarshaller : Marshaller
{
protected override void EmitMarshalArgumentManagedToNative()
{
if (IsNativeByRef && MarshalDirection == MarshalDirection.Forward)
if (IsNativeByRef && MarshalDirection == MarshalDirection.Forward && Index > 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move the special case first and then leave the rest of the method intact.

if (IsHRSwappedRetVal)
{
    ...;
    return;
}

{
ILCodeStream marshallingCodeStream = _ilCodeStreams.MarshallingCodeStream;
ILEmitter emitter = _ilCodeStreams.Emitter;
Expand All @@ -917,10 +926,14 @@ protected override void EmitMarshalArgumentManagedToNative()
marshallingCodeStream.EmitStLoc(native);
_ilCodeStreams.CallsiteSetupCodeStream.EmitLdLoc(native);
}
else
else if (Index > 0)
{
_ilCodeStreams.CallsiteSetupCodeStream.EmitLdArg(Index - 1);
}
else
{
base.EmitMarshalArgumentManagedToNative();
}
}

protected override void EmitMarshalArgumentNativeToManaged()
Expand Down