[PATCH v2 2/3] [sim/riscv] Add support for compressed integer instruction set

jaydeep.patil
2023-10-30T13:01:36Z

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

Added support for compressed integer instruction set ("c").
Added gdb.arch/riscv-insn-simulation.c to test it.
Added simulator tests in sim/testsuite/riscv/c-ext.s
---
.../gdb.arch/riscv-insn-simulation.c | 1544 +++++++++++++++++
.../gdb.arch/riscv-insn-simulation.exp | 31 +
sim/riscv/riscv-sim.h | 4 +
sim/riscv/sim-main.c | 326 +++-
sim/testsuite/riscv/c-ext.s | 110 ++
5 files changed, 2005 insertions(+), 10 deletions(-)
create mode 100755 gdb/testsuite/gdb.arch/riscv-insn-simulation.c
create mode 100755 gdb/testsuite/gdb.arch/riscv-insn-simulation.exp
create mode 100644 sim/testsuite/riscv/c-ext.s

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..db2b8b4f3d8
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-insn-simulation.c
@@ -0,0 +1,1544 @@
+/* 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
+
+#define DISABLE_PRINTS
+
+#if defined (DISABLE_PRINTS)
+#define print(...) ;
+#else
+#define print(format, ...) printf (format, __VA_ARGS__)
+#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 %d tests pass\n", total_tests);
+ }
+ else
+ {
+ print ("*** Total %d tests out of %d fail\n", num_fail, total_tests);
+ }
+
+ 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..f94a31cc236
--- /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 ".*All.*tests pass.*Inferior.*process.*exited normally.*"
diff --git a/sim/riscv/riscv-sim.h b/sim/riscv/riscv-sim.h
index bb296fa6e62..6d9a719eadf 100644
--- a/sim/riscv/riscv-sim.h
+++ b/sim/riscv/riscv-sim.h
@@ -107,4 +107,8 @@ static int gdb_open_modeflags[12] = {
GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
};

+#define C_REG(X) ((X) + 8) /* Register in a compressed instruction. */
+#define REG_RA 1 /* Return address register. */
+#define REG_SP 2 /* Stack piner register. */
+
#endif
diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
index 8e08eb9fc4e..da9a1aa4cdf 100644
--- a/sim/riscv/sim-main.c
+++ b/sim/riscv/sim-main.c
@@ -1193,6 +1193,317 @@ execute_a (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
return pc;
}

+static sim_cia
+execute_c (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
+{
+ SIM_DESC sd = CPU_STATE (cpu);
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ int rd = (iw >> OP_SH_RD) & OP_MASK_RD;
+ int rs1_c = ((iw >> OP_SH_CRS1S) & OP_MASK_CRS1S) + 8;
+ int rs2 = (iw >> OP_SH_CRS2) & OP_MASK_CRS2;
+ int rs2_c = ((iw >> OP_SH_CRS2S) & OP_MASK_CRS2S) + 8;
+ const char *rd_name = riscv_gpr_names_abi[rd];
+ const char *rs1_c_name = riscv_gpr_names_abi[rs1_c];
+ const char *rs2_name = riscv_gpr_names_abi[rs2];
+ const char *rs2_c_name = riscv_gpr_names_abi[rs2_c];
+ signed_word imm;
+ unsigned_word tmp;
+ sim_cia pc = riscv_cpu->pc + 2;
+
+ switch (op->match)
+ {
+ case MATCH_C_JR | MATCH_C_MV:
+ switch (op->mask)
+ {
+ case MASK_C_MV:
+ TRACE_INSN (cpu, "c.mv %s, %s; // %s = %s",
+ rd_name, rs2_name, rd_name, rs2_name);
+ store_rd (cpu, rd, riscv_cpu->regs[rs2]);
+ break;
+ case MASK_C_JR:
+ TRACE_INSN (cpu, "c.jr %s;",
+ rd_name);
+ pc = riscv_cpu->regs[rd];
+ TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
+ break;
+ }
+ break;
+ case MATCH_C_J:
+ imm = EXTRACT_CJTYPE_IMM (iw);
+ TRACE_INSN (cpu, "c.j %" PRIxTW,
+ imm);
+ pc = riscv_cpu->pc + imm;
+ TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
+ break;
+ case MATCH_C_JAL | MATCH_C_ADDIW:
+ /* JAL and ADDIW have the same mask, so switch based on op name. */
+ switch (op->name[2])
+ {
+ case 'j':
+ imm = EXTRACT_CJTYPE_IMM (iw);
+ TRACE_INSN (cpu, "c.jal %" PRIxTW,
+ imm);
+ store_rd (cpu, REG_RA, riscv_cpu->pc + 2);
+ pc = riscv_cpu->pc + imm;
+ TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
+ break;
+ case 'a':
+ imm = EXTRACT_CITYPE_IMM (iw);
+ TRACE_INSN (cpu, "c.addiw %s, %s, %#" PRIxTW "; // %s += %#" PRIxTW,
+ rd_name, rd_name, imm, rd_name, imm);
+ RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+ store_rd (cpu, rd, EXTEND32 (riscv_cpu->regs[rd] + imm));
+ break;
+ default:
+ TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
+ sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled,
+ SIM_SIGILL);
+ }
+ break;
+ case MATCH_C_JALR | MATCH_C_ADD | MATCH_C_EBREAK:
+ switch (op->mask)
+ {
+ case MASK_C_ADD:
+ TRACE_INSN (cpu, "c.add %s, %s; // %s += %s",
+ rd_name, rs2_name, rd_name, rs2_name);
+ store_rd (cpu, rd, riscv_cpu->regs[rd] + riscv_cpu->regs[rs2]);
+ break;
+ case MASK_C_JALR:
+ TRACE_INSN (cpu, "c.jalr %s, %s;",
+ riscv_gpr_names_abi[REG_RA], rd_name);
+ store_rd (cpu, REG_RA, riscv_cpu->pc + 2);
+ pc = riscv_cpu->regs[rd];
+ TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
+ break;
+ case MASK_C_EBREAK:
+ TRACE_INSN (cpu, "ebreak");
+ sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_stopped,
+ SIM_SIGTRAP);
+ }
+ break;
+ case MATCH_C_BEQZ:
+ imm = EXTRACT_CBTYPE_IMM (iw);
+ TRACE_INSN (cpu, "c.beqz %s, %#" PRIxTW "; "
+ "// if (%s == 0) goto %#" PRIxTW,
+ rs1_c_name, imm, rs1_c_name, riscv_cpu->pc + imm);
+ if (riscv_cpu->regs[rs1_c] == riscv_cpu->regs[0])
+ {
+ pc = riscv_cpu->pc + imm;
+ TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
+ }
+ break;
+ case MATCH_C_BNEZ:
+ imm = EXTRACT_CBTYPE_IMM (iw);
+ TRACE_INSN (cpu, "c.bnez %s, %#" PRIxTW "; "
+ "// if (%s != 0) goto %#" PRIxTW,
+ rs1_c_name, imm, rs1_c_name, riscv_cpu->pc + imm);
+ if (riscv_cpu->regs[rs1_c] != riscv_cpu->regs[0])
+ {
+ pc = riscv_cpu->pc + imm;
+ TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
+ }
+ break;
+ case MATCH_C_LWSP:
+ imm = EXTRACT_CITYPE_LWSP_IMM (iw);
+ TRACE_INSN (cpu, "c.lwsp %s, %" PRIiTW "(sp);",
+ rd_name, imm);
+ store_rd (cpu, rd, EXTEND32 (
+ sim_core_read_unaligned_4 (cpu, riscv_cpu->pc, read_map,
+ riscv_cpu->regs[REG_SP] + imm)));
+ break;
+ case MATCH_C_LW:
+ imm = EXTRACT_CLTYPE_LW_IMM (iw);
+ TRACE_INSN (cpu, "c.lw %s, %" PRIiTW "(%s);",
+ rs2_c_name, imm, rs1_c_name);
+ store_rd (cpu, rs2_c, EXTEND32 (
+ sim_core_read_unaligned_4 (cpu, riscv_cpu->pc, read_map,
+ riscv_cpu->regs[rs1_c] + imm)));
+ break;
+ case MATCH_C_SWSP:
+ imm = EXTRACT_CSSTYPE_SWSP_IMM (iw);
+ TRACE_INSN (cpu, "c.swsp %s, %" PRIiTW "(sp);",
+ rs2_name, imm);
+ sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map,
+ riscv_cpu->regs[REG_SP] + imm,
+ riscv_cpu->regs[rs2]);
+ break;
+ case MATCH_C_SW:
+ imm = EXTRACT_CLTYPE_LW_IMM (iw);
+ TRACE_INSN (cpu, "c.sw %s, %" PRIiTW "(%s);",
+ rs2_c_name, imm, rs1_c_name);
+ sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map,
+ riscv_cpu->regs[rs1_c] + (imm),
+ riscv_cpu->regs[rs2_c]);
+ break;
+ case MATCH_C_ADDI:
+ imm = EXTRACT_CITYPE_IMM (iw);
+ TRACE_INSN (cpu, "c.addi %s, %s, %#" PRIxTW "; // %s += %#" PRIxTW,
+ rd_name, rd_name, imm, rd_name, imm);
+ store_rd (cpu, rd, riscv_cpu->regs[rd] + imm);
+ break;
+ case MATCH_C_LUI:
+ imm = EXTRACT_CITYPE_LUI_IMM (iw);
+ TRACE_INSN (cpu, "c.lui %s, %#" PRIxTW ";",
+ rd_name, imm);
+ store_rd (cpu, rd, imm);
+ break;
+ case MATCH_C_LI:
+ imm = EXTRACT_CITYPE_IMM (iw);
+ TRACE_INSN (cpu, "c.li %s, %#" PRIxTW "; // %s = %#" PRIxTW,
+ rd_name, imm, rd_name, imm);
+ store_rd (cpu, rd, imm);
+ break;
+ case MATCH_C_ADDI4SPN:
+ imm = EXTRACT_CIWTYPE_ADDI4SPN_IMM (iw);
+ TRACE_INSN (cpu, "c.addi4spn %s, %" PRIiTW "; // %s = sp + %" PRIiTW,
+ rs2_c_name, imm, rs2_c_name, imm);
+ store_rd (cpu, rs2_c, riscv_cpu->regs[REG_SP] + (imm));
+ break;
+ case MATCH_C_ADDI16SP:
+ imm = EXTRACT_CITYPE_ADDI16SP_IMM (iw);
+ TRACE_INSN (cpu, "c.addi16sp %s, %" PRIiTW "; // %s = sp + %" PRIiTW,
+ rd_name, imm, rd_name, imm);
+ store_rd (cpu, rd, riscv_cpu->regs[REG_SP] + imm);
+ break;
+ case MATCH_C_SUB:
+ TRACE_INSN (cpu, "c.sub %s, %s; // %s = %s - %s",
+ rs1_c_name, rs2_c_name, rs1_c_name, rs1_c_name, rs2_c_name);
+ store_rd (cpu, rs1_c, riscv_cpu->regs[rs1_c] - riscv_cpu->regs[rs2_c]);
+ break;
+ case MATCH_C_AND:
+ TRACE_INSN (cpu, "c.and %s, %s; // %s = %s & %s",
+ rs1_c_name, rs2_c_name, rs1_c_name, rs1_c_name, rs2_c_name);
+ store_rd (cpu, rs1_c, riscv_cpu->regs[rs1_c] & riscv_cpu->regs[rs2_c]);
+ break;
+ case MATCH_C_OR:
+ TRACE_INSN (cpu, "c.or %s, %s; // %s = %s | %s",
+ rs1_c_name, rs2_c_name, rs1_c_name, rs1_c_name, rs2_c_name);
+ store_rd (cpu, rs1_c, riscv_cpu->regs[rs1_c] | riscv_cpu->regs[rs2_c]);
+ break;
+ case MATCH_C_XOR:
+ TRACE_INSN (cpu, "c.xor %s, %s; // %s = %s ^ %s",
+ rs1_c_name, rs2_c_name, rs1_c_name, rs1_c_name, rs2_c_name);
+ store_rd (cpu, rs1_c, riscv_cpu->regs[rs1_c] ^ riscv_cpu->regs[rs2_c]);
+ break;
+ case MATCH_C_SLLI | MATCH_C_SLLI64:
+ if (op->mask == MASK_C_SLLI64)
+ {
+ /* Reserved for custom use. */
+ TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
+ sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled,
+ SIM_SIGILL);
+ break;
+ }
+ imm = EXTRACT_CITYPE_IMM (iw);
+ TRACE_INSN (cpu, "c.slli %s, %" PRIiTW "; // %s = %s << %#" PRIxTW,
+ rd_name, imm, rd_name, rd_name, imm);
+ store_rd (cpu, rd, riscv_cpu->regs[rd] << imm);
+ break;
+ case MATCH_C_SRLI | MATCH_C_SRLI64:
+ if (op->mask == MASK_C_SRLI64)
+ {
+ /* Reserved for custom use. */
+ TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
+ sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled,
+ SIM_SIGILL);
+ break;
+ }
+ imm = EXTRACT_CITYPE_IMM (iw);
+ TRACE_INSN (cpu, "c.srli %s, %" PRIiTW "; // %s = %s >> %#" PRIxTW,
+ rs1_c_name, imm, rs1_c_name, rs1_c_name, imm);
+ if (RISCV_XLEN (cpu) == 32)
+ store_rd (cpu, rs1_c, ((uint32_t) riscv_cpu->regs[rs1_c]) >> imm);
+ else
+ store_rd (cpu, rs1_c, ((uint64_t) riscv_cpu->regs[rs1_c]) >> imm);
+ break;
+ case MATCH_C_SRAI | MATCH_C_SRAI64:
+ if (op->mask == MASK_C_SRAI64)
+ {
+ /* Reserved for custom use. */
+ TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
+ sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled,
+ SIM_SIGILL);
+ break;
+ }
+ imm = EXTRACT_CITYPE_IMM (iw);
+ TRACE_INSN (cpu, "c.srai %s, %" PRIiTW "; // %s = %s >> %#" PRIxTW,
+ rs1_c_name, imm, rs1_c_name, rs1_c_name, imm);
+ if (RISCV_XLEN (cpu) == 32)
+ {
+ if (imm > 0x1f)
+ sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled,
+ SIM_SIGILL);
+ tmp = ashiftrt (riscv_cpu->regs[rs1_c], imm);
+ }
+ else
+ tmp = ashiftrt64 (riscv_cpu->regs[rs1_c], imm);
+ store_rd (cpu, rd, tmp);
+ break;
+ case MATCH_C_ANDI:
+ imm = EXTRACT_CITYPE_IMM (iw);
+ TRACE_INSN (cpu, "c.andi %s, %" PRIiTW "; // %s = %s & %#" PRIxTW,
+ rs1_c_name, imm, rs1_c_name, rs1_c_name, imm);
+ store_rd (cpu, rs1_c, riscv_cpu->regs[rs1_c] & imm);
+ break;
+ case MATCH_C_ADDW:
+ TRACE_INSN (cpu, "c.addw %s, %s; // %s = %s + %s",
+ rs1_c_name, rs2_c_name, rs1_c_name, rs1_c_name, rs2_c_name);
+ RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+ store_rd (cpu, rs1_c,
+ EXTEND32 (riscv_cpu->regs[rs1_c] + riscv_cpu->regs[rs2_c]));
+ break;
+ case MATCH_C_SUBW:
+ TRACE_INSN (cpu, "c.subw %s, %s; // %s = %s - %s",
+ rs1_c_name, rs2_c_name, rs1_c_name, rs1_c_name, rs2_c_name);
+ RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+ store_rd (cpu, rs1_c,
+ EXTEND32 (riscv_cpu->regs[rs1_c] - riscv_cpu->regs[rs2_c]));
+ break;
+ case MATCH_C_LDSP:
+ imm = EXTRACT_CITYPE_LDSP_IMM (iw);
+ TRACE_INSN (cpu, "c.ldsp %s, %" PRIiTW "(sp);",
+ rd_name, imm);
+ RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+ store_rd (cpu, rd,
+ sim_core_read_unaligned_8 (cpu, riscv_cpu->pc, read_map,
+ riscv_cpu->regs[REG_SP] + imm));
+ break;
+ case MATCH_C_LD:
+ imm = EXTRACT_CLTYPE_LD_IMM (iw);
+ TRACE_INSN (cpu, "c.ld %s, %" PRIiTW "(%s);",
+ rs1_c_name, imm, rs2_c_name);
+ RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+ store_rd (cpu, rs2_c,
+ sim_core_read_unaligned_8 (cpu, riscv_cpu->pc, read_map,
+ riscv_cpu->regs[rs1_c] + imm));
+ break;
+ case MATCH_C_SDSP:
+ imm = EXTRACT_CSSTYPE_SDSP_IMM (iw);
+ TRACE_INSN (cpu, "c.sdsp %s, %" PRIiTW "(sp);",
+ rs2_name, imm);
+ RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+ sim_core_write_unaligned_8 (cpu, riscv_cpu->pc, write_map,
+ riscv_cpu->regs[REG_SP] + imm,
+ riscv_cpu->regs[rs2]);
+ break;
+ case MATCH_C_SD:
+ imm = EXTRACT_CLTYPE_LD_IMM (iw);
+ TRACE_INSN (cpu, "c.sd %s, %" PRIiTW "(%s);",
+ rs2_c_name, imm, rs1_c_name);
+ RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+ sim_core_write_unaligned_8 (cpu, riscv_cpu->pc, write_map,
+ riscv_cpu->regs[rs1_c] + imm,
+ riscv_cpu->regs[rs2_c]);
+ break;
+ default:
+ TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
+ sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled,
+ SIM_SIGILL);
+ }
+
+ return pc;
+}
+
static sim_cia
execute_one (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
{
@@ -1214,6 +1525,8 @@ execute_one (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
case INSN_CLASS_M:
case INSN_CLASS_ZMMUL:
return execute_m (cpu, iw, op);
+ case INSN_CLASS_C:
+ return execute_c (cpu, iw, op);
default:
TRACE_INSN (cpu, "UNHANDLED EXTENSION: %d", op->insn_class);
sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled, SIM_SIGILL);
@@ -1239,17 +1552,10 @@ void step_once (SIM_CPU *cpu)

iw = sim_core_read_aligned_2 (cpu, pc, exec_map, pc);

- /* Reject non-32-bit opcodes first. */
len = riscv_insn_length (iw);
- if (len != 4)
- {
- sim_io_printf (sd, "sim: bad insn len %#x @ %#" PRIxTA ": %#" PRIxTW "\n",
- len, pc, iw);
- sim_engine_halt (sd, cpu, NULL, pc, sim_signalled, SIM_SIGILL);
- }
-
- iw |= ((unsigned_word) sim_core_read_aligned_2 (
- cpu, pc, exec_map, pc + 2) << 16);
+ if (len == 4)
+ iw |= ((unsigned_word) sim_core_read_aligned_2
+ (cpu, pc, exec_map, pc + 2) << 16);

TRACE_CORE (cpu, "0x%08" PRIxTW, iw);

diff --git a/sim/testsuite/riscv/c-ext.s b/sim/testsuite/riscv/c-ext.s
new file mode 100644
index 00000000000..049d2b4323e
--- /dev/null
+++ b/sim/testsuite/riscv/c-ext.s
@@ -0,0 +1,110 @@
+# Basic load store tests.
+# mach: riscv
+
+.include "testutils.inc"
+
+ .data
+ .align 4
+_data:
+ .word 1234
+ .word 0
+
+ start
+ la a0, _data
+
+ # Test load-store instructions.
+ .option push
+ .option arch, +c
+ c.lw a1,0(a0)
+ c.sw a1,4(a0)
+ c.lw a2,4(a0)
+ .option pop
+
+ li a5,1234
+ bne a1,a5,test_fail
+ bne a2,a5,test_fail
+
+ # Test basic arithmetic.
+ .option push
+ .option arch, +c
+ c.li a0,0
+ c.li a1,1
+ c.addi a0,1
+ c.addi a0,-1
+ c.addw a0,a1
+ c.subw a0,a1
+ .option pop
+
+ li a5,1
+ bne a0,x0,test_fail
+ bne a1,a5,test_fail
+
+ # Test logical operations.
+ .option push
+ .option arch, +c
+ c.li a0,7
+ c.li a1,7
+ c.li a2,4
+ c.li a3,3
+ c.li a4,3
+ c.andi a0,3
+ c.and a1,a0
+ c.or a2,a3
+ c.xor a4,a4
+ .option pop
+
+ li a5,3
+ bne a0,a5,test_fail
+ bne a1,a5,test_fail
+ bne a4,x0,test_fail
+ li a5,7
+ bne a2,a5,test_fail
+
+ # Test shift operations.
+ .option push
+ .option arch, +c
+ c.li a0,4
+ c.li a1,4
+ c.slli a0,1
+ c.srli a1,1
+ .option pop
+
+ li a5,8
+ bne a0,a5,test_fail
+ li a5,2
+ bne a1,a5,test_fail
+
+ # Test jump instruction.
+ .option push
+ .option arch, +c
+ c.j 1f
+ .option pop
+
+ j test_fail
+1:
+ la a0,2f
+
+ # Test jump register instruction.
+ .option push
+ .option arch, +c
+ c.jr a0
+ .option pop
+
+ j test_fail
+
+2:
+ # Test branch instruction.
+ .option push
+ .option arch, +c
+ c.li a0,1
+ c.beqz a0,test_fail
+ c.li a0,0
+ c.bnez a0,test_fail
+ .option pop
+
+test_pass:
+ pass
+ fail
+
+test_fail:
+ fail
--
2.25.1