Skip to content

Commit d3eeb0e

Browse files
authored
[RISC-V] Fix passing float and uint arguments in VM (#105021)
* Add tests * Fix passing float and uint arguments in VM * Change test lib name so it doesn't clash with managed DLL on Windows
1 parent 676189a commit d3eeb0e

File tree

7 files changed

+281
-8
lines changed

7 files changed

+281
-8
lines changed

src/coreclr/vm/argdestination.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,26 @@ class ArgDestination
173173
_ASSERTE(!"---------UNReachable-------LoongArch64/RISC-V64!!!");
174174
}
175175
}
176+
177+
#ifdef TARGET_RISCV64
178+
void CopySingleFloatToRegister(void* src)
179+
{
180+
void* dest = GetDestinationAddress();
181+
UINT32 value = *(UINT32*)src;
182+
if (TransitionBlock::IsFloatArgumentRegisterOffset(m_offset))
183+
{
184+
// NaN-box the floating register value or single-float instructions will treat it as NaN
185+
*(UINT64*)dest = 0xffffffff00000000L | value;
186+
}
187+
else
188+
{
189+
// When a single float is passed according to integer calling convention
190+
// (in integer register or on stack), the upper bits are not specified.
191+
*(UINT32*)dest = value;
192+
}
193+
}
194+
#endif // TARGET_RISCV64
195+
176196
#endif // !DACCESS_COMPILE
177197

178198
PTR_VOID GetStructGenRegDestinationAddress()

src/coreclr/vm/callhelpers.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,10 +474,15 @@ void MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT *
474474
*((INT64*)pDest) = (INT16)pArguments[arg];
475475
break;
476476
case 4:
477+
#ifdef TARGET_RISCV64
478+
// RISC-V integer calling convention requires to sign-extend `uint` arguments as well
479+
*((INT64*)pDest) = (INT32)pArguments[arg];
480+
#else // TARGET_LOONGARCH64
477481
if (m_argIt.GetArgType() == ELEMENT_TYPE_U4)
478482
*((INT64*)pDest) = (UINT32)pArguments[arg];
479483
else
480484
*((INT64*)pDest) = (INT32)pArguments[arg];
485+
#endif // TARGET_RISCV64
481486
break;
482487
#else
483488
case 1:

src/coreclr/vm/invokeutil.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,9 @@ void InvokeUtil::CopyArg(TypeHandle th, PVOID argRef, ArgDestination *argDest) {
139139

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

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

171172
case ELEMENT_TYPE_I4:
172-
_ASSERTE(argRef != NULL);
173-
*(INT64 *)pArgDst = *(INT32 *)argRef;
174-
break;
175-
176173
case ELEMENT_TYPE_U4:
177174
_ASSERTE(argRef != NULL);
178-
*(UINT64 *)pArgDst = *(UINT32 *)argRef;
175+
*(INT64 *)pArgDst = *(INT32 *)argRef;
179176
break;
180177

181178
#else // !TARGET_RISCV64
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
project (PrimitiveABINative)
2+
include_directories(${INC_PLATFORM_DIR})
3+
4+
if(CLR_CMAKE_HOST_WIN32)
5+
set_source_files_properties(PrimitiveABI.c PROPERTIES COMPILE_OPTIONS /TC) # compile as C
6+
else()
7+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -Oz")
8+
endif()
9+
10+
add_library (PrimitiveABINative SHARED PrimitiveABI.c)
11+
12+
install (TARGETS PrimitiveABINative DESTINATION bin)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
2+
3+
#include <stdint.h>
4+
#include <stddef.h>
5+
#include <stdio.h>
6+
7+
#ifdef _MSC_VER
8+
#define DLLEXPORT __declspec(dllexport)
9+
#else
10+
#define DLLEXPORT __attribute__((visibility("default")))
11+
#endif // _MSC_VER
12+
13+
DLLEXPORT int64_t Echo_ExtendedUint_RiscV(int a0, uint32_t a1)
14+
{
15+
return (int32_t)a1;
16+
}
17+
18+
DLLEXPORT int64_t Echo_ExtendedUint_OnStack_RiscV(
19+
int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, uint32_t stack0)
20+
{
21+
return (int32_t)stack0;
22+
}
23+
24+
DLLEXPORT double Echo_Float_RiscV(float fa0, float fa1)
25+
{
26+
return fa1 + fa0;
27+
}
28+
29+
DLLEXPORT double Echo_Float_InIntegerReg_RiscV(
30+
float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7,
31+
float a0)
32+
{
33+
return a0 + fa7;
34+
}
35+
36+
DLLEXPORT double Echo_Float_OnStack_RiscV(
37+
float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7,
38+
int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float stack0)
39+
{
40+
return stack0 + fa7;
41+
}
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
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+
using System;
5+
using System.Runtime.InteropServices;
6+
using System.Runtime.CompilerServices;
7+
using Xunit;
8+
9+
public static class Program
10+
{
11+
#region ExtendedUint_RiscVTests
12+
[DllImport("PrimitiveABINative")]
13+
public static extern long Echo_ExtendedUint_RiscV(int a0, uint a1);
14+
15+
[MethodImpl(MethodImplOptions.NoInlining)]
16+
public static long Echo_ExtendedUint_RiscV_Managed(int a0, uint a1) => unchecked((int)a1);
17+
18+
[Fact]
19+
public static void Test_ExtendedUint_RiscV()
20+
{
21+
const uint arg = 0xB1ED0C1Eu;
22+
const long ret = unchecked((int)arg);
23+
long managed = Echo_ExtendedUint_RiscV_Managed(0, arg);
24+
long native = Echo_ExtendedUint_RiscV(0, arg);
25+
26+
Assert.Equal(ret, managed);
27+
Assert.Equal(ret, native);
28+
}
29+
30+
[Fact]
31+
public static void Test_ExtendedUint_ByReflection_RiscV()
32+
{
33+
const uint arg = 0xB1ED0C1Eu;
34+
const long ret = unchecked((int)arg);
35+
long managed = (long)typeof(Program).GetMethod("Echo_ExtendedUint_RiscV_Managed").Invoke(
36+
null, new object[] {0, arg});
37+
long native = (long)typeof(Program).GetMethod("Echo_ExtendedUint_RiscV").Invoke(
38+
null, new object[] {0, arg});
39+
40+
Assert.Equal(ret, managed);
41+
Assert.Equal(ret, native);
42+
}
43+
44+
[DllImport("PrimitiveABINative")]
45+
public static extern long Echo_ExtendedUint_OnStack_RiscV(
46+
int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, uint stack0);
47+
48+
[MethodImpl(MethodImplOptions.NoInlining)]
49+
public static long Echo_ExtendedUint_OnStack_RiscV_Managed(
50+
int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, uint stack0) => unchecked((int)stack0);
51+
52+
[Fact]
53+
public static void Test_ExtendedUint_OnStack_RiscV()
54+
{
55+
const uint arg = 0xB1ED0C1Eu;
56+
const long ret = unchecked((int)arg);
57+
long managed = Echo_ExtendedUint_OnStack_RiscV_Managed(0, 0, 0, 0, 0, 0, 0, 0, arg);
58+
long native = Echo_ExtendedUint_OnStack_RiscV(0, 0, 0, 0, 0, 0, 0, 0, arg);
59+
60+
Assert.Equal(ret, managed);
61+
Assert.Equal(ret, native);
62+
}
63+
64+
[Fact]
65+
public static void Test_ExtendedUint_OnStack_ByReflection_RiscV()
66+
{
67+
const uint arg = 0xB1ED0C1Eu;
68+
const long ret = unchecked((int)arg);
69+
long managed = (long)typeof(Program).GetMethod("Echo_ExtendedUint_OnStack_RiscV_Managed").Invoke(
70+
null, new object[] {0, 0, 0, 0, 0, 0, 0, 0, arg});
71+
long native = (long)typeof(Program).GetMethod("Echo_ExtendedUint_OnStack_RiscV").Invoke(
72+
null, new object[] {0, 0, 0, 0, 0, 0, 0, 0, arg});
73+
74+
Assert.Equal(ret, managed);
75+
Assert.Equal(ret, native);
76+
}
77+
#endregion
78+
79+
#region Float_RiscVTests
80+
[DllImport("PrimitiveABINative")]
81+
public static extern double Echo_Float_RiscV(float fa0, float fa1);
82+
83+
[MethodImpl(MethodImplOptions.NoInlining)]
84+
public static double Echo_Float_RiscV_Managed(float fa0, float fa1) => fa1;
85+
86+
[Fact]
87+
public static void Test_Float_RiscV()
88+
{
89+
const float arg = 3.14159f;
90+
const double ret = 3.14159f;
91+
double managed = Echo_Float_RiscV_Managed(0f, arg);
92+
double native = Echo_Float_RiscV(0f, arg);
93+
94+
Assert.Equal(ret, managed);
95+
Assert.Equal(ret, native);
96+
}
97+
98+
[Fact]
99+
public static void Test_Float_ByReflection_RiscV()
100+
{
101+
const float arg = 3.14159f;
102+
const double ret = 3.14159f;
103+
double managed = (double)typeof(Program).GetMethod("Echo_Float_RiscV_Managed").Invoke(
104+
null, new object[] {0f, arg});
105+
double native = (double)typeof(Program).GetMethod("Echo_Float_RiscV").Invoke(
106+
null, new object[] {0f, arg});
107+
108+
Assert.Equal(ret, managed);
109+
Assert.Equal(ret, native);
110+
}
111+
112+
[DllImport("PrimitiveABINative")]
113+
public static extern double Echo_Float_InIntegerReg_RiscV(
114+
float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, float a0);
115+
116+
[MethodImpl(MethodImplOptions.NoInlining)]
117+
public static double Echo_Float_InIntegerReg_RiscV_Managed(
118+
float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, float a0) => a0;
119+
120+
[Fact]
121+
public static void Test_Float_InIntegerReg_RiscV()
122+
{
123+
const float arg = 3.14159f;
124+
const double ret = 3.14159f;
125+
double managed = Echo_Float_InIntegerReg_RiscV_Managed(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, arg);
126+
double native = Echo_Float_InIntegerReg_RiscV(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, arg);
127+
128+
Assert.Equal(ret, managed);
129+
Assert.Equal(ret, native);
130+
}
131+
132+
[Fact]
133+
public static void Test_Float_InIntegerReg_ByReflection_RiscV()
134+
{
135+
const float arg = 3.14159f;
136+
const double ret = 3.14159f;
137+
double managed = (double)typeof(Program).GetMethod("Echo_Float_InIntegerReg_RiscV_Managed").Invoke(
138+
null, new object[] {0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, arg});
139+
double native = (double)typeof(Program).GetMethod("Echo_Float_InIntegerReg_RiscV").Invoke(
140+
null, new object[] {0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, arg});
141+
142+
Assert.Equal(ret, managed);
143+
Assert.Equal(ret, native);
144+
}
145+
146+
[DllImport("PrimitiveABINative")]
147+
public static extern double Echo_Float_OnStack_RiscV(
148+
float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7,
149+
int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float stack0);
150+
151+
[MethodImpl(MethodImplOptions.NoInlining)]
152+
public static double Echo_Float_OnStack_RiscV_Managed(
153+
float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7,
154+
int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float stack0) => stack0;
155+
156+
[Fact]
157+
public static void Test_Float_OnStack_RiscV()
158+
{
159+
const float arg = 3.14159f;
160+
const double ret = 3.14159f;
161+
double managed = Echo_Float_OnStack_RiscV_Managed(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0, 0, 0, 0, 0, 0, 0, 0, arg);
162+
double native = Echo_Float_OnStack_RiscV(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0, 0, 0, 0, 0, 0, 0, 0, arg);
163+
164+
Assert.Equal(ret, managed);
165+
Assert.Equal(ret, native);
166+
}
167+
168+
[Fact]
169+
public static void Test_Float_OnStack_ByReflection_RiscV()
170+
{
171+
const float arg = 3.14159f;
172+
const double ret = 3.14159f;
173+
double managed = (double)typeof(Program).GetMethod("Echo_Float_OnStack_RiscV_Managed").Invoke(
174+
null, new object[] {0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0, 0, 0, 0, 0, 0, 0, 0, arg});
175+
double native = (double)typeof(Program).GetMethod("Echo_Float_OnStack_RiscV").Invoke(
176+
null, new object[] {0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0, 0, 0, 0, 0, 0, 0, 0, arg});
177+
178+
Assert.Equal(ret, managed);
179+
Assert.Equal(ret, native);
180+
}
181+
#endregion
182+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<!-- Needed for CMakeProjectReference -->
4+
<RequiresProcessIsolation>true</RequiresProcessIsolation>
5+
</PropertyGroup>
6+
<PropertyGroup>
7+
<DebugType>PdbOnly</DebugType>
8+
<Optimize>True</Optimize>
9+
</PropertyGroup>
10+
<ItemGroup>
11+
<Compile Include="PrimitiveABI.cs" />
12+
</ItemGroup>
13+
<ItemGroup>
14+
<CMakeProjectReference Include="CMakeLists.txt" />
15+
</ItemGroup>
16+
</Project>

0 commit comments

Comments
 (0)