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 ();
 }
 
 /* Some utils don't like having a NULL environ.  */
