Skip to content

Commit 19a9b70

Browse files
authored
Interpreter P/Invoke support (#115393)
* Squashed, revised PInvoke implementation Marshaling wrappers work so enable test for them Separate call opcode for unmarshaled pinvokes Cleanup Do inlined pinvoke for CALL_PINVOKE opcode Update pinvoke test for new behavior * Repair merge damage * Add InlinedCallFrame and remove special-casing for InterpreterFrame callers in NDirectImportWorker * Switch from 'indirect helper tag' to 'direct helper tag' so tagged helpers are compatible with arm32 thumb Checkpoint pinvoke changes * Cleanup * Fix musl ci build * Address PR feedback Encapsulate inlined callframe creation * Remove macro
1 parent b4fb38c commit 19a9b70

File tree

9 files changed

+210
-15
lines changed

9 files changed

+210
-15
lines changed

src/coreclr/interpreter/compiler.cpp

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2163,10 +2163,14 @@ int32_t InterpCompiler::GetDataItemIndexForHelperFtn(CorInfoHelpFunc ftn)
21632163
CORINFO_CONST_LOOKUP ftnLookup;
21642164
m_compHnd->getHelperFtn(ftn, &ftnLookup);
21652165
void* addr = ftnLookup.addr;
2166-
if (ftnLookup.accessType == IAT_PVALUE)
2166+
if (ftnLookup.accessType == IAT_VALUE)
21672167
{
2168-
addr = (void*)((size_t)addr | INTERP_INDIRECT_HELPER_TAG);
2168+
// We can't use the 1 bit to mark indirect addresses because it is used for real code on arm32 (the thumb bit)
2169+
// So instead, we mark direct addresses with a 1 and then on the other end we will clear the 1 and re-set it as needed for thumb
2170+
addr = (void*)((size_t)addr | INTERP_DIRECT_HELPER_TAG);
21692171
}
2172+
else if (ftnLookup.accessType == IAT_PPVALUE)
2173+
NO_WAY("IAT_PPVALUE helpers not implemented in interpreter");
21702174

21712175
#ifdef DEBUG
21722176
if (!PointerInNameMap(addr))
@@ -2821,6 +2825,9 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
28212825
doCallInsteadOfNew = true;
28222826
}
28232827

2828+
bool isPInvoke = callInfo.methodFlags & CORINFO_FLG_PINVOKE;
2829+
bool isMarshaledPInvoke = isPInvoke && m_compHnd->pInvokeMarshalingRequired(callInfo.hMethod, &callInfo.sig);
2830+
28242831
// Process sVars
28252832
int numArgsFromStack = callInfo.sig.numArgs + (newObj ? 0 : callInfo.sig.hasThis());
28262833
int newObjThisArgLocation = newObj && !doCallInsteadOfNew ? 0 : INT_MAX;
@@ -3057,8 +3064,17 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
30573064
// before the call.
30583065
// TODO: Add null checking behavior somewhere here!
30593066
}
3060-
AddIns(INTOP_CALL);
3067+
AddIns((isPInvoke && !isMarshaledPInvoke) ? INTOP_CALL_PINVOKE : INTOP_CALL);
30613068
m_pLastNewIns->data[0] = GetMethodDataItemIndex(callInfo.hMethod);
3069+
if (isPInvoke && !isMarshaledPInvoke)
3070+
{
3071+
CORINFO_CONST_LOOKUP lookup;
3072+
m_compHnd->getAddressOfPInvokeTarget(callInfo.hMethod, &lookup);
3073+
m_pLastNewIns->data[1] = GetDataItemIndex(lookup.addr);
3074+
m_pLastNewIns->data[2] = lookup.accessType == IAT_PVALUE;
3075+
if (lookup.accessType == IAT_PPVALUE)
3076+
NO_WAY("IAT_PPVALUE pinvokes not implemented in interpreter");
3077+
}
30623078
}
30633079
break;
30643080

@@ -6296,13 +6312,15 @@ void InterpCompiler::PrintHelperFtn(void* helperDirectOrIndirect)
62966312
{
62976313
void* helperAddr = helperDirectOrIndirect;
62986314

6299-
if (((size_t)helperDirectOrIndirect) & INTERP_INDIRECT_HELPER_TAG)
6315+
if (((size_t)helperDirectOrIndirect) & INTERP_DIRECT_HELPER_TAG)
63006316
{
6301-
helperAddr = (void*)(((size_t)helperDirectOrIndirect) & ~INTERP_INDIRECT_HELPER_TAG);
6302-
printf(" (indirect)");
6317+
helperAddr = (void*)(((size_t)helperDirectOrIndirect) & ~INTERP_DIRECT_HELPER_TAG);
6318+
printf(" (direct)");
63036319
}
63046320
else
6305-
printf(" (direct)");
6321+
{
6322+
printf(" (indirect)");
6323+
}
63066324

63076325
PrintPointer(helperAddr);
63086326
}

src/coreclr/interpreter/interpretershared.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#define INTERP_STACK_SLOT_SIZE 8 // Alignment of each var offset on the interpreter stack
1818
#define INTERP_STACK_ALIGNMENT 16 // Alignment of interpreter stack at the start of a frame
1919

20-
#define INTERP_INDIRECT_HELPER_TAG 1 // When a helper ftn's address is indirect we tag it with this tag bit
20+
#define INTERP_DIRECT_HELPER_TAG 1 // When a helper ftn's address is direct we tag it with this tag bit
2121

2222
struct CallStubHeader;
2323

@@ -137,7 +137,7 @@ struct InterpGenericLookup
137137
void* signature;
138138

139139
// Here is the helper you must call. It is one of CORINFO_HELP_RUNTIMEHANDLE_* helpers.
140-
140+
141141
InterpGenericLookupType lookupType;
142142

143143
// Number of indirections to get there

src/coreclr/interpreter/intops.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ OPDEF(INTOP_LDFLDA, "ldflda", 4, 1, 1, InterpOpInt)
358358
OPDEF(INTOP_CALL, "call", 4, 1, 1, InterpOpMethodHandle)
359359
OPDEF(INTOP_CALLI, "calli", 5, 1, 2, InterpOpLdPtr)
360360
OPDEF(INTOP_CALLVIRT, "callvirt", 4, 1, 1, InterpOpMethodHandle)
361+
OPDEF(INTOP_CALL_PINVOKE, "call.pinvoke", 6, 1, 1, InterpOpMethodHandle) // inlined (no marshaling wrapper) pinvokes only
361362
OPDEF(INTOP_NEWOBJ, "newobj", 5, 1, 1, InterpOpMethodHandle)
362363
OPDEF(INTOP_NEWOBJ_GENERIC, "newobj.generic", 6, 1, 2, InterpOpMethodHandle)
363364
OPDEF(INTOP_NEWOBJ_VT, "newobj.vt", 5, 1, 1, InterpOpMethodHandle)

src/coreclr/vm/interpexec.cpp

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
#include "gcenv.h"
88
#include "interpexec.h"
99
#include "callstubgenerator.h"
10+
#include "frames.h"
1011

1112
// for numeric_limits
1213
#include <limits>
1314

14-
void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet)
15+
void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet, PCODE target)
1516
{
1617
CONTRACTL
1718
{
@@ -44,7 +45,8 @@ void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet)
4445
}
4546
}
4647

47-
pHeader->SetTarget(pMD->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY)); // The method to call
48+
// Interpreter-FIXME: Potential race condition if a single CallStubHeader is reused for multiple targets.
49+
pHeader->SetTarget(target); // The method to call
4850

4951
pHeader->Invoke(pHeader->Routines, pArgs, pRet, pHeader->TotalStackSize);
5052
}
@@ -163,10 +165,11 @@ static OBJECTREF CreateMultiDimArray(MethodTable* arrayClass, int8_t* stack, int
163165
template <typename THelper> static THelper GetPossiblyIndirectHelper(void* dataItem)
164166
{
165167
size_t helperDirectOrIndirect = (size_t)dataItem;
166-
if (helperDirectOrIndirect & INTERP_INDIRECT_HELPER_TAG)
167-
return *(THelper *)(helperDirectOrIndirect & ~INTERP_INDIRECT_HELPER_TAG);
168+
if (helperDirectOrIndirect & INTERP_DIRECT_HELPER_TAG)
169+
// Clear the direct flag and then raise the thumb bit as needed
170+
return (THelper)PINSTRToPCODE((TADDR)(helperDirectOrIndirect & ~INTERP_DIRECT_HELPER_TAG));
168171
else
169-
return (THelper)helperDirectOrIndirect;
172+
return *(THelper *)helperDirectOrIndirect;
170173
}
171174

172175
// At present our behavior for float to int conversions is to perform a saturating conversion down to either 32 or 64 bits
@@ -1827,6 +1830,41 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
18271830
break;
18281831
}
18291832

1833+
case INTOP_CALL_PINVOKE:
1834+
{
1835+
// This opcode handles p/invokes that don't use a managed wrapper for marshaling. These
1836+
// calls are special in that they need an InlinedCallFrame in order for proper EH to happen
1837+
1838+
returnOffset = ip[1];
1839+
callArgsOffset = ip[2];
1840+
methodSlot = ip[3];
1841+
int32_t targetAddrSlot = ip[4];
1842+
int32_t indirectFlag = ip[5];
1843+
1844+
ip += 6;
1845+
targetMethod = (MethodDesc*)pMethod->pDataItems[methodSlot];
1846+
PCODE callTarget = indirectFlag
1847+
? *(PCODE *)pMethod->pDataItems[targetAddrSlot]
1848+
: (PCODE)pMethod->pDataItems[targetAddrSlot];
1849+
1850+
InlinedCallFrame inlinedCallFrame;
1851+
inlinedCallFrame.m_pCallerReturnAddress = (TADDR)ip;
1852+
inlinedCallFrame.m_pCallSiteSP = pFrame;
1853+
inlinedCallFrame.m_pCalleeSavedFP = (TADDR)stack;
1854+
inlinedCallFrame.m_pThread = GetThread();
1855+
inlinedCallFrame.m_Datum = NULL;
1856+
inlinedCallFrame.Push();
1857+
1858+
{
1859+
GCX_PREEMP();
1860+
InvokeCompiledMethod(targetMethod, stack + callArgsOffset, stack + returnOffset, callTarget);
1861+
}
1862+
1863+
inlinedCallFrame.Pop();
1864+
1865+
break;
1866+
}
1867+
18301868
case INTOP_CALL:
18311869
{
18321870
returnOffset = ip[1];
@@ -1860,7 +1898,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
18601898
if (targetIp == NULL)
18611899
{
18621900
// If we didn't get the interpreter code pointer setup, then this is a method we need to invoke as a compiled method.
1863-
InvokeCompiledMethod(targetMethod, stack + callArgsOffset, stack + returnOffset);
1901+
InvokeCompiledMethod(targetMethod, stack + callArgsOffset, stack + returnOffset, targetMethod->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY));
18641902
break;
18651903
}
18661904
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
project (pinvoke)
2+
3+
include_directories(${INC_PLATFORM_DIR})
4+
5+
set(SOURCES pinvoke.cpp)
6+
7+
# add the executable
8+
add_library (pinvoke SHARED ${SOURCES})
9+
10+
# add the install targets
11+
install (TARGETS pinvoke DESTINATION bin)

src/tests/JIT/interpreter/Interpreter.cs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,11 @@ public static void RunInterpreterTests()
969969

970970
Console.WriteLine("IntPtr.Zero: {0}, UIntPtr.Zero: {1}", IntPtr.Zero, UIntPtr.Zero);
971971

972+
Console.WriteLine("TestPInvoke");
973+
if (!TestPInvoke())
974+
Environment.FailFast(null);
975+
976+
// For stackwalking validation
972977
System.GC.Collect();
973978

974979
Console.WriteLine("All tests passed successfully!");
@@ -2383,6 +2388,99 @@ static object BoxedSubtraction(object lhs, object rhs)
23832388
return (int)lhs - (int)rhs;
23842389
}
23852390

2391+
[DllImport("pinvoke", CallingConvention = CallingConvention.Cdecl)]
2392+
public static extern int sumTwoInts(int x, int y);
2393+
[DllImport("pinvoke", CallingConvention = CallingConvention.Cdecl)]
2394+
public static extern double sumTwoDoubles(double x, double y);
2395+
[DllImport("pinvoke", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
2396+
public static extern int writeToStdout(string s);
2397+
[DllImport("missingLibrary", CallingConvention = CallingConvention.Cdecl)]
2398+
public static extern void missingPInvoke();
2399+
[DllImport("missingLibrary", CallingConvention = CallingConvention.Cdecl)]
2400+
public static extern void missingPInvokeWithMarshaling(string s);
2401+
2402+
public static bool TestPInvoke()
2403+
{
2404+
if (sumTwoInts(1, 2) != 3)
2405+
return false;
2406+
2407+
double summed = sumTwoDoubles(1, 2);
2408+
if (summed != 3)
2409+
return false;
2410+
2411+
// Test marshaling wrappers
2412+
writeToStdout("Hello world from pinvoke.dll!writeToStdout\n");
2413+
2414+
/* fails, with output:
2415+
Assert failure(PID 32748 [0x00007fec], Thread: 24256 [0x5ec0]): pMD == codeInfo.GetMethodDesc()
2416+
2417+
CORECLR! AppendExceptionStackFrame + 0x331 (0x00007ff9`85879b71)
2418+
SYSTEM.PRIVATE.CORELIB! <no symbol> + 0x0 (0x00007ff9`80d91f30)
2419+
SYSTEM.PRIVATE.CORELIB! <no symbol> + 0x0 (0x00007ff9`80d926b7)
2420+
SYSTEM.PRIVATE.CORELIB! <no symbol> + 0x0 (0x00007ff9`80d92289)
2421+
CORECLR! CallDescrWorkerInternal + 0x83 (0x00007ff9`859811c3)
2422+
CORECLR! CallDescrWorkerWithHandler + 0x130 (0x00007ff9`854755c0)
2423+
CORECLR! DispatchCallSimple + 0x26C (0x00007ff9`8547655c)
2424+
CORECLR! DispatchManagedException + 0x388 (0x00007ff9`85872998)
2425+
CORECLR! DispatchManagedException + 0x67 (0x00007ff9`858725a7)
2426+
CORECLR! UnwindAndContinueRethrowHelperAfterCatch + 0x1F8 (0x00007ff9`851be5e8)
2427+
File: Z:\runtime\src\coreclr\vm\exceptionhandling.cpp:3032
2428+
Image: Z:\runtime\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\corerun.exe
2429+
2430+
pMD is TestPInvoke (correct) and codeInfo.GetMethodDesc() is Main (wrong)
2431+
*/
2432+
/*
2433+
bool caught = false;
2434+
try {
2435+
Console.WriteLine("calling missingPInvoke");
2436+
missingPInvoke();
2437+
return false;
2438+
} catch (DllNotFoundException) {
2439+
Console.WriteLine("caught #1");
2440+
caught = true;
2441+
}
2442+
2443+
if (!caught)
2444+
return false;
2445+
*/
2446+
2447+
/* fails, with output:
2448+
calling missingPInvokeWithMarshaling
2449+
caught #2
2450+
2451+
Assert failure(PID 59772 [0x0000e97c], Thread: 24864 [0x6120]): ohThrowable
2452+
2453+
CORECLR! PreStubWorker$catch$10 + 0x93 (0x00007ff9`580972b3)
2454+
CORECLR! CallSettingFrame_LookupContinuationIndex + 0x20 (0x00007ff9`57f32e70)
2455+
CORECLR! _FrameHandler4::CxxCallCatchBlock + 0x1DE (0x00007ff9`57f1e83e)
2456+
NTDLL! RtlCaptureContext2 + 0x4A6 (0x00007ffa`b7e46606)
2457+
CORECLR! PreStubWorker + 0x4F8 (0x00007ff9`5789dd78)
2458+
CORECLR! ThePreStub + 0x55 (0x00007ff9`57ec29c5)
2459+
CORECLR! CallJittedMethodRetVoid + 0x14 (0x00007ff9`57ec0f34)
2460+
CORECLR! InvokeCompiledMethod + 0x5D7 (0x00007ff9`57afaf67)
2461+
CORECLR! InterpExecMethod + 0x84BB (0x00007ff9`57af68cb)
2462+
CORECLR! ExecuteInterpretedMethod + 0x11B (0x00007ff9`5789c77b)
2463+
File: Z:\runtime\src\coreclr\vm\prestub.cpp:1965
2464+
Image: Z:\runtime\artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root\corerun.exe
2465+
*/
2466+
/*
2467+
bool caught2 = false;
2468+
try {
2469+
Console.WriteLine("calling missingPInvokeWithMarshaling");
2470+
missingPInvokeWithMarshaling("test");
2471+
return false;
2472+
} catch (DllNotFoundException) {
2473+
Console.WriteLine("caught #2");
2474+
caught2 = true;
2475+
}
2476+
2477+
if (!caught2)
2478+
return false;
2479+
*/
2480+
2481+
return true;
2482+
}
2483+
23862484
public static bool TestArray()
23872485
{
23882486
// sbyte

src/tests/JIT/interpreter/Interpreter.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,7 @@
88
<ItemGroup>
99
<Compile Include="Interpreter.cs" />
1010
</ItemGroup>
11+
<ItemGroup>
12+
<CMakeProjectReference Include="CMakeLists.txt" />
13+
</ItemGroup>
1114
</Project>

src/tests/JIT/interpreter/InterpreterTester.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
<ItemGroup>
1414
<Compile Include="InterpreterTester.cs" />
1515
</ItemGroup>
16+
<ItemGroup>
17+
<CMakeProjectReference Include="CMakeLists.txt" />
18+
</ItemGroup>
1619
<ItemGroup>
1720
<ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
1821
<ProjectReference Include="$(MSBuildThisFileDirectory)Interpreter.csproj">
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#include "stdio.h"
5+
#include <stdlib.h>
6+
#include <platformdefines.h>
7+
8+
#define EXPORT_IT extern "C" DLL_EXPORT
9+
10+
EXPORT_IT void writeToStdout (const char *str)
11+
{
12+
puts(str);
13+
}
14+
15+
EXPORT_IT int sumTwoInts (int x, int y)
16+
{
17+
return x + y;
18+
}
19+
20+
EXPORT_IT double sumTwoDoubles (double x, double y)
21+
{
22+
return x + y;
23+
}

0 commit comments

Comments
 (0)