Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
2347774
[RISC-V] Model the F, D, C and A extensions as instruction sets
maximmenshikov Sep 4, 2026
615c8a7
[RISC-V] Gate FP and compressed emission on the F and C instruction sets
maximmenshikov Sep 4, 2026
d645e95
[RISC-V] Make the native build's ISA string and ABI configurable
maximmenshikov Sep 8, 2026
c207f6c
[RISC-V] Apply the ISA string and ABI to the cross toolchain probes
maximmenshikov Sep 8, 2026
d8b9908
[RISC-V] Build libunwind for targets without the F/D extensions
maximmenshikov Sep 8, 2026
01310ba
[RISC-V] Guard FP and atomic instructions in the assembly sources
maximmenshikov Sep 9, 2026
f63476a
[RISC-V] Tie the hwprobe assertion to the build's own -march
maximmenshikov Sep 9, 2026
6399854
[RISC-V] Add the lp64 soft-float ABI to the JIT
maximmenshikov Sep 9, 2026
3edf612
[RISC-V] Add the soft-float helpers and the riscv64-lp64 target
maximmenshikov Sep 16, 2026
99ea61b
[RISC-V] Generate code for the soft-float target
maximmenshikov Sep 9, 2026
c42bdf3
[RISC-V] Add a floating-point semantics test for the soft-float target
maximmenshikov Sep 9, 2026
fed286d
[RISC-V] Let a build select the lp64 ABI for NativeAOT publishing
maximmenshikov Sep 16, 2026
6b75f50
[RISC-V] Pass -mabi=lp64 to the native linker and the shim compiler f…
maximmenshikov Sep 16, 2026
2fdd7d6
[RISC-V] Lower Interlocked and CmpXchg without the A extension
maximmenshikov Sep 16, 2026
2dc1eb4
[RISC-V] Detect the A extension from the ISA string, and bump the JIT…
maximmenshikov Sep 16, 2026
353000e
[RISC-V] Require --assume-no-concurrency to build without the A exten…
maximmenshikov Sep 16, 2026
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
53 changes: 53 additions & 0 deletions eng/common/cross/toolchain.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,59 @@ if(TARGET_ARCH_NAME MATCHES "^(arm|armel)$")
if(TARGET_ARCH_NAME STREQUAL "armel")
add_compile_options(-mfloat-abi=softfp)
endif()
elseif(TARGET_ARCH_NAME STREQUAL "riscv64")
# The ISA string and the ABI have to be settled here rather than in
# configurecompiler.cmake alone: cmake compiles and links its own probes at
# project() time, and a probe built for a different ABI than the sysroot fails
# to link, so the compiler is reported as broken before the build starts.
#
# Both are unset by default, which leaves the toolchain's own defaults in
# place. They may also come from the environment, the way CROSS_ROOTFS,
# TARGET_BUILD_ARCH and TOOLCHAIN already do, so that a build driven through a
# superproject can select them without reaching the cmake command line.
if(DEFINED ENV{CLR_CMAKE_RISCV64_MARCH})
set(CLR_CMAKE_RISCV64_MARCH "$ENV{CLR_CMAKE_RISCV64_MARCH}")
endif()
if(DEFINED ENV{CLR_CMAKE_RISCV64_MABI})
set(CLR_CMAKE_RISCV64_MABI "$ENV{CLR_CMAKE_RISCV64_MABI}")
endif()

set(_riscv64_isa_flags "")
if(DEFINED CLR_CMAKE_RISCV64_MARCH)
string(APPEND _riscv64_isa_flags " -march=${CLR_CMAKE_RISCV64_MARCH}")
endif()
if(DEFINED CLR_CMAKE_RISCV64_MABI)
string(APPEND _riscv64_isa_flags " -mabi=${CLR_CMAKE_RISCV64_MABI}")
endif()

if(NOT _riscv64_isa_flags STREQUAL "")
# *_FLAGS_INIT rather than add_compile_options: a try_compile runs as its own
# project and does not inherit directory properties, so the probe would still
# be built for the compiler's default ABI and fail against the sysroot.
string(APPEND CMAKE_C_FLAGS_INIT "${_riscv64_isa_flags}")
string(APPEND CMAKE_CXX_FLAGS_INIT "${_riscv64_isa_flags}")
string(APPEND CMAKE_ASM_FLAGS_INIT "${_riscv64_isa_flags}")
string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT "${_riscv64_isa_flags}")
string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT "${_riscv64_isa_flags}")
endif()

# Without the A extension the compiler lowers C/C++ atomics to __atomic_*
# calls instead of emitting lr/sc, and those live in libatomic. Not every link
# in the tree passes -latomic on its own.
#
# Only the single-letter part of the ISA string counts: a multi-letter extension
# whose name contains "a" (Zba, for one) is not the A extension, and "g" is
# shorthand for imafd and so implies it.
if(DEFINED CLR_CMAKE_RISCV64_MARCH)
string(REGEX REPLACE "_.*$" "" _riscv_single_letter "${CLR_CMAKE_RISCV64_MARCH}")
string(REGEX REPLACE "^rv[0-9]+" "" _riscv_single_letter "${_riscv_single_letter}")
if(NOT _riscv_single_letter MATCHES "[ag]")
add_toolchain_linker_flag("-latomic")
endif()
endif()

# persist variables across multiple try_compile passes
list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES CLR_CMAKE_RISCV64_MARCH CLR_CMAKE_RISCV64_MABI)
elseif(TARGET_ARCH_NAME STREQUAL "s390x")
add_compile_options("--target=${TOOLCHAIN}")
elseif(TARGET_ARCH_NAME STREQUAL "x86")
Expand Down
46 changes: 44 additions & 2 deletions eng/native/configurecompiler.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -925,8 +925,50 @@ if(CLR_CMAKE_HOST_UNIX_ARMV6)
endif(CLR_CMAKE_HOST_UNIX_ARMV6)

if(CLR_CMAKE_HOST_UNIX_RISCV64)
add_compile_options(-march=rv64gc)
add_compile_options(-mabi=lp64d)
# The ISA string and the ABI the native runtime is built with. The defaults are
# the rv64gc/lp64d baseline, so an unconfigured build is unchanged. A target
# whose sysroot is built for a different ABI - a soft-float lp64 userspace, for
# instance - selects it here, the way armel selects -mfloat-abi=softfp above,
# instead of overriding the flags further down the command line.
#
# The float-ABI field of e_flags follows -mabi (not -march), so setting the ABI
# once here is what makes the whole native build agree with the sysroot; the
# linker rejects a mix, and it is also passed at link time so that the driver
# selects the matching CRT and builtins.
set(CLR_CMAKE_RISCV64_MARCH "rv64gc" CACHE STRING "RISC-V ISA string for the native runtime build")
set(CLR_CMAKE_RISCV64_MABI "lp64d" CACHE STRING "RISC-V ABI for the native runtime build")

# Also settable from the environment, the way CLR_CC, ROOTFS_DIR and TOOLCHAIN
# already are: a build driven through a superproject cannot always reach the
# cmake command line of an individual repository.
if(DEFINED ENV{CLR_CMAKE_RISCV64_MARCH})
set(CLR_CMAKE_RISCV64_MARCH "$ENV{CLR_CMAKE_RISCV64_MARCH}")
endif()
if(DEFINED ENV{CLR_CMAKE_RISCV64_MABI})
set(CLR_CMAKE_RISCV64_MABI "$ENV{CLR_CMAKE_RISCV64_MABI}")
endif()

# Decide whether the ISA string selects the A extension. Only the single-letter
# part counts: a multi-letter extension whose name contains "a" (Zba, for one)
# is not the A extension, and "g" is shorthand for imafd and so implies it.
string(REGEX REPLACE "_.*$" "" _riscv_single_letter "${CLR_CMAKE_RISCV64_MARCH}")
string(REGEX REPLACE "^rv[0-9]+" "" _riscv_single_letter "${_riscv_single_letter}")
if(_riscv_single_letter MATCHES "[ag]")
set(_riscv_has_a ON)
else()
set(_riscv_has_a OFF)
endif()

# Without the A extension every __atomic_* call is "not lock-free" and clang
# warns; the runtime builds with -Werror, which would make that fatal.
if(NOT _riscv_has_a)
add_compile_options(-Wno-atomic-alignment)
endif()

add_compile_options(-march=${CLR_CMAKE_RISCV64_MARCH})
add_compile_options(-mabi=${CLR_CMAKE_RISCV64_MABI})
add_link_options(-march=${CLR_CMAKE_RISCV64_MARCH})
add_link_options(-mabi=${CLR_CMAKE_RISCV64_MABI})
endif(CLR_CMAKE_HOST_UNIX_RISCV64)

if(CLR_CMAKE_HOST_UNIX_X86)
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/debug/di/riscv64/floatconversion.S
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
// input: (in A0) the address of the ULONGLONG to be converted to a double
// output: the double corresponding to the ULONGLONG input value
LEAF_ENTRY FPFillR8, .TEXT
#if __riscv_flen >= 64
fld fa0, 0(a0)
#endif
ret
LEAF_END FPFillR8, .TEXT
1 change: 1 addition & 0 deletions src/coreclr/inc/clrconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zba, W("EnableRiscV64
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zbb, W("EnableRiscV64Zbb"), 1, "Allows RiscV64 Zbb hardware intrinsics to be disabled")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zbs, W("EnableRiscV64Zbs"), 1, "Allows RiscV64 Zbs hardware intrinsics to be disabled")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zicond, W("EnableRiscV64Zicond"), 1, "Allows RiscV64 Zicond hardware intrinsics to be disabled")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Compressed, W("EnableRiscV64Compressed"), 1, "Allows the RiscV64 C (compressed) instruction set to be disabled")
#endif

///
Expand Down
22 changes: 22 additions & 0 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,28 @@ enum CorInfoHelpFunc
CORINFO_HELP_FLTREM,
CORINFO_HELP_DBLREM,

/* Soft-float helpers, for targets without a floating-point unit (RISC-V
without the F/D extensions). The arithmetic helpers have the same
semantics as the corresponding IL instructions. The compare helpers
return a three-way result (< 0, 0, > 0 for less, equal, greater) and
differ only for unordered operands: CMP_LE returns 1 and CMP_GE returns
-1 (the libgcc __le*f2/__ge*f2 conventions), so that every ordered and
unordered IL comparison maps onto a single call. */
CORINFO_HELP_FLTADD,
CORINFO_HELP_FLTSUB,
CORINFO_HELP_FLTMUL,
CORINFO_HELP_FLTDIV,
CORINFO_HELP_DBLADD,
CORINFO_HELP_DBLSUB,
CORINFO_HELP_DBLMUL,
CORINFO_HELP_DBLDIV,
CORINFO_HELP_FLTCMP_LE,
CORINFO_HELP_FLTCMP_GE,
CORINFO_HELP_DBLCMP_LE,
CORINFO_HELP_DBLCMP_GE,
CORINFO_HELP_FLT2DBL,
CORINFO_HELP_DBL2FLT,

/* Allocating a new object. Always use ICorClassInfo::getNewHelper() to decide
which is the right helper to use to allocate an object of a given type. */

Expand Down
24 changes: 24 additions & 0 deletions src/coreclr/inc/corinfoinstructionset.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ enum CORINFO_InstructionSet
InstructionSet_Zbb=3,
InstructionSet_Zbs=4,
InstructionSet_Zicond=5,
InstructionSet_F=6,
InstructionSet_D=7,
InstructionSet_C=8,
InstructionSet_A=9,
#endif // TARGET_RISCV64
#ifdef TARGET_WASM
InstructionSet_WasmBase=1,
Expand Down Expand Up @@ -469,6 +473,14 @@ inline CORINFO_InstructionSetFlags EnsureInstructionSetFlagsAreValid(CORINFO_Ins
resultflags.RemoveInstructionSet(InstructionSet_Zbs);
if (resultflags.HasInstructionSet(InstructionSet_Zicond) && !resultflags.HasInstructionSet(InstructionSet_RiscV64Base))
resultflags.RemoveInstructionSet(InstructionSet_Zicond);
if (resultflags.HasInstructionSet(InstructionSet_F) && !resultflags.HasInstructionSet(InstructionSet_RiscV64Base))
resultflags.RemoveInstructionSet(InstructionSet_F);
if (resultflags.HasInstructionSet(InstructionSet_D) && !resultflags.HasInstructionSet(InstructionSet_F))
resultflags.RemoveInstructionSet(InstructionSet_D);
if (resultflags.HasInstructionSet(InstructionSet_C) && !resultflags.HasInstructionSet(InstructionSet_RiscV64Base))
resultflags.RemoveInstructionSet(InstructionSet_C);
if (resultflags.HasInstructionSet(InstructionSet_A) && !resultflags.HasInstructionSet(InstructionSet_RiscV64Base))
resultflags.RemoveInstructionSet(InstructionSet_A);
#endif // TARGET_RISCV64
#ifdef TARGET_WASM
if (resultflags.HasInstructionSet(InstructionSet_Vector128) && !resultflags.HasInstructionSet(InstructionSet_PackedSimd))
Expand Down Expand Up @@ -777,6 +789,14 @@ inline const char *InstructionSetToString(CORINFO_InstructionSet instructionSet)
return "Zbs";
case InstructionSet_Zicond :
return "Zicond";
case InstructionSet_F :
return "F";
case InstructionSet_D :
return "D";
case InstructionSet_C :
return "C";
case InstructionSet_A :
return "A";
#endif // TARGET_RISCV64
#ifdef TARGET_WASM
case InstructionSet_WasmBase :
Expand Down Expand Up @@ -989,6 +1009,10 @@ inline CORINFO_InstructionSet InstructionSetFromR2RInstructionSet(ReadyToRunInst
case READYTORUN_INSTRUCTION_Zbb: return InstructionSet_Zbb;
case READYTORUN_INSTRUCTION_Zbs: return InstructionSet_Zbs;
case READYTORUN_INSTRUCTION_Zicond: return InstructionSet_Zicond;
case READYTORUN_INSTRUCTION_RiscV64F: return InstructionSet_F;
case READYTORUN_INSTRUCTION_RiscV64D: return InstructionSet_D;
case READYTORUN_INSTRUCTION_RiscV64C: return InstructionSet_C;
case READYTORUN_INSTRUCTION_RiscV64A: return InstructionSet_A;
#endif // TARGET_RISCV64
#ifdef TARGET_WASM
case READYTORUN_INSTRUCTION_WasmBase: return InstructionSet_WasmBase;
Expand Down
4 changes: 3 additions & 1 deletion src/coreclr/inc/corjitflags.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ class CORJIT_FLAGS

#if defined(TARGET_ARM)
CORJIT_FLAG_RELATIVE_CODE_RELOCS = 29, // JIT should generate PC-relative address computations instead of EE relocation records
CORJIT_FLAG_SOFTFP_ABI = 30, // Enable armel calling convention
#endif
#if defined(TARGET_ARM) || defined(TARGET_RISCV64)
CORJIT_FLAG_SOFTFP_ABI = 30, // Enable the soft-float calling convention (armel; lp64 on RISC-V)
#endif
CORJIT_FLAG_USE_DISPATCH_HELPERS = 31, // The JIT should use helpers for interface dispatch instead of virtual stub dispatch
CORJIT_FLAG_VERIFY_GC_MODE_TRANSITIONS = 32, // The JIT should emit the diagnostic helpers that verify GC mode transitions are legal
Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/inc/jiteeversionguid.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@

#include <minipal/guid.h>

constexpr GUID JITEEVersionIdentifier = { /* fa0c6a6f-b219-4b60-b928-042c72667eb3 */
0xfa0c6a6f,
0xb219,
0x4b60,
{0xb9, 0x28, 0x04, 0x2c, 0x72, 0x66, 0x7e, 0xb3}
constexpr GUID JITEEVersionIdentifier = { /* a3daece5-c930-442e-886d-0cf93f4d6e54 */
0xa3daece5,
0xc930,
0x442e,
{0x88, 0x6d, 0x0c, 0xf9, 0x3f, 0x4d, 0x6e, 0x54}
};

#endif // JIT_EE_VERSIONING_GUID_H
18 changes: 18 additions & 0 deletions src/coreclr/inc/jithelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,24 @@
JITHELPER(CORINFO_HELP_FLTREM, JIT_FltRem, METHOD__NIL)
JITHELPER(CORINFO_HELP_DBLREM, JIT_DblRem, METHOD__NIL)

// Soft-float helpers. The JIT only requests them under CORJIT_FLAG_SOFTFP_ABI on
// targets without an FPU (NativeAOT for RISC-V without F/D, where the AOT
// compiler binds them to the compiler-rt builtins); the VM never sets that flag.
JITHELPER(CORINFO_HELP_FLTADD, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_FLTSUB, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_FLTMUL, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_FLTDIV, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_DBLADD, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_DBLSUB, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_DBLMUL, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_DBLDIV, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_FLTCMP_LE, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_FLTCMP_GE, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_DBLCMP_LE, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_DBLCMP_GE, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_FLT2DBL, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_DBL2FLT, NULL, METHOD__NIL)

// Allocating a new object
JITHELPER(CORINFO_HELP_NEWFAST, RhpNew, METHOD__NIL)
JITHELPER(CORINFO_HELP_NEWFAST_MAYBEFROZEN, RhpNewMaybeFrozen, METHOD__NIL)
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/inc/readytoruninstructionset.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ enum ReadyToRunInstructionSet
READYTORUN_INSTRUCTION_Cssc=93,
READYTORUN_INSTRUCTION_Zicond=94,
READYTORUN_INSTRUCTION_Fp16=95,
READYTORUN_INSTRUCTION_RiscV64F=96,
READYTORUN_INSTRUCTION_RiscV64D=97,
READYTORUN_INSTRUCTION_RiscV64C=98,
READYTORUN_INSTRUCTION_RiscV64A=99,

};

Expand Down
9 changes: 9 additions & 0 deletions src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8571,6 +8571,15 @@ void CodeGen::genPoisonFrame(regMaskTP regLiveIn)
//
void CodeGen::genBitCast(var_types targetType, regNumber targetReg, var_types srcType, regNumber srcReg)
{
#ifdef TARGET_RISCV64
if (m_compiler->opts.compUseSoftFP && (srcType == TYP_FLOAT) && (targetType == TYP_INT))
{
// Soft-float: a float lives in an integer register with unspecified upper
// bits (RISC-V psABI), while an int is expected to be sign-extended.
GetEmitter()->emitIns_R_R_I(INS_addiw, EA_4BYTE, targetReg, srcReg, 0);
return;
}
#endif // TARGET_RISCV64
const bool srcFltReg = varTypeUsesFloatReg(srcType);
assert(srcFltReg == genIsValidFloatReg(srcReg));

Expand Down
Loading
Loading