[PATCH v3 3/3] [sim/riscv] Add semi-hosting support

jaydeep.patil
2023-12-17T06:53:22Z

From: Jaydeep Patil <jaydeep.patil@imgtec.com>

Added support for semi-hosting calls. This is based on semi-hosting calls
generated by newlib (--specs=semihost.specs option) and picolibc libraries.
Added gdb.arch/riscv-insn-simulation.c to test compressed instructions and
semi-hosting. Added gdb.arch/riscv-exit-getcmd.c to test get-command-line
options and exit semi-hosting calls.
---
gdb/testsuite/gdb.arch/riscv-exit-getcmd.c | 26 +
gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp | 27 +
.../gdb.arch/riscv-insn-simulation.c | 1542 +++++++++++++++++
.../gdb.arch/riscv-insn-simulation.exp | 31 +
sim/riscv/sim-main.c | 793 ++++++++-
5 files changed, 2416 insertions(+), 3 deletions(-)
create mode 100644 gdb/testsuite/gdb.arch/riscv-exit-getcmd.c
create mode 100755 gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp
create mode 100755 gdb/testsuite/gdb.arch/riscv-insn-simulation.c
create mode 100755 gdb/testsuite/gdb.arch/riscv-insn-simulation.exp

diff --git a/gdb/testsuite/gdb.arch/riscv-exit-getcmd.c b/gdb/testsuite/gdb.arch/riscv-exit-getcmd.c
new file mode 100644
index 00000000000..b557cc83cc5
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-exit-getcmd.c
@@ -0,0 +1,26 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 2023 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Test basic semi-hosting calls GET_CMDLINE and EXIT. */
+
+int
+main (int argc, char **argv)
+{
+ if (argc != 4)
+ return 1;
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp b/gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp
new file mode 100755
index 00000000000..e3e6c6834a6
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp
@@ -0,0 +1,27 @@
+# Copyright 2023 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test basic semi-hosting calls GET_CMDLINE and EXIT.
+
+require {istarget "riscv*-*-*"}
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
+ {debug quiet}] } {
+ return -1
+}
+
+gdb_test "run 1 2 3" ".*Inferior.*process.*exited normally.*"
diff --git a/gdb/testsuite/gdb.arch/riscv-insn-simulation.c b/gdb/testsuite/gdb.arch/riscv-insn-simulation.c
new file mode 100755
index 00000000000..3cb6f9e50b6
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-insn-simulation.c
@@ -0,0 +1,1542 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 2023 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#if (__riscv_xlen >= 64)
+#define SKIP_c_flw
+#define SKIP_c_flwsp
+#define SKIP_c_fsw
+#define SKIP_c_fswsp
+#define SKIP_c_jal
+#endif
+
+#if (__riscv_xlen == 32)
+#define SKIP_c_ld
+#define SKIP_c_ldsp
+#define SKIP_c_sd
+#define SKIP_c_sdsp
+#define SKIP_c_addiw
+#define SKIP_c_addw
+#define SKIP_c_subw
+#endif
+
+/* Disable tests that are not implemented in GDB simulator yet. */
+#define DISABLE_GDB_SIM_TESTS
+
+#if defined (DISABLE_GDB_SIM_TESTS)
+#define SKIP_c_flw
+#define SKIP_c_flwsp
+#define SKIP_c_fsw
+#define SKIP_c_fswsp
+#define SKIP_c_fld
+#define SKIP_c_fldsp
+#define SKIP_c_fsd
+#define SKIP_c_fsdsp
+#endif
+
+#if defined (DISABLE_PRINTS)
+#define print(...) ;
+#else
+#define print(format, ...) printf (format)
+#endif
+
+#if (__riscv_xlen >= 64)
+typedef uint64_t riscv_reg_t;
+#elif (__riscv_xlen == 32)
+typedef uint32_t riscv_reg_t;
+#endif
+
+int total_tests = 0;
+int num_fail = 0;
+int num_pass = 0;
+int debug = 0;
+
+void
+i_check (int line, const char *func, uint32_t value1, uint32_t value2)
+{
+ total_tests++;
+ if (value1 != value2)
+ {
+ print (" *** FAIL: %s:%d: (%d) != (%d)\n", func, line, value1, value2);
+ num_fail++;
+ }
+ else
+ {
+ num_pass ++;
+ if (debug)
+ print (" PASS: %s:%d\n", func, line);
+ }
+}
+
+void
+l_check (int line, const char *func, uint64_t value1, uint64_t value2)
+{
+ total_tests++;
+ if (value1 != value2)
+ {
+ print (" *** FAIL: %s:%d: (0x%lx) != (0x%lx)\n", func, line, value1,
+ value2);
+ num_fail++;
+ }
+ else
+ {
+ num_pass++;
+ if (debug)
+ print (" PASS: %s:%d\n", func, line);
+ }
+}
+
+void
+f_check (int line, const char *func, float value1, float value2)
+{
+ total_tests++;
+ if (value1 != value2)
+ {
+ print (" *** FAIL: %s:%d: (%ff) != (%ff)\n", func, line, value1,
+ value2);
+ num_fail++;
+ }
+ else
+ {
+ num_pass++;
+ if (debug)
+ print (" PASS: %s:%d\n", func, line);
+ }
+}
+
+void
+d_check (int line, const char *func, double value1, double value2)
+{
+ total_tests++;
+ if (value1 != value2)
+ {
+ print (" *** FAIL: %s:%d: (%lf) != (%lf)\n", func, line, value1,
+ value2);
+ num_fail++;
+ }
+ else
+ {
+ num_pass++;
+ if (debug)
+ print (" PASS: %s:%d\n", func, line);
+ }
+}
+
+void
+fail (int line, const char *func, const char *msg)
+{
+ total_tests++;
+ print (" *** FAIL: %s:%d: (%s)\n", func, line, msg);
+ num_fail++;
+}
+
+void
+info (const char *str)
+{
+ print ("%s\n", str);
+}
+
+#define I_CHECK(VAL1, VAL2) i_check (__LINE__, __FUNCTION__, (VAL1), (VAL2));
+#define L_CHECK(VAL1, VAL2) l_check (__LINE__, __FUNCTION__, (VAL1), (VAL2));
+#define F_CHECK(VAL1, VAL2) f_check (__LINE__, __FUNCTION__, (VAL1), (VAL2));
+#define D_CHECK(VAL1, VAL2) d_check (__LINE__, __FUNCTION__, (VAL1), (VAL2));
+#define FAIL(STR) fail (__LINE__, __FUNCTION__, (STR));
+
+void
+test_c_lwsp ()
+{
+ volatile uint32_t on_stack[] = { 0x1111, 0x2222, 0x3333, 0x4444 };
+ uint32_t a = 0, offset = 0;
+ riscv_reg_t stack_ptr, var_ptr;
+
+ info ("Testing c.lwsp");
+
+ /* Find offset of on_stack. */
+ asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+ var_ptr = (riscv_reg_t) &on_stack[0];
+ offset = var_ptr - stack_ptr;
+ (void) offset;
+
+ if (offset == 0)
+ {
+ asm volatile ("c.lwsp %0,0(sp)" : "=r" (a));
+ I_CHECK (a, 0x1111);
+
+ asm volatile ("c.lwsp %0,4(sp)" : "=r" (a));
+ I_CHECK (a, 0x2222);
+
+ asm volatile ("c.lwsp %0,8(sp)" : "=r" (a));
+ I_CHECK (a, 0x3333);
+
+ asm volatile ("c.lwsp %0,12(sp)" : "=r" (a));
+ I_CHECK (a, 0x4444);
+ }
+ else if (offset == 8)
+ {
+ asm volatile ("c.lwsp %0,8(sp)" : "=r" (a));
+ I_CHECK (a, 0x1111);
+
+ asm volatile ("c.lwsp %0,12(sp)" : "=r" (a));
+ I_CHECK (a, 0x2222);
+
+ asm volatile ("c.lwsp %0,16(sp)" : "=r" (a));
+ I_CHECK (a, 0x3333);
+
+ asm volatile ("c.lwsp %0,20(sp)" : "=r" (a));
+ I_CHECK (a, 0x4444);
+ }
+}
+
+void
+test_c_ldsp ()
+{
+#if defined (SKIP_c_ldsp)
+ info ("--- Disable c.ldsp");
+#else
+ volatile uint64_t on_stack[] = { 0x11112222, 0x33334444 };
+ uint64_t a = 0, offset = 0;
+ riscv_reg_t stack_ptr, var_ptr;
+
+ info ("Testing c.ldsp");
+
+ /* Find offset of on_stack. */
+ asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+ var_ptr = (riscv_reg_t) &on_stack[0];
+ offset = var_ptr - stack_ptr;
+ (void) offset;
+
+ asm volatile ("c.ldsp %0,0(sp)" : "=r" (a));
+ L_CHECK (a, 0x11112222ul);
+
+ asm volatile ("c.ldsp %0,8(sp)" : "=r" (a));
+ L_CHECK (a, 0x33334444ul);
+#endif
+}
+
+void
+test_c_flwsp ()
+{
+#if defined (SKIP_c_flwsp)
+ info ("--- Disable c.flwsp");
+#else
+ volatile float on_stack[] = { 1.23f, 3.14f, -5.6f, 10.9f };
+ float a;
+ uint32_t offset = 0;
+ riscv_reg_t stack_ptr, var_ptr;
+
+ info ("Testing c.flwsp");
+
+ /* Find offset of on_stack. */
+ asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+ var_ptr = (riscv_reg_t) &on_stack[0];
+ offset = var_ptr - stack_ptr;
+ (void) offset;
+
+ if (offset == 0)
+ {
+ asm volatile ("c.flwsp %0,0(sp)" : "=f" (a));
+ F_CHECK (a, 1.23f);
+
+ asm volatile ("c.flwsp %0,4(sp)" : "=f" (a));
+ F_CHECK (a, 3.14f);
+
+ asm volatile ("c.flwsp %0,8(sp)" : "=f" (a));
+ F_CHECK (a, -5.6f);
+
+ asm volatile ("c.flwsp %0,12(sp)" : "=f" (a));
+ F_CHECK (a, 10.9f);
+ }
+ else if (offset == 8)
+ {
+ asm volatile ("c.flwsp %0,8(sp)" : "=f" (a));
+ F_CHECK (a, 1.23f);
+
+ asm volatile ("c.flwsp %0,12(sp)" : "=f" (a));
+ F_CHECK (a, 3.14f);
+
+ asm volatile ("c.flwsp %0,16(sp)" : "=f" (a));
+ F_CHECK (a, -5.6f);
+
+ asm volatile ("c.flwsp %0,20(sp)" : "=f" (a));
+ F_CHECK (a, 10.9f);
+ }
+#endif
+}
+
+void
+test_c_fldsp ()
+{
+#if defined (SKIP_c_fldsp)
+ info ("--- Disable c.fldsp");
+#else
+ volatile double on_stack[] = { 1.23, -5.89 };
+ double a = 0;
+ uint32_t offset = 0;
+ riscv_reg_t stack_ptr, var_ptr;
+
+ info ("Testing c.fldsp");
+
+ /* Find offset of on_stack. */
+ asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+ var_ptr = (riscv_reg_t) &on_stack[0];
+ offset = var_ptr - stack_ptr;
+ (void) offset;
+
+ asm volatile ("c.fldsp %0,0(sp)" : "=f" (a));
+ D_CHECK (a, 1.23);
+
+ asm volatile ("c.fldsp %0,8(sp)" : "=f" (a));
+ D_CHECK (a, -5.89);
+#endif
+}
+
+void
+test_c_swsp ()
+{
+ volatile uint32_t on_stack[] = { 0x1111, 0x2222, 0x3333, 0x4444 };
+ uint32_t a, offset = 0;
+ riscv_reg_t stack_ptr, var_ptr;
+
+ info ("Testing c.swsp");
+
+ /* Find offset of on_stack. */
+ asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+ var_ptr = (riscv_reg_t) &on_stack[0];
+ offset = var_ptr - stack_ptr;
+
+ if (offset == 0)
+ {
+ a = 0xbeef;
+ asm volatile ("c.swsp %0,0(sp)" : : "r" (a));
+
+ a = 0xdead;
+ asm volatile ("c.swsp %0,4(sp)" : : "r" (a));
+
+ a = 0xabcd;
+ asm volatile ("c.swsp %0,8(sp)" : : "r" (a));
+
+ a = 0x1298;
+ asm volatile ("c.swsp %0,12(sp)" : : "r" (a));
+ }
+ else if (offset == 8)
+ {
+ a = 0xbeef;
+ asm volatile ("c.swsp %0,8(sp)" : : "r" (a));
+
+ a = 0xdead;
+ asm volatile ("c.swsp %0,12(sp)" : : "r" (a));
+
+ a = 0xabcd;
+ asm volatile ("c.swsp %0,16(sp)" : : "r" (a));
+
+ a = 0x1298;
+ asm volatile ("c.swsp %0,20(sp)" : : "r" (a));
+ }
+ else
+ {
+ FAIL ("Invalid stack offset (expected 0 or 8)");
+ return;
+ }
+
+ I_CHECK (on_stack[0], 0xbeef);
+ I_CHECK (on_stack[1], 0xdead);
+ I_CHECK (on_stack[2], 0xabcd);
+ I_CHECK (on_stack[3], 0x1298);
+}
+
+void
+test_c_sdsp ()
+{
+#if defined (SKIP_c_sdsp)
+ info ("--- Disable c.sdsp");
+#else
+ volatile uint64_t on_stack[] = { 0x11112222, 0x33334444 };
+ uint64_t a = 0, offset = 0;
+ riscv_reg_t stack_ptr, var_ptr;
+
+ info ("Testing c.sdsp");
+
+ /* Find offset of on_stack. */
+ asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+ var_ptr = (riscv_reg_t) &on_stack[0];
+ offset = var_ptr - stack_ptr;
+ (void) offset;
+
+ a = 0xdeadbeef;
+ asm volatile ("c.sdsp %0,0(sp)" : : "r" (a));
+
+ a = 0xabcd1234;
+ asm volatile ("c.sdsp %0,8(sp)" : : "r" (a));
+
+ L_CHECK (on_stack[0], 0xdeadbeef);
+ L_CHECK (on_stack[1], 0xabcd1234);
+#endif
+}
+
+void
+test_c_fswsp ()
+{
+#if defined (SKIP_c_fswsp)
+ info ("--- Disable c.fswsp");
+#else
+ volatile float on_stack[] = { 1.23f, 3.14f, -5.6f, 10.9f };
+ float a;
+ uint32_t offset = 0;
+ riscv_reg_t stack_ptr, var_ptr;
+
+ info ("Testing c.fswsp");
+
+ /* Find offset of on_stack. */
+ asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+ var_ptr = (riscv_reg_t) &on_stack[0];
+ offset = var_ptr - stack_ptr;
+ (void) offset;
+
+ if (offset == 0)
+ {
+ a = -12.5f;
+ asm volatile ("c.fswsp %0,0(sp)" : : "f" (a));
+
+ a = 72.8f;
+ asm volatile ("c.fswsp %0,4(sp)" : "=f" (a));
+
+ a = 0.5f;
+ asm volatile ("c.fswsp %0,8(sp)" : "=f" (a));
+
+ a = 4.7f;
+ asm volatile ("c.fswsp %0,12(sp)" : "=f" (a));
+
+ }
+ else if (offset == 8)
+ {
+ a = -12.5f;
+ asm volatile ("c.fswsp %0,8(sp)" : "=f" (a));
+
+ a = 72.8f;
+ asm volatile ("c.fswsp %0,12(sp)" : "=f" (a));
+
+ a = 0.5f;
+ asm volatile ("c.fswsp %0,16(sp)" : "=f" (a));
+
+ a = 4.7f;
+ asm volatile ("c.fswsp %0,20(sp)" : "=f" (a));
+ }
+ else
+ {
+ FAIL ("Invalid stack offset (expected 0 or 8)");
+ return;
+ }
+
+ F_CHECK (on_stack[0], -12.5f);
+ F_CHECK (on_stack[1], 72.8f);
+ F_CHECK (on_stack[2], 0.5f);
+ F_CHECK (on_stack[3], 4.7f);
+
+#endif
+}
+
+void
+test_c_fsdsp ()
+{
+#if defined (SKIP_c_fsdsp)
+ info ("--- Disable c.fsdsp");
+#else
+ volatile double on_stack[] = { -1.23, 5.89 };
+ double a = 0;
+ uint32_t offset = 0;
+ riscv_reg_t stack_ptr, var_ptr;
+
+ info ("Testing c.fsdsp");
+
+ /* Find offset of on_stack. */
+ asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+ var_ptr = (riscv_reg_t) &on_stack[0];
+ offset = var_ptr - stack_ptr;
+ (void) offset;
+
+ a = 1234.55;
+ asm volatile ("c.fsdsp %0,0(sp)" : : "f" (a));
+
+ a = -7890.15;
+ asm volatile ("c.fsdsp %0,8(sp)" : : "f" (a));
+
+ D_CHECK (on_stack[0], 1234.55);
+ D_CHECK (on_stack[1], -7890.15);
+#endif
+}
+
+void
+test_c_lw ()
+{
+ static uint32_t g_data[] = { 0x1111, 0x2222, 0x3333, 0x4444 };
+ uint32_t a = 0;
+ riscv_reg_t var_ptr;
+
+ info ("Testing c.lw");
+
+ var_ptr = (riscv_reg_t) &g_data[0];
+
+ asm volatile ("c.lw %0,0(%1)" : "=r" (a) : "r" (var_ptr));
+ I_CHECK (a, 0x1111);
+
+ asm volatile ("c.lw %0,4(%1)" : "=r" (a) : "r" (var_ptr));
+ I_CHECK (a, 0x2222);
+
+ asm volatile ("c.lw %0,8(%1)" : "=r" (a) : "r" (var_ptr));
+ I_CHECK (a, 0x3333);
+
+ asm volatile ("c.lw %0,12(%1)" : "=r" (a) : "r" (var_ptr));
+ I_CHECK (a, 0x4444);
+}
+
+void
+test_c_ld ()
+{
+#if defined (SKIP_c_ld)
+ info ("--- Disable c.ld");
+#else
+ static uint64_t g_data[] = { 0x11112222, 0x33334444 };
+ uint32_t a = 0;
+ riscv_reg_t var_ptr;
+
+ info ("Testing c.ld");
+
+ var_ptr = (riscv_reg_t) &g_data[0];
+
+ asm volatile ("c.ld %0,0(%1)" : "=r" (a) : "r" (var_ptr));
+ L_CHECK (a, 0x11112222);
+
+ asm volatile ("c.ld %0,8(%1)" : "=r" (a) : "r" (var_ptr));
+ L_CHECK (a, 0x33334444);
+#endif
+}
+
+void
+test_c_flw ()
+{
+#if defined (SKIP_c_flw)
+ info ("--- Disable c.flw");
+#else
+ static float g_data[] = { 1.23f, 3.14f, -5.6f, 10.9f };
+ float a = 0;
+ riscv_reg_t var_ptr;
+
+ info ("Testing c.flw");
+
+ var_ptr = (riscv_reg_t) &g_data[0];
+
+ asm volatile ("c.flw %0,0(%1)" : "=f" (a) : "r" (var_ptr));
+ F_CHECK (a, 1.23f);
+
+ asm volatile ("c.flw %0,4(%1)" : "=f" (a) : "r" (var_ptr));
+ F_CHECK (a, 3.14f);
+
+ asm volatile ("c.flw %0,8(%1)" : "=f" (a) : "r" (var_ptr));
+ F_CHECK (a, -5.6f);
+
+ asm volatile ("c.flw %0,12(%1)" : "=f" (a) : "r" (var_ptr));
+ F_CHECK (a, 10.9f);
+#endif
+}
+
+void
+test_c_fld ()
+{
+#if defined (SKIP_c_fld)
+ info ("--- Disable c.fld");
+#else
+ static double g_data[] = { 1234.5, -7890.4 };
+ double a = 0;
+ riscv_reg_t var_ptr;
+
+ info ("Testing c.fld");
+
+ var_ptr = (riscv_reg_t) &g_data[0];
+
+ asm volatile ("c.fld %0,0(%1)" : "=f" (a) : "r" (var_ptr));
+ D_CHECK (a, 1234.5);
+
+ asm volatile ("c.fld %0,8(%1)" : "=f" (a) : "r" (var_ptr));
+ D_CHECK (a, -7890.4);
+#endif
+}
+
+void
+test_c_sw ()
+{
+ volatile uint32_t g_data[] = { 0x1111, 0x2222, 0x3333, 0x4444 };
+ uint32_t a;
+ riscv_reg_t stack_ptr, var_ptr;
+
+ info ("Testing c.sw");
+
+ var_ptr = (riscv_reg_t) &g_data[0];
+
+ a = 0xbeef;
+ asm volatile ("c.sw %0,0(%1)" : : "r" (a), "r" (var_ptr));
+
+ a = 0xdead;
+ asm volatile ("c.sw %0,4(%1)" : : "r" (a), "r" (var_ptr));
+
+ a = 0xabcd;
+ asm volatile ("c.sw %0,8(%1)" : : "r" (a), "r" (var_ptr));
+
+ a = 0x1298;
+ asm volatile ("c.sw %0,12(%1)" : : "r" (a), "r" (var_ptr));
+
+ I_CHECK (g_data[0], 0xbeef);
+ I_CHECK (g_data[1], 0xdead);
+ I_CHECK (g_data[2], 0xabcd);
+ I_CHECK (g_data[3], 0x1298);
+}
+
+void
+test_c_sd ()
+{
+#if defined (SKIP_c_sd)
+ info ("--- Disable c.sd");
+#else
+ volatile uint64_t g_data[] = { 0x1111, 0x2222, 0x3333, 0x4444 };
+ uint64_t a;
+ riscv_reg_t var_ptr;
+
+ info ("Testing c.sd");
+
+ var_ptr = (riscv_reg_t) &g_data[0];
+
+ a = 0xbeefdead;
+ asm volatile ("c.sd %0,0(%1)" : : "r" (a), "r" (var_ptr));
+
+ a = 0xabcd1298;
+ asm volatile ("c.sd %0,8(%1)" : : "r" (a), "r" (var_ptr));
+
+ L_CHECK (g_data[0], 0xbeefdead);
+ L_CHECK (g_data[1], 0xabcd1298);
+#endif
+}
+
+void
+test_c_fsw ()
+{
+#if defined (SKIP_c_fsw)
+ info ("--- Disable c.fsw");
+#else
+ volatile float g_data[] = { 1.0f, 2.0f, 3.0f, -4.0f };
+ float a;
+ riscv_reg_t stack_ptr, var_ptr;
+
+ info ("Testing c.fsw");
+
+ var_ptr = (riscv_reg_t) &g_data[0];
+
+ a = 12.5f;
+ asm volatile ("c.fsw %0,0(%1)" : : "f" (a), "r" (var_ptr));
+
+ a = -7.9f;
+ asm volatile ("c.fsw %0,4(%1)" : : "f" (a), "r" (var_ptr));
+
+ a = 123.4f;
+ asm volatile ("c.fsw %0,8(%1)" : : "f" (a), "r" (var_ptr));
+
+ a = 0.5f;
+ asm volatile ("c.fsw %0,12(%1)" : : "f" (a), "r" (var_ptr));
+
+ F_CHECK (g_data[0], 12.5f);
+ F_CHECK (g_data[1], -7.9f);
+ F_CHECK (g_data[2], 123.4f);
+ F_CHECK (g_data[3], 0.5f);
+#endif
+}
+
+void
+test_c_fsd ()
+{
+#if defined (SKIP_c_fsd)
+ info ("--- Disable c.fsd");
+#else
+ volatile double g_data[] = { 1.0, 2.0 };
+ double a;
+ riscv_reg_t stack_ptr, var_ptr;
+
+ info ("Testing c.fsd");
+
+ var_ptr = (riscv_reg_t) &g_data[0];
+
+ a = 1234.5;
+ asm volatile ("c.fsd %0,0(%1)" : : "f" (a), "r" (var_ptr));
+
+ a = -7892.9;
+ asm volatile ("c.fsd %0,8(%1)" : : "f" (a), "r" (var_ptr));
+
+ F_CHECK (g_data[0], 1234.5);
+ F_CHECK (g_data[1], -7892.9);
+
+#endif
+}
+
+void
+test_c_j ()
+{
+ volatile int a = 0, b = 5;
+
+ info ("Testing c.j");
+
+label_b:
+ /* If we have jumped back. */
+ if (a == 7 && b == 5)
+ return;
+
+ asm goto ("c.j %l[label_f]" : : : : label_f);
+ asm volatile ("c.mv %0, %1" : "=r" (a) : "r" (b));
+
+ FAIL ("Jumped at wrong location");
+ return;
+
+label_f:
+
+ I_CHECK (a, 0);
+ I_CHECK (b, 5);
+
+ a = 7;
+ asm goto ("c.j %l[label_b]" : : : : label_b);
+ asm volatile ("c.mv %0, %1" : "=r" (a) : "r" (b));
+}
+
+void
+test_c_jal ()
+{
+#if defined (SKIP_c_jal)
+ info ("--- Disable c.jal");
+#else
+ volatile int a = 0, b = 5;
+ riscv_reg_t ra;
+
+ info ("Testing c.jal");
+
+label_b:
+ /* If we have jumped back. */
+ if (a == 7 && b == 5)
+ {
+ asm volatile ("c.mv %0, ra" : "=r" (ra));
+ L_CHECK (ra, (riscv_reg_t) &&ret_label_2);
+ return;
+ }
+
+ asm goto ("c.jal %l[label_f]" : : : : label_f);
+ret_label_1:
+ asm volatile ("c.mv %0, %1" : "=r" (a) : "r" (b));
+
+ FAIL ("Jumped at wrong location");
+ return;
+
+label_f:
+ asm volatile ("c.mv %0, ra" : "=r" (ra));
+
+ L_CHECK (ra, (riscv_reg_t) &&ret_label_1);
+ I_CHECK (a, 0);
+ I_CHECK (b, 5);
+
+ /* Jump back. */
+ a = 7;
+ asm goto ("c.jal %l[label_b]" : : : : label_b);
+ret_label_2:
+ asm volatile ("c.nop");
+#endif
+}
+
+void
+test_c_jr ()
+{
+ volatile int a = 0, b = 5;
+ riscv_reg_t addr;
+
+ info ("Testing c.jr");
+
+ addr = (riscv_reg_t) &&label_f;
+
+label_b:
+ /* If we have jumped back. */
+ if (a == 7 && b == 5)
+ return;
+
+ asm volatile ("c.jr %0" : : "r" (addr));
+ asm volatile ("c.mv %0, %1" : "=r" (a) : "r" (b));
+
+ FAIL ("Jumped at wrong location");
+
+label_f:
+ I_CHECK(a, 0);
+ I_CHECK(b, 5);
+
+ a = 7;
+ addr = (riscv_reg_t) &&label_b;
+ asm volatile ("c.jr %0" : : "r" (addr));
+
+ FAIL ("Jumped at wrong location");
+}
+
+void
+test_c_jalr ()
+{
+ volatile int a = 0, b = 5;
+ riscv_reg_t addr, ra;
+
+ info ("Testing c.jalr");
+
+ addr = (riscv_reg_t) &&label_f;
+
+label_b:
+ /* If we have jumped back. */
+ if (a == 7)
+ {
+ asm volatile ("c.mv %0, ra" : "=r" (ra));
+ L_CHECK (ra, (riscv_reg_t) &&label_ra_2);
+ return;
+ }
+
+ asm volatile ("c.jalr %0" : : "r" (addr));
+label_ra_1:
+ asm volatile ("c.nop");
+ asm volatile ("c.mv %0, %1" : "=r" (a) : "r" (b));
+
+ FAIL ("Jumped at wrong location");
+
+label_f:
+ asm volatile ("c.mv %0, ra" : "=r" (ra));
+
+ L_CHECK (ra, (riscv_reg_t) &&label_ra_1);
+ I_CHECK (a, 0);
+ I_CHECK (b, 5);
+
+ a = 7;
+ addr = (riscv_reg_t) &&label_b;
+ asm volatile ("c.jalr %0" : : "r" (addr));
+label_ra_2:
+ asm volatile ("c.nop");
+
+ FAIL ("Jumped at wrong location");
+}
+
+void
+test_c_beqz ()
+{
+ volatile int zero = 0, non_zero = 1;
+ int a = 0, b = 5;
+
+ info ("Testing c.beqz");
+
+label_b_1:
+ /* If we have branched back. */
+ if (a == 17)
+ {
+ I_CHECK (b, 8);
+ if (b != 8)
+ FAIL ("Backward-taken branch failed");
+ return;
+ }
+
+ asm goto ("c.beqz %0, %l[label_f_1]"
+ : : "r" (zero) : : label_f_1); /* Forward taken. */
+ asm volatile ("c.li %0, 7" : "=r" (a));
+ asm volatile ("c.li %0, 8" : "=r" (b));
+
+label_f_1:
+ I_CHECK (a, 0);
+ I_CHECK (b, 5);
+ if (a != 0 || b != 5)
+ {
+ FAIL ("Forward-taken branch failed");
+ return;
+ }
+
+ asm goto ("c.beqz %0, %l[label_f_2]"
+ :
+ : "r" (non_zero)
+ :
+ : label_f_2); /* Not taken. */
+
+ a = 7;
+ b = 8;
+
+label_f_2:
+ I_CHECK (a, 7);
+ I_CHECK (b, 8);
+ if (a != 7 || b != 8)
+ {
+ FAIL ("Not-taken branch failed");
+ return;
+ }
+
+ /* Branch back. */
+ a = 17;
+ asm goto ("c.beqz %0, %l[label_b_1]"
+ :
+ : "r" (zero)
+ :
+ : label_b_1); /* Backward taken. */
+
+ FAIL ("Backward-taken branch failed");
+}
+
+void
+test_c_bnez ()
+{
+ volatile int zero = 0, non_zero = 1;
+ int a = 0, b = 5;
+
+ info ("Testing c.bnez");
+
+label_b_1:
+ /* If we have branched back. */
+ if (a == 17)
+ {
+ if (b != 8)
+ FAIL ("Backward-taken branch failed");
+ return;
+ }
+
+ asm goto ("c.bnez %0, %l[label_f_1]"
+ :
+ : "r" (non_zero)
+ :
+ : label_f_1); /* Forward taken. */
+ asm volatile ("c.li %0, 7" : "=r" (a));
+ asm volatile ("c.li %0, 8" : "=r" (b));
+
+label_f_1:
+ if (a != 0 || b != 5)
+ {
+ FAIL ("Forward-taken branch failed");
+ num_fail++;
+ return;
+ }
+
+ asm goto ("c.bnez %0, %l[label_f_2]"
+ :
+ : "r" (zero)
+ :
+ : label_f_2); /* Not taken. */
+ a = 7;
+ b = 8;
+
+label_f_2:
+ if (a != 7 || b != 8)
+ {
+ FAIL ("Not-taken branch failed");
+ num_fail++;
+ return;
+ }
+
+ /* Branch back. */
+ a = 17;
+ asm goto ("c.bnez %0, %l[label_b_1]"
+ :
+ : "r" (non_zero)
+ :
+ : label_b_1); /* Backward taken. */
+
+ FAIL ("backward-taken branch failed");
+ num_fail++;
+}
+
+void
+test_c_li ()
+{
+ riscv_reg_t a, b, c;
+
+ info ("Testing c.li");
+
+ asm volatile ("c.li %0,0" : "=r" (a));
+ asm volatile ("c.li %0,-1" : "=r" (b));
+ asm volatile ("c.li %0,31" : "=r" (c));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (a, 0);
+ L_CHECK (b, -1);
+ L_CHECK (c, 31);
+#else
+ I_CHECK (a, 0);
+ I_CHECK (b, -1);
+ I_CHECK (c, 31);
+#endif
+}
+
+void
+test_c_lui ()
+{
+ int a = 0;
+
+ info ("Testing c.lui");
+
+ asm volatile ("c.lui %0,1" : "=r" (a));
+ I_CHECK (a, 0x1000);
+
+ asm volatile ("c.lui %0,31" : "=r" (a));
+ I_CHECK (a, 0x1F000);
+}
+
+void
+test_c_addi ()
+{
+ int a = 0;
+
+ info ("Testing c.addi");
+
+ asm volatile ("c.addi %0,1" : "+r" (a));
+ I_CHECK (a, 1);
+
+ asm volatile ("c.addi %0,-1" : "+r" (a));
+ I_CHECK (a, 0);
+
+ asm volatile ("c.addi %0,31" : "+r" (a));
+ I_CHECK (a, 31);
+}
+
+void
+test_c_addiw ()
+{
+#if defined (SKIP_c_addiw)
+ info ("--- Disable c.addiw");
+#else
+ int a = 1;
+
+ info ("Testing c.addiw");
+
+ asm volatile ("c.addiw %0,0" : "+r" (a));
+ I_CHECK (a, 1);
+
+ asm volatile ("c.addiw %0,-1" : "+r" (a));
+ I_CHECK (a, 0);
+
+ asm volatile ("c.addiw %0,31" : "+r" (a));
+ I_CHECK (a, 31);
+#endif
+}
+
+void
+test_c_addi16sp ()
+{
+ volatile riscv_reg_t orig_sp, sp_1, sp_2;
+
+ info ("Testing c.addi16sp");
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated"
+ asm volatile ("\
+ c.mv %0, sp \n\
+ c.addi16sp sp,32 \n\
+ c.mv %1, sp \n\
+ c.addi16sp sp,-32 \n\
+ c.mv %2, sp \n\
+ c.mv sp, %0" /* Restore sp. */
+ : "=r" (orig_sp), "=r" (sp_1), "=r" (sp_2)
+ : "r" (orig_sp)
+ : "sp");
+#pragma GCC diagnostic pop
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (orig_sp, sp_2);
+ L_CHECK ((sp_1 - sp_2), 32);
+#else
+ I_CHECK (orig_sp, sp_2);
+ I_CHECK ((sp_1 - sp_2), 32);
+#endif
+}
+
+void
+test_c_addi4spn ()
+{
+ volatile riscv_reg_t orig_sp, sp_1, sp_2;
+
+ info ("Testing c.addi4spn");
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated"
+ asm volatile ("\
+ c.mv %0, sp \n\
+ c.addi4spn %1,sp,16 \n\
+ c.addi4spn %2,sp,32"
+ : "=r" (orig_sp), "=r" (sp_1), "=r" (sp_2)
+ : "r" (orig_sp)
+ : "sp");
+#pragma GCC diagnostic pop
+
+#if (__riscv_xlen >= 64)
+ L_CHECK ((orig_sp + 16), sp_1);
+ L_CHECK ((orig_sp + 32), sp_2);
+#else
+ I_CHECK ((orig_sp + 16), sp_1);
+ I_CHECK ((orig_sp + 32), sp_2);
+#endif
+}
+
+void
+test_c_slli ()
+{
+ volatile riscv_reg_t val = -5, r1, r2, r3;
+
+ info ("Testing c.slli");
+
+ asm volatile ("c.slli %0,1" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r1) : "r" (val));
+ asm volatile ("c.slli %0,2" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r2) : "r" (val));
+ asm volatile ("c.slli %0,3" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r3) : "r" (val));
+ asm volatile ("c.slli %0,4" : "+r" (val));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (r1, -10);
+ L_CHECK (r2, -40);
+ L_CHECK (r3, -320);
+ L_CHECK (val, -5120);
+#else
+ I_CHECK (r1, -10);
+ I_CHECK (r2, -40);
+ I_CHECK (r3, -320);
+ I_CHECK (val, -5120);
+#endif
+
+ val = 5;
+ asm volatile ("c.slli %0,1" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r1) : "r" (val));
+ asm volatile ("c.slli %0,2" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r2) : "r" (val));
+ asm volatile ("c.slli %0,3" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r3) : "r" (val));
+ asm volatile ("c.slli %0,4" : "+r" (val));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (r1, 10);
+ L_CHECK (r2, 40);
+ L_CHECK (r3, 320);
+ L_CHECK (val, 5120);
+#else
+ I_CHECK (r1, 10);
+ I_CHECK (r2, 40);
+ I_CHECK (r3, 320);
+ I_CHECK (val, 5120);
+#endif
+}
+
+void
+test_c_srli ()
+{
+ volatile riscv_reg_t val = -105, r1, r2, r3;
+
+ info ("Testing c.srli");
+
+ asm volatile ("c.srli %0,1" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r1) : "r" (val));
+ asm volatile ("c.srli %0,2" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r2) : "r" (val));
+ asm volatile ("c.srli %0,3" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r3) : "r" (val));
+ asm volatile ("c.srli %0,4" : "+r" (val));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (r1, 0x7fffffffffffffcbul);
+ L_CHECK (r2, 0x1ffffffffffffff2ul);
+ L_CHECK (r3, 0x3fffffffffffffeul);
+ L_CHECK (val, 0x3ffffffffffffful);
+#else
+ I_CHECK (r1, 0x7fffffcb);
+ I_CHECK (r2, 0x1ffffff2);
+ I_CHECK (r3, 0x3fffffe);
+ I_CHECK (val, 0x3fffff);
+#endif
+
+ val = 105;
+ asm volatile ("c.srli %0,1" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r1) : "r" (val));
+ asm volatile ("c.srli %0,2" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r2) : "r" (val));
+ asm volatile ("c.srli %0,3" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r3) : "r" (val));
+ asm volatile ("c.srli %0,4" : "+r" (val));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (r1, 52);
+ L_CHECK (r2, 13);
+ L_CHECK (r3, 1);
+ L_CHECK (val, 0);
+#else
+ I_CHECK (r1, 52);
+ I_CHECK (r2, 13);
+ I_CHECK (r3, 1);
+ I_CHECK (val, 0);
+#endif
+}
+
+void
+test_c_srai ()
+{
+ volatile riscv_reg_t val = -105, r1, r2, r3;
+
+ info ("Testing c.srai");
+
+ asm volatile ("c.srai %0,1" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r1) : "r" (val));
+ asm volatile ("c.srai %0,2" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r2) : "r" (val));
+ asm volatile ("c.srai %0,3" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r3) : "r" (val));
+ asm volatile ("c.srai %0,4" : "+r" (val));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (r1, -53);
+ L_CHECK (r2, -14);
+ L_CHECK (r3, -2);
+ L_CHECK (val, -1);
+#else
+ I_CHECK (r1, -53);
+ I_CHECK (r2, -14);
+ I_CHECK (r3, -2);
+ I_CHECK (val, -1);
+#endif
+
+ val = 105;
+ asm volatile ("c.srai %0,1" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r1) : "r" (val));
+ asm volatile ("c.srai %0,2" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r2) : "r" (val));
+ asm volatile ("c.srai %0,3" : "+r" (val));
+ asm volatile ("c.mv %0,%1" : "=r" (r3) : "r" (val));
+ asm volatile ("c.srai %0,4" : "+r" (val));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (r1, 52);
+ L_CHECK (r2, 13);
+ L_CHECK (r3, 1);
+ L_CHECK (val, 0);
+#else
+ I_CHECK (r1, 52);
+ I_CHECK (r2, 13);
+ I_CHECK (r3, 1);
+ I_CHECK (val, 0);
+#endif
+}
+
+void
+test_c_andi ()
+{
+ riscv_reg_t val1 = -1, val2 = 0x101;
+
+ info ("Testing c.andi");
+
+ asm volatile ("c.andi %0,5" : "+r" (val1));
+ asm volatile ("c.andi %0,7" : "+r" (val2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (val1, 5);
+ L_CHECK (val2, 1);
+#else
+ I_CHECK (val1, 5);
+ I_CHECK (val2, 1);
+#endif
+}
+
+void
+test_c_add ()
+{
+ riscv_reg_t dst, rs2;
+
+ info ("Testing c.add");
+
+ dst = -1;
+ rs2 = 1;
+ asm volatile ("c.add %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (dst, 0);
+ L_CHECK (rs2, 1);
+#else
+ I_CHECK (dst, 0);
+ I_CHECK (rs2, 1);
+#endif
+
+ dst = -1;
+ rs2 = 0;
+ asm volatile ("c.add %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (dst, -1);
+ L_CHECK (rs2, 0);
+#else
+ I_CHECK (dst, -1);
+ I_CHECK (rs2, 0);
+#endif
+}
+
+void
+test_c_and ()
+{
+ riscv_reg_t dst, rs2;
+
+ info ("Testing c.and");
+
+ dst = -1;
+ rs2 = 1;
+ asm volatile ("c.and %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (dst, 1);
+ L_CHECK (rs2, 1);
+#else
+ I_CHECK (dst, 1);
+ I_CHECK (rs2, 1);
+#endif
+
+ dst = -1;
+ rs2 = 0;
+ asm volatile ("c.and %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (dst, 0);
+ L_CHECK (rs2, 0);
+#else
+ I_CHECK (dst, 0);
+ I_CHECK (rs2, 0);
+#endif
+}
+
+void
+test_c_or ()
+{
+ riscv_reg_t dst, rs2;
+
+ info ("Testing c.or");
+
+ dst = -3;
+ rs2 = 2;
+ asm volatile ("c.or %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (dst, -1);
+ L_CHECK (rs2, 2);
+#else
+ I_CHECK (dst, -1);
+ I_CHECK (rs2, 2);
+#endif
+
+ dst = 0x7ffffffd;
+ rs2 = 0x80000002;
+ asm volatile ("c.or %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (dst, 0xffffffff);
+ L_CHECK (rs2, 0x80000002);
+#else
+ I_CHECK (dst, 0xffffffff);
+ I_CHECK (rs2, 0x80000002);
+#endif
+}
+
+void
+test_c_xor ()
+{
+ riscv_reg_t dst, rs2;
+
+ info ("Testing c.xor");
+
+ dst = -3;
+ rs2 = -3;
+ asm volatile ("c.xor %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (dst, 0);
+ L_CHECK (rs2, -3);
+#else
+ I_CHECK (dst, 0);
+ I_CHECK (rs2, -3);
+#endif
+
+ dst = 0x7ffffffd;
+ rs2 = 0x80000002;
+ asm volatile ("c.xor %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (dst, 0xffffffff);
+ L_CHECK (rs2, 0x80000002);
+#else
+ I_CHECK (dst, 0xffffffff);
+ I_CHECK (rs2, 0x80000002);
+#endif
+}
+
+void
+test_c_sub ()
+{
+ riscv_reg_t dst, rs2;
+
+ info ("Testing c.sub");
+
+ dst = -1;
+ rs2 = 1;
+ asm volatile ("c.sub %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (dst, -2);
+ L_CHECK (rs2, 1);
+#else
+ I_CHECK (dst, -2);
+ I_CHECK (rs2, 1);
+#endif
+
+ dst = 0;
+ rs2 = -1;
+ asm volatile ("c.sub %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (dst, 1);
+ L_CHECK (rs2, -1);
+#else
+ I_CHECK (dst, 1);
+ I_CHECK (rs2, -1);
+#endif
+}
+
+void
+test_c_addw ()
+{
+#if defined (SKIP_c_addw)
+ info ("--- Disable c.addw");
+#else
+
+ riscv_reg_t dst, rs2;
+
+ info ("Testing c.addw");
+
+ dst = -1;
+ rs2 = 1;
+ asm volatile ("c.addw %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (dst, 0);
+ L_CHECK (rs2, 1);
+#else
+ I_CHECK (dst, 0);
+ I_CHECK (rs2, 1);
+#endif
+
+ dst = -1;
+ rs2 = 0;
+ asm volatile ("c.addw %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (dst, -1);
+ L_CHECK (rs2, 0);
+#else
+ I_CHECK (dst, -1);
+ I_CHECK (rs2, 0);
+#endif
+#endif
+}
+
+void
+test_c_subw ()
+{
+#if defined (SKIP_c_subw)
+ info ("--- Disable c.subw");
+#else
+ riscv_reg_t dst, rs2;
+
+ info ("Testing c.subw");
+
+ dst = -1;
+ rs2 = 1;
+ asm volatile ("c.subw %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (dst, -2);
+ L_CHECK (rs2, 1);
+#else
+ I_CHECK (dst, -2);
+ I_CHECK (rs2, 1);
+#endif
+
+ dst = 0;
+ rs2 = -1;
+ asm volatile ("c.subw %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+ L_CHECK (dst, 1);
+ L_CHECK (rs2, -1);
+#else
+ I_CHECK (dst, 1);
+ I_CHECK (rs2, -1);
+#endif
+#endif
+}
+
+int
+main ()
+{
+ test_c_lwsp ();
+ test_c_ldsp ();
+ test_c_flwsp ();
+ test_c_fldsp ();
+ test_c_swsp ();
+ test_c_sdsp ();
+ test_c_fswsp ();
+ test_c_fsdsp ();
+ test_c_lw ();
+ test_c_ld ();
+ test_c_flw ();
+ test_c_fld ();
+ test_c_sw ();
+ test_c_sd ();
+ test_c_fsw ();
+ test_c_fsd ();
+ test_c_j ();
+ test_c_jal ();
+ test_c_jr ();
+ test_c_jalr ();
+ test_c_beqz ();
+ test_c_bnez ();
+ test_c_li ();
+ test_c_lui ();
+ test_c_addi ();
+ test_c_addiw ();
+ test_c_addi16sp ();
+ test_c_addi4spn ();
+ test_c_slli ();
+ test_c_srli ();
+ test_c_srai ();
+ test_c_andi ();
+ test_c_add ();
+ test_c_and ();
+ test_c_or ();
+ test_c_xor ();
+ test_c_sub ();
+ test_c_addw ();
+ test_c_subw ();
+
+ if (num_fail == 0)
+ {
+ print ("*** All tests pass\n");
+ }
+ else
+ {
+ print ("*** Not all tests pass\n");
+ }
+
+ return num_fail;
+}
diff --git a/gdb/testsuite/gdb.arch/riscv-insn-simulation.exp b/gdb/testsuite/gdb.arch/riscv-insn-simulation.exp
new file mode 100755
index 00000000000..0191f84015c
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-insn-simulation.exp
@@ -0,0 +1,31 @@
+# Copyright 2023 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Tests to check instruction simulation of RISC-V instructions.
+
+require {istarget "riscv*-*-*"}
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
+ {debug quiet}] } {
+ return -1
+}
+
+if { ![runto_main] } {
+ return -1
+}
+
+gdb_continue_to_end "continue parent to end" "continue" 1
diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
index 1b374323aa7..61aae39dbb4 100644
--- a/sim/riscv/sim-main.c
+++ b/sim/riscv/sim-main.c
@@ -26,7 +26,11 @@

#include <inttypes.h>
#include <time.h>
+#include <sys/stat.h>
+#include <sys/unistd.h>
+#include <sys/fcntl.h>

+#include "bfd.h"
#include "sim-main.h"
#include "sim-signal.h"
#include "sim-syscall.h"
@@ -66,6 +70,66 @@ static const struct riscv_opcode *riscv_hash[OP_MASK_OP + 1];
} \
} while (0)

+/* Semihosting operations. */
+#define SEMIHOST_open 0x01
+#define SEMIHOST_close 0x02
+#define SEMIHOST_writec 0x03
+#define SEMIHOST_write0 0x04
+#define SEMIHOST_write 0x05
+#define SEMIHOST_read 0x06
+#define SEMIHOST_readc 0x07
+#define SEMIHOST_iserror 0x08
+#define SEMIHOST_istty 0x09
+#define SEMIHOST_seek 0x0A
+#define SEMIHOST_flen 0x0C
+#define SEMIHOST_tmpnam 0x0D
+#define SEMIHOST_remove 0x0E
+#define SEMIHOST_rename 0x0F
+#define SEMIHOST_clock 0x10
+#define SEMIHOST_time 0x11
+#define SEMIHOST_system 0x12
+#define SEMIHOST_errno 0x13
+#define SEMIHOST_get_cmdline 0x15
+#define SEMIHOST_heapinfo 0x16
+#define SEMIHOST_exit 0x18
+#define SEMIHOST_exit_extended 0x20
+#define SEMIHOST_elapsed 0x30
+#define SEMIHOST_tickfreq 0x31
+
+/* Used in SEMIHOST_exit call to indicate successful execution of the
+ application. */
+#define APPLICATION_EXIT 0x20026
+
+/* The low level file open function receives these flags. It then converts
+ these flags into modes (defined below) and calls SEMIHOST_open.
+ F_RDONLY -> SEMIHOST_MODE_R
+ F_WRONLY | F_CREAT | F_TRUNC -> SEMIHOST_MODE_W
+ F_WRONLY | F_CREAT | F_APPEND -> SEMIHOST_MODE_A
+ F_RDWR | F_CREAT | F_TRUNC -> SEMIHOST_MODE_WPLUS
+ F_RDWR | F_CREAT | F_APPEND -> SEMIHOST_MODE_APLUS
+ F_RDWR -> SEMIHOST_MODE_RPLUS. */
+#define F_RDONLY 0x000
+#define F_WRONLY 0x001
+#define F_RDWR 0x002
+#define F_APPEND 0x008
+#define F_CREAT 0x200
+#define F_TRUNC 0x400
+
+/* File open modes received by SEMIHOST_open function. These modes are then
+ converted back to flags before calling open function of the host. */
+#define SEMIHOST_MODE_R 0
+#define SEMIHOST_MODE_RPLUS 2
+#define SEMIHOST_MODE_W 4
+#define SEMIHOST_MODE_WPLUS 6
+#define SEMIHOST_MODE_A 8
+#define SEMIHOST_MODE_APLUS 10
+
+/* Maximum length of the file name in SEMIHOST_open. */
+#define MAX_FILE_NAME_LENGTH 1024
+
+/* Used in SEMIHOST_elapsed to measure elapsed clock ticks. */
+static clock_t clock_start = 0;
+
static INLINE void
store_rd (SIM_CPU *cpu, int rd, unsigned_word val)
{
@@ -151,6 +215,723 @@ ashiftrt64 (unsigned_word val, unsigned_word shift)
return (val >> shift) | sign;
}

+/* Read 4 or 8 bytes of data from the core memory. The ADDR and (INDEX * XLEN)
+ form the base address. 4-byte values are zero extended. */
+static unsigned_word
+get_core_data (SIM_CPU *cpu, unsigned_word addr, unsigned_word index)
+{
+ unsigned_word param;
+ int xlen = RISCV_XLEN (cpu);
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ if (xlen == 64)
+ param = sim_core_read_unaligned_8 (cpu, riscv_cpu->pc, read_map,
+ addr + (index * 8));
+ else
+ param = EXTEND32 (sim_core_read_unaligned_4 (cpu, riscv_cpu->pc, read_map,
+ addr + (index * 4)));
+
+ return param;
+}
+
+/* Write string in HOST_BUF at CORE_ADDR. The length of string is LEN. */
+static void
+set_core_string (SIM_CPU *cpu, unsigned_word core_addr, char *host_buf,
+ int len)
+{
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ for (int i = 0; i < len; i++)
+ sim_core_write_unaligned_1 (cpu, riscv_cpu->pc, write_map,
+ core_addr + i, host_buf[i]);
+}
+
+/* Read string of length LEN at address ADDR. */
+static char *
+get_core_string_with_len (SIM_CPU *cpu, unsigned_word addr,
+ unsigned_word len)
+{
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ char * str;
+ str = (char *) xmalloc (len + 1);
+
+ for (int i = 0; i < len; i++)
+ str[i] = sim_core_read_unaligned_1 (cpu, riscv_cpu->pc, read_map,
+ addr + i);
+ str[len] = '\0';
+
+ return str;
+}
+
+/* Write VALUE at specified address. ADDR and (INDEX * XLEN) form the base
+ address. */
+static void
+set_core_data (SIM_CPU *cpu, unsigned_word addr, unsigned_word index,
+ unsigned_word value)
+{
+ int xlen = RISCV_XLEN (cpu);
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ if (xlen == 64)
+ sim_core_write_unaligned_8 (cpu, riscv_cpu->pc, read_map,
+ addr + (index * 8), value);
+ else
+ sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, read_map,
+ addr + (index * 4), (uint32_t) value);
+}
+
+/* Read data of length SLEN and address ADDR. */
+static char *
+get_core_string (SIM_CPU *cpu, unsigned_word addr, int *slen)
+{
+ int len = 0;
+ char * str;
+ const int chunk_size = 128;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ str = (char *) xmalloc (chunk_size);
+
+ while (1)
+ {
+ uint8_t ch = sim_core_read_unaligned_1 (cpu, riscv_cpu->pc, read_map,
+ addr + len);
+ str[len] = ch;
+ if (ch == 0)
+ break;
+ len++;
+ if ((len % chunk_size) == 0)
+ str = (char *) xrealloc (str, len + chunk_size);
+ }
+
+ *slen = len;
+ return str;
+}
+
+/* Find address of the symbol in SYMNAME. */
+static unsigned_word
+get_symbol_value (SIM_CPU *cpu, const char *symname)
+{
+ struct bfd *abfd = STATE_PROG_BFD (CPU_STATE (cpu));
+ static asymbol **symbol_table = NULL;
+ static long number_of_symbols = 0;
+
+ if (symbol_table == NULL)
+ {
+ long storage_needed;
+ storage_needed = bfd_get_symtab_upper_bound (abfd);
+ if (storage_needed <= 0)
+ return 0;
+ symbol_table = (asymbol **) xmalloc (storage_needed);
+ if (symbol_table == NULL)
+ return 0;
+ number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table);
+ if (number_of_symbols <= 0)
+ return 0;
+ }
+
+ for (long i = 0; i < number_of_symbols; i++)
+ {
+ asymbol *sym = symbol_table[i];
+ if (!strcmp (sym->name, symname))
+ return bfd_asymbol_value (sym);
+ }
+
+ return 0;
+}
+
+/* Register a1 points to a buffer containing:
+ Index 0: Pointer: Address of the file name string.
+ Index 1: Integer: File open flags.
+ Index 2: Integer: Length of the file name string.
+ File handle is returned through register a0. */
+static void
+semihosting_open (SIM_CPU *cpu)
+{
+ int flags;
+ char *name;
+ unsigned_word fname_addr, mode, fname_len;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ fname_addr = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 0);
+ mode = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+ fname_len = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 2);
+
+ if (fname_len <= 0 && fname_len > MAX_FILE_NAME_LENGTH)
+ {
+ riscv_cpu->a0 = -1;
+ return;
+ }
+
+ /* By default open in SEMIHOST_MODE_R mode. */
+ if (mode == SEMIHOST_MODE_RPLUS)
+ flags = F_RDWR;
+ else if (mode == SEMIHOST_MODE_W)
+ flags = F_WRONLY | F_CREAT | F_TRUNC;
+ else if (mode == SEMIHOST_MODE_WPLUS)
+ flags = F_RDWR | F_CREAT | F_TRUNC;
+ else if (mode == SEMIHOST_MODE_A)
+ flags = F_WRONLY | F_CREAT | F_APPEND;
+ else if (mode == SEMIHOST_MODE_APLUS)
+ flags = F_RDWR | F_CREAT | F_APPEND;
+ else
+ flags = F_RDONLY;
+
+ name = get_core_string_with_len (cpu, fname_addr, fname_len);
+ riscv_cpu->a0 = sim_io_open (CPU_STATE (cpu), name, flags);
+ free (name);
+}
+
+/* In case of RV32, register a1 holds the application stop reason and the exit
+ code is decided based on it. Otherwise, register a1 points to a buffer
+ containing:
+ Index 0: Integer: Application stop reason (ignored)
+ Index 1: Integer: Exit code. */
+static void
+semihosting_exit (SIM_CPU *cpu)
+{
+ unsigned_word app_code, exit_code;
+ SIM_DESC sd = CPU_STATE (cpu);
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ if (RISCV_XLEN (cpu) == 32)
+ {
+ app_code = riscv_cpu->a1;
+ if (app_code == APPLICATION_EXIT)
+ exit_code = 0;
+ else
+ exit_code = 1;
+ }
+ else
+ exit_code = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+ riscv_cpu->a0 = exit_code;
+ sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_exited, exit_code);
+}
+
+/* Write command line arguments to a buffer. Arguments passed to "run" command
+ are stored in STATE_PROG_ARGV member of SIM_DESC. Register a1 points to a
+ buffer containing:
+ Index 0: Pointer: Buffer to store command line arguments
+ Index 1: Integer: Maximum length of the buffer. */
+static void
+semihosting_get_cmdline (SIM_CPU *cpu)
+{
+ int i = 0, len = 0, total_len = 0;
+ unsigned_word buf_addr, max_buf_len;
+ SIM_DESC sd = CPU_STATE (cpu);
+ char *space = " ";
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ char **prog_argv = STATE_PROG_ARGV (sd);
+ if (prog_argv == NULL)
+ {
+ /* Return non-zero to indicate error. */
+ riscv_cpu->a0 = 1;
+ return;
+ }
+
+ buf_addr = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 0);
+ max_buf_len = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+
+ while (prog_argv[i])
+ {
+ len = strlen (prog_argv[i]);
+ if ((total_len + len) > max_buf_len)
+ break;
+ set_core_string (cpu, buf_addr, prog_argv[i], len);
+ /* Add space at the end. */
+ set_core_string (cpu, buf_addr + len, space, 1);
+ len++;
+ buf_addr += len;
+ total_len += len;
+ i++;
+ }
+ riscv_cpu->a0 = 0;
+}
+
+/* Register a1 points to a buffer containing:
+ Index 0: Integer: File handle.
+ The file size in bytes is returned through register a0. */
+static void
+semihosting_flen (SIM_CPU *cpu)
+{
+ int fd;
+ struct stat sb;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+
+ if (fd > STDERR_FILENO)
+ {
+ fstat (fd, &sb);
+ riscv_cpu->a0 = sb.st_size;
+ }
+ else
+ riscv_cpu->a0 = 0;
+}
+
+/* Write data to a file. Register a1 points to a buffer containing:
+ Index 0: Integer: File handle.
+ Index 1: Pointer: Pointer to buffer.
+ Index 2: Integer: Number of bytes to write.
+ Number of bytes written is returned through register a0. */
+static void
+semihosting_write (SIM_CPU *cpu)
+{
+ int i, fd;
+ char *str;
+ unsigned_word buf, count;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+ buf = get_core_data (cpu, riscv_cpu->a1, 1);
+ count = get_core_data (cpu, riscv_cpu->a1, 2);
+
+ if (count <= 0)
+ {
+ riscv_cpu->a0 = -1;
+ return;
+ }
+
+ str = get_core_string_with_len (cpu, buf, count);
+ riscv_cpu->a0 = sim_io_write (CPU_STATE (cpu), fd, str, count);
+ free (str);
+}
+
+/* Register a1 points to a buffer containing:
+ Index 0: Integer: Character to write to console.
+ Number of bytes written is returned through register a0. */
+static void
+semihosting_writec (SIM_CPU *cpu)
+{
+ char ch;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ ch = (char) get_core_data (cpu, riscv_cpu->a1, 0);
+ riscv_cpu->a0 = sim_io_write_stdout (CPU_STATE (cpu), &ch, 1);
+}
+
+/* Register a1 points to a buffer containing:
+ Index 0: Integer: Null terminated string.
+ Number of bytes written is returned through register a0. */
+static void
+semihosting_write0 (SIM_CPU *cpu)
+{
+ int len;
+ char *str;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ str = get_core_string (cpu, riscv_cpu->a1, &len);
+ riscv_cpu->a0 = sim_io_write_stdout (CPU_STATE (cpu), str, len);
+}
+
+/* Read data from a file. Register a1 points to a buffer containing:
+ Index 0: Integer: File handle.
+ Index 1: Pointer: Pointer to destination buffer.
+ Index 2: Integer: Number of bytes to read.
+ Number of bytes read is returned through register a0. */
+static void
+semihosting_read (SIM_CPU *cpu)
+{
+ int i, fd, read_len;
+ unsigned_word dst_buf, count;
+ char *host_buf;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+ dst_buf = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+ count = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 2);
+
+ if (count <= 0)
+ {
+ riscv_cpu->a0 = 0;
+ return;
+ }
+
+ host_buf = (char *) xmalloc (count);
+ read_len = sim_io_read (CPU_STATE (cpu), fd, host_buf, count);
+ if (read_len > 0)
+ set_core_string (cpu, dst_buf, host_buf, read_len);
+ riscv_cpu->a0 = read_len;
+ free (host_buf);
+}
+
+/* Read a char from console and return it through register a0. */
+static void
+semihosting_readc (SIM_CPU *cpu)
+{
+ char ch;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ sim_io_read_stdin (CPU_STATE (cpu), &ch, 1);
+ riscv_cpu->a0 = ch;
+}
+
+/* Close a file. Register a1 points to a buffer containing:
+ Index 0: Integer: File handle.
+ Return status in register a0. */
+static void
+semihosting_close (SIM_CPU *cpu)
+{
+ int fd;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+ riscv_cpu->a0 = sim_io_close (CPU_STATE (cpu), fd);
+}
+
+/* Seek in a file. Register a1 points to a buffer containing:
+ Index 0: Integer: File handle.
+ Index 1: Integer: Number of bytes to seek in the file.
+ Return status in register a0. */
+static void
+semihosting_seek (SIM_CPU *cpu)
+{
+ int fd;
+ unsigned_word pos;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+ pos = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+ riscv_cpu->a0 = sim_io_lseek (CPU_STATE (cpu), fd, pos, 0);
+}
+
+/* Register a1 points to a buffer containing:
+ Index 0: Integer: Application code.
+ Index 1: Integer: Exit code. */
+static void
+semihosting_exit_extended (SIM_CPU *cpu)
+{
+ int ret;
+ unsigned_word app_code, exit_status;
+ SIM_DESC sd = CPU_STATE (cpu);
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ app_code = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 0);
+ exit_status = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+ if (app_code == APPLICATION_EXIT)
+ ret = exit_status;
+ else
+ ret = 1;
+ riscv_cpu->a0 = ret;
+ sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_exited, ret);
+}
+
+/* Register a1 points to a buffer containing:
+ Index 0: Integer: Status code. */
+static void
+semihosting_iserror (SIM_CPU *cpu)
+{
+ signed_word status;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ status = (signed_word) get_core_data (cpu, riscv_cpu->a1, 0);
+ riscv_cpu->a0 = (status < 0);
+}
+
+/* Register a1 points to a buffer containing:
+ Index 0: Integer: File handle. */
+static void
+semihosting_istty (SIM_CPU *cpu)
+{
+ int fd;
+ SIM_DESC sd = CPU_STATE (cpu);
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+ riscv_cpu->a0 = sim_io_isatty (sd, fd);
+}
+
+/* Register a1 points to a buffer containing:
+ Index 0: Pointer: Destination buffer address.
+ Index 1: Integer: ID (ignored).
+ Index 2: Integer: Maximum length of the buffer.
+ Pointer to temporary name is returned through a0. */
+static void
+semihosting_tmpnam (SIM_CPU *cpu)
+{
+ unsigned_word t_pname;
+ int len, fd, maxpath;
+ char pname[] = "/tmp/fileXXXXXX";
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ t_pname = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 0);
+ maxpath = (int) get_core_data (cpu, riscv_cpu->a1, 2);
+
+ fd = mkstemp (pname);
+
+ if (fd == -1)
+ {
+ riscv_cpu->a0 = 0;
+ return;
+ }
+
+ len = strlen (pname);
+ if (maxpath > len)
+ {
+ riscv_cpu->a0 = 0;
+ return;
+ }
+
+ set_core_string (cpu, t_pname, pname, len + 1);
+ riscv_cpu->a0 = t_pname;
+ close (fd);
+}
+
+/* Register a1 points to a buffer containing:
+ Index 0: Pointer: Name of the file to remove.
+ Index 1: Integer: Length of the file name. */
+static void
+semihosting_remove (SIM_CPU *cpu)
+{
+ unsigned_word t_pname;
+ int len;
+ char *pname;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ t_pname = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 0);
+ len = (int) get_core_data (cpu, riscv_cpu->a1, 1);
+ pname = get_core_string_with_len (cpu, t_pname, len);
+ riscv_cpu->a0 = sim_io_unlink (CPU_STATE (cpu), pname);
+ free (pname);
+}
+
+/* Register a1 points to a buffer containing:
+ Index 0: Pointer: Old file path.
+ Index 1: Integer: Length of the old file name.
+ Index 2: Pointer: New file name.
+ Index 3: Integer: Length of the new file name. */
+static void
+semihosting_rename (SIM_CPU *cpu)
+{
+ unsigned_word old_name_addr, new_name_addr;
+ unsigned_word old_len, new_len;
+ char *old_host_name, *new_host_name;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ old_name_addr = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 0);
+ old_len = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+ new_name_addr = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 2);
+ new_len = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 3);
+ old_host_name = get_core_string_with_len (cpu, old_name_addr, old_len);
+ new_host_name = get_core_string_with_len (cpu, new_name_addr, new_len);
+ riscv_cpu->a0 = sim_io_rename (CPU_STATE (cpu), old_host_name,
+ new_host_name);
+ free (old_host_name);
+ free (new_host_name);
+}
+
+/* Register a1 points to a buffer containing:
+ Index 0: Pointer: Command string.
+ Index 1: Integer: Length of the command string.
+ Status of the command is returned through a0. */
+static void
+semihosting_system (SIM_CPU *cpu)
+{
+ unsigned_word cmd_addr;
+ unsigned_word cmd_len;
+ char *cmd_host;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ cmd_addr = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 0);
+ cmd_len = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+ cmd_host = get_core_string_with_len (cpu, cmd_addr, cmd_len);
+ riscv_cpu->a0 = sim_io_system (CPU_STATE (cpu), cmd_host);
+ free (cmd_host);
+}
+
+/* Return the elapsed seconds in a buffer pointed by register a1. */
+static void
+semihosting_elapsed (SIM_CPU *cpu)
+{
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ clock_t elapsed = clock () - clock_start;
+ if (RISCV_XLEN (cpu) == 32)
+ {
+ sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map,
+ riscv_cpu->a1, (uint32_t) elapsed);
+ sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map,
+ riscv_cpu->a1 + 4, (uint32_t) (elapsed >> 32));
+ }
+ else
+ sim_core_write_unaligned_8 (cpu, riscv_cpu->pc, write_map, riscv_cpu->a1,
+ elapsed);
+}
+
+/* Return the heap start, heap end, stack start and stack end in a buffer
+ pointed by register a1. */
+static void
+semihosting_heapinfo (SIM_CPU *cpu)
+{
+ static unsigned_word heap_base = 0, heap_limit = 0,
+ stack_base = 0, stack_limit = 0, stack_size = 0;
+ static bool have_heap = false, have_stack = false;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ if (have_heap == false)
+ {
+ heap_base = get_symbol_value (cpu, "__heap_start");
+ heap_limit = get_symbol_value (cpu, "__heap_end");
+ have_heap = true;
+ }
+
+ if (have_stack == false)
+ {
+ stack_base = get_symbol_value (cpu, "__stack");
+ stack_size = get_symbol_value (cpu, "__stack_size");
+ stack_limit = stack_base + stack_size;
+ have_stack = true;
+ }
+
+ set_core_data (cpu, riscv_cpu->a1, 0, heap_base);
+ set_core_data (cpu, riscv_cpu->a1, 1, heap_limit);
+ set_core_data (cpu, riscv_cpu->a1, 2, stack_base);
+ set_core_data (cpu, riscv_cpu->a1, 3, stack_limit);
+}
+
+/* Handle semi-hosting calls. Register a0 contains semi-hosting call number
+ and a1 contains pointer to a buffer containing additional arguments. Return
+ 1 (true) to indicate a call was handled. */
+static int
+do_semihosting (SIM_CPU *cpu)
+{
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ switch (riscv_cpu->a0)
+ {
+ case SEMIHOST_open:
+ semihosting_open (cpu);
+ break;
+ case SEMIHOST_get_cmdline:
+ semihosting_get_cmdline (cpu);
+ break;
+ case SEMIHOST_exit:
+ semihosting_exit (cpu);
+ break;
+ case SEMIHOST_close:
+ semihosting_close (cpu);
+ break;
+ case SEMIHOST_writec:
+ semihosting_writec (cpu);
+ break;
+ case SEMIHOST_write0:
+ semihosting_write0 (cpu);
+ break;
+ case SEMIHOST_write:
+ semihosting_write (cpu);
+ break;
+ case SEMIHOST_read:
+ semihosting_read (cpu);
+ break;
+ case SEMIHOST_readc:
+ semihosting_readc (cpu);
+ break;
+ case SEMIHOST_iserror:
+ semihosting_iserror (cpu);
+ break;
+ case SEMIHOST_istty:
+ semihosting_istty (cpu);
+ break;
+ case SEMIHOST_seek:
+ semihosting_seek (cpu);
+ break;
+ case SEMIHOST_flen:
+ semihosting_flen (cpu);
+ break;
+ case SEMIHOST_tmpnam:
+ semihosting_tmpnam (cpu);
+ break;
+ case SEMIHOST_remove:
+ semihosting_remove (cpu);
+ break;
+ case SEMIHOST_rename:
+ semihosting_rename (cpu);
+ break;
+ case SEMIHOST_clock:
+ riscv_cpu->a0 = (clock () / (CLOCKS_PER_SEC / 100));
+ break;
+ case SEMIHOST_time:
+ riscv_cpu->a0 = sim_io_time (CPU_STATE (cpu));
+ break;
+ case SEMIHOST_system:
+ semihosting_system (cpu);
+ break;
+ case SEMIHOST_errno:
+ riscv_cpu->a0 = sim_io_get_errno (CPU_STATE (cpu));
+ break;
+ case SEMIHOST_heapinfo:
+ semihosting_heapinfo (cpu);
+ break;
+ case SEMIHOST_exit_extended:
+ semihosting_exit_extended (cpu);
+ break;
+ case SEMIHOST_elapsed:
+ semihosting_elapsed (cpu);
+ break;
+ case SEMIHOST_tickfreq:
+ riscv_cpu->a0 = 1000000000;
+ break;
+ default:
+ /* Semi-hosting call not supported. */
+ return 0;
+ }
+
+ return 1;
+}
+
+/* The instruction at *PC_PTR is assumed to be an ebreak instruction.
+ Check if this ebreak is surrounded by the required slli/srai
+ instructions that indicate this is a semihosting call.
+
+ If this is a semihosting call then extract the argument data and
+ perform the syscall, return 1 (true) to indicate a call was handled. In
+ this case, *PC_PTR is updated to point at the next instruction to
+ execute, this will be the instruction after the srai.
+
+ If no semihosting call was handled then return 0 (false). */
+static int
+check_and_handle_riscv_semihosting (SIM_CPU *cpu, sim_cia pc)
+{
+ int iw_len;
+ unsigned_word insn;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ /* If ebreak is at PC 0 then do not check for semi-hosting. */
+ if (pc == 0)
+ return 0;
+
+ /* RISC-V semi-hosting call is flagged using these three instructions
+
+ slli zero,zero,0x1f
+ ebreak
+ srai zero,zero,0x7
+
+ Register a0 holds the system call number and a1 holds the
+ pointer to parameter buffer. */
+
+ insn = sim_core_read_aligned_4 (cpu, pc - 4, exec_map, pc - 4);
+ iw_len = riscv_insn_length (insn);
+ if (iw_len != 4)
+ return 0;
+
+/* Instruction encoding of slli zero,zero,0x1f instruction. */
+#define SEMIHOST_SLLI_INSN 0x01f01013
+
+/* Instruction encoding of srai zero,zero,0x7 instruction. */
+#define SEMIHOST_SRAI_INSN 0x40705013
+
+ if (insn == SEMIHOST_SLLI_INSN)
+ {
+ insn = sim_core_read_aligned_4 (cpu, pc + 4, exec_map, pc + 4);
+ iw_len = riscv_insn_length (insn);
+ if (iw_len != 4)
+ return 0;
+
+ if (insn == SEMIHOST_SRAI_INSN)
+ {
+ TRACE_INSN (cpu, "semi-hosting a0=%lx,a1=%lx;", riscv_cpu->a0,
+ riscv_cpu->a1);
+
+ if (do_semihosting (cpu))
+ return 1;
+ else
+ {
+ /* Invalid semi-hosting call. */
+ SIM_DESC sd = CPU_STATE (cpu);
+ TRACE_INSN (cpu, "ebreak;");
+ sim_engine_halt (sd, cpu, NULL, pc, sim_stopped, SIM_SIGTRAP);
+ }
+ }
+ }
+
+ return 0;
+}
+
static sim_cia
execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
{
@@ -623,10 +1404,15 @@ execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
TRACE_INSN (cpu, "fence.i;");
break;
case MATCH_EBREAK:
+ if (check_and_handle_riscv_semihosting (cpu, riscv_cpu->pc))
+ {
+ pc = pc + 4;
+ break;
+ }
+
+ /* Not a RISC-V semihosting syscall. */
TRACE_INSN (cpu, "ebreak;");
- /* GDB expects us to step over EBREAK. */
- sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc + 4, sim_stopped,
- SIM_SIGTRAP);
+ sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_stopped, SIM_SIGTRAP);
break;
case MATCH_ECALL:
TRACE_INSN (cpu, "ecall;");
@@ -1517,6 +2303,7 @@ initialize_cpu (SIM_DESC sd, SIM_CPU *cpu, int mhartid)

riscv_cpu->csr.mimpid = 0x8000;
riscv_cpu->csr.mhartid = mhartid;
+ clock_start = clock ();
}
\f
/* Some utils don't like having a NULL environ. */
--
2.25.1