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

JIT: Sign/zero-extend small primitive arguments for pinvokes on arm32 and Apple arm64 #106314

Merged
merged 11 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 10 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
5 changes: 5 additions & 0 deletions docs/design/coreclr/botr/clr-abi.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ ARM64: See [Overview of ARM64 ABI conventions](https://learn.microsoft.com/cpp/b
## Non-Windows ABI documentation

Arm corporation ABI documentation (for ARM32 and ARM64) is [here](https://developer.arm.com/architectures/system-architectures/software-standards/abi) and [here](https://github.com/ARM-software/abi-aa).
Apple's ARM64 calling convention differences can be found [here](https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms).

The Linux System V x86_64 ABI is documented in [System V Application Binary Interface / AMD64 Architecture Processor Supplement](https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf), with document source material [here](https://gitlab.com/x86-psABIs/x86-64-ABI).

Expand Down Expand Up @@ -117,6 +118,10 @@ ARM64-only: When a method returns a structure that is larger than 16 bytes the c

Primitive value types smaller than 32-bits are widened to 32-bits: signed small types are sign extended and unsigned small types are zero extended. This can be different from the standard calling conventions that may leave the state of unused bits in the return register undefined.

## Small primitive arguments

Small primitive arguments have undefined upper bits. This can be different from the standard calling conventions that may require normalization (e.g. on ARM32 and Apple ARM64).

# PInvokes

The convention is that any method with an InlinedCallFrame (either an IL stub or a normal method with an inlined PInvoke) saves/restores all non-volatile integer registers in its prolog/epilog respectively. This is done so that the InlinedCallFrame can just contain a return address, a stack pointer and a frame pointer. Then using just those three it can start a full stack walk using the normal RtlVirtualUnwind.
Expand Down
12 changes: 12 additions & 0 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6184,6 +6184,18 @@ void Compiler::impPopCallArgs(CORINFO_SIG_INFO* sig, GenTreeCall* call)
argNode = impImplicitR4orR8Cast(argNode, jitSigType);
// insert any widening or narrowing casts for backwards compatibility
argNode = impImplicitIorI4Cast(argNode, jitSigType);

if ((compAppleArm64Abi() || TargetArchitecture::IsArm32) && call->IsUnmanaged() &&
varTypeIsSmall(jitSigType))
{
// Apple arm64 and arm32 ABIs require arguments to be zero/sign
// extended up to 32 bit. The managed ABI does not require
// this.
if (fgCastNeeded(argNode, jitSigType))
{
argNode = gtNewCastNode(TYP_INT, argNode, false, jitSigType);
}
}
}

NewCallArg arg;
Expand Down
7 changes: 2 additions & 5 deletions src/coreclr/nativeaot/Runtime/CommonMacros.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,14 +293,11 @@ typedef uint8_t CODE_LOCATION;

typedef bool CLR_BOOL;
Copy link
Member

Choose a reason for hiding this comment

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

Delete?

Copy link
Member Author

Choose a reason for hiding this comment

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

Looks like it's still used as the return value for HandleTableRestrictedCallbackFunction.

Copy link
Member

Choose a reason for hiding this comment

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

And TryGetTaggedMemoryCallback.

These correspond to the following managed methods:

CLR_BOOL is not quite right for neither of these, but I do not think that this mismatch is going to cause any real problem. It would be nice to clean this up in a follow up.


#if defined(TARGET_X86) || defined(TARGET_AMD64)
// The return value is artificially widened on x86 and amd64
typedef int32_t FC_BOOL_RET;
#else
typedef bool FC_BOOL_RET;
#endif
typedef int32_t FC_BOOL_ARG;

#define FC_RETURN_BOOL(x) do { return !!(x); } while(0)
#define FC_ACCESS_BOOL(x) ((uint8_t)x != 0)

#ifndef DACCESS_COMPILE
#define IN_DAC(x)
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/nativeaot/Runtime/GCHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,9 @@ FCIMPL0(int32_t, RhGetMaxGcGeneration)
}
FCIMPLEND

FCIMPL2(int32_t, RhGetGcCollectionCount, int32_t generation, CLR_BOOL getSpecialGCCount)
FCIMPL2(int32_t, RhGetGcCollectionCount, int32_t generation, FC_BOOL_ARG getSpecialGCCount)
{
return GCHeapUtilities::GetGCHeap()->CollectionCount(generation, getSpecialGCCount);
return GCHeapUtilities::GetGCHeap()->CollectionCount(generation, FC_ACCESS_BOOL(getSpecialGCCount));
}
FCIMPLEND

Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2139,9 +2139,9 @@ bool StackFrameIterator::Next(uint32_t* puExCollideClauseIdx, bool* pfUnwoundRev
return isValid;
}

FCIMPL4(FC_BOOL_RET, RhpSfiInit, StackFrameIterator* pThis, PAL_LIMITED_CONTEXT* pStackwalkCtx, CLR_BOOL instructionFault, CLR_BOOL* pfIsExceptionIntercepted)
FCIMPL4(FC_BOOL_RET, RhpSfiInit, StackFrameIterator* pThis, PAL_LIMITED_CONTEXT* pStackwalkCtx, FC_BOOL_ARG instructionFault, CLR_BOOL* pfIsExceptionIntercepted)
{
bool isValid = pThis->Init(pStackwalkCtx, instructionFault);
bool isValid = pThis->Init(pStackwalkCtx, FC_ACCESS_BOOL(instructionFault));

if (pfIsExceptionIntercepted)
{
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/nativeaot/Runtime/threadstore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,9 +406,9 @@ EXTERN_C void* QCALLTYPE RhpGetCurrentThread()
return ThreadStore::GetCurrentThread();
}

FCIMPL3(void, RhpInitiateThreadAbort, void* thread, Object * threadAbortException, CLR_BOOL doRudeAbort)
FCIMPL3(void, RhpInitiateThreadAbort, void* thread, Object * threadAbortException, FC_BOOL_ARG doRudeAbort)
{
GetThreadStore()->InitiateThreadAbort((Thread*)thread, threadAbortException, doRudeAbort);
GetThreadStore()->InitiateThreadAbort((Thread*)thread, threadAbortException, FC_ACCESS_BOOL(doRudeAbort));
}
FCIMPLEND

Expand Down
12 changes: 6 additions & 6 deletions src/coreclr/vm/binder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -775,22 +775,22 @@ static void FCallCheckSignature(MethodDesc* pMD, PCODE pImpl)
expectedType = pMD->IsCtor() ? NULL : "void";
break;
case ELEMENT_TYPE_BOOLEAN:
expectedType = (argIndex == -2) ? "FC_BOOL_RET" : "CLR_BOOL";
expectedType = (argIndex == -2) ? "FC_BOOL_RET" : "FC_BOOL_ARG";
break;
case ELEMENT_TYPE_CHAR:
expectedType = (argIndex == -2) ? "FC_CHAR_RET" : "CLR_CHAR";
expectedType = (argIndex == -2) ? "FC_CHAR_RET" : "FC_CHAR_ARG";
break;
case ELEMENT_TYPE_I1:
expectedType = (argIndex == -2) ? "FC_INT8_RET" : "INT8";
expectedType = (argIndex == -2) ? "FC_INT8_RET" : "FC_INT8_ARG";
break;
case ELEMENT_TYPE_U1:
expectedType = (argIndex == -2) ? "FC_UINT8_RET" : "UINT8";
expectedType = (argIndex == -2) ? "FC_UINT8_RET" : "FC_UINT8_ARG";
break;
case ELEMENT_TYPE_I2:
expectedType = (argIndex == -2) ? "FC_INT16_RET" : "INT16";
expectedType = (argIndex == -2) ? "FC_INT16_RET" : "FC_INT16_ARG";
break;
case ELEMENT_TYPE_U2:
expectedType = (argIndex == -2) ? "FC_UINT16_RET" : "UINT16";
expectedType = (argIndex == -2) ? "FC_UINT16_RET" : "FC_UINT16_ARG";
break;
//case ELEMENT_TYPE_I4:
// expectedType = "INT32";
Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/vm/comwaithandle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#include "excep.h"
#include "comwaithandle.h"

FCIMPL3(INT32, WaitHandleNative::CorWaitOneNative, HANDLE handle, INT32 timeout, CLR_BOOL useTrivialWaits)
FCIMPL3(INT32, WaitHandleNative::CorWaitOneNative, HANDLE handle, INT32 timeout, FC_BOOL_ARG useTrivialWaits)
{
FCALL_CONTRACT;

Expand All @@ -28,7 +28,7 @@ FCIMPL3(INT32, WaitHandleNative::CorWaitOneNative, HANDLE handle, INT32 timeout,

Thread* pThread = GET_THREAD();

WaitMode waitMode = (WaitMode)((!useTrivialWaits ? WaitMode_Alertable : WaitMode_None) | WaitMode_IgnoreSyncCtx);
WaitMode waitMode = (WaitMode)((!FC_ACCESS_BOOL(useTrivialWaits) ? WaitMode_Alertable : WaitMode_None) | WaitMode_IgnoreSyncCtx);
retVal = pThread->DoAppropriateWait(1, &handle, TRUE, timeout, waitMode);

HELPER_METHOD_FRAME_END();
Expand All @@ -55,7 +55,7 @@ extern "C" INT32 QCALLTYPE WaitHandle_CorWaitOnePrioritizedNative(HANDLE handle,
}
#endif

FCIMPL4(INT32, WaitHandleNative::CorWaitMultipleNative, HANDLE *handleArray, INT32 numHandles, CLR_BOOL waitForAll, INT32 timeout)
FCIMPL4(INT32, WaitHandleNative::CorWaitMultipleNative, HANDLE *handleArray, INT32 numHandles, FC_BOOL_ARG waitForAll, INT32 timeout)
{
FCALL_CONTRACT;

Expand All @@ -67,13 +67,13 @@ FCIMPL4(INT32, WaitHandleNative::CorWaitMultipleNative, HANDLE *handleArray, INT
#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
// There are some issues with wait-all from an STA thread
// - https://github.com/dotnet/runtime/issues/10243#issuecomment-385117537
if (waitForAll && numHandles > 1 && pThread->GetApartment() == Thread::AS_InSTA)
if (FC_ACCESS_BOOL(waitForAll) && numHandles > 1 && pThread->GetApartment() == Thread::AS_InSTA)
{
COMPlusThrow(kNotSupportedException, W("NotSupported_WaitAllSTAThread"));
}
#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT

ret = pThread->DoAppropriateWait(numHandles, handleArray, waitForAll, timeout, (WaitMode)(WaitMode_Alertable | WaitMode_IgnoreSyncCtx));
ret = pThread->DoAppropriateWait(numHandles, handleArray, FC_ACCESS_BOOL(waitForAll), timeout, (WaitMode)(WaitMode_Alertable | WaitMode_IgnoreSyncCtx));

HELPER_METHOD_FRAME_END();
return ret;
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/comwaithandle.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
class WaitHandleNative
{
public:
static FCDECL3(INT32, CorWaitOneNative, HANDLE handle, INT32 timeout, CLR_BOOL useTrivialWaits);
static FCDECL4(INT32, CorWaitMultipleNative, HANDLE *handleArray, INT32 numHandles, CLR_BOOL waitForAll, INT32 timeout);
static FCDECL3(INT32, CorWaitOneNative, HANDLE handle, INT32 timeout, FC_BOOL_ARG useTrivialWaits);
static FCDECL4(INT32, CorWaitMultipleNative, HANDLE *handleArray, INT32 numHandles, FC_BOOL_ARG waitForAll, INT32 timeout);
static FCDECL3(INT32, CorSignalAndWaitOneNative, HANDLE waitHandleSignalUNSAFE, HANDLE waitHandleWaitUNSAFE, INT32 timeout);
};
#ifdef TARGET_UNIX
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/fcall.h
Original file line number Diff line number Diff line change
Expand Up @@ -1335,7 +1335,10 @@ typedef UINT32 FC_UINT8_RET;
typedef INT32 FC_INT16_RET;
typedef UINT32 FC_UINT16_RET;

// Small primitive args are not widened.
Copy link
Member

Choose a reason for hiding this comment

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

The comment around line 150 in this file still mentions CLR_BOOL.

Copy link
Member Author

Choose a reason for hiding this comment

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

Mind if I update that in a separate change tomorrow?

Copy link
Member

Choose a reason for hiding this comment

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

Fine with me

Copy link
Member Author

Choose a reason for hiding this comment

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

Nevermind, updated it as well now due to those other places I missed the conversion.

typedef INT32 FC_BOOL_ARG;

#define FC_ACCESS_BOOL(x) ((BYTE)x != 0)

// The fcall entrypoints has to be at unique addresses. Use this helper macro to make
// the code of the fcalls unique if you get assert in ecall.cpp that mentions it.
Expand Down
12 changes: 6 additions & 6 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,27 +106,27 @@ int64_t AtomicLoad64WithoutTearing(int64_t volatile *valueRef)
#endif // TARGET_64BIT
}

FCIMPL1(INT64, GetCompiledILBytes, CLR_BOOL currentThread)
FCIMPL1(INT64, GetCompiledILBytes, FC_BOOL_ARG currentThread)
{
FCALL_CONTRACT;

return currentThread ? t_cbILJittedForThread : AtomicLoad64WithoutTearing(&g_cbILJitted);
return FC_ACCESS_BOOL(currentThread) ? t_cbILJittedForThread : AtomicLoad64WithoutTearing(&g_cbILJitted);
}
FCIMPLEND

FCIMPL1(INT64, GetCompiledMethodCount, CLR_BOOL currentThread)
FCIMPL1(INT64, GetCompiledMethodCount, FC_BOOL_ARG currentThread)
{
FCALL_CONTRACT;

return currentThread ? t_cMethodsJittedForThread : AtomicLoad64WithoutTearing(&g_cMethodsJitted);
return FC_ACCESS_BOOL(currentThread) ? t_cMethodsJittedForThread : AtomicLoad64WithoutTearing(&g_cMethodsJitted);
}
FCIMPLEND

FCIMPL1(INT64, GetCompilationTimeInTicks, CLR_BOOL currentThread)
FCIMPL1(INT64, GetCompilationTimeInTicks, FC_BOOL_ARG currentThread)
{
FCALL_CONTRACT;

return currentThread ? t_c100nsTicksInJitForThread : AtomicLoad64WithoutTearing(&g_c100nsTicksInJit);
return FC_ACCESS_BOOL(currentThread) ? t_c100nsTicksInJitForThread : AtomicLoad64WithoutTearing(&g_c100nsTicksInJit);
}
FCIMPLEND

Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/vm/jitinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -1136,8 +1136,8 @@ extern thread_local int64_t t_cbILJittedForThread;
extern thread_local int64_t t_cMethodsJittedForThread;
extern thread_local int64_t t_c100nsTicksInJitForThread;

FCDECL1(INT64, GetCompiledILBytes, CLR_BOOL currentThread);
FCDECL1(INT64, GetCompiledMethodCount, CLR_BOOL currentThread);
FCDECL1(INT64, GetCompilationTimeInTicks, CLR_BOOL currentThread);
FCDECL1(INT64, GetCompiledILBytes, FC_BOOL_ARG currentThread);
FCDECL1(INT64, GetCompiledMethodCount, FC_BOOL_ARG currentThread);
FCDECL1(INT64, GetCompilationTimeInTicks, FC_BOOL_ARG currentThread);

#endif // JITINTERFACE_H
2 changes: 1 addition & 1 deletion src/coreclr/vm/qcall.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
//
//
// The preferred type of QCall arguments is primitive types that efficiently handled by the P/Invoke marshaler (INT32, LPCWSTR, BOOL).
// (Notice that BOOL is the correct boolean flavor for QCall arguments. CLR_BOOL is the correct boolean flavor for FCall arguments.)
// (Notice that BOOL is the correct boolean flavor for QCall arguments. FC_BOOL_ARG is the correct boolean flavor for FCall arguments.)
//
// The pointers to common unmanaged EE structures should be wrapped into helper handle types. This is to make the managed implementation
// type safe and avoid falling into unsafe C# everywhere. See the AssemblyHandle below for a good example.
Expand Down
8 changes: 4 additions & 4 deletions src/coreclr/vm/reflectioninvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod,
Object *target,
PVOID* args, // An array of byrefs
SignatureNative* pSigUNSAFE,
CLR_BOOL fConstructor)
FC_BOOL_ARG fConstructor)
{
FCALL_CONTRACT;

Expand Down Expand Up @@ -443,7 +443,7 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod,

BOOL fCtorOfVariableSizedObject = FALSE;

if (fConstructor)
if (FC_ACCESS_BOOL(fConstructor))
{
// If we are invoking a constructor on an array then we must
// handle this specially.
Expand Down Expand Up @@ -550,7 +550,7 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod,
if (!pMeth->IsStatic() && !fCtorOfVariableSizedObject) {
PVOID pThisPtr;

if (fConstructor)
if (FC_ACCESS_BOOL(fConstructor))
{
// Copy "this" pointer: only unbox if type is value type and method is not unboxing stub
if (ownerType.IsValueType() && !pMeth->IsUnboxingStub()) {
Expand Down Expand Up @@ -672,7 +672,7 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod,
CallDescrWorkerWithHandler(&callDescrData);

// It is still illegal to do a GC here. The return type might have/contain GC pointers.
if (fConstructor)
if (FC_ACCESS_BOOL(fConstructor))
{
// We have a special case for Strings...The object is returned...
if (fCtorOfVariableSizedObject) {
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/runtimehandles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1815,7 +1815,7 @@ FCIMPLEND
FCIMPL3(Object *, SignatureNative::GetCustomModifiersAtOffset,
SignatureNative* pSignatureUNSAFE,
INT32 offset,
CLR_BOOL fRequired)
FC_BOOL_ARG fRequired)
{
FCALL_CONTRACT;

Expand All @@ -1840,7 +1840,7 @@ FCIMPL3(Object *, SignatureNative::GetCustomModifiersAtOffset,
INT32 cMods = 0;
CorElementType cmodType;

CorElementType cmodTypeExpected = fRequired ? ELEMENT_TYPE_CMOD_REQD : ELEMENT_TYPE_CMOD_OPT;
CorElementType cmodTypeExpected = FC_ACCESS_BOOL(fRequired) ? ELEMENT_TYPE_CMOD_REQD : ELEMENT_TYPE_CMOD_OPT;

// Discover the number of required and optional custom modifiers.
while(TRUE)
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/runtimehandles.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_RegisterCollectibleTypeDependency(QC
class RuntimeMethodHandle {

public:
static FCDECL4(Object*, InvokeMethod, Object *target, PVOID* args, SignatureNative* pSig, CLR_BOOL fConstructor);
static FCDECL4(Object*, InvokeMethod, Object *target, PVOID* args, SignatureNative* pSig, FC_BOOL_ARG fConstructor);

static FCDECL2(Object*, ReboxToNullable, Object *pBoxedValUNSAFE, ReflectClassBaseObject *pDestUNSAFE);
static FCDECL1(Object*, ReboxFromNullable, Object *pBoxedValUNSAFE);
Expand Down Expand Up @@ -368,7 +368,7 @@ class SignatureNative : public Object

static FCDECL2(FC_INT8_RET, GetCallingConventionFromFunctionPointerAtOffset, SignatureNative* pSig, INT32 offset);

static FCDECL3(Object *, GetCustomModifiersAtOffset, SignatureNative* pSig, INT32 offset, CLR_BOOL fRequired);
static FCDECL3(Object *, GetCustomModifiersAtOffset, SignatureNative* pSig, INT32 offset, FC_BOOL_ARG fRequired);

BOOL HasThis() { LIMITED_METHOD_CONTRACT; return (m_managedCallingConvention & CALLCONV_HasThis); }
INT32 NumFixedArgs() { WRAPPER_NO_CONTRACT; return m_PtrArrayarguments->GetNumComponents(); }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
include_directories(${INC_PLATFORM_DIR})

# This test always needs to be optimized to hit the problem.
set(CMAKE_BUILD_TYPE Release)

add_library(Runtime101046Native SHARED Runtime_101046.cpp)
target_link_libraries(Runtime101046Native PRIVATE platformdefines)
10 changes: 10 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_101046/Runtime_101046.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include <platformdefines.h>
#include <stdint.h>

extern "C" DLL_EXPORT int32_t ReturnExtendedShort(int16_t s)
{
return s;
}
31 changes: 31 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_101046/Runtime_101046.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// Reference source for the .il file

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Xunit;

public unsafe class Runtime_101046
{
[Fact]
public static int TestEntryPoint()
{
ushort value = unchecked((ushort)-123);
return Test(ref value) == -123 ? 100 : 101;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static int Test(ref ushort p)
{
return Runtime101046Native.ReturnExtendedShort((short)p); // this (short) cast is removed in the .il version
}
}

internal static unsafe class Runtime101046Native
{
[DllImport(nameof(Runtime101046Native))]
public static extern int ReturnExtendedShort(short s);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Needed for CMakeProjectReference -->
<RequiresProcessIsolation>true</RequiresProcessIsolation>
<Optimize>True</Optimize>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<CMakeProjectReference Include="CMakeLists.txt" />
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
</Project>
Loading
Loading