Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernhard Urban <bernhard.urban@xamarin.com>2018-11-07 23:04:00 +0300
committermonojenkins <jo.shields+jenkins@xamarin.com>2018-11-07 23:04:00 +0300
commite8aa7a2b73b34e09fabd15a1f332943b1093e7d5 (patch)
treeb59f1ab5006f4b5dadf8bac71ad02ad56d251389
parentf0fe3fc42615e5c63630f80911cf9c0aef084f13 (diff)
initial RISC-V support (#11593)
initial RISC-V support I rebased @alexrp's work (https://github.com/alexrp/mono/commits/riscv) and added stubs so this minimal example works in the interpreter: ```console $ qemu-riscv64 ./mono/mini/mono-sgen --version Mono JIT compiler version 5.21.0 (riscv/f4802305009 Wed Nov 7 02:58:20 PST 2018) Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com TLS: __thread SIGSEGV: normal Notifications: epoll Architecture: riscv64,double-fp Disabled: none Misc: Interpreter: yes Suspend: preemptive GC: sgen (concurrent by default) $ export MONO_PATH=../mono-riscv-bclbuild/b/lib/mono/4.5 $ grep -A 2 test_0_return mono/mini/basic.cs public static int test_0_return () { return 0; } $ INTERP_FILTER_METHOD=test_0_return qemu-riscv64 ./mono/mini/mono-sgen --interp --regression basic.exe Test run: image=/home/lewurm/work/mono-riscv/basic.exe Results: total tests: 1, all pass Elapsed time: 0.002441 secs (0.002441, 0.000000) Overall results: tests: 1, 100% pass ```
-rw-r--r--.github/CODEOWNERS4
-rw-r--r--configure.ac131
-rw-r--r--docs/riscv.md116
-rw-r--r--mono/arch/Makefile.am6
-rw-r--r--mono/arch/riscv/.gitignore5
-rw-r--r--mono/arch/riscv/Makefile.am19
-rw-r--r--mono/arch/riscv/riscv-codegen-test.c238
-rwxr-xr-xmono/arch/riscv/riscv-codegen-test.sh15
-rw-r--r--mono/arch/riscv/riscv-codegen.exp32130
-rw-r--r--mono/arch/riscv/riscv-codegen.exp64168
-rw-r--r--mono/arch/riscv/riscv-codegen.h824
-rw-r--r--mono/metadata/icall.c2
-rw-r--r--mono/metadata/metadata-cross-helpers.c4
-rw-r--r--mono/metadata/mono-config.c6
-rw-r--r--mono/metadata/sgen-client-mono.h2
-rwxr-xr-xmono/mini/Makefile.am.in27
-rw-r--r--mono/mini/aot-compiler.c36
-rw-r--r--mono/mini/cpu-riscv32.md30
-rw-r--r--mono/mini/cpu-riscv64.md29
-rw-r--r--mono/mini/exceptions-riscv.c188
-rwxr-xr-xmono/mini/genmdesc.py3
-rw-r--r--mono/mini/helpers.c4
-rw-r--r--mono/mini/mini-arch.h2
-rw-r--r--mono/mini/mini-codegen.c2
-rw-r--r--mono/mini/mini-gc.c20
-rw-r--r--mono/mini/mini-riscv.c690
-rw-r--r--mono/mini/mini-riscv.h211
-rw-r--r--mono/mini/tramp-riscv.c303
-rw-r--r--mono/mini/unwind.c20
-rw-r--r--mono/sgen/sgen-archdep.h4
-rw-r--r--mono/sgen/sgen-cardtable.c4
-rw-r--r--mono/utils/Makefile.am4
-rw-r--r--mono/utils/mono-context.c30
-rw-r--r--mono/utils/mono-context.h113
-rw-r--r--mono/utils/mono-hwcap-riscv.c41
-rw-r--r--mono/utils/mono-hwcap-vars.h16
-rw-r--r--mono/utils/mono-sigcontext.h22
-rw-r--r--mono/utils/mono-tls.c15
38 files changed, 3474 insertions, 10 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 284cef89272..1a2ba002ff6 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -22,6 +22,7 @@
/data/lldb @lewurm @vargaz
/docs @luhenry @marek-safar @akoeplinger
+/docs/riscv.md @alexrp @lewurm
/docs/sources/mono-api-profiler.html @alexrp
/external @akoeplinger @marek-safar
@@ -75,6 +76,8 @@
/mono @luhenry
+/mono/arch/riscv @alexrp @lewurm
+
/mono/btls @baulig
/mono/dis @lambdageek @vargaz
/mono/eglib @vargaz @luhenry
@@ -93,6 +96,7 @@
/mono/mini/*cfgdump* @lewurm
/mono/mini/*exceptions* @lewurm
/mono/mini/*ppc* @lewurm
+/mono/mini/*riscv* @alexrp @lewurm
/mono/mini/*type-check* @lewurm
/mono/mini/interp/* @lewurm @BrzVlad
/mono/mini/*profiler* @alexrp
diff --git a/configure.ac b/configure.ac
index 54b567bd6e4..2b6b6ae1a38 100644
--- a/configure.ac
+++ b/configure.ac
@@ -292,6 +292,10 @@ case "$host" in
# https://bugzilla.novell.com/show_bug.cgi?id=504411
disable_munmap=yes
;;
+ riscv*)
+ support_boehm=no
+ with_gc=sgen
+ ;;
esac
with_sgen_default_concurrent=yes
;;
@@ -4094,6 +4098,24 @@ case "$host" in
BTLS_PLATFORM=s390x
CFLAGS="$CFLAGS -mbackchain -D__USE_STRING_INLINES"
;;
+ riscv32-*)
+ TARGET=RISCV32
+ ACCESS_UNALIGNED=no
+ AOT_SUPPORTED=no
+ BTLS_SUPPORTED=yes
+ BTLS_PLATFORM=riscv32
+ arch_target=riscv32
+ boehm_supported=false
+ ;;
+ riscv64*)
+ TARGET=RISCV64
+ ACCESS_UNALIGNED=no
+ AOT_SUPPORTED=no
+ BTLS_SUPPORTED=yes
+ BTLS_PLATFORM=riscv64
+ arch_target=riscv64
+ boehm_supported=false
+ ;;
esac
HOST=$TARGET
@@ -4173,6 +4195,22 @@ if test "x$host" != "x$target"; then
aarch64-*)
TARGET=ARM64
;;
+ riscv32*)
+ TARGET=RISCV32
+ AC_DEFINE([TARGET_RISCV], [1], [Target is RISC-V])
+ AC_DEFINE([TARGET_RISCV32], [1], [Target is 32-bit RISC-V])
+ arch_target=riscv32
+ target_mach=no
+ with_tls=pthread
+ ;;
+ riscv64*)
+ TARGET=RISCV64
+ AC_DEFINE([TARGET_RISCV], [1], [Target is RISC-V])
+ AC_DEFINE([TARGET_RISCV64], [1], [Target is 64-bit RISC-V])
+ arch_target=riscv64
+ target_mach=no
+ with_tls=pthread
+ ;;
*)
AC_MSG_ERROR([Cross compiling is not supported for target $target])
esac
@@ -4231,6 +4269,14 @@ SPARC)
SPARC64)
AC_DEFINE(TARGET_SPARC64, 1, [...])
;;
+RISCV32)
+ AC_DEFINE([TARGET_RISCV], [1], [Target is RISC-V])
+ AC_DEFINE([TARGET_RISCV32], [1], [Target is 32-bit RISC-V])
+ ;;
+RISCV64)
+ AC_DEFINE([TARGET_RISCV], [1], [Target is RISC-V])
+ AC_DEFINE([TARGET_RISCV64], [1], [Target is 64-bit RISC-V])
+ ;;
esac
case "$HOST" in
@@ -4268,6 +4314,14 @@ SPARC)
SPARC64)
AC_DEFINE(HOST_SPARC64, 1, [...])
;;
+RISCV32)
+ AC_DEFINE([HOST_RISCV], [1], [Host is RISC-V])
+ AC_DEFINE([HOST_RISCV32], [1], [Host is 32-bit RISC-V])
+ ;;
+RISCV64)
+ AC_DEFINE([HOST_RISCV], [1], [Host is RISC-V])
+ AC_DEFINE([HOST_RISCV64], [1], [Host is 64-bit RISC-V])
+ ;;
esac
MONO_ARCH_GSHAREDVT_SUPPORTED=0
@@ -4323,7 +4377,7 @@ fi
dnl Use GCC atomic ops if they work on the target.
if test x$GCC = "xyes"; then
case $TARGET in
- X86 | AMD64 | ARM | ARM64 | POWERPC | POWERPC64 | MIPS | S390X | SPARC | SPARC64)
+ X86 | AMD64 | ARM | ARM64 | POWERPC | POWERPC64 | MIPS | S390X | SPARC | SPARC64 | RISCV32 | RISCV64)
AC_DEFINE(USE_GCC_ATOMIC_OPS, 1, [...])
;;
esac
@@ -4526,7 +4580,7 @@ case $host in
esac
case $target in
-arm*-darwin*|aarch64*-*)
+arm*-darwin*|aarch64*-*|riscv*)
CFLAGS="$CFLAGS -U_FORTIFY_SOURCE"
;;
i*86-*-darwin*)
@@ -4900,6 +4954,66 @@ if test ${TARGET} = ARM; then
fi
fi
+if test ${TARGET} = RISCV32 -o ${TARGET} = RISCV64; then
+ AC_ARG_WITH([riscv-fpabi], [ --with-riscv-fpabi=auto,double,soft Select RISC-V floating point ABI (auto)], [fpabi=$withval], [fpabi=double])
+
+ AC_MSG_CHECKING([which RISC-V floating point ABI to use])
+
+ if test x$fpabi = xauto; then
+ AC_TRY_COMPILE([], [
+ #ifdef __riscv_float_abi_double
+ #error "double"
+ #endif
+ ], [
+ ], [
+ fpabi=double
+ ])
+ fi
+
+ if test x$fpabi = xauto; then
+ AC_TRY_COMPILE([], [
+ #ifdef __riscv_float_abi_single
+ #error "single"
+ #endif
+ ], [
+ ], [
+ fpabi=single
+ ])
+ fi
+
+ if test x$fpabi = xauto; then
+ AC_TRY_COMPILE([], [
+ #ifdef __riscv_float_abi_soft
+ #error "soft"
+ #endif
+ ], [
+ ], [
+ fpabi=soft
+ ])
+ fi
+
+ case $fpabi in
+ double)
+ AC_DEFINE([RISCV_FPABI_DOUBLE], [1], [RISC-V FPABI is double-precision])
+ AC_MSG_RESULT([double-precision])
+ ;;
+ single)
+ AC_DEFINE([RISCV_FPABI_SINGLE], [1], [RISC-V FPABI is single-precision])
+ AC_MSG_ERROR([single-precision, not supported])
+ ;;
+ soft)
+ AC_DEFINE([RISCV_FPABI_SOFT], [1], [RISC-V FPABI is soft float])
+ AC_MSG_RESULT([soft float])
+ ;;
+ auto)
+ AC_MSG_ERROR([unknown])
+ ;;
+ *)
+ AC_MSG_ERROR([invalid option: $fpabi])
+ ;;
+ esac
+fi
+
if test ${TARGET} = unknown; then
CPPFLAGS="$CPPFLAGS -DNO_PORT"
AC_MSG_WARN("mono has not been ported to $host: some things may not work.")
@@ -5262,6 +5376,12 @@ if test "x$enable_btls" = "xyes"; then
android-x64)
BTLS_CMAKE_ARGS="-DANDROID_ABI=\"x86_64\" -DANDROID_NATIVE_API_LEVEL=21"
;;
+ riscv32)
+ btls_arch=riscv32
+ ;;
+ riscv64)
+ btls_arch=riscv64
+ ;;
*)
AC_MSG_ERROR(Invalid BTLS platform)
esac
@@ -5318,11 +5438,17 @@ AM_CONDITIONAL(POWERPC64, test x$TARGET = xPOWERPC64)
AM_CONDITIONAL(ARM, test x$TARGET = xARM)
AM_CONDITIONAL(ARM64, test x$TARGET = xARM64)
AM_CONDITIONAL(S390X, test x$TARGET = xS390X)
+AM_CONDITIONAL([RISCV], [test x$TARGET = xRISCV32 -o x$TARGET = xRISCV64])
+AM_CONDITIONAL([RISCV32], [test x$TARGET = xRISCV32])
+AM_CONDITIONAL([RISCV64], [test x$TARGET = xRISCV64])
AM_CONDITIONAL(WASM, test x$TARGET = xWASM)
AM_CONDITIONAL(HOST_X86, test x$HOST = xX86)
AM_CONDITIONAL(HOST_AMD64, test x$HOST = xAMD64)
AM_CONDITIONAL(HOST_ARM, test x$HOST = xARM)
AM_CONDITIONAL(HOST_ARM64, test x$HOST = xARM64)
+AM_CONDITIONAL([HOST_RISCV], [test x$HOST = xRISCV32 -o x$HOST = xRISCV64])
+AM_CONDITIONAL([HOST_RISCV32], [test x$HOST = xRISCV32])
+AM_CONDITIONAL([HOST_RISCV64], [test x$HOST = xRISCV64])
AM_CONDITIONAL(HOST_WASM, test x$HOST = xWASM)
AM_CONDITIONAL(CROSS_COMPILE, test "x$host" != "x$target")
@@ -5818,6 +5944,7 @@ mono/arch/s390x/Makefile
mono/arch/arm/Makefile
mono/arch/arm64/Makefile
mono/arch/mips/Makefile
+mono/arch/riscv/Makefile
mono/sgen/Makefile
mono/native/platform-type.c
mono/native/platform-type-compat.c
diff --git a/docs/riscv.md b/docs/riscv.md
new file mode 100644
index 00000000000..53e42761514
--- /dev/null
+++ b/docs/riscv.md
@@ -0,0 +1,116 @@
+# Mono RISC-V Port
+
+These are some useful links and notes pertaining to the RISC-V port of Mono.
+
+## Useful RISC-V documents
+
+* [RISC-V User-Level ISA Specification](https://riscv.org/specifications/)
+* [RISC-V Privileged ISA Specification](https://riscv.org/specifications/privileged-isa/)
+* [RISC-V Debug Specification Standard](https://github.com/riscv/riscv-debug-spec/blob/master/riscv-debug-spec.pdf)
+* [RISC-V Assembly Programmer's Manual](https://github.com/riscv/riscv-asm-manual/blob/master/riscv-asm.md)
+* [RISC-V ELF psABI Specification](https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md)
+
+## Useful RISC-V repositories
+
+* [RISC-V Organization](https://github.com/riscv)
+ * [RISC-V Linux](https://github.com/riscv/riscv-linux)
+ * [RISC-V LLD](https://github.com/riscv/riscv-lld)
+ * [RISC-V Tools](https://github.com/riscv/riscv-tools)
+ * [RISC-V GNU Toolchain](https://github.com/riscv/riscv-gnu-toolchain)
+ * [RISC-V Binutils](https://github.com/riscv/riscv-binutils-gdb)
+ * [RISC-V GCC](https://github.com/riscv/riscv-gcc)
+ * [RISC-V Glibc](https://github.com/riscv/riscv-glibc)
+ * [RISC-V Newlib](https://github.com/riscv/riscv-newlib)
+ * [RISC-V QEMU](https://github.com/riscv/riscv-qemu)
+ * [RISC-V Opcodes](https://github.com/riscv/riscv-opcodes)
+ * [RISC-V Tests](https://github.com/riscv/riscv-tests)
+* [lowRISC Organization](https://github.com/lowrisc)
+ * [RISC-V LLVM](https://github.com/lowrisc/riscv-llvm)
+
+## Setting up a cross environment
+
+Setting up a cross environment with a Linux toolchain and QEMU is quite easy.
+
+First, add these to your `.bashrc` (or some other script that you run):
+
+```bash
+export RISCV=$HOME/riscv
+export PATH=$RISCV/bin:$PATH
+export QEMU_LD_PREFIX=$RISCV/sysroot
+```
+
+Install some dependencies needed to build the toolchain:
+
+```console
+# apt install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev libusb-1.0-0-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev device-tree-compiler pkg-config libexpat-dev libglib2.0-dev libpixman-1-dev
+```
+
+Now you can build the toolchain:
+
+```console
+$ git clone --recursive git@github.com:riscv/riscv-tools.git
+$ cd riscv-tools
+$ ./build.sh
+$ cd riscv-gnu-toolchain
+$ ./configure --prefix=$RISCV --enable-multilib
+$ make linux
+$ cd ../..
+$ git clone --recursive git@github.com:riscv/riscv-qemu.git
+$ cd riscv-qemu
+$ mkdir build
+$ cd build
+$ ../configure --prefix=$RISCV --disable-werror
+$ make
+$ make install
+```
+
+## Building Mono with a cross toolchain
+
+Building Mono is quite straightforward:
+
+```console
+$ ./autogen.sh --prefix=$RISCV/sysroot --host=riscv64-unknown-linux-gnu
+$ make
+$ make install
+```
+
+You can set `CFLAGS` as appropriate to change the RISC-V options, such as which
+standard extensions and ABI to use. For example, to use the 64-bit soft float
+ABI:
+
+```console
+$ CFLAGS="-mabi=lp64" ./autogen.sh --prefix=$RISCV/sysroot --host=riscv64-unknown-linux-gnu
+```
+
+Note that, since this is a cross build, the `mcs` directory won't be built. You
+will have to build the managed libraries and tools through a native build of
+Mono and copy them into `$RISCV/sysroot`.
+
+You can run Mono with QEMU like this:
+
+```console
+$ qemu-riscv64 $RISCV/sysroot/bin/mono hello.exe
+```
+
+## Debugging
+
+```console
+$ qemu-riscv64 -g 12345 ./mono/mini/mono-sgen --interp basic.exe &
+$ riscv64-unknown-elf-gdb -ex 'target remote localhost:12345' -ex 'b main' -ex 'c' ./mono/mini/mono-sgen
+```
+
+## Things to be done
+
+In no particular order:
+
+* Complete the soft float port.
+* Complete the 32-bit port.
+* Add unwind info to trampolines.
+* Implement AOT support.
+* Implement interpreter support.
+* Implement LLVM support.
+* Implement SDB support.
+* Implement `dyn_call` support.
+* Ensure all runtime tests pass.
+* Ensure all corlib tests pass.
+* Set up CI on Jenkins.
diff --git a/mono/arch/Makefile.am b/mono/arch/Makefile.am
index 9f136d32473..61f5522ee1c 100644
--- a/mono/arch/Makefile.am
+++ b/mono/arch/Makefile.am
@@ -1,6 +1,6 @@
include $(top_srcdir)/mk/common.mk
-DIST_SUBDIRS = x86 ppc sparc arm arm64 s390x amd64 mips
+DIST_SUBDIRS = x86 ppc sparc arm arm64 s390x amd64 mips riscv
AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir)
@@ -9,4 +9,6 @@ if ARM
SUBDIRS = arm
endif
-
+if RISCV
+SUBDIRS = riscv
+endif
diff --git a/mono/arch/riscv/.gitignore b/mono/arch/riscv/.gitignore
new file mode 100644
index 00000000000..f1e931fc770
--- /dev/null
+++ b/mono/arch/riscv/.gitignore
@@ -0,0 +1,5 @@
+/Makefile
+/riscv-codegen-test
+/riscv-codegen
+/riscv-codegen.elf
+/riscv-codegen.s
diff --git a/mono/arch/riscv/Makefile.am b/mono/arch/riscv/Makefile.am
new file mode 100644
index 00000000000..16505ff8e5a
--- /dev/null
+++ b/mono/arch/riscv/Makefile.am
@@ -0,0 +1,19 @@
+AM_CPPFLAGS = $(GLIB_CFLAGS)
+
+EXTRA_DIST = riscv-codegen.h
+
+bin_PROGRAMS = riscv-codegen-test
+
+riscv_codegen_test_SOURCES = riscv-codegen-test.c
+
+if RISCV64
+bits = 64
+else
+bits = 32
+endif
+
+check-local: riscv-codegen-test
+ ./riscv-codegen-test > riscv-codegen.s
+ $(AS) riscv-codegen.s -o riscv-codegen.elf
+ $(OBJDUMP) -D -M numeric,no-aliases riscv-codegen.elf > riscv-codegen.res
+ diff -u riscv-codegen.exp$(bits) riscv-codegen.res
diff --git a/mono/arch/riscv/riscv-codegen-test.c b/mono/arch/riscv/riscv-codegen-test.c
new file mode 100644
index 00000000000..722dc0f820e
--- /dev/null
+++ b/mono/arch/riscv/riscv-codegen-test.c
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the .NET Foundation under one or more agreements.
+ * The .NET Foundation licenses this file to you under the MIT license.
+ * See the LICENSE file in the project root for more information.
+ */
+
+#define MONO_RISCV_CODEGEN_TEST
+
+#include "riscv-codegen.h"
+#include <stdio.h>
+
+static guint8 code [4096 * 16];
+
+int
+main (void)
+{
+ guint8 *p = code;
+
+ {
+ // R
+
+ riscv_add (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_sub (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_sll (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_slt (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_sltu (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_xor (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_srl (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_sra (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_or (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_and (p, RISCV_X0, RISCV_X1, RISCV_X2);
+#ifdef TARGET_RISCV64
+ riscv_addw (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_subw (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_sllw (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_srlw (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_sraw (p, RISCV_X0, RISCV_X1, RISCV_X2);
+#endif
+ riscv_mul (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_mulh (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_mulhsu (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_mulhu (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_div (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_divu (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_rem (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_remu (p, RISCV_X0, RISCV_X1, RISCV_X2);
+#ifdef TARGET_RISCV64
+ riscv_mulw (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_divw (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_divuw (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_remw (p, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_remuw (p, RISCV_X0, RISCV_X1, RISCV_X2);
+#endif
+ riscv_fadd_s (p, RISCV_ROUND_NE, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fsub_s (p, RISCV_ROUND_TZ, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fmul_s (p, RISCV_ROUND_DN, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fdiv_s (p, RISCV_ROUND_UP, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fsqrt_s (p, RISCV_ROUND_MM, RISCV_F0, RISCV_F1);
+ riscv_fsgnj_s (p, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fsgnjn_s (p, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fsgnjx_s (p, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fmin_s (p, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fmax_s (p, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fcvt_w_s (p, RISCV_ROUND_DY, RISCV_X0, RISCV_F1);
+ riscv_fcvt_wu_s (p, RISCV_ROUND_NE, RISCV_X0, RISCV_F1);
+ riscv_fmv_x_w (p, RISCV_X0, RISCV_F1);
+ riscv_feq_s (p, RISCV_X0, RISCV_F1, RISCV_F2);
+ riscv_flt_s (p, RISCV_X0, RISCV_F1, RISCV_F2);
+ riscv_fle_s (p, RISCV_X0, RISCV_F1, RISCV_F2);
+ riscv_fclass_s (p, RISCV_X0, RISCV_F1);
+ riscv_fcvt_s_w (p, RISCV_ROUND_TZ, RISCV_F0, RISCV_X1);
+ riscv_fcvt_s_wu (p, RISCV_ROUND_DN, RISCV_F0, RISCV_X1);
+ riscv_fmv_w_x (p, RISCV_F0, RISCV_X1);
+#ifdef TARGET_RISCV64
+ riscv_fcvt_l_s (p, RISCV_ROUND_UP, RISCV_X0, RISCV_F1);
+ riscv_fcvt_lu_s (p, RISCV_ROUND_MM, RISCV_X0, RISCV_F1);
+ riscv_fcvt_s_l (p, RISCV_ROUND_DY, RISCV_F0, RISCV_X1);
+ riscv_fcvt_s_lu (p, RISCV_ROUND_NE, RISCV_F0, RISCV_X1);
+#endif
+ riscv_fadd_d (p, RISCV_ROUND_TZ, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fsub_d (p, RISCV_ROUND_DN, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fmul_d (p, RISCV_ROUND_UP, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fdiv_d (p, RISCV_ROUND_MM, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fsqrt_d (p, RISCV_ROUND_DY, RISCV_F0, RISCV_F1);
+ riscv_fsgnj_d (p, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fsgnjn_d (p, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fsgnjx_d (p, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fmin_d (p, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fmax_d (p, RISCV_F0, RISCV_F1, RISCV_F2);
+ riscv_fcvt_s_d (p, RISCV_ROUND_NE, RISCV_F0, RISCV_F1);
+ riscv_fcvt_d_s (p, RISCV_F0, RISCV_F1);
+ riscv_feq_d (p, RISCV_X0, RISCV_F1, RISCV_F2);
+ riscv_flt_d (p, RISCV_X0, RISCV_F1, RISCV_F2);
+ riscv_fle_d (p, RISCV_X0, RISCV_F1, RISCV_F2);
+ riscv_fclass_d (p, RISCV_X0, RISCV_F1);
+ riscv_fcvt_w_d (p, RISCV_ROUND_TZ, RISCV_X0, RISCV_F1);
+ riscv_fcvt_wu_d (p, RISCV_ROUND_DN, RISCV_X0, RISCV_F1);
+ riscv_fcvt_d_w (p, RISCV_F0, RISCV_X1);
+ riscv_fcvt_d_wu (p, RISCV_F0, RISCV_X1);
+#ifdef TARGET_RISCV64
+ riscv_fcvt_l_d (p, RISCV_ROUND_UP, RISCV_X0, RISCV_F1);
+ riscv_fcvt_lu_d (p, RISCV_ROUND_MM, RISCV_X0, RISCV_F1);
+ riscv_fmv_x_d (p, RISCV_X0, RISCV_F1);
+ riscv_fcvt_d_l (p, RISCV_ROUND_DY, RISCV_F0, RISCV_X1);
+ riscv_fcvt_d_lu (p, RISCV_ROUND_NE, RISCV_F0, RISCV_X1);
+ riscv_fmv_d_x (p, RISCV_F0, RISCV_X1);
+#endif
+
+ // R4
+
+ riscv_fmadd_s (p, RISCV_ROUND_NE, RISCV_F0, RISCV_F1, RISCV_F2, RISCV_F3);
+ riscv_fmsub_s (p, RISCV_ROUND_TZ, RISCV_F0, RISCV_F1, RISCV_F2, RISCV_F3);
+ riscv_fnmadd_s (p, RISCV_ROUND_DN, RISCV_F0, RISCV_F1, RISCV_F2, RISCV_F3);
+ riscv_fnmadd_s (p, RISCV_ROUND_UP, RISCV_F0, RISCV_F1, RISCV_F2, RISCV_F3);
+ riscv_fmadd_d (p, RISCV_ROUND_MM, RISCV_F0, RISCV_F1, RISCV_F2, RISCV_F3);
+ riscv_fmsub_d (p, RISCV_ROUND_DY, RISCV_F0, RISCV_F1, RISCV_F2, RISCV_F3);
+ riscv_fnmadd_d (p, RISCV_ROUND_NE, RISCV_F0, RISCV_F1, RISCV_F2, RISCV_F3);
+ riscv_fnmadd_d (p, RISCV_ROUND_TZ, RISCV_F0, RISCV_F1, RISCV_F2, RISCV_F3);
+
+ // I
+
+ riscv_jalr (p, RISCV_X0, RISCV_X1, 123);
+ riscv_jalr (p, RISCV_X0, RISCV_X1, -123);
+ riscv_lb (p, RISCV_X0, RISCV_X1, 321);
+ riscv_lh (p, RISCV_X0, RISCV_X1, -321);
+ riscv_lw (p, RISCV_X0, RISCV_X1, 123);
+ riscv_lbu (p, RISCV_X0, RISCV_X1, -123);
+ riscv_lhu (p, RISCV_X0, RISCV_X1, 321);
+ riscv_addi (p, RISCV_X0, RISCV_X1, -321);
+ riscv_slti (p, RISCV_X0, RISCV_X1, 123);
+ riscv_sltiu (p, RISCV_X0, RISCV_X1, -123);
+ riscv_xori (p, RISCV_X0, RISCV_X1, 321);
+ riscv_ori (p, RISCV_X0, RISCV_X1, -321);
+ riscv_andi (p, RISCV_X0, RISCV_X1, 123);
+ riscv_ecall (p);
+ riscv_ebreak (p);
+#ifdef TARGET_RISCV64
+ riscv_lwu (p, RISCV_X0, RISCV_X1, -123);
+ riscv_ld (p, RISCV_X0, RISCV_X1, 321);
+ riscv_addiw (p, RISCV_X0, RISCV_X1, -321);
+#endif
+ riscv_flw (p, RISCV_F0, RISCV_X1, 123);
+ riscv_fld (p, RISCV_F0, RISCV_X1, -123);
+
+ // IS/LS
+
+ riscv_slli (p, RISCV_X0, RISCV_X1, 1);
+ riscv_srli (p, RISCV_X0, RISCV_X1, 2);
+ riscv_srai (p, RISCV_X0, RISCV_X1, 3);
+#ifdef TARGET_RISCV64
+ riscv_slliw (p, RISCV_X0, RISCV_X1, 1);
+ riscv_srliw (p, RISCV_X0, RISCV_X1, 2);
+ riscv_sraiw (p, RISCV_X0, RISCV_X1, 3);
+#endif
+
+ // IC
+
+ riscv_csrrw (p, RISCV_X0, RISCV_CSR_FFLAGS, RISCV_X1);
+ riscv_csrrs (p, RISCV_X0, RISCV_CSR_FRM, RISCV_X1);
+ riscv_csrrc (p, RISCV_X0, RISCV_CSR_FCSR, RISCV_X1);
+ riscv_csrrwi (p, RISCV_X0, RISCV_CSR_CYCLE, 1);
+ riscv_csrrsi (p, RISCV_X0, RISCV_CSR_TIME, 2);
+ riscv_csrrci (p, RISCV_X0, RISCV_CSR_INSTRET, 3);
+
+ // S
+
+ riscv_sb (p, RISCV_X0, RISCV_X1, 123);
+ riscv_sh (p, RISCV_X0, RISCV_X1, -123);
+ riscv_sw (p, RISCV_X0, RISCV_X1, 321);
+#ifdef TARGET_RISCV64
+ riscv_sd (p, RISCV_X0, RISCV_X1, -321);
+#endif
+ riscv_fsw (p, RISCV_F0, RISCV_X1, 123);
+ riscv_fsd (p, RISCV_F0, RISCV_X1, -123);
+
+ // B
+
+ riscv_beq (p, RISCV_X0, RISCV_X1, 128);
+ riscv_bne (p, RISCV_X0, RISCV_X1, -128);
+ riscv_blt (p, RISCV_X0, RISCV_X1, 256);
+ riscv_bge (p, RISCV_X0, RISCV_X1, -256);
+ riscv_bltu (p, RISCV_X0, RISCV_X1, 512);
+ riscv_bgeu (p, RISCV_X0, RISCV_X1, -512);
+
+ // U
+
+ riscv_lui (p, RISCV_X0, 123);
+ riscv_auipc (p, RISCV_X0, 321);
+
+ // J
+
+ riscv_jal (p, RISCV_X0, 128);
+ riscv_jal (p, RISCV_X0, -128);
+
+ // F
+
+ riscv_fence (p, RISCV_FENCE_W, RISCV_FENCE_R);
+ riscv_fence (p, RISCV_FENCE_O, RISCV_FENCE_I);
+ riscv_fence (p, RISCV_FENCE_MEM, RISCV_FENCE_DEV);
+ riscv_fence (p, RISCV_FENCE_NONE, RISCV_FENCE_ALL);
+ riscv_fence_i (p);
+
+ // A
+
+ riscv_lr_w (p, RISCV_ORDER_RL, RISCV_X0, RISCV_X1);
+ riscv_sc_w (p, RISCV_ORDER_AQ, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amoswap_w (p, RISCV_ORDER_ALL, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amoadd_w (p, RISCV_ORDER_RL, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amoxor_w (p, RISCV_ORDER_AQ, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amoand_w (p, RISCV_ORDER_ALL, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amoor_w (p, RISCV_ORDER_RL, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amomin_w (p, RISCV_ORDER_AQ, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amomax_w (p, RISCV_ORDER_ALL, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amominu_w (p, RISCV_ORDER_RL, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amomaxu_w (p, RISCV_ORDER_AQ, RISCV_X0, RISCV_X1, RISCV_X2);
+#ifdef TARGET_RISCV64
+ riscv_lr_d (p, RISCV_ORDER_RL, RISCV_X0, RISCV_X1);
+ riscv_sc_d (p, RISCV_ORDER_AQ, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amoswap_d (p, RISCV_ORDER_ALL, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amoadd_d (p, RISCV_ORDER_RL, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amoxor_d (p, RISCV_ORDER_AQ, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amoand_d (p, RISCV_ORDER_ALL, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amoor_d (p, RISCV_ORDER_RL, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amomin_d (p, RISCV_ORDER_AQ, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amomax_d (p, RISCV_ORDER_ALL, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amominu_d (p, RISCV_ORDER_RL, RISCV_X0, RISCV_X1, RISCV_X2);
+ riscv_amomaxu_d (p, RISCV_ORDER_AQ, RISCV_X0, RISCV_X1, RISCV_X2);
+#endif
+ }
+
+ guint8 *p2 = code;
+
+ do {
+ printf (".byte %d\n", *p2);
+ } while (++p2 != p);
+
+ return 0;
+}
diff --git a/mono/arch/riscv/riscv-codegen-test.sh b/mono/arch/riscv/riscv-codegen-test.sh
new file mode 100755
index 00000000000..22c565b4da4
--- /dev/null
+++ b/mono/arch/riscv/riscv-codegen-test.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+# This can be used to test instruction encodings when cross-compiling.
+
+if grep "#define TARGET_RISCV64 1" ../../../config.h > /dev/null; then
+ bits=64
+else
+ bits=32
+fi
+
+gcc -I../../.. -I../../eglib riscv-codegen-test.c -o riscv-codegen-test $@ || exit 1
+./riscv-codegen-test > riscv-codegen.s || exit 1
+riscv64-unknown-linux-gnu-as riscv-codegen.s -o riscv-codegen.elf || exit 1
+riscv64-unknown-linux-gnu-objdump -D -M numeric,no-aliases riscv-codegen.elf > riscv-codegen.res || exit 1
+diff -u riscv-codegen.exp${bits} riscv-codegen.res || exit 1
diff --git a/mono/arch/riscv/riscv-codegen.exp32 b/mono/arch/riscv/riscv-codegen.exp32
new file mode 100644
index 00000000000..3966ea39d8f
--- /dev/null
+++ b/mono/arch/riscv/riscv-codegen.exp32
@@ -0,0 +1,130 @@
+
+riscv-codegen.elf: file format elf32-littleriscv
+
+
+Disassembly of section .text:
+
+00000000 <.text>:
+ 0: 00208033 add x0,x1,x2
+ 4: 40208033 sub x0,x1,x2
+ 8: 00209033 sll x0,x1,x2
+ c: 0020a033 slt x0,x1,x2
+ 10: 0020b033 sltu x0,x1,x2
+ 14: 0020c033 xor x0,x1,x2
+ 18: 0020d033 srl x0,x1,x2
+ 1c: 4020d033 sra x0,x1,x2
+ 20: 0020e033 or x0,x1,x2
+ 24: 0020f033 and x0,x1,x2
+ 28: 02208033 mul x0,x1,x2
+ 2c: 02209033 mulh x0,x1,x2
+ 30: 0220a033 mulhsu x0,x1,x2
+ 34: 0220b033 mulhu x0,x1,x2
+ 38: 0220c033 div x0,x1,x2
+ 3c: 0220d033 divu x0,x1,x2
+ 40: 0220e033 rem x0,x1,x2
+ 44: 0220f033 remu x0,x1,x2
+ 48: 00208053 fadd.s f0,f1,f2,rne
+ 4c: 08209053 fsub.s f0,f1,f2,rtz
+ 50: 1020a053 fmul.s f0,f1,f2,rdn
+ 54: 1820b053 fdiv.s f0,f1,f2,rup
+ 58: 5800c053 fsqrt.s f0,f1,rmm
+ 5c: 20208053 fsgnj.s f0,f1,f2
+ 60: 20209053 fsgnjn.s f0,f1,f2
+ 64: 2020a053 fsgnjx.s f0,f1,f2
+ 68: 28208053 fmin.s f0,f1,f2
+ 6c: 28209053 fmax.s f0,f1,f2
+ 70: c000f053 fcvt.w.s x0,f1
+ 74: c0108053 fcvt.wu.s x0,f1,rne
+ 78: e0008053 fmv.x.w x0,f1
+ 7c: a020a053 feq.s x0,f1,f2
+ 80: a0209053 flt.s x0,f1,f2
+ 84: a0208053 fle.s x0,f1,f2
+ 88: e0009053 fclass.s x0,f1
+ 8c: d0009053 fcvt.s.w f0,x1,rtz
+ 90: d010a053 fcvt.s.wu f0,x1,rdn
+ 94: f0008053 fmv.w.x f0,x1
+ 98: 02209053 fadd.d f0,f1,f2,rtz
+ 9c: 0a20a053 fsub.d f0,f1,f2,rdn
+ a0: 1220b053 fmul.d f0,f1,f2,rup
+ a4: 1a20c053 fdiv.d f0,f1,f2,rmm
+ a8: 5a00f053 fsqrt.d f0,f1
+ ac: 22208053 fsgnj.d f0,f1,f2
+ b0: 22209053 fsgnjn.d f0,f1,f2
+ b4: 2220a053 fsgnjx.d f0,f1,f2
+ b8: 2a208053 fmin.d f0,f1,f2
+ bc: 2a209053 fmax.d f0,f1,f2
+ c0: 40108053 fcvt.s.d f0,f1,rne
+ c4: 42008053 fcvt.d.s f0,f1
+ c8: a220a053 feq.d x0,f1,f2
+ cc: a2209053 flt.d x0,f1,f2
+ d0: a2208053 fle.d x0,f1,f2
+ d4: e2009053 fclass.d x0,f1
+ d8: c2009053 fcvt.w.d x0,f1,rtz
+ dc: c210a053 fcvt.wu.d x0,f1,rdn
+ e0: d2008053 fcvt.d.w f0,x1
+ e4: d2108053 fcvt.d.wu f0,x1
+ e8: 18208043 fmadd.s f0,f1,f2,f3,rne
+ ec: 18209047 fmsub.s f0,f1,f2,f3,rtz
+ f0: 1820a04b fnmsub.s f0,f1,f2,f3,rdn
+ f4: 1820b04b fnmsub.s f0,f1,f2,f3,rup
+ f8: 1a20c043 fmadd.d f0,f1,f2,f3,rmm
+ fc: 1a20f047 fmsub.d f0,f1,f2,f3
+ 100: 1a20804b fnmsub.d f0,f1,f2,f3,rne
+ 104: 1a20904b fnmsub.d f0,f1,f2,f3,rtz
+ 108: 07b08067 jalr x0,123(x1)
+ 10c: f8508067 jalr x0,-123(x1)
+ 110: 14108003 lb x0,321(x1)
+ 114: ebf09003 lh x0,-321(x1)
+ 118: 07b0a003 lw x0,123(x1)
+ 11c: f850c003 lbu x0,-123(x1)
+ 120: 1410d003 lhu x0,321(x1)
+ 124: ebf08013 addi x0,x1,-321
+ 128: 07b0a013 slti x0,x1,123
+ 12c: f850b013 sltiu x0,x1,-123
+ 130: 1410c013 xori x0,x1,321
+ 134: ebf0e013 ori x0,x1,-321
+ 138: 07b0f013 andi x0,x1,123
+ 13c: 00000073 ecall
+ 140: 00100073 ebreak
+ 144: 07b0a007 flw f0,123(x1)
+ 148: f850b007 fld f0,-123(x1)
+ 14c: 00109013 slli x0,x1,0x1
+ 150: 0020d013 srli x0,x1,0x2
+ 154: 4030d013 srai x0,x1,0x3
+ 158: 00109073 fsflags x1
+ 15c: 0020a073 csrrs x0,frm,x1
+ 160: 0030b073 csrrc x0,fcsr,x1
+ 164: c000d073 csrrwi x0,cycle,1
+ 168: c0116073 csrrsi x0,time,2
+ 16c: c021f073 csrrci x0,instret,3
+ 170: 06008da3 sb x0,123(x1)
+ 174: f80092a3 sh x0,-123(x1)
+ 178: 1400a0a3 sw x0,321(x1)
+ 17c: 0600ada7 fsw f0,123(x1)
+ 180: f800b2a7 fsd f0,-123(x1)
+ 184: 08100063 beq x0,x1,0x204
+ 188: f81010e3 bne x0,x1,0x108
+ 18c: 10104063 blt x0,x1,0x28c
+ 190: f01050e3 bge x0,x1,0x90
+ 194: 20106063 bltu x0,x1,0x394
+ 198: e01070e3 bgeu x0,x1,0xffffff98
+ 19c: 0007b037 lui x0,0x7b
+ 1a0: 00141017 auipc x0,0x141
+ 1a4: 0800006f jal x0,0x224
+ 1a8: f81ff06f jal x0,0x128
+ 1ac: 0120000f fence w,r
+ 1b0: 0480000f fence o,i
+ 1b4: 03c0000f fence rw,io
+ 1b8: 00f0000f fence unknown,iorw
+ 1bc: 0000100f fence.i
+ 1c0: 1200a02f lr.w.rl x0,(x1)
+ 1c4: 1c11202f sc.w.aq x0,x1,(x2)
+ 1c8: 0e11202f amoswap.w.aqrl x0,x1,(x2)
+ 1cc: 0211202f amoadd.w.rl x0,x1,(x2)
+ 1d0: 2411202f amoxor.w.aq x0,x1,(x2)
+ 1d4: 6611202f amoand.w.aqrl x0,x1,(x2)
+ 1d8: 4211202f amoor.w.rl x0,x1,(x2)
+ 1dc: 8411202f amomin.w.aq x0,x1,(x2)
+ 1e0: a611202f amomax.w.aqrl x0,x1,(x2)
+ 1e4: c211202f amominu.w.rl x0,x1,(x2)
+ 1e8: e411202f amomaxu.w.aq x0,x1,(x2)
diff --git a/mono/arch/riscv/riscv-codegen.exp64 b/mono/arch/riscv/riscv-codegen.exp64
new file mode 100644
index 00000000000..68e9b78c077
--- /dev/null
+++ b/mono/arch/riscv/riscv-codegen.exp64
@@ -0,0 +1,168 @@
+
+riscv-codegen.elf: file format elf64-littleriscv
+
+
+Disassembly of section .text:
+
+0000000000000000 <.text>:
+ 0: 00208033 add x0,x1,x2
+ 4: 40208033 sub x0,x1,x2
+ 8: 00209033 sll x0,x1,x2
+ c: 0020a033 slt x0,x1,x2
+ 10: 0020b033 sltu x0,x1,x2
+ 14: 0020c033 xor x0,x1,x2
+ 18: 0020d033 srl x0,x1,x2
+ 1c: 4020d033 sra x0,x1,x2
+ 20: 0020e033 or x0,x1,x2
+ 24: 0020f033 and x0,x1,x2
+ 28: 0020803b addw x0,x1,x2
+ 2c: 4020803b subw x0,x1,x2
+ 30: 0020903b sllw x0,x1,x2
+ 34: 0020d03b srlw x0,x1,x2
+ 38: 4020d03b sraw x0,x1,x2
+ 3c: 02208033 mul x0,x1,x2
+ 40: 02209033 mulh x0,x1,x2
+ 44: 0220a033 mulhsu x0,x1,x2
+ 48: 0220b033 mulhu x0,x1,x2
+ 4c: 0220c033 div x0,x1,x2
+ 50: 0220d033 divu x0,x1,x2
+ 54: 0220e033 rem x0,x1,x2
+ 58: 0220f033 remu x0,x1,x2
+ 5c: 0220803b mulw x0,x1,x2
+ 60: 0220c03b divw x0,x1,x2
+ 64: 0220d03b divuw x0,x1,x2
+ 68: 0220e03b remw x0,x1,x2
+ 6c: 0220f03b remuw x0,x1,x2
+ 70: 00208053 fadd.s f0,f1,f2,rne
+ 74: 08209053 fsub.s f0,f1,f2,rtz
+ 78: 1020a053 fmul.s f0,f1,f2,rdn
+ 7c: 1820b053 fdiv.s f0,f1,f2,rup
+ 80: 5800c053 fsqrt.s f0,f1,rmm
+ 84: 20208053 fsgnj.s f0,f1,f2
+ 88: 20209053 fsgnjn.s f0,f1,f2
+ 8c: 2020a053 fsgnjx.s f0,f1,f2
+ 90: 28208053 fmin.s f0,f1,f2
+ 94: 28209053 fmax.s f0,f1,f2
+ 98: c000f053 fcvt.w.s x0,f1
+ 9c: c0108053 fcvt.wu.s x0,f1,rne
+ a0: e0008053 fmv.x.w x0,f1
+ a4: a020a053 feq.s x0,f1,f2
+ a8: a0209053 flt.s x0,f1,f2
+ ac: a0208053 fle.s x0,f1,f2
+ b0: e0009053 fclass.s x0,f1
+ b4: d0009053 fcvt.s.w f0,x1,rtz
+ b8: d010a053 fcvt.s.wu f0,x1,rdn
+ bc: f0008053 fmv.w.x f0,x1
+ c0: c020b053 fcvt.l.s x0,f1,rup
+ c4: c030c053 fcvt.lu.s x0,f1,rmm
+ c8: d020f053 fcvt.s.l f0,x1
+ cc: d0308053 fcvt.s.lu f0,x1,rne
+ d0: 02209053 fadd.d f0,f1,f2,rtz
+ d4: 0a20a053 fsub.d f0,f1,f2,rdn
+ d8: 1220b053 fmul.d f0,f1,f2,rup
+ dc: 1a20c053 fdiv.d f0,f1,f2,rmm
+ e0: 5a00f053 fsqrt.d f0,f1
+ e4: 22208053 fsgnj.d f0,f1,f2
+ e8: 22209053 fsgnjn.d f0,f1,f2
+ ec: 2220a053 fsgnjx.d f0,f1,f2
+ f0: 2a208053 fmin.d f0,f1,f2
+ f4: 2a209053 fmax.d f0,f1,f2
+ f8: 40108053 fcvt.s.d f0,f1,rne
+ fc: 42008053 fcvt.d.s f0,f1
+ 100: a220a053 feq.d x0,f1,f2
+ 104: a2209053 flt.d x0,f1,f2
+ 108: a2208053 fle.d x0,f1,f2
+ 10c: e2009053 fclass.d x0,f1
+ 110: c2009053 fcvt.w.d x0,f1,rtz
+ 114: c210a053 fcvt.wu.d x0,f1,rdn
+ 118: d2008053 fcvt.d.w f0,x1
+ 11c: d2108053 fcvt.d.wu f0,x1
+ 120: c220b053 fcvt.l.d x0,f1,rup
+ 124: c230c053 fcvt.lu.d x0,f1,rmm
+ 128: e2008053 fmv.x.d x0,f1
+ 12c: d220f053 fcvt.d.l f0,x1
+ 130: d2308053 fcvt.d.lu f0,x1,rne
+ 134: f2008053 fmv.d.x f0,x1
+ 138: 18208043 fmadd.s f0,f1,f2,f3,rne
+ 13c: 18209047 fmsub.s f0,f1,f2,f3,rtz
+ 140: 1820a04b fnmsub.s f0,f1,f2,f3,rdn
+ 144: 1820b04b fnmsub.s f0,f1,f2,f3,rup
+ 148: 1a20c043 fmadd.d f0,f1,f2,f3,rmm
+ 14c: 1a20f047 fmsub.d f0,f1,f2,f3
+ 150: 1a20804b fnmsub.d f0,f1,f2,f3,rne
+ 154: 1a20904b fnmsub.d f0,f1,f2,f3,rtz
+ 158: 07b08067 jalr x0,123(x1)
+ 15c: f8508067 jalr x0,-123(x1)
+ 160: 14108003 lb x0,321(x1)
+ 164: ebf09003 lh x0,-321(x1)
+ 168: 07b0a003 lw x0,123(x1)
+ 16c: f850c003 lbu x0,-123(x1)
+ 170: 1410d003 lhu x0,321(x1)
+ 174: ebf08013 addi x0,x1,-321
+ 178: 07b0a013 slti x0,x1,123
+ 17c: f850b013 sltiu x0,x1,-123
+ 180: 1410c013 xori x0,x1,321
+ 184: ebf0e013 ori x0,x1,-321
+ 188: 07b0f013 andi x0,x1,123
+ 18c: 00000073 ecall
+ 190: 00100073 ebreak
+ 194: f850e003 lwu x0,-123(x1)
+ 198: 1410b003 ld x0,321(x1)
+ 19c: ebf0801b addiw x0,x1,-321
+ 1a0: 07b0a007 flw f0,123(x1)
+ 1a4: f850b007 fld f0,-123(x1)
+ 1a8: 00109013 slli x0,x1,0x1
+ 1ac: 0020d013 srli x0,x1,0x2
+ 1b0: 4030d013 srai x0,x1,0x3
+ 1b4: 0010901b slliw x0,x1,0x1
+ 1b8: 0020d01b srliw x0,x1,0x2
+ 1bc: 4030d01b sraiw x0,x1,0x3
+ 1c0: 00109073 fsflags x1
+ 1c4: 0020a073 csrrs x0,frm,x1
+ 1c8: 0030b073 csrrc x0,fcsr,x1
+ 1cc: c000d073 csrrwi x0,cycle,1
+ 1d0: c0116073 csrrsi x0,time,2
+ 1d4: c021f073 csrrci x0,instret,3
+ 1d8: 06008da3 sb x0,123(x1)
+ 1dc: f80092a3 sh x0,-123(x1)
+ 1e0: 1400a0a3 sw x0,321(x1)
+ 1e4: ea00bfa3 sd x0,-321(x1)
+ 1e8: 0600ada7 fsw f0,123(x1)
+ 1ec: f800b2a7 fsd f0,-123(x1)
+ 1f0: 08100063 beq x0,x1,0x270
+ 1f4: f81010e3 bne x0,x1,0x174
+ 1f8: 10104063 blt x0,x1,0x2f8
+ 1fc: f01050e3 bge x0,x1,0xfc
+ 200: 20106063 bltu x0,x1,0x400
+ 204: e01070e3 bgeu x0,x1,0x4
+ 208: 0007b037 lui x0,0x7b
+ 20c: 00141017 auipc x0,0x141
+ 210: 0800006f jal x0,0x290
+ 214: f81ff06f jal x0,0x194
+ 218: 0120000f fence w,r
+ 21c: 0480000f fence o,i
+ 220: 03c0000f fence rw,io
+ 224: 00f0000f fence unknown,iorw
+ 228: 0000100f fence.i
+ 22c: 1200a02f lr.w.rl x0,(x1)
+ 230: 1c11202f sc.w.aq x0,x1,(x2)
+ 234: 0e11202f amoswap.w.aqrl x0,x1,(x2)
+ 238: 0211202f amoadd.w.rl x0,x1,(x2)
+ 23c: 2411202f amoxor.w.aq x0,x1,(x2)
+ 240: 6611202f amoand.w.aqrl x0,x1,(x2)
+ 244: 4211202f amoor.w.rl x0,x1,(x2)
+ 248: 8411202f amomin.w.aq x0,x1,(x2)
+ 24c: a611202f amomax.w.aqrl x0,x1,(x2)
+ 250: c211202f amominu.w.rl x0,x1,(x2)
+ 254: e411202f amomaxu.w.aq x0,x1,(x2)
+ 258: 1200b02f lr.d.rl x0,(x1)
+ 25c: 1c11302f sc.d.aq x0,x1,(x2)
+ 260: 0e11302f amoswap.d.aqrl x0,x1,(x2)
+ 264: 0211302f amoadd.d.rl x0,x1,(x2)
+ 268: 2411302f amoxor.d.aq x0,x1,(x2)
+ 26c: 6611302f amoand.d.aqrl x0,x1,(x2)
+ 270: 4211302f amoor.d.rl x0,x1,(x2)
+ 274: 8411302f amomin.d.aq x0,x1,(x2)
+ 278: a611302f amomax.d.aqrl x0,x1,(x2)
+ 27c: c211302f amominu.d.rl x0,x1,(x2)
+ 280: e411302f amomaxu.d.aq x0,x1,(x2)
diff --git a/mono/arch/riscv/riscv-codegen.h b/mono/arch/riscv/riscv-codegen.h
new file mode 100644
index 00000000000..9bd4b4a5822
--- /dev/null
+++ b/mono/arch/riscv/riscv-codegen.h
@@ -0,0 +1,824 @@
+/*
+ * Licensed to the .NET Foundation under one or more agreements.
+ * The .NET Foundation licenses this file to you under the MIT license.
+ * See the LICENSE file in the project root for more information.
+ */
+
+#ifndef __MONO_RISCV_CODEGEN_H__
+#define __MONO_RISCV_CODEGEN_H__
+
+#include "config.h"
+#include <glib.h>
+
+#ifdef MONO_RISCV_CODEGEN_TEST
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+ * Avoid having to link with eglib in the codegen test program so that it can
+ * be built with a native toolchain even if we're building Mono with a cross
+ * toolchain.
+ */
+
+#undef g_assert
+#define g_assert(expr) \
+ do { \
+ if (G_UNLIKELY (!(expr))) { \
+ fprintf (stderr, "* Assertion at %s:%d, condition `%s' not met\n", __FILE__, __LINE__, #expr); \
+ abort (); \
+ } \
+ } while (0)
+
+#endif
+
+enum {
+ RISCV_X0 = 0,
+ RISCV_X1 = 1,
+ RISCV_X2 = 2,
+ RISCV_X3 = 3,
+ RISCV_X4 = 4,
+ RISCV_X5 = 5,
+ RISCV_X6 = 6,
+ RISCV_X7 = 7,
+ RISCV_X8 = 8,
+ RISCV_X9 = 9,
+ RISCV_X10 = 10,
+ RISCV_X11 = 11,
+ RISCV_X12 = 12,
+ RISCV_X13 = 13,
+ RISCV_X14 = 14,
+ RISCV_X15 = 15,
+ RISCV_X16 = 16,
+ RISCV_X17 = 17,
+ RISCV_X18 = 18,
+ RISCV_X19 = 19,
+ RISCV_X20 = 20,
+ RISCV_X21 = 21,
+ RISCV_X22 = 22,
+ RISCV_X23 = 23,
+ RISCV_X24 = 24,
+ RISCV_X25 = 25,
+ RISCV_X26 = 26,
+ RISCV_X27 = 27,
+ RISCV_X28 = 28,
+ RISCV_X29 = 29,
+ RISCV_X30 = 30,
+ RISCV_X31 = 31,
+
+ RISCV_ZERO = RISCV_X0,
+
+ // Argument and return registers.
+
+ RISCV_A0 = RISCV_X10,
+ RISCV_A1 = RISCV_X11,
+ RISCV_A2 = RISCV_X12,
+ RISCV_A3 = RISCV_X13,
+ RISCV_A4 = RISCV_X14,
+ RISCV_A5 = RISCV_X15,
+ RISCV_A6 = RISCV_X16,
+ RISCV_A7 = RISCV_X17,
+
+ // Callee-saved registers.
+
+ RISCV_S0 = RISCV_X8,
+ RISCV_S1 = RISCV_X9,
+ RISCV_S2 = RISCV_X18,
+ RISCV_S3 = RISCV_X19,
+ RISCV_S4 = RISCV_X20,
+ RISCV_S5 = RISCV_X21,
+ RISCV_S6 = RISCV_X22,
+ RISCV_S7 = RISCV_X23,
+ RISCV_S8 = RISCV_X24,
+ RISCV_S9 = RISCV_X25,
+ RISCV_S10 = RISCV_X26,
+ RISCV_S11 = RISCV_X27,
+
+ // Temporary registers.
+
+ RISCV_T0 = RISCV_X5,
+ RISCV_T1 = RISCV_X6,
+ RISCV_T2 = RISCV_X7,
+ RISCV_T3 = RISCV_X28,
+ RISCV_T4 = RISCV_X29,
+ RISCV_T5 = RISCV_X30,
+ RISCV_T6 = RISCV_X31,
+
+ // Call stack registers.
+
+ RISCV_SP = RISCV_X2, // Stack pointer.
+ RISCV_RA = RISCV_X1, // Return address (AKA link register).
+ RISCV_FP = RISCV_S0, // Frame pointer (AKA base pointer).
+
+ // ABI implementation registers.
+
+ RISCV_GP = RISCV_X3,
+ RISCV_TP = RISCV_X4,
+};
+
+#define RISCV_N_GREGS (32)
+#define RISCV_N_GAREGS (8)
+#define RISCV_N_GSREGS (12)
+#define RISCV_N_GTREGS (7)
+
+enum {
+ RISCV_F0 = 0,
+ RISCV_F1 = 1,
+ RISCV_F2 = 2,
+ RISCV_F3 = 3,
+ RISCV_F4 = 4,
+ RISCV_F5 = 5,
+ RISCV_F6 = 6,
+ RISCV_F7 = 7,
+ RISCV_F8 = 8,
+ RISCV_F9 = 9,
+ RISCV_F10 = 10,
+ RISCV_F11 = 11,
+ RISCV_F12 = 12,
+ RISCV_F13 = 13,
+ RISCV_F14 = 14,
+ RISCV_F15 = 15,
+ RISCV_F16 = 16,
+ RISCV_F17 = 17,
+ RISCV_F18 = 18,
+ RISCV_F19 = 19,
+ RISCV_F20 = 20,
+ RISCV_F21 = 21,
+ RISCV_F22 = 22,
+ RISCV_F23 = 23,
+ RISCV_F24 = 24,
+ RISCV_F25 = 25,
+ RISCV_F26 = 26,
+ RISCV_F27 = 27,
+ RISCV_F28 = 28,
+ RISCV_F29 = 29,
+ RISCV_F30 = 30,
+ RISCV_F31 = 31,
+
+ // Argument and return registers.
+
+ RISCV_FA0 = RISCV_F10,
+ RISCV_FA1 = RISCV_F11,
+ RISCV_FA2 = RISCV_F12,
+ RISCV_FA3 = RISCV_F13,
+ RISCV_FA4 = RISCV_F14,
+ RISCV_FA5 = RISCV_F15,
+ RISCV_FA6 = RISCV_F16,
+ RISCV_FA7 = RISCV_F17,
+
+ // Callee-saved registers.
+
+ RISCV_FS0 = RISCV_F8,
+ RISCV_FS1 = RISCV_F9,
+ RISCV_FS2 = RISCV_F18,
+ RISCV_FS3 = RISCV_F19,
+ RISCV_FS4 = RISCV_F20,
+ RISCV_FS5 = RISCV_F21,
+ RISCV_FS6 = RISCV_F22,
+ RISCV_FS7 = RISCV_F23,
+ RISCV_FS8 = RISCV_F24,
+ RISCV_FS9 = RISCV_F25,
+ RISCV_FS10 = RISCV_F26,
+ RISCV_FS11 = RISCV_F27,
+
+ // Temporary registers.
+
+ RISCV_FT0 = RISCV_F0,
+ RISCV_FT1 = RISCV_F1,
+ RISCV_FT2 = RISCV_F2,
+ RISCV_FT3 = RISCV_F3,
+ RISCV_FT4 = RISCV_F4,
+ RISCV_FT5 = RISCV_F5,
+ RISCV_FT6 = RISCV_F6,
+ RISCV_FT7 = RISCV_F7,
+ RISCV_FT8 = RISCV_F28,
+ RISCV_FT9 = RISCV_F29,
+ RISCV_FT10 = RISCV_F30,
+ RISCV_FT11 = RISCV_F31,
+};
+
+#define RISCV_N_FREGS (32)
+#define RISCV_N_FAREGS (8)
+#define RISCV_N_FSREGS (12)
+#define RISCV_N_FTREGS (12)
+
+enum {
+ // Floating point.
+
+ RISCV_CSR_FFLAGS = 0x001, // Accrued exceptions.
+ RISCV_CSR_FRM = 0x002, // Rounding mode.
+ RISCV_CSR_FCSR = 0x003, // Combination of FFLAGS and FRM.
+
+ // Counters and timers.
+
+ RISCV_CSR_CYCLE = 0xc00, // Cycle counter.
+ RISCV_CSR_TIME = 0xc01, // Wall clock time.
+ RISCV_CSR_INSTRET = 0xc02, // Instruction counter.
+
+#ifdef TARGET_RISCV32
+ RISCV_CSR_CYCLEH = 0xc80, // Upper 32 bits of CYCLE.
+ RISCV_CSR_TIMEH = 0xc81, // Upper 32 bits of TIME.
+ RISCV_CSR_INSTRETH = 0xc82, // Upper 32 bits of INSTRET.
+#endif
+};
+
+enum {
+ RISCV_FENCE_NONE = 0b0000,
+
+ RISCV_FENCE_W = 0b0001, // Memory writes.
+ RISCV_FENCE_R = 0b0010, // Memory reads.
+ RISCV_FENCE_O = 0b0100, // Device outputs.
+ RISCV_FENCE_I = 0b1000, // Device inputs.
+
+ RISCV_FENCE_MEM = RISCV_FENCE_W | RISCV_FENCE_R,
+ RISCV_FENCE_DEV = RISCV_FENCE_O | RISCV_FENCE_I,
+ RISCV_FENCE_ALL = RISCV_FENCE_DEV | RISCV_FENCE_MEM,
+};
+
+enum {
+ RISCV_ORDER_NONE = 0b00,
+
+ RISCV_ORDER_RL = 0b01, // Release semantics.
+ RISCV_ORDER_AQ = 0b10, // Acquire semantics.
+
+ RISCV_ORDER_ALL = RISCV_ORDER_RL | RISCV_ORDER_AQ,
+};
+
+enum {
+ RISCV_ROUND_NE = 0b000, // Round to nearest (ties to even).
+ RISCV_ROUND_TZ = 0b001, // Round towards zero.
+ RISCV_ROUND_DN = 0b010, // Round down (towards negative infinity).
+ RISCV_ROUND_UP = 0b011, // Round up (towards positive infinity).
+ RISCV_ROUND_MM = 0b100, // Round to nearest (ties to max magnitude).
+ RISCV_ROUND_DY = 0b111, // Use current rounding mode in the FRM CSR.
+};
+
+#define _riscv_emit(p, insn) \
+ do { \
+ *(guint32 *) (p) = (insn); \
+ (p) += sizeof (guint32); \
+ } while (0)
+
+#define RISCV_BITS(value, start, count) (((value) >> (start)) & ((1 << (count)) - 1))
+#define RISCV_SIGN(value) (-(((value) >> (sizeof (guint32) * 8 - 1)) & 1))
+
+// Encode an imemdiate for use in an instruction.
+
+#define RISCV_ENCODE_I_IMM(imm) \
+ (RISCV_BITS ((imm), 0, 12) << 20)
+#define RISCV_ENCODE_S_IMM(imm) \
+ ((RISCV_BITS ((imm), 0, 5) << 7) | (RISCV_BITS ((imm), 5, 7) << 25))
+#define RISCV_ENCODE_B_IMM(imm) \
+ ((RISCV_BITS ((imm), 11, 1) << 7) | (RISCV_BITS ((imm), 1, 4) << 8) | \
+ (RISCV_BITS ((imm), 5, 6) << 25) | (RISCV_BITS ((imm), 12, 1) << 31))
+#define RISCV_ENCODE_U_IMM(imm) \
+ (RISCV_BITS ((imm), 0, 20) << 12)
+#define RISCV_ENCODE_J_IMM(imm) \
+ ((RISCV_BITS ((imm), 1, 10) << 21) | (RISCV_BITS ((imm), 11, 1) << 20) | \
+ (RISCV_BITS ((imm), 12, 8) << 12) | (RISCV_BITS ((imm), 20, 1) << 31))
+
+// Decode an immediate from an instruction.
+
+#define RISCV_DECODE_I_IMM(ins) \
+ ((RISCV_BITS ((ins), 20, 12) << 0) | (RISCV_SIGN ((ins)) << 12))
+#define RISCV_DECODE_S_IMM(ins) \
+ ((RISCV_BITS ((ins), 7, 5) << 0) | (RISCV_BITS ((ins), 25, 7) << 5) | \
+ (RISCV_SIGN ((ins)) << 12))
+#define RISCV_DECODE_B_IMM(ins) \
+ ((RISCV_BITS ((ins), 8, 4) << 1) | (RISCV_BITS ((ins), 25, 6) << 5) | \
+ (RISCV_BITS ((ins), 7, 1) << 11) | (RISCV_SIGN((ins)) << 12))
+#define RISCV_DECODE_U_IMM(ins) \
+ (RISCV_BITS ((ins), 12, 20) << 0)
+#define RISCV_DECODE_J_IMM(ins) \
+ ((RISCV_BITS ((ins), 21, 10) << 1) | (RISCV_BITS ((ins), 20, 1) << 11) | \
+ (RISCV_BITS ((ins), 12, 8) << 12) | (RISCV_SIGN ((ins)) << 20))
+
+// Check a value for validity as an immediate.
+
+#define RISCV_VALID_I_IMM(value) \
+ (RISCV_DECODE_I_IMM (RISCV_ENCODE_I_IMM ((value))) == (value))
+#define RISCV_VALID_S_IMM(value) \
+ (RISCV_DECODE_S_IMM (RISCV_ENCODE_S_IMM ((value))) == (value))
+#define RISCV_VALID_B_IMM(value) \
+ (RISCV_DECODE_B_IMM (RISCV_ENCODE_B_IMM ((value))) == (value))
+#define RISCV_VALID_U_IMM(value) \
+ (RISCV_DECODE_U_IMM (RISCV_ENCODE_U_IMM ((value))) == (value))
+#define RISCV_VALID_J_IMM(value) \
+ (RISCV_DECODE_J_IMM (RISCV_ENCODE_J_IMM ((value))) == (value))
+
+// Check various values for validity in an instruction.
+
+#define RISCV_VALID_REG(value) \
+ (RISCV_BITS ((value), 0, 5) == (value))
+#define RISCV_VALID_CSR(value) \
+ (RISCV_BITS ((value), 0, 12) == (value))
+#define RISCV_VALID_IS_AMOUNT(value) \
+ (RISCV_BITS ((value), 0, 5) == (value))
+#define RISCV_VALID_LS_AMOUNT(value) \
+ (RISCV_BITS ((value), 0, 6) == (value))
+#define RISCV_VALID_FENCE(value) \
+ (RISCV_BITS ((value), 0, 4) == (value))
+#define RISCV_VALID_ORDERING(value) \
+ (RISCV_BITS ((value), 0, 2) == (value))
+
+/*
+ * The R-type encoding is used for a variety of instructions that operate on
+ * registers only, such as most integer instructions, atomic instructions, and
+ * some floating point instructions.
+ *
+ * [0....6] opcode
+ * [7...11] rd
+ * [12..14] funct3
+ * [15..19] rs1
+ * [20..24] rs2
+ * [25..31] funct7
+ */
+
+#define _riscv_r_op(p, opcode, funct3, funct7, rd, rs1, rs2) \
+ do { \
+ g_assert (RISCV_VALID_REG ((rd))); \
+ g_assert (RISCV_VALID_REG ((rs1))); \
+ g_assert (RISCV_VALID_REG ((rs2))); \
+ _riscv_emit ((p), ((opcode) << 0) | \
+ ((rd) << 7) | \
+ ((funct3) << 12) | \
+ ((rs1) << 15) | \
+ ((rs2) << 20) | \
+ ((funct7) << 25)); \
+ } while (0)
+
+/*
+ * The R4-type encoding is used for floating point fused multiply-add
+ * instructions.
+ *
+ * [0....6] opcode
+ * [7...11] rd
+ * [12..14] funct3
+ * [15..19] rs1
+ * [20..24] rs2
+ * [25..26] funct2
+ * [27..31] rs3
+ */
+
+#define _riscv_r4_op(p, opcode, funct3, funct2, rd, rs1, rs2, rs3) \
+ do { \
+ g_assert (RISCV_VALID_REG ((rd))); \
+ g_assert (RISCV_VALID_REG ((rs1))); \
+ g_assert (RISCV_VALID_REG ((rs2))); \
+ g_assert (RISCV_VALID_REG ((rs3))); \
+ _riscv_emit ((p), ((opcode) << 0) | \
+ ((rd) << 7) | \
+ ((funct3) << 12) | \
+ ((rs1) << 15) | \
+ ((rs2) << 20) | \
+ ((funct2) << 25) | \
+ ((rs3) << 27)); \
+ } while (0)
+
+/*
+ * The I-type encoding is used for a variety of instructions, such as JALR,
+ * loads, and most integer instructions that operate on immediates.
+ *
+ * [0....6] opcode
+ * [7...11] rd
+ * [12..14] funct3
+ * [15..19] rs1
+ * [20..31] imm[0..11]
+ */
+
+#define _riscv_i_op(p, opcode, funct3, rd, rs1, imm) \
+ do { \
+ g_assert (RISCV_VALID_REG ((rd))); \
+ g_assert (RISCV_VALID_REG ((rs1))); \
+ g_assert (RISCV_VALID_I_IMM ((gint32) (gssize) (imm))); \
+ _riscv_emit ((p), ((opcode) << 0) | \
+ ((rd) << 7) | \
+ ((funct3) << 12) | \
+ ((rs1) << 15) | \
+ (RISCV_ENCODE_I_IMM ((gint32) (gssize) (imm)))); \
+ } while (0)
+
+/*
+ * This is a specialization of the I-type encoding used for shifts by immediate
+ * values. The shift amount and right shift type are encoded into separate
+ * parts of the imm field.
+ *
+ * [0....6] opcode
+ * [7...11] rd
+ * [12..14] funct3
+ * [15..19] rs1
+ * [20..24] shamt
+ * [25..31] rstype
+ */
+
+#define _riscv_is_op(p, opcode, funct3, rstype, rd, rs1, shamt) \
+ do { \
+ g_assert (RISCV_VALID_REG ((rd))); \
+ g_assert (RISCV_VALID_REG ((rs1))); \
+ g_assert (RISCV_VALID_IS_AMOUNT ((shamt))); \
+ _riscv_emit ((p), ((opcode) << 0) | \
+ ((rd) << 7) | \
+ ((funct3) << 12) | \
+ ((rs1) << 15) | \
+ (RISCV_BITS ((shamt), 0, 5) << 20) | \
+ ((rstype) << 25)); \
+ } while (0)
+
+/*
+ * A further specialization of the I-type encoding used for shifts by immediate
+ * values in RV64I. The shift amount field is larger.
+ *
+ * [0....6] opcode
+ * [7...11] rd
+ * [12..14] funct3
+ * [15..19] rs1
+ * [20..25] shamt
+ * [26..31] rstype
+ */
+
+#define _riscv_ls_op(p, opcode, funct3, rstype, rd, rs1, shamt) \
+ do { \
+ g_assert (RISCV_VALID_REG ((rd))); \
+ g_assert (RISCV_VALID_REG ((rs1))); \
+ g_assert (RISCV_VALID_LS_AMOUNT ((shamt))); \
+ _riscv_emit ((p), ((opcode) << 0) | \
+ ((rd) << 7) | \
+ ((funct3) << 12) | \
+ ((rs1) << 15) | \
+ (RISCV_BITS ((shamt), 0, 6) << 20) | \
+ ((rstype) << 26)); \
+ } while (0)
+
+/*
+ * This is a specialization of the I-type encoding used for accessing control
+ * and status registers.
+ *
+ * [0....6] opcode
+ * [7...11] rd
+ * [12..14] funct3
+ * [15..19] rs1/zimm
+ * [20..31] csr
+ */
+
+#define _riscv_ic_op(p, opcode, funct3, rd, csr, rs1) \
+ do { \
+ g_assert (RISCV_VALID_REG ((rd))); \
+ g_assert (RISCV_VALID_REG ((rs1))); \
+ g_assert (RISCV_VALID_CSR ((csr))); \
+ _riscv_emit ((p), ((opcode) << 0) | \
+ ((rd) << 7) | \
+ ((funct3) << 12) | \
+ ((rs1) << 15) | \
+ (RISCV_BITS ((csr), 0, 12) << 20)); \
+ } while (0)
+
+/*
+ * The S-type encoding is used for stores with signed offsets.
+ *
+ * [0....6] opcode
+ * [7...11] imm[0..4]
+ * [12..14] funct3
+ * [15..19] rs1
+ * [20..24] rs2
+ * [25..31] imm[5..11]
+ */
+
+#define _riscv_s_op(p, opcode, funct3, rs2, rs1, imm) \
+ do { \
+ g_assert (RISCV_VALID_REG ((rs1))); \
+ g_assert (RISCV_VALID_REG ((rs2))); \
+ g_assert (RISCV_VALID_S_IMM ((gint32) (gssize) (imm))); \
+ _riscv_emit ((p), ((opcode) << 0) | \
+ ((funct3) << 12) | \
+ ((rs1) << 15) | \
+ ((rs2) << 20) | \
+ (RISCV_ENCODE_S_IMM ((gint32) (gssize) (imm)))); \
+ } while (0)
+
+/*
+ * The B-type encoding is used for conditional branches with signed offsets.
+ *
+ * [0....6] opcode
+ * [7....7] imm[11]
+ * [8...11] imm[1..4]
+ * [12..14] funct3
+ * [15..19] rs1
+ * [20..24] rs2
+ * [25..30] imm[5..10]
+ * [31..31] imm[12]
+ */
+
+#define _riscv_b_op(p, opcode, funct3, rs1, rs2, imm) \
+ do { \
+ g_assert (RISCV_VALID_REG ((rs1))); \
+ g_assert (RISCV_VALID_REG ((rs2))); \
+ g_assert (RISCV_VALID_B_IMM ((gint32) (gssize) (imm))); \
+ _riscv_emit ((p), ((opcode) << 0) | \
+ ((funct3) << 12) | \
+ ((rs1) << 15) | \
+ ((rs2) << 20) | \
+ (RISCV_ENCODE_B_IMM ((gint32) (gssize) (imm)))); \
+ } while (0)
+
+/*
+ * The U-type encoding is used for LUI and AUIPC only, i.e. for instructions
+ * that create 32-bit values from 20-bit immediates.
+ *
+ * [0....6] opcode
+ * [7...11] rd
+ * [12..31] imm[12..31]
+ */
+
+#define _riscv_u_op(p, opcode, rd, imm) \
+ do { \
+ g_assert (RISCV_VALID_REG ((rd))); \
+ g_assert (RISCV_VALID_U_IMM ((guint32) (gsize) (imm))); \
+ _riscv_emit ((p), ((opcode) << 0) | \
+ ((rd) << 7) | \
+ (RISCV_ENCODE_U_IMM ((guint32) (gsize) (imm)))); \
+ } while (0)
+
+/*
+ * The J-type encoding is used exclusively for JAL.
+ *
+ * [0....6] opcode
+ * [7...11] rd
+ * [12..19] imm[12..19]
+ * [20..20] imm[11]
+ * [21..30] imm[1..10]
+ * [31..31] imm[20]
+ */
+
+#define _riscv_j_op(p, opcode, rd, imm) \
+ do { \
+ g_assert (RISCV_VALID_REG ((rd))); \
+ g_assert (RISCV_VALID_J_IMM ((gint32) (gssize) (imm))); \
+ _riscv_emit ((p), ((opcode) << 0) | \
+ ((rd) << 7) | \
+ (RISCV_ENCODE_J_IMM ((gint32) (gssize) (imm)))); \
+ } while (0)
+
+/*
+ * Fence instructions have a peculiar encoding that isn't quite like any of the
+ * other formal encoding categories.
+ *
+ * [0....6] opcode
+ * [7...11] rd
+ * [12..14] funct3
+ * [15..19] rs1
+ * [20..23] succ
+ * [24..27] pred
+ * [28..31] imm[0..3]
+ */
+
+#define _riscv_f_op(p, opcode, funct3, rd, rs1, pred, succ, imm) \
+ do { \
+ g_assert (RISCV_VALID_REG ((rd))); \
+ g_assert (RISCV_VALID_REG ((rs1))); \
+ g_assert (RISCV_VALID_FENCE ((pred))); \
+ g_assert (RISCV_VALID_FENCE ((succ))); \
+ _riscv_emit ((p), ((opcode) << 0) | \
+ ((rd) << 7) | \
+ ((funct3) << 12) | \
+ ((rs1) << 15) | \
+ (RISCV_BITS ((succ), 0, 4) << 20) | \
+ (RISCV_BITS ((pred), 0, 4) << 24) | \
+ (RISCV_BITS ((guint32) (gsize) (imm), 0, 4) << 28)); \
+ } while (0)
+
+/*
+ * Atomic instructions have a peculiar encoding that isn't quite like any of
+ * the other formal encoding categories.
+ *
+ * [0....6] opcode
+ * [7...11] rd
+ * [12..14] funct3
+ * [15..19] rs1
+ * [20..24] rs2
+ * [25..26] ordering
+ * [27..31] funct5
+ */
+
+#define _riscv_a_op(p, opcode, funct3, funct5, ordering, rd, rs2, rs1) \
+ do { \
+ g_assert (RISCV_VALID_REG ((rd))); \
+ g_assert (RISCV_VALID_REG ((rs1))); \
+ g_assert (RISCV_VALID_REG ((rs2))); \
+ g_assert (RISCV_VALID_ORDERING ((ordering))); \
+ _riscv_emit ((p), ((opcode) << 0) | \
+ ((rd) << 7) | \
+ ((funct3) << 12) | \
+ ((rs1) << 15) | \
+ ((rs2) << 20) | \
+ (RISCV_BITS ((ordering), 0, 2) << 25) | \
+ ((funct5) << 27)); \
+ } while (0)
+
+/*
+ * NOTE: When you add new codegen macros or change existing ones, you must
+ * expand riscv-codegen-test.c to cover them, and update riscv-codegen.exp32
+ * and riscv-codegen.exp64 as needed.
+ */
+
+// RV32I
+
+#define riscv_lui(p, rd, imm) _riscv_u_op ((p), 0b0110111, (rd), (imm))
+#define riscv_auipc(p, rd, imm) _riscv_u_op ((p), 0b0010111, (rd), (imm))
+#define riscv_jal(p, rd, imm) _riscv_j_op ((p), 0b1101111, (rd), (imm))
+#define riscv_jalr(p, rd, rs1, imm) _riscv_i_op ((p), 0b1100111, 0b000, (rd), (rs1), (imm))
+#define riscv_beq(p, rs1, rs2, imm) _riscv_b_op ((p), 0b1100011, 0b000, (rs1), (rs2), (imm))
+#define riscv_bne(p, rs1, rs2, imm) _riscv_b_op ((p), 0b1100011, 0b001, (rs1), (rs2), (imm))
+#define riscv_blt(p, rs1, rs2, imm) _riscv_b_op ((p), 0b1100011, 0b100, (rs1), (rs2), (imm))
+#define riscv_bge(p, rs1, rs2, imm) _riscv_b_op ((p), 0b1100011, 0b101, (rs1), (rs2), (imm))
+#define riscv_bltu(p, rs1, rs2, imm) _riscv_b_op ((p), 0b1100011, 0b110, (rs1), (rs2), (imm))
+#define riscv_bgeu(p, rs1, rs2, imm) _riscv_b_op ((p), 0b1100011, 0b111, (rs1), (rs2), (imm))
+#define riscv_lb(p, rd, rs1, imm) _riscv_i_op ((p), 0b0000011, 0b000, (rd), (rs1), (imm))
+#define riscv_lh(p, rd, rs1, imm) _riscv_i_op ((p), 0b0000011, 0b001, (rd), (rs1), (imm))
+#define riscv_lw(p, rd, rs1, imm) _riscv_i_op ((p), 0b0000011, 0b010, (rd), (rs1), (imm))
+#define riscv_lbu(p, rd, rs1, imm) _riscv_i_op ((p), 0b0000011, 0b100, (rd), (rs1), (imm))
+#define riscv_lhu(p, rd, rs1, imm) _riscv_i_op ((p), 0b0000011, 0b101, (rd), (rs1), (imm))
+#define riscv_sb(p, rs2, rs1, imm) _riscv_s_op ((p), 0b0100011, 0b000, (rs2), (rs1), (imm))
+#define riscv_sh(p, rs2, rs1, imm) _riscv_s_op ((p), 0b0100011, 0b001, (rs2), (rs1), (imm))
+#define riscv_sw(p, rs2, rs1, imm) _riscv_s_op ((p), 0b0100011, 0b010, (rs2), (rs1), (imm))
+#define riscv_addi(p, rd, rs1, imm) _riscv_i_op ((p), 0b0010011, 0b000, (rd), (rs1), (imm))
+#define riscv_slti(p, rd, rs1, imm) _riscv_i_op ((p), 0b0010011, 0b010, (rd), (rs1), (imm))
+#define riscv_sltiu(p, rd, rs1, imm) _riscv_i_op ((p), 0b0010011, 0b011, (rd), (rs1), (imm))
+#define riscv_xori(p, rd, rs1, imm) _riscv_i_op ((p), 0b0010011, 0b100, (rd), (rs1), (imm))
+#define riscv_ori(p, rd, rs1, imm) _riscv_i_op ((p), 0b0010011, 0b110, (rd), (rs1), (imm))
+#define riscv_andi(p, rd, rs1, imm) _riscv_i_op ((p), 0b0010011, 0b111, (rd), (rs1), (imm))
+#ifdef TARGET_RISCV32
+#define riscv_slli(p, rd, rs1, shamt) _riscv_is_op ((p), 0b0010011, 0b001, 0b0000000, (rd), (rs1), (shamt))
+#define riscv_srli(p, rd, rs1, shamt) _riscv_is_op ((p), 0b0010011, 0b101, 0b0000000, (rd), (rs1), (shamt))
+#define riscv_srai(p, rd, rs1, shamt) _riscv_is_op ((p), 0b0010011, 0b101, 0b0100000, (rd), (rs1), (shamt))
+#endif
+#define riscv_add(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b000, 0b0000000, (rd), (rs1), (rs2))
+#define riscv_sub(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b000, 0b0100000, (rd), (rs1), (rs2))
+#define riscv_sll(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b001, 0b0000000, (rd), (rs1), (rs2))
+#define riscv_slt(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b010, 0b0000000, (rd), (rs1), (rs2))
+#define riscv_sltu(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b011, 0b0000000, (rd), (rs1), (rs2))
+#define riscv_xor(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b100, 0b0000000, (rd), (rs1), (rs2))
+#define riscv_srl(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b101, 0b0000000, (rd), (rs1), (rs2))
+#define riscv_sra(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b101, 0b0100000, (rd), (rs1), (rs2))
+#define riscv_or(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b110, 0b0000000, (rd), (rs1), (rs2))
+#define riscv_and(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b111, 0b0000000, (rd), (rs1), (rs2))
+#define riscv_fence(p, pred, succ) _riscv_f_op ((p), 0b0001111, 0b000, 0b00000, 0b00000, (pred), (succ), 0b0000)
+#define riscv_fence_i(p) _riscv_f_op ((p), 0b0001111, 0b001, 0b00000, 0b00000, 0b0000, 0b0000, 0b0000)
+#define riscv_ecall(p) _riscv_i_op ((p), 0b1110011, 0b000, 0b00000, 0b00000, 0b000000000000)
+#define riscv_ebreak(p) _riscv_i_op ((p), 0b1110011, 0b000, 0b00000, 0b00000, 0b000000000001)
+#define riscv_csrrw(p, rd, csr, rs1) _riscv_ic_op ((p), 0b1110011, 0b001, (rd), (csr), (rs1))
+#define riscv_csrrs(p, rd, csr, rs1) _riscv_ic_op ((p), 0b1110011, 0b010, (rd), (csr), (rs1))
+#define riscv_csrrc(p, rd, csr, rs1) _riscv_ic_op ((p), 0b1110011, 0b011, (rd), (csr), (rs1))
+#define riscv_csrrwi(p, rd, csr, imm) _riscv_ic_op ((p), 0b1110011, 0b101, (rd), (csr), (imm))
+#define riscv_csrrsi(p, rd, csr, imm) _riscv_ic_op ((p), 0b1110011, 0b110, (rd), (csr), (imm))
+#define riscv_csrrci(p, rd, csr, imm) _riscv_ic_op ((p), 0b1110011, 0b111, (rd), (csr), (imm))
+
+// RV64I
+
+#ifdef TARGET_RISCV64
+#define riscv_lwu(p, rd, rs1, imm) _riscv_i_op ((p), 0b0000011, 0b110, (rd), (rs1), (imm))
+#define riscv_ld(p, rd, rs1, imm) _riscv_i_op ((p), 0b0000011, 0b011, (rd), (rs1), (imm))
+#define riscv_sd(p, rs2, rs1, imm) _riscv_s_op ((p), 0b0100011, 0b011, (rs2), (rs1), (imm))
+#define riscv_slli(p, rd, rs1, shamt) _riscv_ls_op ((p), 0b0010011, 0b001, 0b000000, (rd), (rs1), (shamt))
+#define riscv_srli(p, rd, rs1, shamt) _riscv_ls_op ((p), 0b0010011, 0b101, 0b000000, (rd), (rs1), (shamt))
+#define riscv_srai(p, rd, rs1, shamt) _riscv_ls_op ((p), 0b0010011, 0b101, 0b010000, (rd), (rs1), (shamt))
+#define riscv_addiw(p, rd, rs1, imm) _riscv_i_op ((p), 0b0011011, 0b000, (rd), (rs1), (imm))
+#define riscv_slliw(p, rd, rs1, shamt) _riscv_is_op ((p), 0b0011011, 0b001, 0b0000000, (rd), (rs1), (shamt))
+#define riscv_srliw(p, rd, rs1, shamt) _riscv_is_op ((p), 0b0011011, 0b101, 0b0000000, (rd), (rs1), (shamt))
+#define riscv_sraiw(p, rd, rs1, shamt) _riscv_is_op ((p), 0b0011011, 0b101, 0b0100000, (rd), (rs1), (shamt))
+#define riscv_addw(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0111011, 0b000, 0b0000000, (rd), (rs1), (rs2))
+#define riscv_subw(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0111011, 0b000, 0b0100000, (rd), (rs1), (rs2))
+#define riscv_sllw(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0111011, 0b001, 0b0000000, (rd), (rs1), (rs2))
+#define riscv_srlw(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0111011, 0b101, 0b0000000, (rd), (rs1), (rs2))
+#define riscv_sraw(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0111011, 0b101, 0b0100000, (rd), (rs1), (rs2))
+#endif
+
+// RV32M
+
+#define riscv_mul(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b000, 0b0000001, (rd), (rs1), (rs2))
+#define riscv_mulh(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b001, 0b0000001, (rd), (rs1), (rs2))
+#define riscv_mulhsu(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b010, 0b0000001, (rd), (rs1), (rs2))
+#define riscv_mulhu(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b011, 0b0000001, (rd), (rs1), (rs2))
+#define riscv_div(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b100, 0b0000001, (rd), (rs1), (rs2))
+#define riscv_divu(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b101, 0b0000001, (rd), (rs1), (rs2))
+#define riscv_rem(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b110, 0b0000001, (rd), (rs1), (rs2))
+#define riscv_remu(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0110011, 0b111, 0b0000001, (rd), (rs1), (rs2))
+
+// RV64M
+
+#ifdef TARGET_RISCV64
+#define riscv_mulw(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0111011, 0b000, 0b0000001, (rd), (rs1), (rs2))
+#define riscv_divw(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0111011, 0b100, 0b0000001, (rd), (rs1), (rs2))
+#define riscv_divuw(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0111011, 0b101, 0b0000001, (rd), (rs1), (rs2))
+#define riscv_remw(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0111011, 0b110, 0b0000001, (rd), (rs1), (rs2))
+#define riscv_remuw(p, rd, rs1, rs2) _riscv_r_op ((p), 0b0111011, 0b111, 0b0000001, (rd), (rs1), (rs2))
+#endif
+
+// RV32A
+
+#define riscv_lr_w(p, ordering, rd, rs1) _riscv_a_op ((p), 0b0101111, 0b010, 0b00010, (ordering), (rd), 0b00000, (rs1))
+#define riscv_sc_w(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b010, 0b00011, (ordering), (rd), (rs2), (rs1))
+#define riscv_amoswap_w(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b010, 0b00001, (ordering), (rd), (rs2), (rs1))
+#define riscv_amoadd_w(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b010, 0b00000, (ordering), (rd), (rs2), (rs1))
+#define riscv_amoxor_w(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b010, 0b00100, (ordering), (rd), (rs2), (rs1))
+#define riscv_amoand_w(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b010, 0b01100, (ordering), (rd), (rs2), (rs1))
+#define riscv_amoor_w(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b010, 0b01000, (ordering), (rd), (rs2), (rs1))
+#define riscv_amomin_w(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b010, 0b10000, (ordering), (rd), (rs2), (rs1))
+#define riscv_amomax_w(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b010, 0b10100, (ordering), (rd), (rs2), (rs1))
+#define riscv_amominu_w(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b010, 0b11000, (ordering), (rd), (rs2), (rs1))
+#define riscv_amomaxu_w(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b010, 0b11100, (ordering), (rd), (rs2), (rs1))
+
+// RV64A
+
+#ifdef TARGET_RISCV64
+#define riscv_lr_d(p, ordering, rd, rs1) _riscv_a_op ((p), 0b0101111, 0b011, 0b00010, (ordering), (rd), 0b00000, (rs1))
+#define riscv_sc_d(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b011, 0b00011, (ordering), (rd), (rs2), (rs1))
+#define riscv_amoswap_d(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b011, 0b00001, (ordering), (rd), (rs2), (rs1))
+#define riscv_amoadd_d(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b011, 0b00000, (ordering), (rd), (rs2), (rs1))
+#define riscv_amoxor_d(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b011, 0b00100, (ordering), (rd), (rs2), (rs1))
+#define riscv_amoand_d(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b011, 0b01100, (ordering), (rd), (rs2), (rs1))
+#define riscv_amoor_d(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b011, 0b01000, (ordering), (rd), (rs2), (rs1))
+#define riscv_amomin_d(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b011, 0b10000, (ordering), (rd), (rs2), (rs1))
+#define riscv_amomax_d(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b011, 0b10100, (ordering), (rd), (rs2), (rs1))
+#define riscv_amominu_d(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b011, 0b11000, (ordering), (rd), (rs2), (rs1))
+#define riscv_amomaxu_d(p, ordering, rd, rs2, rs1) _riscv_a_op ((p), 0b0101111, 0b011, 0b11100, (ordering), (rd), (rs2), (rs1))
+#endif
+
+// RV32F
+
+#define riscv_flw(p, rd, rs1, imm) _riscv_i_op ((p), 0b0000111, 0b010, (rd), (rs1), (imm))
+#define riscv_fsw(p, rs2, rs1, imm) _riscv_s_op ((p), 0b0100111, 0b010, (rs2), (rs1), (imm))
+#define riscv_fmadd_s(p, rm, rd, rs1, rs2, rs3) _riscv_r4_op ((p), 0b1000011, (rm), 0b00, (rd), (rs1), (rs2), (rs3))
+#define riscv_fmsub_s(p, rm, rd, rs1, rs2, rs3) _riscv_r4_op ((p), 0b1000111, (rm), 0b00, (rd), (rs1), (rs2), (rs3))
+#define riscv_fnmadd_s(p, rm, rd, rs1, rs2, rs3) _riscv_r4_op ((p), 0b1001011, (rm), 0b00, (rd), (rs1), (rs2), (rs3))
+#define riscv_fnmsub_s(p, rm, rd, rs1, rs2, rs3) _riscv_r4_op ((p), 0b1001111, (rm), 0b00, (rd), (rs1), (rs2), (rs3))
+#define riscv_fadd_s(p, rm, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, (rm), 0b0000000, (rd), (rs1), (rs2))
+#define riscv_fsub_s(p, rm, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, (rm), 0b0000100, (rd), (rs1), (rs2))
+#define riscv_fmul_s(p, rm, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, (rm), 0b0001000, (rd), (rs1), (rs2))
+#define riscv_fdiv_s(p, rm, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, (rm), 0b0001100, (rd), (rs1), (rs2))
+#define riscv_fsqrt_s(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b0101100, (rd), (rs1), 0b00000)
+#define riscv_fsgnj_s(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b000, 0b0010000, (rd), (rs1), (rs2))
+#define riscv_fsgnjn_s(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b001, 0b0010000, (rd), (rs1), (rs2))
+#define riscv_fsgnjx_s(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b010, 0b0010000, (rd), (rs1), (rs2))
+#define riscv_fmin_s(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b000, 0b0010100, (rd), (rs1), (rs2))
+#define riscv_fmax_s(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b001, 0b0010100, (rd), (rs1), (rs2))
+#define riscv_fcvt_w_s(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b1100000, (rd), (rs1), 0b00000)
+#define riscv_fcvt_wu_s(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b1100000, (rd), (rs1), 0b00001)
+#define riscv_fmv_x_w(p, rd, rs1) _riscv_r_op ((p), 0b1010011, 0b000, 0b1110000, (rd), (rs1), 0b00000)
+#define riscv_feq_s(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b010, 0b1010000, (rd), (rs1), (rs2))
+#define riscv_flt_s(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b001, 0b1010000, (rd), (rs1), (rs2))
+#define riscv_fle_s(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b000, 0b1010000, (rd), (rs1), (rs2))
+#define riscv_fclass_s(p, rd, rs1) _riscv_r_op ((p), 0b1010011, 0b001, 0b1110000, (rd), (rs1), 0b00000)
+#define riscv_fcvt_s_w(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b1101000, (rd), (rs1), 0b00000)
+#define riscv_fcvt_s_wu(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b1101000, (rd), (rs1), 0b00001)
+#define riscv_fmv_w_x(p, rd, rs1) _riscv_r_op ((p), 0b1010011, 0b000, 0b1111000, (rd), (rs1), 0b00000)
+
+// RV64F
+
+#ifdef TARGET_RISCV64
+#define riscv_fcvt_l_s(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b1100000, (rd), (rs1), 0b00010)
+#define riscv_fcvt_lu_s(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b1100000, (rd), (rs1), 0b00011)
+#define riscv_fcvt_s_l(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b1101000, (rd), (rs1), 0b00010)
+#define riscv_fcvt_s_lu(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b1101000, (rd), (rs1), 0b00011)
+#endif
+
+// RV32D
+
+#define riscv_fld(p, rd, rs1, imm) _riscv_i_op ((p), 0b0000111, 0b011, (rd), (rs1), (imm))
+#define riscv_fsd(p, rs2, rs1, imm) _riscv_s_op ((p), 0b0100111, 0b011, (rs2), (rs1), (imm))
+#define riscv_fmadd_d(p, rm, rd, rs1, rs2, rs3) _riscv_r4_op ((p), 0b1000011, (rm), 0b01, (rd), (rs1), (rs2), (rs3))
+#define riscv_fmsub_d(p, rm, rd, rs1, rs2, rs3) _riscv_r4_op ((p), 0b1000111, (rm), 0b01, (rd), (rs1), (rs2), (rs3))
+#define riscv_fnmadd_d(p, rm, rd, rs1, rs2, rs3) _riscv_r4_op ((p), 0b1001011, (rm), 0b01, (rd), (rs1), (rs2), (rs3))
+#define riscv_fnmsub_d(p, rm, rd, rs1, rs2, rs3) _riscv_r4_op ((p), 0b1001111, (rm), 0b01, (rd), (rs1), (rs2), (rs3))
+#define riscv_fadd_d(p, rm, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, (rm), 0b0000001, (rd), (rs1), (rs2))
+#define riscv_fsub_d(p, rm, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, (rm), 0b0000101, (rd), (rs1), (rs2))
+#define riscv_fmul_d(p, rm, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, (rm), 0b0001001, (rd), (rs1), (rs2))
+#define riscv_fdiv_d(p, rm, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, (rm), 0b0001101, (rd), (rs1), (rs2))
+#define riscv_fsqrt_d(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b0101101, (rd), (rs1), 0b00000)
+#define riscv_fsgnj_d(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b000, 0b0010001, (rd), (rs1), (rs2))
+#define riscv_fsgnjn_d(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b001, 0b0010001, (rd), (rs1), (rs2))
+#define riscv_fsgnjx_d(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b010, 0b0010001, (rd), (rs1), (rs2))
+#define riscv_fmin_d(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b000, 0b0010101, (rd), (rs1), (rs2))
+#define riscv_fmax_d(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b001, 0b0010101, (rd), (rs1), (rs2))
+#define riscv_fcvt_s_d(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b0100000, (rd), (rs1), 0b00001)
+#define riscv_fcvt_d_s(p, rd, rs1) _riscv_r_op ((p), 0b1010011, 0b000, 0b0100001, (rd), (rs1), 0b00000)
+#define riscv_feq_d(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b010, 0b1010001, (rd), (rs1), (rs2))
+#define riscv_flt_d(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b001, 0b1010001, (rd), (rs1), (rs2))
+#define riscv_fle_d(p, rd, rs1, rs2) _riscv_r_op ((p), 0b1010011, 0b000, 0b1010001, (rd), (rs1), (rs2))
+#define riscv_fclass_d(p, rd, rs1) _riscv_r_op ((p), 0b1010011, 0b001, 0b1110001, (rd), (rs1), 0b00000)
+#define riscv_fcvt_w_d(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b1100001, (rd), (rs1), 0b00000)
+#define riscv_fcvt_wu_d(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b1100001, (rd), (rs1), 0b00001)
+#define riscv_fcvt_d_w(p, rd, rs1) _riscv_r_op ((p), 0b1010011, 0b000, 0b1101001, (rd), (rs1), 0b00000)
+#define riscv_fcvt_d_wu(p, rd, rs1) _riscv_r_op ((p), 0b1010011, 0b000, 0b1101001, (rd), (rs1), 0b00001)
+
+// RV64D
+
+#ifdef TARGET_RISCV64
+#define riscv_fcvt_l_d(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b1100001, (rd), (rs1), 0b00010)
+#define riscv_fcvt_lu_d(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b1100001, (rd), (rs1), 0b00011)
+#define riscv_fmv_x_d(p, rd, rs1) _riscv_r_op ((p), 0b1010011, 0b000, 0b1110001, (rd), (rs1), 0b00000)
+#define riscv_fcvt_d_l(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b1101001, (rd), (rs1), 0b00010)
+#define riscv_fcvt_d_lu(p, rm, rd, rs1) _riscv_r_op ((p), 0b1010011, (rm), 0b1101001, (rd), (rs1), 0b00011)
+#define riscv_fmv_d_x(p, rd, rs1) _riscv_r_op ((p), 0b1010011, 0b000, 0b1111001, (rd), (rs1), 0b00000)
+#endif
+
+#endif
diff --git a/mono/metadata/icall.c b/mono/metadata/icall.c
index 4cf792555e4..420a02d7862 100644
--- a/mono/metadata/icall.c
+++ b/mono/metadata/icall.c
@@ -6833,7 +6833,7 @@ mono_icall_is_64bit_os (void)
struct utsname name;
if (uname (&name) >= 0) {
- return strcmp (name.machine, "x86_64") == 0 || strncmp (name.machine, "aarch64", 7) == 0 || strncmp (name.machine, "ppc64", 5) == 0;
+ return strcmp (name.machine, "x86_64") == 0 || strncmp (name.machine, "aarch64", 7) == 0 || strncmp (name.machine, "ppc64", 5) == 0 || strncmp (name.machine, "riscv64", 7) == 0;
}
#endif
return FALSE;
diff --git a/mono/metadata/metadata-cross-helpers.c b/mono/metadata/metadata-cross-helpers.c
index 003c888b675..d153fc970ab 100644
--- a/mono/metadata/metadata-cross-helpers.c
+++ b/mono/metadata/metadata-cross-helpers.c
@@ -34,6 +34,10 @@ dump_arch (void)
g_print ("#ifdef TARGET_ARM\n");
#elif defined (TARGET_ARM64)
g_print ("#ifdef TARGET_ARM64\n");
+#elif defined (TARGET_RISCV32)
+ g_print ("#ifdef TARGET_RISCV32\n");
+#elif defined (TARGET_RISCV64)
+ g_print ("#ifdef TARGET_RISCV64\n");
#else
return 0;
#endif
diff --git a/mono/metadata/mono-config.c b/mono/metadata/mono-config.c
index 5c1058d5a8d..b44c84556b5 100644
--- a/mono/metadata/mono-config.c
+++ b/mono/metadata/mono-config.c
@@ -84,6 +84,12 @@
#elif defined(mips) || defined(__mips) || defined(_mips)
#define CONFIG_CPU "mips"
#define CONFIG_WORDSIZE "32"
+#elif defined (TARGET_RISCV32)
+#define CONFIG_CPU "riscv32"
+#define CONFIG_WORDSIZE "32"
+#elif defined (TARGET_RISCV64)
+#define CONFIG_CPU "riscv64"
+#define CONFIG_WORDSIZE "64"
#elif defined(TARGET_WASM)
#define CONFIG_CPU "wasm"
#define CONFIG_WORDSIZE "32"
diff --git a/mono/metadata/sgen-client-mono.h b/mono/metadata/sgen-client-mono.h
index a26014d4488..b3773f8bb98 100644
--- a/mono/metadata/sgen-client-mono.h
+++ b/mono/metadata/sgen-client-mono.h
@@ -700,7 +700,7 @@ sgen_client_binary_protocol_collection_end_stats (long long major_scan, long lon
*
* TODO: Query the JIT instead of this ifdef hack.
*/
-#if defined (TARGET_X86) || defined (TARGET_AMD64) || (defined (TARGET_ARM) && defined (HAVE_ARMV7)) || defined (TARGET_ARM64)
+#if defined (TARGET_X86) || defined (TARGET_AMD64) || (defined (TARGET_ARM) && defined (HAVE_ARMV7)) || defined (TARGET_ARM64) || defined (TARGET_RISCV)
#define MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION
#endif
#endif
diff --git a/mono/mini/Makefile.am.in b/mono/mini/Makefile.am.in
index 6c840f88b8d..97478cb44b7 100755
--- a/mono/mini/Makefile.am.in
+++ b/mono/mini/Makefile.am.in
@@ -364,6 +364,12 @@ s390x_sources = \
exceptions-s390x.c \
tramp-s390x.c
+riscv_sources = \
+ mini-riscv.c \
+ mini-riscv.h \
+ exceptions-riscv.c \
+ tramp-riscv.c
+
darwin_sources = \
mini-darwin.c
@@ -627,6 +633,20 @@ arch_define=__s390__
target_define=TARGET_S390X
endif
+if RISCV32
+arch_sources = $(riscv_sources)
+arch_built = cpu-riscv32.h
+arch_define = __riscv
+target_define = TARGET_RISCV32
+endif
+
+if RISCV64
+arch_sources = $(riscv_sources)
+arch_built = cpu-riscv64.h
+arch_define = __riscv
+target_define = TARGET_RISCV64
+endif
+
if HOST_WIN32
os_sources = $(windows_sources)
monobin_platform_ldflags=
@@ -774,6 +794,12 @@ cpu-s390x.h: mini-ops.h cpu-s390x.md
cpu-mips.h: mini-ops.h cpu-mips.md
$(GENMDESC_PRG) cpu-mips.h mips_desc $(srcdir)/cpu-mips.md
+cpu-riscv32.h: cpu-riscv32.md
+ $(GENMDESC_PRG) cpu-riscv32.h riscv32_cpu_desc $(srcdir)/cpu-riscv32.md
+
+cpu-riscv64.h: cpu-riscv64.md
+ $(GENMDESC_PRG) cpu-riscv64.h riscv64_cpu_desc $(srcdir)/cpu-riscv64.md
+
testi: mono test.exe
$(MINI_RUNTIME) -v -v --ncompile 1 --compile Test:$(mtest) test.exe
@@ -957,6 +983,7 @@ EXTRA_DIST = TestDriver.cs \
$(mips_sources) cpu-mips.md \
$(sparc_sources) cpu-sparc.md \
$(s390x_sources) cpu-s390x.md \
+ $(riscv_sources) cpu-riscv32.md cpu-riscv64.md \
$(windows_sources) \
$(darwin_sources) Info.plist \
$(posix_sources) \
diff --git a/mono/mini/aot-compiler.c b/mono/mini/aot-compiler.c
index 52ec2ae2c38..70334e99220 100644
--- a/mono/mini/aot-compiler.c
+++ b/mono/mini/aot-compiler.c
@@ -948,7 +948,7 @@ emit_code_bytes (MonoAotCompile *acfg, const guint8* buf, int size)
/* ARCHITECTURE SPECIFIC CODE */
-#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_POWERPC) || defined(TARGET_ARM64)
+#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_POWERPC) || defined(TARGET_ARM64) || defined (TARGET_RISCV)
#define EMIT_DWARF_INFO 1
#endif
@@ -1048,6 +1048,14 @@ arch_emit_unwind_info_sections (MonoAotCompile *acfg, const char *function_start
#endif
#endif
+#ifdef TARGET_RISCV32
+#define AOT_TARGET_STR "RISCV32"
+#endif
+
+#ifdef TARGET_RISCV64
+#define AOT_TARGET_STR "RISCV64"
+#endif
+
#ifdef TARGET_X86
#ifdef TARGET_WIN32
#define AOT_TARGET_STR "X86 (WIN32)"
@@ -1131,6 +1139,32 @@ arch_init (MonoAotCompile *acfg)
acfg->need_pt_gnu_stack = TRUE;
#endif
+#ifdef TARGET_RISCV
+ if (acfg->aot_opts.mtriple)
+ mono_arch_set_target (acfg->aot_opts.mtriple);
+
+#ifdef TARGET_RISCV64
+
+ g_string_append (acfg->as_args, " -march=rv64i ");
+#ifdef RISCV_FPABI_DOUBLE
+ g_string_append (acfg->as_args, " -mabi=lp64d");
+#else
+ g_string_append (acfg->as_args, " -mabi=lp64");
+#endif
+
+#else
+
+ g_string_append (acfg->as_args, " -march=rv32i ");
+#ifdef RISCV_FPABI_DOUBLE
+ g_string_append (acfg->as_args, " -mabi=ilp32d ");
+#else
+ g_string_append (acfg->as_args, " -mabi=ilp32 ");
+#endif
+
+#endif
+
+#endif
+
#ifdef MONOTOUCH
acfg->global_symbols = TRUE;
#endif
diff --git a/mono/mini/cpu-riscv32.md b/mono/mini/cpu-riscv32.md
new file mode 100644
index 00000000000..65b602cc530
--- /dev/null
+++ b/mono/mini/cpu-riscv32.md
@@ -0,0 +1,30 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+#
+# RISC-V RV32 Machine Description
+#
+# This file describes various properties of Mini instructions for RV32 and is
+# read by genmdesc.py to generate a C header file used by various parts of the
+# JIT.
+#
+# Lines are of the form:
+#
+# <name>: len:<length> [dest:<rspec>] [src1:<rspec>] [src2:<rspec>] [src3:<rspec>] [clob:<cspec>]
+#
+# Here, <name> is the name of the instruction as specified in mini-ops.h.
+# length is the maximum number of bytes that could be needed to generate native
+# code for the instruction. dest, src1, src2, and src3 specify output and input
+# registers needed by the instruction. <rspec> can be one of:
+#
+# a a0
+# i any integer register
+# b any integer register (used as a pointer)
+# f any float register (a0..a1 pair in soft float)
+# l a0..a1 pair
+#
+# clob specifies which registers are clobbered (i.e. overwritten with garbage)
+# by the instruction. <cspec> can be one of:
+#
+# a a0
+# c all caller-saved registers
diff --git a/mono/mini/cpu-riscv64.md b/mono/mini/cpu-riscv64.md
new file mode 100644
index 00000000000..e8c29d3824c
--- /dev/null
+++ b/mono/mini/cpu-riscv64.md
@@ -0,0 +1,29 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+#
+# RISC-V RV64 Machine Description
+#
+# This file describes various properties of Mini instructions for RV64 and is
+# read by genmdesc.py to generate a C header file used by various parts of the
+# JIT.
+#
+# Lines are of the form:
+#
+# <name>: len:<length> [dest:<rspec>] [src1:<rspec>] [src2:<rspec>] [src3:<rspec>] [clob:<cspec>]
+#
+# Here, <name> is the name of the instruction as specified in mini-ops.h.
+# length is the maximum number of bytes that could be needed to generate native
+# code for the instruction. dest, src1, src2, and src3 specify output and input
+# registers needed by the instruction. <rspec> can be one of:
+#
+# a a0
+# i any integer register
+# b any integer register (used as a pointer)
+# f any float register (a0 in soft float)
+#
+# clob specifies which registers are clobbered (i.e. overwritten with garbage)
+# by the instruction. <cspec> can be one of:
+#
+# a a0
+# c all caller-saved registers
diff --git a/mono/mini/exceptions-riscv.c b/mono/mini/exceptions-riscv.c
new file mode 100644
index 00000000000..815375b340d
--- /dev/null
+++ b/mono/mini/exceptions-riscv.c
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the .NET Foundation under one or more agreements.
+ * The .NET Foundation licenses this file to you under the MIT license.
+ * See the LICENSE file in the project root for more information.
+ */
+
+#include "mini-runtime.h"
+
+#include <mono/metadata/abi-details.h>
+#include <mono/utils/mono-sigcontext.h>
+
+#ifndef DISABLE_JIT
+
+
+static gpointer
+nop_stub (void)
+{
+ guint8 *code, *start;
+
+ start = code = mono_global_codeman_reserve (0x50);
+
+ /* nop */
+ riscv_addi (code, RISCV_X0, RISCV_X0, 0);
+
+ mono_arch_flush_icache (start, code - start);
+
+ return start;
+}
+
+gpointer
+mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
+{
+ *info = NULL;
+ return nop_stub ();
+}
+
+gpointer
+mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
+{
+ *info = NULL;
+ return nop_stub ();
+}
+
+gpointer
+mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot)
+{
+ *info = NULL;
+ return nop_stub ();
+}
+
+gpointer
+mono_arch_get_rethrow_exception (MonoTrampInfo **info, gboolean aot)
+{
+ *info = NULL;
+ return nop_stub ();
+}
+
+gpointer
+mono_arch_get_rethrow_preserve_exception (MonoTrampInfo **info, gboolean aot)
+{
+ *info = NULL;
+ return nop_stub ();
+}
+
+gpointer
+mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot)
+{
+ *info = NULL;
+ return nop_stub ();
+}
+
+#else
+
+gpointer
+mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_arch_get_rethrow_exception (MonoTrampInfo **info, gboolean aot)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+#endif
+
+void
+mono_arch_exceptions_init (void)
+{
+ // NOT_IMPLEMENTED;
+}
+
+gboolean
+mono_arch_unwind_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *ji,
+ MonoContext *ctx, MonoContext *new_ctx, MonoLMF **lmf,
+ mgreg_t **save_locations, StackFrameInfo *frame)
+{
+ NOT_IMPLEMENTED;
+ return FALSE;
+}
+
+static void
+handle_signal_exception (gpointer obj)
+{
+ MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
+ MonoContext ctx = jit_tls->ex_ctx;
+
+ mono_handle_exception (&ctx, obj);
+ mono_restore_context (&ctx);
+}
+
+gboolean
+mono_arch_handle_exception (void *ctx, gpointer obj)
+{
+ MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
+
+ mono_sigctx_to_monoctx (ctx, &jit_tls->ex_ctx);
+
+ // Call handle_signal_exception () on the normal stack.
+ UCONTEXT_GREGS (ctx) [RISCV_A0] = (long) obj;
+ UCONTEXT_REG_PC (ctx) = (long) handle_signal_exception;
+
+ return TRUE;
+}
+
+gpointer
+mono_arch_ip_from_context (void *sigctx)
+{
+ return (gpointer) UCONTEXT_REG_PC (sigctx);
+}
+
+void
+mono_arch_setup_async_callback (MonoContext *ctx, void (*async_cb)(void *fun), gpointer user_data)
+{
+ // Allocate a stack frame and redirect PC.
+ MONO_CONTEXT_SET_SP (ctx, (mgreg_t) MONO_CONTEXT_GET_SP (ctx) - 32);
+
+ mono_arch_setup_resume_sighandler_ctx (ctx, async_cb);
+}
+
+void
+mono_arch_setup_resume_sighandler_ctx (MonoContext *ctx, gpointer func)
+{
+ MONO_CONTEXT_SET_IP (ctx, func);
+}
+
+void
+mono_arch_undo_ip_adjustment (MonoContext *context)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_do_ip_adjustment (MonoContext *context)
+{
+ g_assert_not_reached ();
+}
diff --git a/mono/mini/genmdesc.py b/mono/mini/genmdesc.py
index 987332bdf1a..624088944bb 100755
--- a/mono/mini/genmdesc.py
+++ b/mono/mini/genmdesc.py
@@ -26,6 +26,9 @@ allowed_defines = { "TARGET_X86" : 1,
"TARGET_SPARC" : 1,
"TARGET_S390X" : 1,
"TARGET_MIPS" : 1,
+ "TARGET_RISCV" : 1,
+ "TARGET_RISCV32" : 1,
+ "TARGET_RISCV64" : 1,
"TARGET_WASM" : 1
}
diff --git a/mono/mini/helpers.c b/mono/mini/helpers.c
index f1644dbc4b2..a4364fe6a9d 100644
--- a/mono/mini/helpers.c
+++ b/mono/mini/helpers.c
@@ -239,6 +239,10 @@ mono_disassemble_code (MonoCompile *cfg, guint8 *code, int size, char *id)
#define AS_CMD "as -arch ppc64"
#elif defined(__powerpc64__)
#define AS_CMD "as -mppc64"
+#elif defined (TARGET_RISCV64)
+#define AS_CMD "as -march=rv64ima"
+#elif defined (TARGET_RISCV32)
+#define AS_CMD "as -march=rv32ima"
#else
#define AS_CMD "as"
#endif
diff --git a/mono/mini/mini-arch.h b/mono/mini/mini-arch.h
index c49ed5a41d5..da89dbb0439 100644
--- a/mono/mini/mini-arch.h
+++ b/mono/mini/mini-arch.h
@@ -25,6 +25,8 @@
#include "mini-arm64.h"
#elif defined(__mips__)
#include "mini-mips.h"
+#elif defined (TARGET_RISCV)
+#include "mini-riscv.h"
#elif TARGET_WASM
#include "mini-wasm.h"
#else
diff --git a/mono/mini/mini-codegen.c b/mono/mini/mini-codegen.c
index 3a1cdd03901..91b756f37fc 100644
--- a/mono/mini/mini-codegen.c
+++ b/mono/mini/mini-codegen.c
@@ -1148,7 +1148,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
desc_to_fixed_reg_inited = TRUE;
/* Validate the cpu description against the info in mini-ops.h */
-#if defined(TARGET_AMD64) || defined(TARGET_X86) || defined(TARGET_ARM) || defined(TARGET_ARM64)
+#if defined(TARGET_AMD64) || defined(TARGET_X86) || defined(TARGET_ARM) || defined(TARGET_ARM64) || defined (TARGET_RISCV)
for (i = OP_LOAD; i < OP_LAST; ++i) {
const char *ispec;
diff --git a/mono/mini/mini-gc.c b/mono/mini/mini-gc.c
index c44898c84f3..754938c14fc 100644
--- a/mono/mini/mini-gc.c
+++ b/mono/mini/mini-gc.c
@@ -408,6 +408,11 @@ encode_frame_reg (int frame_reg)
return 0;
else if (frame_reg == S390_FP)
return 1;
+#elif defined (TARGET_RISCV)
+ if (frame_reg == RISCV_SP)
+ return 0;
+ else if (frame_reg == RISCV_FP)
+ return 1;
#else
NOT_IMPLEMENTED;
#endif
@@ -438,6 +443,11 @@ decode_frame_reg (int encoded)
return S390_SP;
else if (encoded == 1)
return S390_FP;
+#elif defined (TARGET_RISCV)
+ if (encoded == 0)
+ return RISCV_SP;
+ else if (encoded == 1)
+ return RISCV_FP;
#else
NOT_IMPLEMENTED;
#endif
@@ -469,6 +479,11 @@ static int callee_saved_regs [] = {
ppc_r29, ppc_r30, ppc_r31 };
#elif defined(TARGET_POWERPC)
static int callee_saved_regs [] = { ppc_r6, ppc_r7, ppc_r8, ppc_r9, ppc_r10, ppc_r11, ppc_r12, ppc_r13, ppc_r14 };
+#elif defined (TARGET_RISCV)
+static int callee_saved_regs [] = {
+ RISCV_S0, RISCV_S1, RISCV_S2, RISCV_S3, RISCV_S4, RISCV_S5,
+ RISCV_S6, RISCV_S7, RISCV_S8, RISCV_S9, RISCV_S10, RISCV_S11,
+};
#endif
static guint32
@@ -721,6 +736,11 @@ get_frame_pointer (MonoContext *ctx, int frame_reg)
return (host_mgreg_t)MONO_CONTEXT_GET_SP (ctx);
else if (frame_reg == S390_FP)
return (host_mgreg_t)MONO_CONTEXT_GET_BP (ctx);
+#elif defined (TARGET_RISCV)
+ if (frame_reg == RISCV_SP)
+ return MONO_CONTEXT_GET_SP (ctx);
+ else if (frame_reg == RISCV_FP)
+ return MONO_CONTEXT_GET_BP (ctx);
#endif
g_assert_not_reached ();
return 0;
diff --git a/mono/mini/mini-riscv.c b/mono/mini/mini-riscv.c
new file mode 100644
index 00000000000..ab0203e7ac0
--- /dev/null
+++ b/mono/mini/mini-riscv.c
@@ -0,0 +1,690 @@
+/*
+ * Licensed to the .NET Foundation under one or more agreements.
+ * The .NET Foundation licenses this file to you under the MIT license.
+ * See the LICENSE file in the project root for more information.
+ */
+
+#include <mono/utils/mono-hwcap.h>
+
+#include "mini-runtime.h"
+
+#ifdef TARGET_RISCV64
+#include "cpu-riscv64.h"
+#else
+#include "cpu-riscv32.h"
+#endif
+
+static gboolean riscv_stdext_a, riscv_stdext_b, riscv_stdext_c,
+ riscv_stdext_d, riscv_stdext_f, riscv_stdext_j,
+ riscv_stdext_l, riscv_stdext_m, riscv_stdext_n,
+ riscv_stdext_p, riscv_stdext_q, riscv_stdext_t,
+ riscv_stdext_v;
+
+void
+mono_arch_cpu_init (void)
+{
+}
+
+void
+mono_arch_init (void)
+{
+ riscv_stdext_a = mono_hwcap_riscv_has_stdext_a;
+ riscv_stdext_c = mono_hwcap_riscv_has_stdext_c;
+ riscv_stdext_d = mono_hwcap_riscv_has_stdext_d;
+ riscv_stdext_f = mono_hwcap_riscv_has_stdext_f;
+ riscv_stdext_m = mono_hwcap_riscv_has_stdext_m;
+}
+
+void
+mono_arch_finish_init (void)
+{
+}
+
+void
+mono_arch_register_lowlevel_calls (void)
+{
+}
+
+void
+mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
+{
+}
+
+void
+mono_arch_cleanup (void)
+{
+}
+
+void
+mono_arch_set_target (char *mtriple)
+{
+ // riscv{32,64}[extensions]-[<vendor>-]<system>-<abi>
+
+ size_t len = strlen (MONO_RISCV_ARCHITECTURE);
+
+ if (!strncmp (mtriple, MONO_RISCV_ARCHITECTURE, len)) {
+ mtriple += len;
+
+ for (;;) {
+ char c = *mtriple;
+
+ if (!c || c == '-')
+ break;
+
+ // ISA manual says upper and lower case are both OK.
+ switch (c) {
+ case 'A':
+ case 'a':
+ riscv_stdext_a = TRUE;
+ break;
+ case 'B':
+ case 'b':
+ riscv_stdext_b = TRUE;
+ break;
+ case 'C':
+ case 'c':
+ riscv_stdext_c = TRUE;
+ break;
+ case 'D':
+ case 'd':
+ riscv_stdext_d = TRUE;
+ break;
+ case 'F':
+ case 'f':
+ riscv_stdext_f = TRUE;
+ break;
+ case 'J':
+ case 'j':
+ riscv_stdext_j = TRUE;
+ break;
+ case 'L':
+ case 'l':
+ riscv_stdext_l = TRUE;
+ break;
+ case 'M':
+ case 'm':
+ riscv_stdext_m = TRUE;
+ break;
+ case 'N':
+ case 'n':
+ riscv_stdext_n = TRUE;
+ break;
+ case 'P':
+ case 'p':
+ riscv_stdext_p = TRUE;
+ break;
+ case 'Q':
+ case 'q':
+ riscv_stdext_q = TRUE;
+ break;
+ case 'T':
+ case 't':
+ riscv_stdext_t = TRUE;
+ break;
+ case 'V':
+ case 'v':
+ riscv_stdext_v = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ mtriple++;
+ }
+ }
+}
+
+guint32
+mono_arch_cpu_optimizations (guint32 *exclude_mask)
+{
+ *exclude_mask = 0;
+ return 0;
+}
+
+guint32
+mono_arch_cpu_enumerate_simd_versions (void)
+{
+ return 0;
+}
+
+gboolean
+mono_arch_have_fast_tls (void)
+{
+ return TRUE;
+}
+
+gboolean
+mono_arch_opcode_supported (int opcode)
+{
+ switch (opcode) {
+ case OP_ATOMIC_ADD_I4:
+ case OP_ATOMIC_EXCHANGE_I4:
+ case OP_ATOMIC_CAS_I4:
+ case OP_ATOMIC_LOAD_I1:
+ case OP_ATOMIC_LOAD_I2:
+ case OP_ATOMIC_LOAD_I4:
+ case OP_ATOMIC_LOAD_U1:
+ case OP_ATOMIC_LOAD_U2:
+ case OP_ATOMIC_LOAD_U4:
+ case OP_ATOMIC_STORE_I1:
+ case OP_ATOMIC_STORE_I2:
+ case OP_ATOMIC_STORE_I4:
+ case OP_ATOMIC_STORE_U1:
+ case OP_ATOMIC_STORE_U2:
+ case OP_ATOMIC_STORE_U4:
+#ifdef TARGET_RISCV64
+ case OP_ATOMIC_ADD_I8:
+ case OP_ATOMIC_EXCHANGE_I8:
+ case OP_ATOMIC_CAS_I8:
+ case OP_ATOMIC_LOAD_I8:
+ case OP_ATOMIC_LOAD_U8:
+ case OP_ATOMIC_STORE_I8:
+ case OP_ATOMIC_STORE_U8:
+#endif
+ return riscv_stdext_a;
+ case OP_ATOMIC_LOAD_R4:
+ case OP_ATOMIC_STORE_R4:
+#ifdef TARGET_RISCV64
+ case OP_ATOMIC_LOAD_R8:
+ case OP_ATOMIC_STORE_R8:
+#endif
+ return riscv_stdext_a && riscv_stdext_d;
+ default:
+ return FALSE;
+ }
+}
+
+const char *
+mono_arch_regname (int reg)
+{
+ static const char *names [RISCV_N_GREGS] = {
+ "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
+ "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5",
+ "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7",
+ "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6",
+ };
+
+ if (reg >= 0 && reg < G_N_ELEMENTS (names))
+ return names [reg];
+
+ return "x?";
+}
+
+const char*
+mono_arch_fregname (int reg)
+{
+ static const char *names [RISCV_N_FREGS] = {
+ "ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7",
+ "fs0", "fs1", "fa0", "fa1", "fa2", "fa3", "fa4", "fa5",
+ "fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7",
+ "fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11",
+ };
+
+ if (reg >= 0 && reg < G_N_ELEMENTS (names))
+ return names [reg];
+
+ return "f?";
+}
+
+gpointer
+mono_arch_get_this_arg_from_call (mgreg_t *regs, guint8 *code)
+{
+ return (gpointer) regs [RISCV_A0];
+}
+
+MonoMethod *
+mono_arch_find_imt_method (mgreg_t *regs, guint8 *code)
+{
+ return (MonoMethod *) regs [MONO_ARCH_IMT_REG];
+}
+
+MonoVTable *
+mono_arch_find_static_call_vtable (mgreg_t *regs, guint8 *code)
+{
+ return (MonoVTable *) regs [MONO_ARCH_VTABLE_REG];
+}
+
+mgreg_t
+mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
+{
+ return ctx->gregs [reg];
+}
+
+void
+mono_arch_context_set_int_reg (MonoContext *ctx, int reg, mgreg_t val)
+{
+ ctx->gregs [reg] = val;
+}
+
+void
+mono_arch_flush_register_windows (void)
+{
+}
+
+void
+mono_arch_flush_icache (guint8 *code, gint size)
+{
+#ifndef MONO_CROSS_COMPILE
+ __builtin___clear_cache (code, code + size);
+#endif
+}
+
+MonoDynCallInfo *
+mono_arch_dyn_call_prepare (MonoMethodSignature *sig)
+{
+ NOT_IMPLEMENTED;
+ return NULL;
+}
+
+void
+mono_arch_dyn_call_free (MonoDynCallInfo *info)
+{
+ NOT_IMPLEMENTED;
+}
+
+int
+mono_arch_dyn_call_get_buf_size (MonoDynCallInfo *info)
+{
+ NOT_IMPLEMENTED;
+ return 0;
+}
+
+void
+mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret,
+ guint8 *buf)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_finish_dyn_call (MonoDynCallInfo *info, guint8 *buf)
+{
+ NOT_IMPLEMENTED;
+}
+
+int
+mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count,
+ MonoJitArgumentInfo *arg_info)
+{
+ NOT_IMPLEMENTED;
+ return 0;
+}
+
+void
+mono_arch_patch_code_new (MonoCompile *cfg, MonoDomain *domain, guint8 *code,
+ MonoJumpInfo *ji, gpointer target)
+{
+ NOT_IMPLEMENTED;
+}
+
+/* Set arguments in the ccontext (for i2n entry) */
+void
+mono_arch_set_native_call_context_args (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig)
+{
+ NOT_IMPLEMENTED;
+}
+
+/* Set return value in the ccontext (for n2i return) */
+void
+mono_arch_set_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig)
+{
+ NOT_IMPLEMENTED;
+}
+
+/* Gets the arguments from ccontext (for n2i entry) */
+void
+mono_arch_get_native_call_context_args (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig)
+{
+ NOT_IMPLEMENTED;
+}
+
+/* Gets the return value from ccontext (for i2n exit) */
+void
+mono_arch_get_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig)
+{
+ NOT_IMPLEMENTED;
+}
+
+#ifndef DISABLE_JIT
+
+#ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
+
+gboolean
+mono_arch_is_soft_float (void)
+{
+ return !riscv_stdext_d;
+}
+
+#endif
+
+gboolean
+mono_arch_opcode_needs_emulation (MonoCompile *cfg, int opcode)
+{
+ switch (opcode) {
+ case OP_IDIV:
+ case OP_IDIV_UN:
+ case OP_IREM:
+ case OP_IREM_UN:
+#ifdef TARGET_RISCV64
+ case OP_LDIV:
+ case OP_LDIV_UN:
+ case OP_LREM:
+ case OP_LREM_UN:
+#endif
+ return !riscv_stdext_m;
+ default:
+ return TRUE;
+ }
+}
+
+gboolean
+mono_arch_tailcall_supported (MonoCompile *cfg, MonoMethodSignature *caller_sig, MonoMethodSignature *callee_sig, gboolean virtual_)
+{
+ NOT_IMPLEMENTED;
+}
+
+gboolean
+mono_arch_is_inst_imm (int opcode, int imm_opcode, gint64 imm)
+{
+ // TODO: Make a proper decision based on opcode.
+ return TRUE;
+}
+
+GList *
+mono_arch_get_allocatable_int_vars (MonoCompile *cfg)
+{
+ GList *vars = NULL;
+
+ for (guint i = 0; i < cfg->num_varinfo; i++) {
+ MonoInst *ins = cfg->varinfo [i];
+ MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
+
+ if (vmv->range.first_use.abs_pos >= vmv->range.last_use.abs_pos)
+ continue;
+
+ if ((ins->flags & (MONO_INST_IS_DEAD | MONO_INST_VOLATILE | MONO_INST_INDIRECT)) ||
+ (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG))
+ continue;
+
+ if (!mono_is_regsize_var (ins->inst_vtype))
+ continue;
+
+ vars = g_list_prepend (vars, vmv);
+ }
+
+ vars = mono_varlist_sort (cfg, vars, 0);
+
+ return vars;
+}
+
+GList *
+mono_arch_get_global_int_regs (MonoCompile *cfg)
+{
+ GList *regs = NULL;
+
+ for (int i = RISCV_S0; i <= RISCV_S11; i++)
+ regs = g_list_prepend (regs, GUINT_TO_POINTER (i));
+
+ return regs;
+}
+
+guint32
+mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
+{
+ return cfg->varinfo [vmv->idx]->opcode == OP_ARG ? 1 : 2;
+}
+
+#ifdef ENABLE_LLVM
+
+LLVMCallInfo*
+mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
+{
+ NOT_IMPLEMENTED;
+}
+
+#endif
+
+void
+mono_arch_create_vars (MonoCompile *cfg)
+{
+ NOT_IMPLEMENTED;
+}
+
+MonoInst *
+mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod,
+ MonoMethodSignature *fsig, MonoInst **args)
+{
+ return NULL;
+}
+
+void
+mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_decompose_opts (MonoCompile *cfg, MonoInst *ins)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *long_ins)
+{
+#ifdef TARGET_RISCV32
+ NOT_IMPLEMENTED;
+#endif
+}
+
+void
+mono_arch_allocate_vars (MonoCompile *cfg)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+}
+
+void
+mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+}
+
+// Uses at most 8 bytes on RV32I and 16 bytes on RV64I.
+guint8 *
+mono_riscv_emit_imm (guint8 *code, int rd, gsize imm)
+{
+#ifdef TARGET_RISCV64
+ if (RISCV_VALID_I_IMM (imm)) {
+ riscv_addi (code, rd, RISCV_ZERO, imm);
+ return code;
+ }
+
+ /*
+ * This is not pretty, but RV64I doesn't make it easy to load constants.
+ * Need to figure out something better.
+ */
+ riscv_jal (code, rd, sizeof (guint64));
+ *(guint64 *) code = imm;
+ code += sizeof (guint64);
+ riscv_ld (code, rd, rd, 0);
+#else
+ if (RISCV_VALID_I_IMM (imm)) {
+ riscv_addi (code, rd, RISCV_ZERO, imm);
+ return code;
+ }
+
+ riscv_lui (code, rd, RISCV_BITS (imm, 12, 20));
+
+ if (!RISCV_VALID_U_IMM (imm))
+ riscv_ori (code, rd, rd, RISCV_BITS (imm, 0, 12));
+#endif
+
+ return code;
+}
+
+// Uses at most 16 bytes on RV32I and 24 bytes on RV64I.
+guint8 *
+mono_riscv_emit_load (guint8 *code, int rd, int rs1, gint32 imm)
+{
+ if (RISCV_VALID_I_IMM (imm)) {
+#ifdef TARGET_RISCV64
+ riscv_ld (code, rd, rs1, imm);
+#else
+ riscv_lw (code, rd, rs1, imm);
+#endif
+ } else {
+ code = mono_riscv_emit_imm (code, rd, imm);
+ riscv_add (code, rd, rs1, rd);
+#ifdef TARGET_RISCV64
+ riscv_ld (code, rd, rd, 0);
+#else
+ riscv_lw (code, rd, rd, 0);
+#endif
+ }
+
+ return code;
+}
+
+// May clobber t1. Uses at most 16 bytes on RV32I and 24 bytes on RV64I.
+guint8 *
+mono_riscv_emit_store (guint8 *code, int rs1, int rs2, gint32 imm)
+{
+ if (RISCV_VALID_S_IMM (imm)) {
+#ifdef TARGET_RISCV64
+ riscv_sd (code, rs1, rs2, imm);
+#else
+ riscv_sw (code, rs1, rs2, imm);
+#endif
+ } else {
+ code = mono_riscv_emit_imm (code, RISCV_T1, imm);
+ riscv_add (code, RISCV_T1, rs2, RISCV_T1);
+#ifdef TARGET_RISCV64
+ riscv_sd (code, rs1, RISCV_T1, 0);
+#else
+ riscv_sw (code, rs1, RISCV_T1, 0);
+#endif
+ }
+
+ return code;
+}
+
+guint8 *
+mono_arch_emit_prolog (MonoCompile *cfg)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_emit_epilog (MonoCompile *cfg)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_emit_exceptions (MonoCompile *cfg)
+{
+ NOT_IMPLEMENTED;
+}
+
+guint32
+mono_arch_get_patch_offset (guint8 *code)
+{
+ NOT_IMPLEMENTED;
+ return 0;
+}
+
+GSList *
+mono_arch_get_trampolines (gboolean aot)
+{
+ NOT_IMPLEMENTED;
+ return NULL;
+}
+
+#endif
+
+#if defined(MONO_ARCH_SOFT_DEBUG_SUPPORTED)
+void
+mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_start_single_stepping (void)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_stop_single_stepping (void)
+{
+ NOT_IMPLEMENTED;
+}
+
+gboolean
+mono_arch_is_single_step_event (void *info, void *sigctx)
+{
+ NOT_IMPLEMENTED;
+ return FALSE;
+}
+
+gboolean
+mono_arch_is_breakpoint_event (void *info, void *sigctx)
+{
+ NOT_IMPLEMENTED;
+ return FALSE;
+}
+
+void
+mono_arch_skip_breakpoint (MonoContext *ctx, MonoJitInfo *ji)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_skip_single_step (MonoContext *ctx)
+{
+ NOT_IMPLEMENTED;
+}
+
+gpointer
+mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code)
+{
+ NOT_IMPLEMENTED;
+ return NULL;
+}
+#endif /* MONO_ARCH_SOFT_DEBUG_SUPPORTED */
diff --git a/mono/mini/mini-riscv.h b/mono/mini/mini-riscv.h
new file mode 100644
index 00000000000..eb431743a8e
--- /dev/null
+++ b/mono/mini/mini-riscv.h
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the .NET Foundation under one or more agreements.
+ * The .NET Foundation licenses this file to you under the MIT license.
+ * See the LICENSE file in the project root for more information.
+ */
+
+#ifndef __MONO_MINI_RISCV_H__
+#define __MONO_MINI_RISCV_H__
+
+#include <mono/arch/riscv/riscv-codegen.h>
+
+#ifdef TARGET_RISCV64
+#define MONO_RISCV_ARCHITECTURE "riscv64"
+#else
+#define MONO_RISCV_ARCHITECTURE "riscv32"
+#endif
+
+#if defined (RISCV_FPABI_SOFT)
+#define MONO_ARCH_SOFT_FLOAT_FALLBACK
+#define RISCV_FP_MODEL "soft-fp"
+#elif defined (RISCV_FPABI_DOUBLE)
+#define RISCV_FP_MODEL "double-fp"
+#elif defined (RISCV_FPABI_SINGLE)
+#define RISCV_FP_MODEL "single-fp"
+#error "The single-precision RISC-V hard float ABI is not currently supported."
+#else
+#error "Unknown RISC-V FPABI. This is probably a bug in configure.ac."
+#endif
+
+#define MONO_ARCH_ARCHITECTURE MONO_RISCV_ARCHITECTURE "," RISCV_FP_MODEL
+
+#ifdef TARGET_RISCV64
+#define MONO_ARCH_CPU_SPEC mono_riscv64_cpu_desc
+#else
+#define MONO_ARCH_CPU_SPEC mono_riscv32_cpu_desc
+#endif
+
+#define MONO_MAX_IREGS (RISCV_N_GREGS)
+#define MONO_MAX_FREGS (RISCV_N_FREGS)
+
+/*
+ * Register usage conventions:
+ *
+ * - a0..a7 and fa0..fa7 are argument/return registers.
+ * - s0..11 and fs0..fs11 are callee-saved registers.
+ * - a0..a1 are used as fixed registers (for the 'a' spec, soft float, and
+ * longs on 32-bit, as appropriate).
+ * - t0..t1, ra, and ft0..ft2 are used as scratch registers and can't be
+ * allocated by the register allocator.
+ * - t2 is used as the RGCTX/IMT register and can't be allocated by the
+ * register allocator.
+ * - a0 is used as the VTable register for lazy fetch trampolines.
+ * - sp, fp, gp, and tp are all reserved by the ABI and can't be allocated by
+ * the register allocator.
+ * - x0 is hard-wired to zero and can't be allocated by the register allocator.
+ */
+
+#define MONO_ARCH_CALLEE_REGS (0b11110000000000111111110000000000)
+#define MONO_ARCH_CALLEE_SAVED_REGS (0b00001111111111000000001100000000)
+
+#ifdef RISCV_FPABI_SOFT
+
+#define MONO_ARCH_CALLEE_FREGS (0b11111111111111111111111111111000)
+#define MONO_ARCH_CALLEE_SAVED_FREGS (0b00000000000000000000000000000000)
+
+#else
+
+#define MONO_ARCH_CALLEE_FREGS (0b11110000000000111111110011111000)
+#define MONO_ARCH_CALLEE_SAVED_FREGS (0b00001111111111000000001100000000)
+
+#endif
+
+#define MONO_ARCH_INST_SREG2_MASK(ins) \
+ (0)
+#define MONO_ARCH_INST_IS_FLOAT(desc) \
+ (!mono_arch_is_soft_float () && (desc) == 'f')
+
+#ifdef TARGET_RISCV64
+
+#define MONO_ARCH_INST_FIXED_REG(desc) \
+ ((desc) == 'a' || (mono_arch_is_soft_float () && (desc) == 'f') ? RISCV_A0 : -1)
+#define MONO_ARCH_INST_IS_REGPAIR(desc) \
+ (FALSE)
+#define MONO_ARCH_INST_REGPAIR_REG2(desc, hreg1) \
+ (-1)
+
+#else
+
+#define MONO_ARCH_INST_FIXED_REG(desc) \
+ ((desc) == 'a' || (desc) == 'l' || (mono_arch_is_soft_float () && (desc) == 'f') ? RISCV_A0 : -1)
+#define MONO_ARCH_INST_IS_REGPAIR(desc) \
+ ((desc) == 'l' || (mono_arch_is_soft_float () && (desc) == 'f'))
+#define MONO_ARCH_INST_REGPAIR_REG2(desc, hreg1) \
+ (RISCV_A1)
+
+#endif
+
+#define MONO_ARCH_RGCTX_REG (RISCV_T2)
+#define MONO_ARCH_IMT_REG (RISCV_T2)
+#define MONO_ARCH_VTABLE_REG (RISCV_A0)
+
+#define MONO_ARCH_HAVE_VOLATILE_NON_PARAM_REGISTER 0
+
+#define MONO_ARCH_USE_FPSTACK (FALSE)
+
+#define MONO_ARCH_FRAME_ALIGNMENT (16)
+#define MONO_ARCH_CODE_ALIGNMENT (32)
+
+#define MONO_ARCH_EMULATE_MUL_DIV (1)
+#define MONO_ARCH_EMULATE_FREM (1)
+
+#ifdef TARGET_RISCV64
+
+#define MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS (1)
+
+#endif
+
+#define MONO_ARCH_EMULATE_CONV_R8_UN (1)
+#define MONO_ARCH_EMULATE_FCONV_TO_I8 (1)
+#define MONO_ARCH_EMULATE_LCONV_TO_R8 (1)
+#define MONO_ARCH_EMULATE_LCONV_TO_R4 (1)
+#define MONO_ARCH_EMULATE_LCONV_TO_R8_UN (1)
+
+#define MONO_ARCH_NEED_DIV_CHECK (1)
+
+#define MONO_ARCH_HAVE_OP_TAIL_CALL (1)
+#define MONO_ARCH_HAVE_OP_GENERIC_CLASS_INIT (1)
+#define MONO_ARCH_HAVE_CARD_TABLE_WBARRIER (1)
+
+#define MONO_ARCH_HAVE_GENERALIZED_IMT_TRAMPOLINE (1)
+#define MONO_ARCH_HAVE_GENERAL_RGCTX_LAZY_FETCH_TRAMPOLINE (1)
+#define MONO_ARCH_HAVE_SDB_TRAMPOLINES (1)
+#define MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP (1)
+#define MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES (1)
+
+#define MONO_ARCH_USE_SIGACTION (1)
+#define MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX (1)
+
+#define MONO_ARCH_HAVE_CONTEXT_SET_INT_REG (1)
+#define MONO_ARCH_HAVE_DECOMPOSE_LONG_OPTS (1)
+#define MONO_ARCH_HAVE_DECOMPOSE_OPTS (1)
+#define MONO_ARCH_HAVE_EXCEPTIONS_INIT (1)
+#define MONO_ARCH_HAVE_GET_TRAMPOLINES (1)
+#define MONO_ARCH_HAVE_OPCODE_NEEDS_EMULATION (1)
+#define MONO_ARCH_HAVE_PATCH_CODE_NEW (1)
+#define MONO_ARCH_HAVE_SETUP_ASYNC_CALLBACK (1)
+#define MONO_ARCH_HAVE_SETUP_RESUME_FROM_SIGNAL_HANDLER_CTX (1)
+
+#define MONO_ARCH_GSHARED_SUPPORTED (1)
+#define MONO_ARCH_INTERPRETER_SUPPORTED (1)
+//#define MONO_ARCH_AOT_SUPPORTED (1)
+//#define MONO_ARCH_LLVM_SUPPORTED (1)
+//#define MONO_ARCH_SOFT_DEBUG_SUPPORTED (1)
+
+
+// #define MONO_ARCH_HAVE_INTERP_ENTRY_TRAMPOLINE (1)
+// #define MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP (1)
+// #define MONO_ARCH_HAVE_INTERP_NATIVE_TO_MANAGED (1)
+
+typedef struct {
+} MonoCompileArch;
+
+#define MONO_CONTEXT_SET_LLVM_EXC_REG(ctx, exc) \
+ do { \
+ (ctx)->gregs [RISCV_A0] = (mgreg_t) exc; \
+ } while (0)
+
+#define MONO_INIT_CONTEXT_FROM_FUNC(ctx, func) \
+ do { \
+ MONO_CONTEXT_SET_IP ((ctx), (func)); \
+ MONO_CONTEXT_SET_SP ((ctx), __builtin_frame_address (0)); \
+ MONO_CONTEXT_SET_BP ((ctx), __builtin_frame_address (0)); \
+ } while (0)
+
+struct MonoLMF {
+ // If the second-lowest bit of this field is set, this is a MonoLMFExt.
+ gpointer previous_lmf;
+ gpointer lmf_addr;
+ mgreg_t pc;
+ mgreg_t sp;
+ mgreg_t ra;
+ mgreg_t gregs [RISCV_N_GSREGS]; // s0..s11
+ double fregs [RISCV_N_FSREGS]; // fs0..fs11
+};
+
+#define MONO_ARCH_INIT_TOP_LMF_ENTRY(lmf)
+
+typedef struct {
+} CallContext;
+
+enum {
+ MONO_R_RISCV_IMM = 1,
+ MONO_R_RISCV_B = 2,
+ MONO_R_RISCV_BEQ = 3,
+ MONO_R_RISCV_BNE = 4,
+ MONO_R_RISCV_BLT = 5,
+ MONO_R_RISCV_BGE = 6,
+ MONO_R_RISCV_BLTU = 7,
+ MONO_R_RISCV_BGEU = 8,
+};
+
+__attribute__ ((warn_unused_result)) guint8 *
+mono_riscv_emit_imm (guint8 *code, int rd, gsize imm);
+
+__attribute__ ((warn_unused_result)) guint8 *
+mono_riscv_emit_load (guint8 *code, int rd, int rs1, gint32 imm);
+
+__attribute__ ((warn_unused_result)) guint8 *
+mono_riscv_emit_store (guint8 *code, int rs1, int rs2, gint32 imm);
+
+#endif
diff --git a/mono/mini/tramp-riscv.c b/mono/mini/tramp-riscv.c
new file mode 100644
index 00000000000..f6f2f421d0f
--- /dev/null
+++ b/mono/mini/tramp-riscv.c
@@ -0,0 +1,303 @@
+/*
+ * Licensed to the .NET Foundation under one or more agreements.
+ * The .NET Foundation licenses this file to you under the MIT license.
+ * See the LICENSE file in the project root for more information.
+ */
+
+#include "mini-runtime.h"
+
+#include <mono/metadata/abi-details.h>
+
+void
+mono_arch_patch_callsite (guint8 *method_start, guint8 *code_ptr, guint8 *addr)
+{
+ NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
+{
+ NOT_IMPLEMENTED;
+}
+
+guint8 *
+mono_arch_get_call_target (guint8 *code)
+{
+ NOT_IMPLEMENTED;
+ return NULL;
+}
+
+guint32
+mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code)
+{
+ NOT_IMPLEMENTED;
+ return 0;
+}
+
+GSList *
+mono_arch_get_delegate_invoke_impls (void)
+{
+ NOT_IMPLEMENTED;
+ return NULL;
+}
+
+gpointer
+mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target)
+{
+ NOT_IMPLEMENTED;
+ return NULL;
+}
+
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig,
+ MonoMethod *method, int offset,
+ gboolean load_imt_reg)
+{
+ NOT_IMPLEMENTED;
+ return NULL;
+}
+
+#ifndef DISABLE_JIT
+
+guchar *
+mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info,
+ gboolean aot)
+{
+ if (aot)
+ NOT_IMPLEMENTED;
+
+ guint8 *buf = mono_global_codeman_reserve (1024), *code = buf;
+
+ if (info) {
+ char *name = mono_get_generic_trampoline_name (tramp_type);
+ *info = mono_tramp_info_create (name, buf, code - buf, NULL, NULL);
+ g_free (name);
+ }
+
+ return buf;
+}
+
+gpointer
+mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type,
+ MonoDomain *domain, guint32 *code_len)
+{
+ guint8 *buf = mono_domain_code_reserve (domain, 64), *code = buf;
+ guint8 *tramp = mono_get_trampoline_code (tramp_type);
+
+ // Pass the argument in scratch t0.
+ code = mono_riscv_emit_imm (code, RISCV_T0, (gsize) arg1);
+ code = mono_riscv_emit_imm (code, RISCV_T1, (gsize) tramp);
+ riscv_jalr (code, RISCV_ZERO, RISCV_T1, 0);
+
+ mono_arch_flush_icache (buf, code - buf);
+
+ if (code_len)
+ *code_len = code - buf;
+
+ return buf;
+}
+
+gpointer
+mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
+{
+ guint8 *buf = mono_domain_code_reserve (mono_domain_get (), 64), *code = buf;
+
+ // Pass the argument in a0.
+ code = mono_riscv_emit_imm (code, RISCV_A0, sizeof (MonoObject));
+ code = mono_riscv_emit_imm (code, RISCV_T0, (gsize) addr);
+ riscv_jalr (code, RISCV_ZERO, RISCV_T0, 0);
+
+ mono_arch_flush_icache (buf, code - buf);
+
+ return buf;
+}
+
+gpointer
+mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoDomain *domain,
+ MonoIMTCheckItem **imt_entries, int count,
+ gpointer fail_tramp)
+{
+ NOT_IMPLEMENTED;
+ return NULL;
+}
+
+gpointer
+mono_arch_get_static_rgctx_trampoline (gpointer arg, gpointer addr)
+{
+ guint8 *buf = mono_domain_code_reserve (mono_domain_get (), 64), *code = buf;
+
+ // Pass the argument in the RGCTX register.
+ code = mono_riscv_emit_imm (code, MONO_ARCH_RGCTX_REG, (gsize) arg);
+ code = mono_riscv_emit_imm (code, RISCV_T0, (gsize) addr);
+ riscv_jalr (code, RISCV_ZERO, RISCV_T0, 0);
+
+ mono_arch_flush_icache (buf, code - buf);
+
+ return buf;
+}
+
+gpointer
+mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info,
+ gboolean aot)
+{
+ if (aot)
+ NOT_IMPLEMENTED;
+
+ gboolean is_mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
+ int index = MONO_RGCTX_SLOT_INDEX (slot);
+
+ if (is_mrgctx)
+ index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
+
+ int depth;
+
+ for (depth = 0; ; depth++) {
+ int size = mono_class_rgctx_get_array_size (depth, is_mrgctx);
+
+ if (index < size - 1)
+ break;
+
+ index -= size - 1;
+ }
+
+ guint8 *buf = mono_global_codeman_reserve (128 * depth), *code = buf;
+ guint8 **null_jumps = g_malloc0 (sizeof (guint8 *) * (depth + 2));
+
+ if (!is_mrgctx) {
+ } else
+ riscv_addi (code, RISCV_T1, RISCV_A0, 0);
+
+ mono_arch_flush_icache (buf, code - buf);
+
+ if (info) {
+ char *name = mono_get_rgctx_fetch_trampoline_name (slot);
+ *info = mono_tramp_info_create (name, buf, code - buf, NULL, NULL);
+ g_free (name);
+ }
+
+ return buf;
+}
+
+gpointer
+mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboolean aot)
+{
+ if (aot)
+ NOT_IMPLEMENTED;
+
+ guint8 *buf = mono_global_codeman_reserve (64), *code = buf;
+
+ /*
+ * The RGCTX register holds a pointer to a <slot, trampoline address> pair.
+ * Load the trampoline address and branch to it. a0 holds the actual
+ * (M)RGCTX or VTable.
+ */
+ code = mono_riscv_emit_load (code, RISCV_T0, MONO_ARCH_RGCTX_REG, sizeof (gpointer));
+ riscv_jalr (code, RISCV_ZERO, RISCV_T0, 0);
+
+ mono_arch_flush_icache (buf, code - buf);
+
+ if (info)
+ *info = mono_tramp_info_create ("rgctx_fetch_trampoline_general", buf, code - buf, NULL, NULL);
+
+ return buf;
+}
+
+guint8 *
+mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot)
+{
+ NOT_IMPLEMENTED;
+ return NULL;
+}
+
+gpointer
+mono_arch_get_interp_to_native_trampoline (MonoTrampInfo **info)
+{
+ NOT_IMPLEMENTED;
+ return NULL;
+}
+
+gpointer
+mono_arch_get_native_to_interp_trampoline (MonoTrampInfo **info)
+{
+ NOT_IMPLEMENTED;
+ return NULL;
+}
+
+#else
+
+guchar *
+mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info,
+ gboolean aot)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type,
+ MonoDomain *domain, guint32 *code_len)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoDomain *domain,
+ MonoIMTCheckItem **imt_entries, int count,
+ gpointer fail_tramp)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_arch_get_static_rgctx_trampoline (gpointer arg, gpointer addr)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info,
+ gboolean aot)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboolean aot)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+guint8 *
+mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_arch_get_interp_to_native_trampoline (MonoTrampInfo **info)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_arch_get_native_to_interp_trampoline (MonoTrampInfo **info)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+#endif
diff --git a/mono/mini/unwind.c b/mono/mini/unwind.c
index 263cba2505c..03f88149f31 100644
--- a/mono/mini/unwind.c
+++ b/mono/mini/unwind.c
@@ -113,6 +113,26 @@ static int map_hw_reg_to_dwarf_reg [32] = {
#define NUM_DWARF_REGS 32
#define DWARF_DATA_ALIGN (-(gint32)sizeof (mgreg_t))
#define DWARF_PC_REG (mono_hw_reg_to_dwarf_reg (mips_ra))
+#elif defined(TARGET_RISCV)
+
+/*
+ * These values have not currently been formalized in the RISC-V psABI. See
+ * instead gcc/config/riscv/riscv.h in the GCC source tree.
+ */
+
+#define NUM_DWARF_REGS (RISCV_N_GREGS + RISCV_N_FREGS)
+#define DWARF_DATA_ALIGN (-4)
+#define DWARF_PC_REG (mono_hw_reg_to_dwarf_reg (RISCV_RA))
+
+static int map_hw_reg_to_dwarf_reg [NUM_DWARF_REGS] = {
+ // x0..x31
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ // f0..f31
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+};
+
#else
static int map_hw_reg_to_dwarf_reg [16];
#define NUM_DWARF_REGS 16
diff --git a/mono/sgen/sgen-archdep.h b/mono/sgen/sgen-archdep.h
index 9a3ad015704..008c0943884 100644
--- a/mono/sgen/sgen-archdep.h
+++ b/mono/sgen/sgen-archdep.h
@@ -65,6 +65,10 @@
#define REDZONE_SIZE 0
+#elif defined (TARGET_RISCV)
+
+#define REDZONE_SIZE (0)
+
#elif defined (TARGET_WASM)
#define REDZONE_SIZE 0
diff --git a/mono/sgen/sgen-cardtable.c b/mono/sgen/sgen-cardtable.c
index 4c8a94fe1a3..f03da24ab0a 100644
--- a/mono/sgen/sgen-cardtable.c
+++ b/mono/sgen/sgen-cardtable.c
@@ -524,9 +524,9 @@ static inline int
find_card_offset (mword card)
{
/*XXX Use assembly as this generates some pretty bad code */
-#if (defined(__i386__) || defined(__arm__)) && defined(__GNUC__)
+#if (defined(__i386__) || defined(__arm__) || (defined (__riscv) && __riscv_xlen == 32)) && defined(__GNUC__)
return (__builtin_ffs (card) - 1) / 8;
-#elif (defined(__x86_64__) || defined(__aarch64__)) && defined(__GNUC__)
+#elif (defined(__x86_64__) || defined(__aarch64__) || (defined (__riscv) && __riscv_xlen == 64)) && defined(__GNUC__)
return (__builtin_ffsll (card) - 1) / 8;
#elif defined(__s390x__)
return (__builtin_ffsll (GUINT64_TO_LE(card)) - 1) / 8;
diff --git a/mono/utils/Makefile.am b/mono/utils/Makefile.am
index d5ed57941ad..b44d399ba44 100644
--- a/mono/utils/Makefile.am
+++ b/mono/utils/Makefile.am
@@ -292,6 +292,10 @@ if S390X
arch_sources += mono-hwcap-s390x.c
endif
+if RISCV
+arch_sources += mono-hwcap-riscv.c
+endif
+
if HOST_WASM
arch_sources += mono-hwcap-wasm.c
endif
diff --git a/mono/utils/mono-context.c b/mono/utils/mono-context.c
index 17982731a01..706dab8e2b2 100644
--- a/mono/utils/mono-context.c
+++ b/mono/utils/mono-context.c
@@ -566,4 +566,34 @@ mono_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
g_error ("MonoContext not supported");
}
+#elif ((defined (HOST_RISCV) || defined (HOST_RISCV64)) && !defined (MONO_CROSS_COMPILE)) || defined (TARGET_RISCV)
+
+#include <mono/utils/mono-context.h>
+
+void
+mono_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
+{
+#ifdef MONO_CROSS_COMPILE
+ g_assert_not_reached ();
+#else
+ ucontext_t *uctx = sigctx;
+
+ memcpy (&mctx->gregs, &uctx->uc_mcontext.gregs, sizeof (mgreg_t) * G_N_ELEMENTS (mctx->gregs));
+ memcpy (&mctx->fregs, &uctx->uc_mcontext.fpregs, sizeof (double) * G_N_ELEMENTS (mctx->fregs));
+#endif
+}
+
+void
+mono_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
+{
+#ifdef MONO_CROSS_COMPILE
+ g_assert_not_reached ();
+#else
+ ucontext_t *uctx = sigctx;
+
+ memcpy (&uctx->uc_mcontext.gregs, &mctx->gregs, sizeof (mgreg_t) * G_N_ELEMENTS (mctx->gregs));
+ memcpy (&uctx->uc_mcontext.fpregs, &mctx->fregs, sizeof (double) * G_N_ELEMENTS (mctx->fregs));
+#endif
+}
+
#endif /* #if defined(__i386__) */
diff --git a/mono/utils/mono-context.h b/mono/utils/mono-context.h
index 9bfb607565d..59e84aafbfd 100644
--- a/mono/utils/mono-context.h
+++ b/mono/utils/mono-context.h
@@ -892,6 +892,119 @@ typedef struct ucontext MonoContext;
: "memory" \
)
+#elif (defined (HOST_RISCV) && !defined (MONO_CROSS_COMPILE)) || defined (TARGET_RISCV)
+
+#include <mono/arch/riscv/riscv-codegen.h>
+
+typedef struct {
+ mgreg_t gregs [RISCV_N_GREGS]; // [0] contains pc since x0 is hard-wired to zero anyway.
+ double fregs [RISCV_N_FREGS * 2 + 2]; // [32] contains fcsr (32 bits), the rest is for quad-precision values (currently unused).
+} MonoContext;
+
+#define MONO_CONTEXT_SET_IP(ctx, ip) do { (ctx)->gregs [RISCV_ZERO] = (mgreg_t) (ip); } while (0)
+#define MONO_CONTEXT_SET_BP(ctx, bp) do { (ctx)->gregs [RISCV_FP] = (mgreg_t) (bp); } while (0)
+#define MONO_CONTEXT_SET_SP(ctx, sp) do { (ctx)->gregs [RISCV_SP] = (mgreg_t) (sp); } while (0)
+
+#define MONO_CONTEXT_GET_IP(ctx) ((gpointer) ((ctx)->gregs [RISCV_ZERO]))
+#define MONO_CONTEXT_GET_BP(ctx) ((gpointer) ((ctx)->gregs [RISCV_FP]))
+#define MONO_CONTEXT_GET_SP(ctx) ((gpointer) ((ctx)->gregs [RISCV_SP]))
+
+#ifdef TARGET_RISCV64
+#define _RISCV_STR "sd"
+#define _RISCV_SZ "8"
+#else
+#define _RISCV_STR "sw"
+#define _RISCV_SZ "4"
+#endif
+
+#define MONO_CONTEXT_GET_CURRENT(ctx) \
+ do { \
+ __asm__ __volatile__ ( \
+ _RISCV_STR " x1, " _RISCV_SZ "*1(%0)\n" \
+ _RISCV_STR " x2, " _RISCV_SZ "*2(%0)\n" \
+ _RISCV_STR " x3, " _RISCV_SZ "*3(%0)\n" \
+ _RISCV_STR " x4, " _RISCV_SZ "*4(%0)\n" \
+ _RISCV_STR " x5, " _RISCV_SZ "*5(%0)\n" \
+ _RISCV_STR " x6, " _RISCV_SZ "*6(%0)\n" \
+ _RISCV_STR " x7, " _RISCV_SZ "*7(%0)\n" \
+ _RISCV_STR " x8, " _RISCV_SZ "*8(%0)\n" \
+ _RISCV_STR " x9, " _RISCV_SZ "*9(%0)\n" \
+ _RISCV_STR " x10, " _RISCV_SZ "*10(%0)\n" \
+ _RISCV_STR " x11, " _RISCV_SZ "*11(%0)\n" \
+ _RISCV_STR " x12, " _RISCV_SZ "*12(%0)\n" \
+ _RISCV_STR " x13, " _RISCV_SZ "*13(%0)\n" \
+ _RISCV_STR " x14, " _RISCV_SZ "*14(%0)\n" \
+ _RISCV_STR " x15, " _RISCV_SZ "*15(%0)\n" \
+ _RISCV_STR " x16, " _RISCV_SZ "*16(%0)\n" \
+ _RISCV_STR " x17, " _RISCV_SZ "*17(%0)\n" \
+ _RISCV_STR " x18, " _RISCV_SZ "*18(%0)\n" \
+ _RISCV_STR " x19, " _RISCV_SZ "*19(%0)\n" \
+ _RISCV_STR " x20, " _RISCV_SZ "*20(%0)\n" \
+ _RISCV_STR " x21, " _RISCV_SZ "*21(%0)\n" \
+ _RISCV_STR " x22, " _RISCV_SZ "*22(%0)\n" \
+ _RISCV_STR " x23, " _RISCV_SZ "*23(%0)\n" \
+ _RISCV_STR " x24, " _RISCV_SZ "*24(%0)\n" \
+ _RISCV_STR " x25, " _RISCV_SZ "*25(%0)\n" \
+ _RISCV_STR " x26, " _RISCV_SZ "*26(%0)\n" \
+ _RISCV_STR " x27, " _RISCV_SZ "*27(%0)\n" \
+ _RISCV_STR " x28, " _RISCV_SZ "*28(%0)\n" \
+ _RISCV_STR " x29, " _RISCV_SZ "*29(%0)\n" \
+ _RISCV_STR " x30, " _RISCV_SZ "*30(%0)\n" \
+ _RISCV_STR " x31, " _RISCV_SZ "*31(%0)\n" \
+ : \
+ : "r" (&(ctx).gregs) \
+ : "memory" \
+ ); \
+ __asm__ __volatile__ ( \
+ "frcsr t0\n" \
+ "fsd f0, 8*0(%0)\n" \
+ "fsd f1, 8*1(%0)\n" \
+ "fsd f2, 8*2(%0)\n" \
+ "fsd f3, 8*3(%0)\n" \
+ "fsd f4, 8*4(%0)\n" \
+ "fsd f5, 8*5(%0)\n" \
+ "fsd f6, 8*6(%0)\n" \
+ "fsd f7, 8*7(%0)\n" \
+ "fsd f8, 8*8(%0)\n" \
+ "fsd f9, 8*9(%0)\n" \
+ "fsd f10, 8*10(%0)\n" \
+ "fsd f11, 8*11(%0)\n" \
+ "fsd f12, 8*12(%0)\n" \
+ "fsd f13, 8*13(%0)\n" \
+ "fsd f14, 8*14(%0)\n" \
+ "fsd f15, 8*15(%0)\n" \
+ "fsd f16, 8*16(%0)\n" \
+ "fsd f17, 8*17(%0)\n" \
+ "fsd f18, 8*18(%0)\n" \
+ "fsd f19, 8*19(%0)\n" \
+ "fsd f20, 8*20(%0)\n" \
+ "fsd f21, 8*21(%0)\n" \
+ "fsd f22, 8*22(%0)\n" \
+ "fsd f23, 8*23(%0)\n" \
+ "fsd f24, 8*24(%0)\n" \
+ "fsd f25, 8*25(%0)\n" \
+ "fsd f26, 8*26(%0)\n" \
+ "fsd f27, 8*27(%0)\n" \
+ "fsd f28, 8*28(%0)\n" \
+ "fsd f29, 8*29(%0)\n" \
+ "fsd f30, 8*30(%0)\n" \
+ "fsd f31, 8*31(%0)\n" \
+ "sw t0, 8*32(%0)\n" \
+ : \
+ : "r" (&(ctx).fregs) \
+ : "t0", "memory" \
+ ); \
+ __asm__ __volatile__ ( \
+ "auipc t0, 0\n" \
+ _RISCV_STR " t0, (%0)\n" \
+ : \
+ : "r" (&(ctx).gregs [0]) \
+ : "t0", "memory" \
+ ); \
+ } while (0)
+
+#define MONO_ARCH_HAS_MONO_CONTEXT (1)
+
#else
#error "Implement mono-context for the current arch"
diff --git a/mono/utils/mono-hwcap-riscv.c b/mono/utils/mono-hwcap-riscv.c
new file mode 100644
index 00000000000..b2f5dbcfe40
--- /dev/null
+++ b/mono/utils/mono-hwcap-riscv.c
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the .NET Foundation under one or more agreements.
+ * The .NET Foundation licenses this file to you under the MIT license.
+ * See the LICENSE file in the project root for more information.
+ */
+
+#include <mono/utils/mono-hwcap.h>
+
+#include <sys/auxv.h>
+
+void
+mono_hwcap_arch_init (void)
+{
+ // See arch/riscv/include/uapi/asm/hwcap.h in the kernel source tree.
+
+ unsigned long hwcap;
+
+ if ((hwcap = getauxval (AT_HWCAP))) {
+ // COMPAT_HWCAP_ISA_A
+ if (hwcap & (1 << ('A' - 'A')))
+ mono_hwcap_riscv_has_stdext_a = TRUE;
+
+ // COMPAT_HWCAP_ISA_C
+ if (hwcap & (1 << ('C' - 'A')))
+ mono_hwcap_riscv_has_stdext_c = TRUE;
+
+ // COMPAT_HWCAP_ISA_D
+ if (hwcap & (1 << ('D' - 'A')))
+ mono_hwcap_riscv_has_stdext_d = TRUE;
+
+ // COMPAT_HWCAP_ISA_F
+ if (hwcap & (1 << ('F' - 'A')))
+ mono_hwcap_riscv_has_stdext_f = TRUE;
+
+ // Why does COMPAT_HWCAP_ISA_I even exist...?
+
+ // COMPAT_HWCAP_ISA_M
+ if (hwcap & (1 << ('M' - 'A')))
+ mono_hwcap_riscv_has_stdext_m = TRUE;
+ }
+}
diff --git a/mono/utils/mono-hwcap-vars.h b/mono/utils/mono-hwcap-vars.h
index 05f89a34ef9..5fefe5ca5e4 100644
--- a/mono/utils/mono-hwcap-vars.h
+++ b/mono/utils/mono-hwcap-vars.h
@@ -31,6 +31,22 @@ MONO_HWCAP_VAR(ppc_is_isa_64)
MONO_HWCAP_VAR(ppc_has_move_fpr_gpr)
MONO_HWCAP_VAR(ppc_has_multiple_ls_units)
+#elif defined (TARGET_RISCV)
+
+MONO_HWCAP_VAR(riscv_has_stdext_a)
+MONO_HWCAP_VAR(riscv_has_stdext_b)
+MONO_HWCAP_VAR(riscv_has_stdext_c)
+MONO_HWCAP_VAR(riscv_has_stdext_d)
+MONO_HWCAP_VAR(riscv_has_stdext_f)
+MONO_HWCAP_VAR(riscv_has_stdext_j)
+MONO_HWCAP_VAR(riscv_has_stdext_l)
+MONO_HWCAP_VAR(riscv_has_stdext_m)
+MONO_HWCAP_VAR(riscv_has_stdext_n)
+MONO_HWCAP_VAR(riscv_has_stdext_p)
+MONO_HWCAP_VAR(riscv_has_stdext_q)
+MONO_HWCAP_VAR(riscv_has_stdext_t)
+MONO_HWCAP_VAR(riscv_has_stdext_v)
+
#elif defined (TARGET_S390X)
MONO_HWCAP_VAR(s390x_has_fpe)
diff --git a/mono/utils/mono-sigcontext.h b/mono/utils/mono-sigcontext.h
index 04eab4ba910..f21598d6f03 100644
--- a/mono/utils/mono-sigcontext.h
+++ b/mono/utils/mono-sigcontext.h
@@ -533,4 +533,26 @@ typedef struct ucontext
# define UCONTEXT_GREGS(ctx) (((ucontext_t *)(ctx))->uc_mcontext.gregs)
#endif
+#elif defined (TARGET_RISCV)
+
+#if defined(MONO_CROSS_COMPILE)
+
+#define UCONTEXT_GREGS(ctx) (NULL)
+#define UCONTEXT_FREGS(ctx) (NULL)
+#define UCONTEXT_REG_PC(ctx) (NULL)
+#define UCONTEXT_REG_BP(ctx) (NULL)
+#define UCONTEXT_REG_SP(ctx) (NULL)
+
+#else
+
+#include <ucontext.h>
+
+#define UCONTEXT_GREGS(ctx) (((ucontext_t *) (ctx))->uc_mcontext.gregs)
+#define UCONTEXT_FREGS(ctx) (((ucontext_t *) (ctx))->uc_mcontext.fpregs)
+#define UCONTEXT_REG_PC(ctx) (UCONTEXT_GREGS ((ctx)) [REG_PC])
+#define UCONTEXT_REG_BP(ctx) (UCONTEXT_GREGS ((ctx)) [REG_S0])
+#define UCONTEXT_REG_SP(ctx) (UCONTEXT_GREGS ((ctx)) [REG_SP])
+
+#endif
+
#endif
diff --git a/mono/utils/mono-tls.c b/mono/utils/mono-tls.c
index 10c57b02801..8162e8a5704 100644
--- a/mono/utils/mono-tls.c
+++ b/mono/utils/mono-tls.c
@@ -152,6 +152,21 @@
: "=r" (foo) : : "1"); \
offset = foo; } while (0)
# endif
+
+#elif defined (TARGET_RISCV) && !defined (PIC)
+
+#define MONO_THREAD_VAR_OFFSET(var, offset) \
+ do { \
+ guint32 temp; \
+ __asm__ ( \
+ "lui %0, %%tprel_hi(" #var ")\n" \
+ "add %0, %0, tp, %%tprel_add(" #var ")\n" \
+ "addi %0, %0, %%tprel_lo(" #var ")\n" \
+ : "=r" (temp) \
+ ); \
+ offset = temp; \
+ } while (0)
+
#else
#define MONO_THREAD_VAR_OFFSET(var,offset) (offset) = -1