Skip to content

Commit

Permalink
[RISC-V] Fix passing float and uint arguments in VM (#105021)
Browse files Browse the repository at this point in the history
* Add tests

* Fix passing float and uint arguments in VM

* Change test lib name so it doesn't clash with managed DLL on Windows
  • Loading branch information
tomeksowi authored and directhex committed Jul 26, 2024
1 parent 62c6854 commit a75140d
Show file tree
Hide file tree
Showing 7 changed files with 281 additions and 8 deletions.
20 changes: 20 additions & 0 deletions src/coreclr/vm/argdestination.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,26 @@ class ArgDestination
_ASSERTE(!"---------UNReachable-------LoongArch64/RISC-V64!!!");
}
}

#ifdef TARGET_RISCV64
void CopySingleFloatToRegister(void* src)
{
void* dest = GetDestinationAddress();
UINT32 value = *(UINT32*)src;
if (TransitionBlock::IsFloatArgumentRegisterOffset(m_offset))
{
// NaN-box the floating register value or single-float instructions will treat it as NaN
*(UINT64*)dest = 0xffffffff00000000L | value;
}
else
{
// When a single float is passed according to integer calling convention
// (in integer register or on stack), the upper bits are not specified.
*(UINT32*)dest = value;
}
}
#endif // TARGET_RISCV64

#endif // !DACCESS_COMPILE

PTR_VOID GetStructGenRegDestinationAddress()
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/vm/callhelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,10 +474,15 @@ void MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT *
*((INT64*)pDest) = (INT16)pArguments[arg];
break;
case 4:
#ifdef TARGET_RISCV64
// RISC-V integer calling convention requires to sign-extend `uint` arguments as well
*((INT64*)pDest) = (INT32)pArguments[arg];
#else // TARGET_LOONGARCH64
if (m_argIt.GetArgType() == ELEMENT_TYPE_U4)
*((INT64*)pDest) = (UINT32)pArguments[arg];
else
*((INT64*)pDest) = (INT32)pArguments[arg];
#endif // TARGET_RISCV64
break;
#else
case 1:
Expand Down
13 changes: 5 additions & 8 deletions src/coreclr/vm/invokeutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,9 @@ void InvokeUtil::CopyArg(TypeHandle th, PVOID argRef, ArgDestination *argDest) {

switch (type) {
#ifdef TARGET_RISCV64
// RISC-V call convention requires signed ints sign-extended (unsigned -- zero-extended) to register width
// RISC-V call convention requires integer scalars narrower than XLEN bits to be widened according to the sign
// of their type up to 32 bits, then sign-extended to XLEN bits. In practice it means type-extending all ints
// except `uint` which is sign-extended regardless.
case ELEMENT_TYPE_BOOLEAN:
case ELEMENT_TYPE_U1:
_ASSERTE(argRef != NULL);
Expand All @@ -164,18 +166,13 @@ void InvokeUtil::CopyArg(TypeHandle th, PVOID argRef, ArgDestination *argDest) {

case ELEMENT_TYPE_R4:
_ASSERTE(argRef != NULL);
// NaN-box the register value or single-float instructions will treat it as NaN
*(UINT64 *)pArgDst = 0xffffffff00000000L | *(UINT32 *)argRef;
argDest->CopySingleFloatToRegister(argRef);
break;

case ELEMENT_TYPE_I4:
_ASSERTE(argRef != NULL);
*(INT64 *)pArgDst = *(INT32 *)argRef;
break;

case ELEMENT_TYPE_U4:
_ASSERTE(argRef != NULL);
*(UINT64 *)pArgDst = *(UINT32 *)argRef;
*(INT64 *)pArgDst = *(INT32 *)argRef;
break;

#else // !TARGET_RISCV64
Expand Down
12 changes: 12 additions & 0 deletions src/tests/JIT/Directed/PrimitiveABI/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
project (PrimitiveABINative)
include_directories(${INC_PLATFORM_DIR})

if(CLR_CMAKE_HOST_WIN32)
set_source_files_properties(PrimitiveABI.c PROPERTIES COMPILE_OPTIONS /TC) # compile as C
else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -Oz")
endif()

add_library (PrimitiveABINative SHARED PrimitiveABI.c)

install (TARGETS PrimitiveABINative DESTINATION bin)
41 changes: 41 additions & 0 deletions src/tests/JIT/Directed/PrimitiveABI/PrimitiveABI.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#include <stdint.h>
#include <stddef.h>
#include <stdio.h>

#ifdef _MSC_VER
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT __attribute__((visibility("default")))
#endif // _MSC_VER

DLLEXPORT int64_t Echo_ExtendedUint_RiscV(int a0, uint32_t a1)
{
return (int32_t)a1;
}

DLLEXPORT int64_t Echo_ExtendedUint_OnStack_RiscV(
int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, uint32_t stack0)
{
return (int32_t)stack0;
}

DLLEXPORT double Echo_Float_RiscV(float fa0, float fa1)
{
return fa1 + fa0;
}

DLLEXPORT double Echo_Float_InIntegerReg_RiscV(
float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7,
float a0)
{
return a0 + fa7;
}

DLLEXPORT double Echo_Float_OnStack_RiscV(
float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7,
int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float stack0)
{
return stack0 + fa7;
}
182 changes: 182 additions & 0 deletions src/tests/JIT/Directed/PrimitiveABI/PrimitiveABI.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

public static class Program
{
#region ExtendedUint_RiscVTests
[DllImport("PrimitiveABINative")]
public static extern long Echo_ExtendedUint_RiscV(int a0, uint a1);

[MethodImpl(MethodImplOptions.NoInlining)]
public static long Echo_ExtendedUint_RiscV_Managed(int a0, uint a1) => unchecked((int)a1);

[Fact]
public static void Test_ExtendedUint_RiscV()
{
const uint arg = 0xB1ED0C1Eu;
const long ret = unchecked((int)arg);
long managed = Echo_ExtendedUint_RiscV_Managed(0, arg);
long native = Echo_ExtendedUint_RiscV(0, arg);

Assert.Equal(ret, managed);
Assert.Equal(ret, native);
}

[Fact]
public static void Test_ExtendedUint_ByReflection_RiscV()
{
const uint arg = 0xB1ED0C1Eu;
const long ret = unchecked((int)arg);
long managed = (long)typeof(Program).GetMethod("Echo_ExtendedUint_RiscV_Managed").Invoke(
null, new object[] {0, arg});
long native = (long)typeof(Program).GetMethod("Echo_ExtendedUint_RiscV").Invoke(
null, new object[] {0, arg});

Assert.Equal(ret, managed);
Assert.Equal(ret, native);
}

[DllImport("PrimitiveABINative")]
public static extern long Echo_ExtendedUint_OnStack_RiscV(
int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, uint stack0);

[MethodImpl(MethodImplOptions.NoInlining)]
public static long Echo_ExtendedUint_OnStack_RiscV_Managed(
int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, uint stack0) => unchecked((int)stack0);

[Fact]
public static void Test_ExtendedUint_OnStack_RiscV()
{
const uint arg = 0xB1ED0C1Eu;
const long ret = unchecked((int)arg);
long managed = Echo_ExtendedUint_OnStack_RiscV_Managed(0, 0, 0, 0, 0, 0, 0, 0, arg);
long native = Echo_ExtendedUint_OnStack_RiscV(0, 0, 0, 0, 0, 0, 0, 0, arg);

Assert.Equal(ret, managed);
Assert.Equal(ret, native);
}

[Fact]
public static void Test_ExtendedUint_OnStack_ByReflection_RiscV()
{
const uint arg = 0xB1ED0C1Eu;
const long ret = unchecked((int)arg);
long managed = (long)typeof(Program).GetMethod("Echo_ExtendedUint_OnStack_RiscV_Managed").Invoke(
null, new object[] {0, 0, 0, 0, 0, 0, 0, 0, arg});
long native = (long)typeof(Program).GetMethod("Echo_ExtendedUint_OnStack_RiscV").Invoke(
null, new object[] {0, 0, 0, 0, 0, 0, 0, 0, arg});

Assert.Equal(ret, managed);
Assert.Equal(ret, native);
}
#endregion

#region Float_RiscVTests
[DllImport("PrimitiveABINative")]
public static extern double Echo_Float_RiscV(float fa0, float fa1);

[MethodImpl(MethodImplOptions.NoInlining)]
public static double Echo_Float_RiscV_Managed(float fa0, float fa1) => fa1;

[Fact]
public static void Test_Float_RiscV()
{
const float arg = 3.14159f;
const double ret = 3.14159f;
double managed = Echo_Float_RiscV_Managed(0f, arg);
double native = Echo_Float_RiscV(0f, arg);

Assert.Equal(ret, managed);
Assert.Equal(ret, native);
}

[Fact]
public static void Test_Float_ByReflection_RiscV()
{
const float arg = 3.14159f;
const double ret = 3.14159f;
double managed = (double)typeof(Program).GetMethod("Echo_Float_RiscV_Managed").Invoke(
null, new object[] {0f, arg});
double native = (double)typeof(Program).GetMethod("Echo_Float_RiscV").Invoke(
null, new object[] {0f, arg});

Assert.Equal(ret, managed);
Assert.Equal(ret, native);
}

[DllImport("PrimitiveABINative")]
public static extern double Echo_Float_InIntegerReg_RiscV(
float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, float a0);

[MethodImpl(MethodImplOptions.NoInlining)]
public static double Echo_Float_InIntegerReg_RiscV_Managed(
float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, float a0) => a0;

[Fact]
public static void Test_Float_InIntegerReg_RiscV()
{
const float arg = 3.14159f;
const double ret = 3.14159f;
double managed = Echo_Float_InIntegerReg_RiscV_Managed(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, arg);
double native = Echo_Float_InIntegerReg_RiscV(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, arg);

Assert.Equal(ret, managed);
Assert.Equal(ret, native);
}

[Fact]
public static void Test_Float_InIntegerReg_ByReflection_RiscV()
{
const float arg = 3.14159f;
const double ret = 3.14159f;
double managed = (double)typeof(Program).GetMethod("Echo_Float_InIntegerReg_RiscV_Managed").Invoke(
null, new object[] {0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, arg});
double native = (double)typeof(Program).GetMethod("Echo_Float_InIntegerReg_RiscV").Invoke(
null, new object[] {0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, arg});

Assert.Equal(ret, managed);
Assert.Equal(ret, native);
}

[DllImport("PrimitiveABINative")]
public static extern double Echo_Float_OnStack_RiscV(
float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7,
int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float stack0);

[MethodImpl(MethodImplOptions.NoInlining)]
public static double Echo_Float_OnStack_RiscV_Managed(
float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7,
int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float stack0) => stack0;

[Fact]
public static void Test_Float_OnStack_RiscV()
{
const float arg = 3.14159f;
const double ret = 3.14159f;
double managed = Echo_Float_OnStack_RiscV_Managed(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0, 0, 0, 0, 0, 0, 0, 0, arg);
double native = Echo_Float_OnStack_RiscV(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0, 0, 0, 0, 0, 0, 0, 0, arg);

Assert.Equal(ret, managed);
Assert.Equal(ret, native);
}

[Fact]
public static void Test_Float_OnStack_ByReflection_RiscV()
{
const float arg = 3.14159f;
const double ret = 3.14159f;
double managed = (double)typeof(Program).GetMethod("Echo_Float_OnStack_RiscV_Managed").Invoke(
null, new object[] {0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0, 0, 0, 0, 0, 0, 0, 0, arg});
double native = (double)typeof(Program).GetMethod("Echo_Float_OnStack_RiscV").Invoke(
null, new object[] {0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0, 0, 0, 0, 0, 0, 0, 0, arg});

Assert.Equal(ret, managed);
Assert.Equal(ret, native);
}
#endregion
}
16 changes: 16 additions & 0 deletions src/tests/JIT/Directed/PrimitiveABI/PrimitiveABI.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Needed for CMakeProjectReference -->
<RequiresProcessIsolation>true</RequiresProcessIsolation>
</PropertyGroup>
<PropertyGroup>
<DebugType>PdbOnly</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="PrimitiveABI.cs" />
</ItemGroup>
<ItemGroup>
<CMakeProjectReference Include="CMakeLists.txt" />
</ItemGroup>
</Project>

0 comments on commit a75140d

Please sign in to comment.