Skip to content

Interpreter P/Invoke support #115393

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 58 additions & 3 deletions src/coreclr/interpreter/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1821,6 +1821,7 @@ void InterpCompiler::EmitCall(CORINFO_CLASS_HANDLE constrainedClass, bool readon
m_compHnd->getMethodSig(targetMethod, &targetSignature);

uint32_t mflags = m_compHnd->getMethodAttribs(targetMethod);
bool isPInvoke = mflags & CORINFO_FLG_PINVOKE;

if (isVirtual && (!(mflags & CORINFO_FLG_VIRTUAL) || (mflags & CORINFO_FLG_FINAL)))
isVirtual = false;
Expand Down Expand Up @@ -1873,6 +1874,36 @@ void InterpCompiler::EmitCall(CORINFO_CLASS_HANDLE constrainedClass, bool readon
AddIns(INTOP_CALLVIRT);
m_pLastNewIns->data[0] = GetDataItemIndex(targetMethod);
}
else if (isPInvoke)
{
if (m_compHnd->pInvokeMarshalingRequired(targetMethod, &targetSignature))
{
assert(!"PInvoke with marshaling not implemented");
}
else
{
void *addressOfAddress;
CORINFO_CONST_LOOKUP lookup;
m_compHnd->getAddressOfPInvokeTarget(targetMethod, &lookup);
switch (lookup.accessType)
{
case IAT_VALUE:
addressOfAddress = lookup.addr;
assert(!((size_t)lookup.addr & INTERP_PVALUE_TAG));
break;
case IAT_PVALUE:
addressOfAddress = (void *)(((size_t)lookup.addr) | INTERP_PVALUE_TAG);
break;
default:
addressOfAddress = nullptr;
assert(!"PInvoke target access type not implemented");
break;
}
AddIns(INTOP_CALL_PINVOKE);
m_pLastNewIns->data[0] = GetMethodDataItemIndex(targetMethod);
m_pLastNewIns->data[1] = GetDataItemIndex(addressOfAddress);
}
}
else
{
AddIns(INTOP_CALL);
Expand Down Expand Up @@ -2104,8 +2135,34 @@ void InterpCompiler::EmitStaticFieldAddress(CORINFO_FIELD_INFO *pFieldInfo, CORI
}
}

void InterpCompiler::EmitStaticFieldIntrinsic(InterpType interpFieldType, CORINFO_FIELD_INFO *pFieldInfo, CORINFO_RESOLVED_TOKEN *pResolvedToken)
{
switch (pFieldInfo->fieldAccessor)
{
case CORINFO_FIELD_INTRINSIC_ZERO:
AddIns(INTOP_LDNULL);
PushInterpType(InterpTypeI, NULL);
m_pLastNewIns->SetDVar(m_pStackPointer[-1].var);
return;
default:
assert(!"Static field intrinsic not implemented");
return;
}
}

void InterpCompiler::EmitStaticFieldAccess(InterpType interpFieldType, CORINFO_FIELD_INFO *pFieldInfo, CORINFO_RESOLVED_TOKEN *pResolvedToken, bool isLoad)
{
switch (pFieldInfo->fieldAccessor)
{
case CORINFO_FIELD_INTRINSIC_ZERO:
case CORINFO_FIELD_INTRINSIC_EMPTY_STRING:
case CORINFO_FIELD_INTRINSIC_ISLITTLEENDIAN:
EmitStaticFieldIntrinsic(interpFieldType, pFieldInfo, pResolvedToken);
return;
default:
break;
}

EmitStaticFieldAddress(pFieldInfo, pResolvedToken);
if (isLoad)
EmitLdind(interpFieldType, pFieldInfo->structType, 0);
Expand Down Expand Up @@ -2352,12 +2409,10 @@ int InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
}
case CEE_LDC_R8:
{
int64_t val = getI8LittleEndian(m_ip + 1);
AddIns(INTOP_LDC_R8);
PushInterpType(InterpTypeR8, NULL);
m_pLastNewIns->SetDVar(m_pStackPointer[-1].var);
m_pLastNewIns->data[0] = (int32_t)val;
m_pLastNewIns->data[1] = (int32_t)(val >> 32);
memcpy(m_pLastNewIns->data, m_ip + 1, sizeof(double));
m_ip += 9;
break;
}
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/interpreter/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ class InterpCompiler
void EmitStelem(InterpType type);
void EmitStaticFieldAddress(CORINFO_FIELD_INFO *pFieldInfo, CORINFO_RESOLVED_TOKEN *pResolvedToken);
void EmitStaticFieldAccess(InterpType interpFieldType, CORINFO_FIELD_INFO *pFieldInfo, CORINFO_RESOLVED_TOKEN *pResolvedToken, bool isLoad);
void EmitStaticFieldIntrinsic(InterpType interpFieldType, CORINFO_FIELD_INFO *pFieldInfo, CORINFO_RESOLVED_TOKEN *pResolvedToken);
void EmitLdLocA(int32_t var);

// Var Offset allocator
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/interpreter/interpretershared.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
#define INTERP_STACK_SLOT_SIZE 8 // Alignment of each var offset on the interpreter stack
#define INTERP_STACK_ALIGNMENT 16 // Alignment of interpreter stack at the start of a frame

#define INTERP_METHOD_HANDLE_TAG 4 // Tag of a MethodDesc in the interp method dataItems
#define INTERP_METHOD_HANDLE_TAG 4 // Tag of a MethodDesc in the interp method dataItems
#define INTERP_INDIRECT_HELPER_TAG 1 // When a helper ftn's address is indirect we tag it with this tag bit
#define INTERP_PVALUE_TAG 1 // Tag of a PVALUE indirect pinvoke target in interp method dataItems

struct InterpMethod
{
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/interpreter/intops.def
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ OPDEF(INTOP_LDFLDA, "ldflda", 4, 1, 1, InterpOpInt)

// Calls
OPDEF(INTOP_CALL, "call", 4, 1, 1, InterpOpMethodHandle)
OPDEF(INTOP_CALL_PINVOKE, "call.pinvoke", 5, 1, 1, InterpOpMethodHandle) // also has an extra int after the token for the method ptr data item
OPDEF(INTOP_CALLVIRT, "callvirt", 4, 1, 1, InterpOpMethodHandle)
OPDEF(INTOP_NEWOBJ, "newobj", 5, 1, 1, InterpOpMethodHandle)
OPDEF(INTOP_NEWOBJ_VT, "newobj.vt", 5, 1, 1, InterpOpMethodHandle)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/interpreter/intops.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ inline float getR4LittleEndian(const uint8_t* ptr)
return *(float*)&val;
}

// FIXME: This may be broken, but I'm not sure how -kg
inline double getR8LittleEndian(const uint8_t* ptr)
{
int64_t val = getI8LittleEndian(ptr);
Expand Down
18 changes: 16 additions & 2 deletions src/coreclr/vm/dllimport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5886,8 +5886,22 @@ EXTERN_C LPVOID STDCALL NDirectImportWorker(NDirectMethodDesc* pMD)
//
INDEBUG(Thread *pThread = GetThread());
{
_ASSERTE((pThread->GetFrame() != FRAME_TOP && pThread->GetFrame()->GetFrameIdentifier() == FrameIdentifier::InlinedCallFrame)
|| pMD->ShouldSuppressGCTransition());
#ifdef FEATURE_INTERPRETER
_ASSERTE(
(
(pThread->GetFrame() != FRAME_TOP) && (
(pThread->GetFrame()->GetFrameIdentifier() == FrameIdentifier::InlinedCallFrame) ||
(pThread->GetFrame()->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame)
)
) || pMD->ShouldSuppressGCTransition()
);
#else
_ASSERTE(
(
(pThread->GetFrame() != FRAME_TOP) && (pThread->GetFrame()->GetFrameIdentifier() == FrameIdentifier::InlinedCallFrame)
) || pMD->ShouldSuppressGCTransition()
);
#endif

CONSISTENCY_CHECK(pMD->IsNDirect());
//
Expand Down
40 changes: 34 additions & 6 deletions src/coreclr/vm/interpexec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "interpexec.h"
#include "callstubgenerator.h"

void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet)
void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet, PCODE pCode)
{
CONTRACTL
{
Expand Down Expand Up @@ -41,7 +41,8 @@ void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet)
}
}

pHeader->SetTarget(pMD->GetNativeCode()); // The method to call
_ASSERTE(pCode);
pHeader->SetTarget(pCode); // The method to call

pHeader->Invoke(pHeader->Routines, pArgs, pRet, pHeader->TotalStackSize);
}
Expand Down Expand Up @@ -151,7 +152,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
ip += 3;
break;
case INTOP_LDC_R8:
LOCAL_VAR(ip[1], int64_t) = (int64_t)ip[2] + ((int64_t)ip[3] << 32);
memcpy(LOCAL_VAR_ADDR(ip[1], double), &ip[2], sizeof(double));
ip += 4;
break;
case INTOP_LDPTR:
Expand Down Expand Up @@ -1103,6 +1104,30 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
goto CALL_TARGET_IP;
}

case INTOP_CALL_PINVOKE:
{
returnOffset = ip[1];
callArgsOffset = ip[2];
methodSlot = ip[3];
size_t possiblyIndirectAddress = (size_t)pMethod->pDataItems[ip[4]];
ip += 5;

size_t targetMethod = (size_t)pMethod->pDataItems[methodSlot];
assert(targetMethod & INTERP_METHOD_HANDLE_TAG);
MethodDesc *pMD = (MethodDesc*)(targetMethod & ~INTERP_METHOD_HANDLE_TAG);

PCODE actualCallTarget = (possiblyIndirectAddress & INTERP_PVALUE_TAG)
? *(PCODE *)(possiblyIndirectAddress & ~INTERP_PVALUE_TAG)
: (PCODE)possiblyIndirectAddress;

{
pInterpreterFrame->SetTopInterpMethodContextFrame(pFrame);
GCX_PREEMP();
InvokeCompiledMethod(pMD, stack + callArgsOffset, stack + returnOffset, actualCallTarget);
}
break;
}

case INTOP_CALL:
{
returnOffset = ip[1];
Expand Down Expand Up @@ -1151,7 +1176,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
else if (codeInfo.GetCodeManager() != ExecutionManager::GetInterpreterCodeManager())
{
MethodDesc *pMD = codeInfo.GetMethodDesc();
InvokeCompiledMethod(pMD, stack + callArgsOffset, stack + returnOffset);
InvokeCompiledMethod(pMD, stack + callArgsOffset, stack + returnOffset, pMD->GetNativeCode());
break;
}

Expand Down Expand Up @@ -1283,11 +1308,14 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
else
helper = (HELPER_FTN_BOX_UNBOX)helperDirectOrIndirect;

if (opcode == INTOP_BOX) {
if (opcode == INTOP_BOX)
{
// internal static object Box(MethodTable* typeMT, ref byte unboxedData)
void *unboxedData = LOCAL_VAR_ADDR(sreg, void);
LOCAL_VAR(dreg, Object*) = (Object*)helper(pMT, unboxedData);
} else {
}
else
{
// private static ref byte Unbox(MethodTable* toTypeHnd, object obj)
Object *src = LOCAL_VAR(sreg, Object*);
void *unboxedData = helper(pMT, src);
Expand Down
11 changes: 11 additions & 0 deletions src/tests/JIT/interpreter/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
project (pinvoke)

include_directories(${INC_PLATFORM_DIR})

set(SOURCES pinvoke.cpp)

# add the executable
add_library (pinvoke SHARED ${SOURCES})

# add the install targets
install (TARGETS pinvoke DESTINATION bin)
44 changes: 41 additions & 3 deletions src/tests/JIT/interpreter/Interpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

public interface ITest
{
Expand Down Expand Up @@ -426,6 +427,10 @@ public static void RunInterpreterTests()
Environment.FailFast(null);
*/

if (!TestPInvoke())
Environment.FailFast(null);

// For stackwalking validation
System.GC.Collect();
}

Expand Down Expand Up @@ -706,11 +711,44 @@ public static bool TestBoxing()
}

[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
static object BoxedSubtraction (object lhs, object rhs) {
return (int)lhs - (int)rhs;
static object BoxedSubtraction (object lhs, object rhs) =>
(int)lhs - (int)rhs;

[DllImport("pinvoke", CallingConvention = CallingConvention.Cdecl)]
public static extern int sumTwoInts(int x, int y);
[DllImport("pinvoke", CallingConvention = CallingConvention.Cdecl)]
public static extern double sumTwoDoubles(double x, double y);
[DllImport("pinvoke", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int writeToStdout(string s);
[DllImport("missingLibrary", CallingConvention = CallingConvention.Cdecl)]
public static extern void missingPInvoke();

public static bool TestPInvoke()
{
if (sumTwoInts(1, 2) != 3)
return false;

double summed = sumTwoDoubles(1, 2);
if (summed != 3)
return false;

// This asserts at compile time because we do not have a way to get the IL stub for the marshaling
// in order to invoke it from the interpreter.
// writeToStdout("Hello world from pinvoke.dll!writeToStdout\n");

// We don't currently have exception handling support so the DllNotFoundException from this will make everything explode
/*
try {
missingPInvoke();
return false;
} catch (DllNotFoundException) {
}
*/

return true;
}

public static bool TestArray()
public static bool TestArray()
{
// sbyte
if (!ArraySByte(0, 0)) return false;
Expand Down
3 changes: 3 additions & 0 deletions src/tests/JIT/interpreter/Interpreter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@
<ItemGroup>
<Compile Include="Interpreter.cs" />
</ItemGroup>
<ItemGroup>
<CMakeProjectReference Include="CMakeLists.txt" />
</ItemGroup>
</Project>
3 changes: 3 additions & 0 deletions src/tests/JIT/interpreter/InterpreterTester.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
<ItemGroup>
<Compile Include="InterpreterTester.cs" />
</ItemGroup>
<ItemGroup>
<CMakeProjectReference Include="CMakeLists.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)Interpreter.csproj">
Expand Down
23 changes: 23 additions & 0 deletions src/tests/JIT/interpreter/pinvoke.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include "stdio.h"
#include <stdlib.h>
#include <platformdefines.h>

#define EXPORT_IT extern "C" DLL_EXPORT

EXPORT_IT void writeToStdout (const char *str)
{
puts(str);
}

EXPORT_IT int sumTwoInts (int x, int y)
{
return x + y;
}

EXPORT_IT double sumTwoDoubles (double x, double y)
{
return x + y;
}
Loading