public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v3 0/3] sim: riscv: Compressed instruction simulation and semi-hosting support
@ 2023-12-17  6:52 jaydeep.patil
  2023-12-17  6:52 ` [PATCH v3 1/3] [sim/riscv] Fix crash during instruction decoding jaydeep.patil
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: jaydeep.patil @ 2023-12-17  6:52 UTC (permalink / raw)
  To: gdb-patches
  Cc: aburgess, vapier, joseph.faulls, bhushan.attarde, jaydeep.patil

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

Hi Andrew,

Addressed review comments.

The compressed instructions can be tested without the need for GDB test
and semi-hosting support. Simulator specific tests are added in
sim/testsuite/riscv/c-ext.s file.

I have now combined the semi-hosting support in one patch (v3-0003-*). This is
based on semi-hosting calls generated by newlib (--specs=semihost.specs option)
and picolibc libraries.

Jaydeep Patil (3):
  [sim/riscv] Fix crash during instruction decoding
  [sim/riscv] Add support for compressed integer instruction set simulation
  [sim/riscv] Add semi-hosting support

 gdb/testsuite/gdb.arch/riscv-exit-getcmd.c    |   26 +
 gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp  |   27 +
 .../gdb.arch/riscv-insn-simulation.c          | 1542 +++++++++++++++++
 .../gdb.arch/riscv-insn-simulation.exp        |   31 +
 sim/riscv/sim-main.c                          | 1131 +++++++++++-
 sim/testsuite/riscv/c-ext.s                   |  110 ++
 6 files changed, 2853 insertions(+), 14 deletions(-)
 create mode 100644 gdb/testsuite/gdb.arch/riscv-exit-getcmd.c
 create mode 100755 gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp
 create mode 100755 gdb/testsuite/gdb.arch/riscv-insn-simulation.c
 create mode 100755 gdb/testsuite/gdb.arch/riscv-insn-simulation.exp
 create mode 100755 sim/testsuite/riscv/c-ext.s

-- 
2.25.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v3 1/3] [sim/riscv] Fix crash during instruction decoding
  2023-12-17  6:52 [PATCH v3 0/3] sim: riscv: Compressed instruction simulation and semi-hosting support jaydeep.patil
@ 2023-12-17  6:52 ` jaydeep.patil
  2023-12-18 16:26   ` Mike Frysinger
  2023-12-17  6:52 ` [PATCH v3 2/3] [sim/riscv] Add support for compressed integer instruction set simulation jaydeep.patil
  2023-12-17  6:52 ` [PATCH v3 3/3] [sim/riscv] Add semi-hosting support jaydeep.patil
  2 siblings, 1 reply; 10+ messages in thread
From: jaydeep.patil @ 2023-12-17  6:52 UTC (permalink / raw)
  To: gdb-patches
  Cc: aburgess, vapier, joseph.faulls, bhushan.attarde, jaydeep.patil

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

The match_never() function has been removed and thus step_once() crashes
during instruction decoding. Fixed it by checking for null pointer before
invoking function attached to match_func member of riscv_opcode structure.
---
 sim/riscv/sim-main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
index afdfcf50656..8a23d2aa1f9 100644
--- a/sim/riscv/sim-main.c
+++ b/sim/riscv/sim-main.c
@@ -1041,7 +1041,7 @@ void step_once (SIM_CPU *cpu)
   for (; op->name; op++)
     {
       /* Does the opcode match?  */
-      if (! op->match_func (op, iw))
+      if (! op->match_func || ! op->match_func (op, iw))
 	continue;
       /* Is this a pseudo-instruction and may we print it as such?  */
       if (op->pinfo & INSN_ALIAS)
-- 
2.25.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v3 2/3] [sim/riscv] Add support for compressed integer instruction set simulation
  2023-12-17  6:52 [PATCH v3 0/3] sim: riscv: Compressed instruction simulation and semi-hosting support jaydeep.patil
  2023-12-17  6:52 ` [PATCH v3 1/3] [sim/riscv] Fix crash during instruction decoding jaydeep.patil
@ 2023-12-17  6:52 ` jaydeep.patil
  2023-12-18 16:29   ` Mike Frysinger
  2023-12-17  6:52 ` [PATCH v3 3/3] [sim/riscv] Add semi-hosting support jaydeep.patil
  2 siblings, 1 reply; 10+ messages in thread
From: jaydeep.patil @ 2023-12-17  6:52 UTC (permalink / raw)
  To: gdb-patches
  Cc: aburgess, vapier, joseph.faulls, bhushan.attarde, jaydeep.patil

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

Added support for compressed integer instruction set ("c"). Added test file
sim/testsuite/riscv/c-ext.s to test compressed instructions.
---
 sim/riscv/sim-main.c        | 336 ++++++++++++++++++++++++++++++++++--
 sim/testsuite/riscv/c-ext.s | 110 ++++++++++++
 2 files changed, 436 insertions(+), 10 deletions(-)
 create mode 100755 sim/testsuite/riscv/c-ext.s

diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
index 8a23d2aa1f9..1b374323aa7 100644
--- a/sim/riscv/sim-main.c
+++ b/sim/riscv/sim-main.c
@@ -974,6 +974,327 @@ execute_a (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
   return pc;
 }
 
+/* Register in a compressed instruction.  */
+#define C_REG(X) ((X) + 8)
+
+/* Return address register.  */
+#define REG_RA 1
+
+/* Stack pointer register.  */
+#define REG_SP 2
+
+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,
+		  EXTEND32 ((uint32_t) riscv_cpu->regs[rs1_c] >> imm));
+      else
+	store_rd (cpu, rs1_c, 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)
 {
@@ -989,6 +1310,8 @@ execute_one (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
     {
     case INSN_CLASS_A:
       return execute_a (cpu, iw, op);
+    case INSN_CLASS_C:
+      return execute_c (cpu, iw, op);
     case INSN_CLASS_I:
       return execute_i (cpu, iw, op);
     case INSN_CLASS_M:
@@ -1019,17 +1342,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 100755
index 00000000000..5bba2058d8e
--- /dev/null
+++ b/sim/testsuite/riscv/c-ext.s
@@ -0,0 +1,110 @@
+# Check that the compressed instructions ("c") run without any faults.
+# 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


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v3 3/3] [sim/riscv] Add semi-hosting support
  2023-12-17  6:52 [PATCH v3 0/3] sim: riscv: Compressed instruction simulation and semi-hosting support jaydeep.patil
  2023-12-17  6:52 ` [PATCH v3 1/3] [sim/riscv] Fix crash during instruction decoding jaydeep.patil
  2023-12-17  6:52 ` [PATCH v3 2/3] [sim/riscv] Add support for compressed integer instruction set simulation jaydeep.patil
@ 2023-12-17  6:52 ` jaydeep.patil
  2023-12-18 16:30   ` Mike Frysinger
  2 siblings, 1 reply; 10+ messages in thread
From: jaydeep.patil @ 2023-12-17  6:52 UTC (permalink / raw)
  To: gdb-patches
  Cc: aburgess, vapier, joseph.faulls, bhushan.attarde, jaydeep.patil

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

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

diff --git a/gdb/testsuite/gdb.arch/riscv-exit-getcmd.c b/gdb/testsuite/gdb.arch/riscv-exit-getcmd.c
new file mode 100644
index 00000000000..b557cc83cc5
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-exit-getcmd.c
@@ -0,0 +1,26 @@
+/* This file is part of GDB, the GNU debugger.
+
+   Copyright 2023 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Test basic semi-hosting calls GET_CMDLINE and EXIT.  */
+
+int
+main (int argc, char **argv)
+{
+  if (argc != 4)
+    return 1;
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp b/gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp
new file mode 100755
index 00000000000..e3e6c6834a6
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-exit-getcmd.exp
@@ -0,0 +1,27 @@
+# Copyright 2023 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test basic semi-hosting calls GET_CMDLINE and EXIT.
+
+require {istarget "riscv*-*-*"}
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
+	  {debug quiet}] } {
+    return -1
+}
+
+gdb_test "run 1 2 3" ".*Inferior.*process.*exited normally.*"
diff --git a/gdb/testsuite/gdb.arch/riscv-insn-simulation.c b/gdb/testsuite/gdb.arch/riscv-insn-simulation.c
new file mode 100755
index 00000000000..3cb6f9e50b6
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-insn-simulation.c
@@ -0,0 +1,1542 @@
+/* This file is part of GDB, the GNU debugger.
+
+   Copyright 2023 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#if (__riscv_xlen >= 64)
+#define SKIP_c_flw
+#define SKIP_c_flwsp
+#define SKIP_c_fsw
+#define SKIP_c_fswsp
+#define SKIP_c_jal
+#endif
+
+#if (__riscv_xlen == 32)
+#define SKIP_c_ld
+#define SKIP_c_ldsp
+#define SKIP_c_sd
+#define SKIP_c_sdsp
+#define SKIP_c_addiw
+#define SKIP_c_addw
+#define SKIP_c_subw
+#endif
+
+/* Disable tests that are not implemented in GDB simulator yet.  */
+#define DISABLE_GDB_SIM_TESTS
+
+#if defined (DISABLE_GDB_SIM_TESTS)
+#define SKIP_c_flw
+#define SKIP_c_flwsp
+#define SKIP_c_fsw
+#define SKIP_c_fswsp
+#define SKIP_c_fld
+#define SKIP_c_fldsp
+#define SKIP_c_fsd
+#define SKIP_c_fsdsp
+#endif
+
+#if defined (DISABLE_PRINTS)
+#define print(...) ;
+#else
+#define print(format, ...) printf (format)
+#endif
+
+#if (__riscv_xlen >= 64)
+typedef uint64_t riscv_reg_t;
+#elif (__riscv_xlen == 32)
+typedef uint32_t riscv_reg_t;
+#endif
+
+int total_tests = 0;
+int num_fail = 0;
+int num_pass = 0;
+int debug = 0;
+
+void
+i_check (int line, const char *func, uint32_t value1, uint32_t value2)
+{
+  total_tests++;
+  if (value1 != value2)
+    {
+      print ("  *** FAIL: %s:%d: (%d) != (%d)\n", func, line, value1, value2);
+      num_fail++;
+    }
+  else
+    {
+      num_pass ++;
+      if (debug)
+	print ("  PASS: %s:%d\n", func, line);
+    }
+}
+
+void
+l_check (int line, const char *func, uint64_t value1, uint64_t value2)
+{
+  total_tests++;
+  if (value1 != value2)
+    {
+      print ("  *** FAIL: %s:%d: (0x%lx) != (0x%lx)\n", func, line, value1,
+	     value2);
+      num_fail++;
+    }
+  else
+    {
+      num_pass++;
+      if (debug)
+	print ("  PASS: %s:%d\n", func, line);
+    }
+}
+
+void
+f_check (int line, const char *func, float value1, float value2)
+{
+  total_tests++;
+  if (value1 != value2)
+    {
+      print ("  *** FAIL: %s:%d: (%ff) != (%ff)\n", func, line, value1,
+	     value2);
+      num_fail++;
+    }
+  else
+    {
+      num_pass++;
+      if (debug)
+	print ("  PASS: %s:%d\n", func, line);
+    }
+}
+
+void
+d_check (int line, const char *func, double value1, double value2)
+{
+  total_tests++;
+  if (value1 != value2)
+    {
+      print ("  *** FAIL: %s:%d: (%lf) != (%lf)\n", func, line, value1,
+	     value2);
+      num_fail++;
+    }
+  else
+    {
+      num_pass++;
+      if (debug)
+	print ("  PASS: %s:%d\n", func, line);
+    }
+}
+
+void
+fail (int line, const char *func, const char *msg)
+{
+  total_tests++;
+  print ("  *** FAIL: %s:%d: (%s)\n", func, line, msg);
+  num_fail++;
+}
+
+void
+info (const char *str)
+{
+  print ("%s\n", str);
+}
+
+#define I_CHECK(VAL1, VAL2) i_check (__LINE__, __FUNCTION__, (VAL1), (VAL2));
+#define L_CHECK(VAL1, VAL2) l_check (__LINE__, __FUNCTION__, (VAL1), (VAL2));
+#define F_CHECK(VAL1, VAL2) f_check (__LINE__, __FUNCTION__, (VAL1), (VAL2));
+#define D_CHECK(VAL1, VAL2) d_check (__LINE__, __FUNCTION__, (VAL1), (VAL2));
+#define FAIL(STR) fail (__LINE__, __FUNCTION__, (STR));
+
+void
+test_c_lwsp ()
+{
+  volatile uint32_t on_stack[] = { 0x1111, 0x2222, 0x3333, 0x4444 };
+  uint32_t a = 0, offset = 0;
+  riscv_reg_t stack_ptr, var_ptr;
+
+  info ("Testing c.lwsp");
+
+  /* Find offset of on_stack.  */
+  asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+  var_ptr = (riscv_reg_t) &on_stack[0];
+  offset = var_ptr - stack_ptr;
+  (void) offset;
+
+  if (offset == 0)
+    {
+      asm volatile ("c.lwsp %0,0(sp)" : "=r" (a));
+      I_CHECK (a, 0x1111);
+
+      asm volatile ("c.lwsp %0,4(sp)" : "=r" (a));
+      I_CHECK (a, 0x2222);
+
+      asm volatile ("c.lwsp %0,8(sp)" : "=r" (a));
+      I_CHECK (a, 0x3333);
+
+      asm volatile ("c.lwsp %0,12(sp)" : "=r" (a));
+      I_CHECK (a, 0x4444);
+    }
+  else if (offset == 8)
+    {
+      asm volatile ("c.lwsp %0,8(sp)" : "=r" (a));
+      I_CHECK (a, 0x1111);
+
+      asm volatile ("c.lwsp %0,12(sp)" : "=r" (a));
+      I_CHECK (a, 0x2222);
+
+      asm volatile ("c.lwsp %0,16(sp)" : "=r" (a));
+      I_CHECK (a, 0x3333);
+
+      asm volatile ("c.lwsp %0,20(sp)" : "=r" (a));
+      I_CHECK (a, 0x4444);
+    }
+}
+
+void
+test_c_ldsp ()
+{
+#if defined (SKIP_c_ldsp)
+  info ("--- Disable c.ldsp");
+#else
+  volatile uint64_t on_stack[] = { 0x11112222, 0x33334444 };
+  uint64_t a = 0, offset = 0;
+  riscv_reg_t stack_ptr, var_ptr;
+
+  info ("Testing c.ldsp");
+
+  /* Find offset of on_stack.  */
+  asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+  var_ptr = (riscv_reg_t) &on_stack[0];
+  offset = var_ptr - stack_ptr;
+  (void) offset;
+
+  asm volatile ("c.ldsp %0,0(sp)" : "=r" (a));
+  L_CHECK (a, 0x11112222ul);
+
+  asm volatile ("c.ldsp %0,8(sp)" : "=r" (a));
+  L_CHECK (a, 0x33334444ul);
+#endif
+}
+
+void
+test_c_flwsp ()
+{
+#if defined (SKIP_c_flwsp)
+  info ("--- Disable c.flwsp");
+#else
+  volatile float on_stack[] = { 1.23f, 3.14f, -5.6f, 10.9f };
+  float a;
+  uint32_t offset = 0;
+  riscv_reg_t stack_ptr, var_ptr;
+
+  info ("Testing c.flwsp");
+
+  /* Find offset of on_stack.  */
+  asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+  var_ptr = (riscv_reg_t) &on_stack[0];
+  offset = var_ptr - stack_ptr;
+  (void) offset;
+
+  if (offset == 0)
+    {
+      asm volatile ("c.flwsp %0,0(sp)" : "=f" (a));
+      F_CHECK (a, 1.23f);
+
+      asm volatile ("c.flwsp %0,4(sp)" : "=f" (a));
+      F_CHECK (a, 3.14f);
+
+      asm volatile ("c.flwsp %0,8(sp)" : "=f" (a));
+      F_CHECK (a, -5.6f);
+
+      asm volatile ("c.flwsp %0,12(sp)" : "=f" (a));
+      F_CHECK (a, 10.9f);
+    }
+  else if (offset == 8)
+    {
+      asm volatile ("c.flwsp %0,8(sp)" : "=f" (a));
+      F_CHECK (a, 1.23f);
+
+      asm volatile ("c.flwsp %0,12(sp)" : "=f" (a));
+      F_CHECK (a, 3.14f);
+
+      asm volatile ("c.flwsp %0,16(sp)" : "=f" (a));
+      F_CHECK (a, -5.6f);
+
+      asm volatile ("c.flwsp %0,20(sp)" : "=f" (a));
+      F_CHECK (a, 10.9f);
+    }
+#endif
+}
+
+void
+test_c_fldsp ()
+{
+#if defined (SKIP_c_fldsp)
+  info ("--- Disable c.fldsp");
+#else
+  volatile double on_stack[] = { 1.23, -5.89 };
+  double a = 0;
+  uint32_t offset = 0;
+  riscv_reg_t stack_ptr, var_ptr;
+
+  info ("Testing c.fldsp");
+
+  /* Find offset of on_stack.  */
+  asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+  var_ptr = (riscv_reg_t) &on_stack[0];
+  offset = var_ptr - stack_ptr;
+  (void) offset;
+
+  asm volatile ("c.fldsp %0,0(sp)" : "=f" (a));
+  D_CHECK (a, 1.23);
+
+  asm volatile ("c.fldsp %0,8(sp)" : "=f" (a));
+  D_CHECK (a, -5.89);
+#endif
+}
+
+void
+test_c_swsp ()
+{
+  volatile uint32_t on_stack[] = { 0x1111, 0x2222, 0x3333, 0x4444 };
+  uint32_t a, offset = 0;
+  riscv_reg_t stack_ptr, var_ptr;
+
+  info ("Testing c.swsp");
+
+  /* Find offset of on_stack.  */
+  asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+  var_ptr = (riscv_reg_t) &on_stack[0];
+  offset = var_ptr - stack_ptr;
+
+  if (offset == 0)
+    {
+      a = 0xbeef;
+      asm volatile ("c.swsp %0,0(sp)" : : "r" (a));
+
+      a = 0xdead;
+      asm volatile ("c.swsp %0,4(sp)" : : "r" (a));
+
+      a = 0xabcd;
+      asm volatile ("c.swsp %0,8(sp)" : : "r" (a));
+
+      a = 0x1298;
+      asm volatile ("c.swsp %0,12(sp)" : : "r" (a));
+    }
+  else if (offset == 8)
+    {
+      a = 0xbeef;
+      asm volatile ("c.swsp %0,8(sp)" : : "r" (a));
+
+      a = 0xdead;
+      asm volatile ("c.swsp %0,12(sp)" : : "r" (a));
+
+      a = 0xabcd;
+      asm volatile ("c.swsp %0,16(sp)" : : "r" (a));
+
+      a = 0x1298;
+      asm volatile ("c.swsp %0,20(sp)" : : "r" (a));
+    }
+  else
+    {
+      FAIL ("Invalid stack offset (expected 0 or 8)");
+      return;
+    }
+
+  I_CHECK (on_stack[0], 0xbeef);
+  I_CHECK (on_stack[1], 0xdead);
+  I_CHECK (on_stack[2], 0xabcd);
+  I_CHECK (on_stack[3], 0x1298);
+}
+
+void
+test_c_sdsp ()
+{
+#if defined (SKIP_c_sdsp)
+  info ("--- Disable c.sdsp");
+#else
+  volatile uint64_t on_stack[] = { 0x11112222, 0x33334444 };
+  uint64_t a = 0, offset = 0;
+  riscv_reg_t stack_ptr, var_ptr;
+
+  info ("Testing c.sdsp");
+
+  /* Find offset of on_stack.  */
+  asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+  var_ptr = (riscv_reg_t) &on_stack[0];
+  offset = var_ptr - stack_ptr;
+  (void) offset;
+
+  a = 0xdeadbeef;
+  asm volatile ("c.sdsp %0,0(sp)" : : "r" (a));
+
+  a = 0xabcd1234;
+  asm volatile ("c.sdsp %0,8(sp)" : : "r" (a));
+
+  L_CHECK (on_stack[0], 0xdeadbeef);
+  L_CHECK (on_stack[1], 0xabcd1234);
+#endif
+}
+
+void
+test_c_fswsp ()
+{
+#if defined (SKIP_c_fswsp)
+  info ("--- Disable c.fswsp");
+#else
+  volatile float on_stack[] = { 1.23f, 3.14f, -5.6f, 10.9f };
+  float a;
+  uint32_t offset = 0;
+  riscv_reg_t stack_ptr, var_ptr;
+
+  info ("Testing c.fswsp");
+
+  /* Find offset of on_stack.  */
+  asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+  var_ptr = (riscv_reg_t) &on_stack[0];
+  offset = var_ptr - stack_ptr;
+  (void) offset;
+
+  if (offset == 0)
+    {
+      a = -12.5f;
+      asm volatile ("c.fswsp %0,0(sp)" : : "f" (a));
+
+      a = 72.8f;
+      asm volatile ("c.fswsp %0,4(sp)" : "=f" (a));
+
+      a = 0.5f;
+      asm volatile ("c.fswsp %0,8(sp)" : "=f" (a));
+
+      a = 4.7f;
+      asm volatile ("c.fswsp %0,12(sp)" : "=f" (a));
+
+    }
+  else if (offset == 8)
+    {
+      a = -12.5f;
+      asm volatile ("c.fswsp %0,8(sp)" : "=f" (a));
+
+      a = 72.8f;
+      asm volatile ("c.fswsp %0,12(sp)" : "=f" (a));
+
+      a = 0.5f;
+      asm volatile ("c.fswsp %0,16(sp)" : "=f" (a));
+
+      a = 4.7f;
+      asm volatile ("c.fswsp %0,20(sp)" : "=f" (a));
+    }
+  else
+    {
+      FAIL ("Invalid stack offset (expected 0 or 8)");
+      return;
+    }
+
+  F_CHECK (on_stack[0], -12.5f);
+  F_CHECK (on_stack[1], 72.8f);
+  F_CHECK (on_stack[2], 0.5f);
+  F_CHECK (on_stack[3], 4.7f);
+
+#endif
+}
+
+void
+test_c_fsdsp ()
+{
+#if defined (SKIP_c_fsdsp)
+  info ("--- Disable c.fsdsp");
+#else
+  volatile double on_stack[] = { -1.23, 5.89 };
+  double a = 0;
+  uint32_t offset = 0;
+  riscv_reg_t stack_ptr, var_ptr;
+
+  info ("Testing c.fsdsp");
+
+  /* Find offset of on_stack.  */
+  asm volatile ("c.mv %0,sp" : "=r" (stack_ptr));
+  var_ptr = (riscv_reg_t) &on_stack[0];
+  offset = var_ptr - stack_ptr;
+  (void) offset;
+
+  a = 1234.55;
+  asm volatile ("c.fsdsp %0,0(sp)" : : "f" (a));
+
+  a = -7890.15;
+  asm volatile ("c.fsdsp %0,8(sp)" : : "f" (a));
+
+  D_CHECK (on_stack[0], 1234.55);
+  D_CHECK (on_stack[1], -7890.15);
+#endif
+}
+
+void
+test_c_lw ()
+{
+  static uint32_t g_data[] = { 0x1111, 0x2222, 0x3333, 0x4444 };
+  uint32_t a = 0;
+  riscv_reg_t var_ptr;
+
+  info ("Testing c.lw");
+
+  var_ptr = (riscv_reg_t) &g_data[0];
+
+  asm volatile ("c.lw %0,0(%1)" : "=r" (a) : "r" (var_ptr));
+  I_CHECK (a, 0x1111);
+
+  asm volatile ("c.lw %0,4(%1)" : "=r" (a) : "r" (var_ptr));
+  I_CHECK (a, 0x2222);
+
+  asm volatile ("c.lw %0,8(%1)" : "=r" (a) : "r" (var_ptr));
+  I_CHECK (a, 0x3333);
+
+  asm volatile ("c.lw %0,12(%1)" : "=r" (a) : "r" (var_ptr));
+  I_CHECK (a, 0x4444);
+}
+
+void
+test_c_ld ()
+{
+#if defined (SKIP_c_ld)
+  info ("--- Disable c.ld");
+#else
+  static uint64_t g_data[] = { 0x11112222, 0x33334444 };
+  uint32_t a = 0;
+  riscv_reg_t var_ptr;
+
+  info ("Testing c.ld");
+
+  var_ptr = (riscv_reg_t) &g_data[0];
+
+  asm volatile ("c.ld %0,0(%1)" : "=r" (a) : "r" (var_ptr));
+  L_CHECK (a, 0x11112222);
+
+  asm volatile ("c.ld %0,8(%1)" : "=r" (a) : "r" (var_ptr));
+  L_CHECK (a, 0x33334444);
+#endif
+}
+
+void
+test_c_flw ()
+{
+#if defined (SKIP_c_flw)
+  info ("--- Disable c.flw");
+#else
+  static float g_data[] = { 1.23f, 3.14f, -5.6f, 10.9f };
+  float a = 0;
+  riscv_reg_t var_ptr;
+
+  info ("Testing c.flw");
+
+  var_ptr = (riscv_reg_t) &g_data[0];
+
+  asm volatile ("c.flw %0,0(%1)" : "=f" (a) : "r" (var_ptr));
+  F_CHECK (a, 1.23f);
+
+  asm volatile ("c.flw %0,4(%1)" : "=f" (a) : "r" (var_ptr));
+  F_CHECK (a, 3.14f);
+
+  asm volatile ("c.flw %0,8(%1)" : "=f" (a) : "r" (var_ptr));
+  F_CHECK (a, -5.6f);
+
+  asm volatile ("c.flw %0,12(%1)" : "=f" (a) : "r" (var_ptr));
+  F_CHECK (a, 10.9f);
+#endif
+}
+
+void
+test_c_fld ()
+{
+#if defined (SKIP_c_fld)
+  info ("--- Disable c.fld");
+#else
+  static double g_data[] = { 1234.5, -7890.4 };
+  double a = 0;
+  riscv_reg_t var_ptr;
+
+  info ("Testing c.fld");
+
+  var_ptr = (riscv_reg_t) &g_data[0];
+
+  asm volatile ("c.fld %0,0(%1)" : "=f" (a) : "r" (var_ptr));
+  D_CHECK (a, 1234.5);
+
+  asm volatile ("c.fld %0,8(%1)" : "=f" (a) : "r" (var_ptr));
+  D_CHECK (a, -7890.4);
+#endif
+}
+
+void
+test_c_sw ()
+{
+  volatile uint32_t g_data[] = { 0x1111, 0x2222, 0x3333, 0x4444 };
+  uint32_t a;
+  riscv_reg_t stack_ptr, var_ptr;
+
+  info ("Testing c.sw");
+
+  var_ptr = (riscv_reg_t) &g_data[0];
+
+  a = 0xbeef;
+  asm volatile ("c.sw %0,0(%1)" : : "r" (a), "r" (var_ptr));
+
+  a = 0xdead;
+  asm volatile ("c.sw %0,4(%1)" : : "r" (a), "r" (var_ptr));
+
+  a = 0xabcd;
+  asm volatile ("c.sw %0,8(%1)" : : "r" (a), "r" (var_ptr));
+
+  a = 0x1298;
+  asm volatile ("c.sw %0,12(%1)" : : "r" (a), "r" (var_ptr));
+
+  I_CHECK (g_data[0], 0xbeef);
+  I_CHECK (g_data[1], 0xdead);
+  I_CHECK (g_data[2], 0xabcd);
+  I_CHECK (g_data[3], 0x1298);
+}
+
+void
+test_c_sd ()
+{
+#if defined (SKIP_c_sd)
+  info ("--- Disable c.sd");
+#else
+  volatile uint64_t g_data[] = { 0x1111, 0x2222, 0x3333, 0x4444 };
+  uint64_t a;
+  riscv_reg_t var_ptr;
+
+  info ("Testing c.sd");
+
+  var_ptr = (riscv_reg_t) &g_data[0];
+
+  a = 0xbeefdead;
+  asm volatile ("c.sd %0,0(%1)" : : "r" (a), "r" (var_ptr));
+
+  a = 0xabcd1298;
+  asm volatile ("c.sd %0,8(%1)" : : "r" (a), "r" (var_ptr));
+
+  L_CHECK (g_data[0], 0xbeefdead);
+  L_CHECK (g_data[1], 0xabcd1298);
+#endif
+}
+
+void
+test_c_fsw ()
+{
+#if defined (SKIP_c_fsw)
+  info ("--- Disable c.fsw");
+#else
+  volatile float g_data[] = { 1.0f, 2.0f, 3.0f, -4.0f };
+  float a;
+  riscv_reg_t stack_ptr, var_ptr;
+
+  info ("Testing c.fsw");
+
+  var_ptr = (riscv_reg_t) &g_data[0];
+
+  a = 12.5f;
+  asm volatile ("c.fsw %0,0(%1)" : : "f" (a), "r" (var_ptr));
+
+  a = -7.9f;
+  asm volatile ("c.fsw %0,4(%1)" : : "f" (a), "r" (var_ptr));
+
+  a = 123.4f;
+  asm volatile ("c.fsw %0,8(%1)" : : "f" (a), "r" (var_ptr));
+
+  a = 0.5f;
+  asm volatile ("c.fsw %0,12(%1)" : : "f" (a), "r" (var_ptr));
+
+  F_CHECK (g_data[0], 12.5f);
+  F_CHECK (g_data[1], -7.9f);
+  F_CHECK (g_data[2], 123.4f);
+  F_CHECK (g_data[3], 0.5f);
+#endif
+}
+
+void
+test_c_fsd ()
+{
+#if defined (SKIP_c_fsd)
+  info ("--- Disable c.fsd");
+#else
+  volatile double g_data[] = { 1.0, 2.0 };
+  double a;
+  riscv_reg_t stack_ptr, var_ptr;
+
+  info ("Testing c.fsd");
+
+  var_ptr = (riscv_reg_t) &g_data[0];
+
+  a = 1234.5;
+  asm volatile ("c.fsd %0,0(%1)" : : "f" (a), "r" (var_ptr));
+
+  a = -7892.9;
+  asm volatile ("c.fsd %0,8(%1)" : : "f" (a), "r" (var_ptr));
+
+  F_CHECK (g_data[0], 1234.5);
+  F_CHECK (g_data[1], -7892.9);
+
+#endif
+}
+
+void
+test_c_j ()
+{
+  volatile int a = 0, b = 5;
+
+  info ("Testing c.j");
+
+label_b:
+  /* If we have jumped back.  */
+  if (a == 7 && b == 5)
+    return;
+
+  asm goto ("c.j %l[label_f]" : : : : label_f);
+  asm volatile ("c.mv %0, %1" : "=r" (a) : "r" (b));
+
+  FAIL ("Jumped at wrong location");
+  return;
+
+label_f:
+
+  I_CHECK (a, 0);
+  I_CHECK (b, 5);
+
+  a = 7;
+  asm goto ("c.j %l[label_b]" : : : : label_b);
+  asm volatile ("c.mv %0, %1" : "=r" (a) : "r" (b));
+}
+
+void
+test_c_jal ()
+{
+#if defined (SKIP_c_jal)
+  info ("--- Disable c.jal");
+#else
+  volatile int a = 0, b = 5;
+  riscv_reg_t ra;
+
+  info ("Testing c.jal");
+
+label_b:
+  /* If we have jumped back.  */
+  if (a == 7 && b == 5)
+    {
+      asm volatile ("c.mv %0, ra" : "=r" (ra));
+      L_CHECK (ra, (riscv_reg_t) &&ret_label_2);
+      return;
+    }
+
+  asm goto ("c.jal %l[label_f]" : : : : label_f);
+ret_label_1:
+  asm volatile ("c.mv %0, %1" : "=r" (a) : "r" (b));
+
+  FAIL ("Jumped at wrong location");
+  return;
+
+label_f:
+  asm volatile ("c.mv %0, ra" : "=r" (ra));
+
+  L_CHECK (ra, (riscv_reg_t) &&ret_label_1);
+  I_CHECK (a, 0);
+  I_CHECK (b, 5);
+
+  /* Jump back.  */
+  a = 7;
+  asm goto ("c.jal %l[label_b]" : : : : label_b);
+ret_label_2:
+  asm volatile ("c.nop");
+#endif
+}
+
+void
+test_c_jr ()
+{
+  volatile int a = 0, b = 5;
+  riscv_reg_t addr;
+
+  info ("Testing c.jr");
+
+  addr = (riscv_reg_t) &&label_f;
+
+label_b:
+  /* If we have jumped back.  */
+  if (a == 7 && b == 5)
+    return;
+
+  asm volatile ("c.jr %0" : : "r" (addr));
+  asm volatile ("c.mv %0, %1" : "=r" (a) : "r" (b));
+
+  FAIL ("Jumped at wrong location");
+
+label_f:
+  I_CHECK(a, 0);
+  I_CHECK(b, 5);
+
+  a = 7;
+  addr = (riscv_reg_t) &&label_b;
+  asm volatile ("c.jr %0" : : "r" (addr));
+
+  FAIL ("Jumped at wrong location");
+}
+
+void
+test_c_jalr ()
+{
+  volatile int a = 0, b = 5;
+  riscv_reg_t addr, ra;
+
+  info ("Testing c.jalr");
+
+  addr = (riscv_reg_t) &&label_f;
+
+label_b:
+  /* If we have jumped back.  */
+  if (a == 7)
+    {
+      asm volatile ("c.mv %0, ra" : "=r" (ra));
+      L_CHECK (ra, (riscv_reg_t) &&label_ra_2);
+      return;
+    }
+
+  asm volatile ("c.jalr %0" : : "r" (addr));
+label_ra_1:
+  asm volatile ("c.nop");
+  asm volatile ("c.mv %0, %1" : "=r" (a) : "r" (b));
+
+  FAIL ("Jumped at wrong location");
+
+label_f:
+  asm volatile ("c.mv %0, ra" : "=r" (ra));
+
+  L_CHECK (ra, (riscv_reg_t) &&label_ra_1);
+  I_CHECK (a, 0);
+  I_CHECK (b, 5);
+
+  a = 7;
+  addr = (riscv_reg_t) &&label_b;
+  asm volatile ("c.jalr %0" : : "r" (addr));
+label_ra_2:
+  asm volatile ("c.nop");
+
+  FAIL ("Jumped at wrong location");
+}
+
+void
+test_c_beqz ()
+{
+  volatile int zero = 0, non_zero = 1;
+  int a = 0, b = 5;
+
+  info ("Testing c.beqz");
+
+label_b_1:
+  /* If we have branched back.  */
+  if (a == 17)
+    {
+      I_CHECK (b, 8);
+      if (b != 8)
+	FAIL ("Backward-taken branch failed");
+      return;
+    }
+
+  asm goto ("c.beqz %0, %l[label_f_1]"
+	    : : "r" (zero) : : label_f_1);	/* Forward taken.  */
+  asm volatile ("c.li %0, 7" : "=r" (a));
+  asm volatile ("c.li %0, 8" : "=r" (b));
+
+label_f_1:
+  I_CHECK (a, 0);
+  I_CHECK (b, 5);
+  if (a != 0 || b != 5)
+    {
+      FAIL ("Forward-taken branch failed");
+      return;
+    }
+
+  asm goto ("c.beqz %0, %l[label_f_2]"
+	    :
+	    : "r" (non_zero)
+	    :
+	    : label_f_2);	/* Not taken.  */
+
+  a = 7;
+  b = 8;
+
+label_f_2:
+  I_CHECK (a, 7);
+  I_CHECK (b, 8);
+  if (a != 7 || b != 8)
+    {
+      FAIL ("Not-taken branch failed");
+      return;
+    }
+
+  /* Branch back.  */
+  a = 17;
+  asm goto ("c.beqz %0, %l[label_b_1]"
+	    :
+	    : "r" (zero)
+	    :
+	    : label_b_1);	/* Backward taken.  */
+
+  FAIL ("Backward-taken branch failed");
+}
+
+void
+test_c_bnez ()
+{
+  volatile int zero = 0, non_zero = 1;
+  int a = 0, b = 5;
+
+  info ("Testing c.bnez");
+
+label_b_1:
+  /* If we have branched back.  */
+  if (a == 17)
+    {
+      if (b != 8)
+	FAIL ("Backward-taken branch failed");
+      return;
+    }
+
+  asm goto ("c.bnez %0, %l[label_f_1]"
+	    :
+	    : "r" (non_zero)
+	    :
+	    : label_f_1);	/* Forward taken.  */
+  asm volatile ("c.li %0, 7" : "=r" (a));
+  asm volatile ("c.li %0, 8" : "=r" (b));
+
+label_f_1:
+  if (a != 0 || b != 5)
+    {
+      FAIL ("Forward-taken branch failed");
+      num_fail++;
+      return;
+    }
+
+  asm goto ("c.bnez %0, %l[label_f_2]"
+	    :
+	    : "r" (zero)
+	    :
+	    : label_f_2);	/* Not taken.  */
+  a = 7;
+  b = 8;
+
+label_f_2:
+  if (a != 7 || b != 8)
+    {
+      FAIL ("Not-taken branch failed");
+      num_fail++;
+      return;
+    }
+
+  /* Branch back.  */
+  a = 17;
+  asm goto ("c.bnez %0, %l[label_b_1]"
+	    :
+	    : "r" (non_zero)
+	    :
+	    : label_b_1);	/* Backward taken.  */
+
+  FAIL ("backward-taken branch failed");
+  num_fail++;
+}
+
+void
+test_c_li ()
+{
+  riscv_reg_t a, b, c;
+
+  info ("Testing c.li");
+
+  asm volatile ("c.li %0,0" : "=r" (a));
+  asm volatile ("c.li %0,-1" : "=r" (b));
+  asm volatile ("c.li %0,31" : "=r" (c));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (a, 0);
+  L_CHECK (b, -1);
+  L_CHECK (c, 31);
+#else
+  I_CHECK (a, 0);
+  I_CHECK (b, -1);
+  I_CHECK (c, 31);
+#endif
+}
+
+void
+test_c_lui ()
+{
+  int a = 0;
+
+  info ("Testing c.lui");
+
+  asm volatile ("c.lui %0,1" : "=r" (a));
+  I_CHECK (a, 0x1000);
+
+  asm volatile ("c.lui %0,31" : "=r" (a));
+  I_CHECK (a, 0x1F000);
+}
+
+void
+test_c_addi ()
+{
+  int a = 0;
+
+  info ("Testing c.addi");
+
+  asm volatile ("c.addi %0,1" : "+r" (a));
+  I_CHECK (a, 1);
+
+  asm volatile ("c.addi %0,-1" : "+r" (a));
+  I_CHECK (a, 0);
+
+  asm volatile ("c.addi %0,31" : "+r" (a));
+  I_CHECK (a, 31);
+}
+
+void
+test_c_addiw ()
+{
+#if defined (SKIP_c_addiw)
+  info ("--- Disable c.addiw");
+#else
+  int a = 1;
+
+  info ("Testing c.addiw");
+
+  asm volatile ("c.addiw %0,0" : "+r" (a));
+  I_CHECK (a, 1);
+
+  asm volatile ("c.addiw %0,-1" : "+r" (a));
+  I_CHECK (a, 0);
+
+  asm volatile ("c.addiw %0,31" : "+r" (a));
+  I_CHECK (a, 31);
+#endif
+}
+
+void
+test_c_addi16sp ()
+{
+  volatile riscv_reg_t orig_sp, sp_1, sp_2;
+
+  info ("Testing c.addi16sp");
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated"
+  asm volatile ("\
+		c.mv %0, sp		\n\
+		c.addi16sp sp,32	\n\
+		c.mv %1, sp		\n\
+		c.addi16sp sp,-32	\n\
+		c.mv %2, sp		\n\
+		c.mv sp, %0"	/* Restore sp.  */
+		: "=r" (orig_sp), "=r" (sp_1), "=r" (sp_2)
+		: "r" (orig_sp)
+		: "sp");
+#pragma GCC diagnostic pop
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (orig_sp, sp_2);
+  L_CHECK ((sp_1 - sp_2), 32);
+#else
+  I_CHECK (orig_sp, sp_2);
+  I_CHECK ((sp_1 - sp_2), 32);
+#endif
+}
+
+void
+test_c_addi4spn ()
+{
+  volatile riscv_reg_t orig_sp, sp_1, sp_2;
+
+  info ("Testing c.addi4spn");
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated"
+  asm volatile ("\
+		c.mv %0, sp		\n\
+		c.addi4spn %1,sp,16	\n\
+		c.addi4spn %2,sp,32"
+		: "=r" (orig_sp), "=r" (sp_1), "=r" (sp_2)
+		: "r" (orig_sp)
+		: "sp");
+#pragma GCC diagnostic pop
+
+#if (__riscv_xlen >= 64)
+  L_CHECK ((orig_sp + 16), sp_1);
+  L_CHECK ((orig_sp + 32), sp_2);
+#else
+  I_CHECK ((orig_sp + 16), sp_1);
+  I_CHECK ((orig_sp + 32), sp_2);
+#endif
+}
+
+void
+test_c_slli ()
+{
+  volatile riscv_reg_t val = -5, r1, r2, r3;
+
+  info ("Testing c.slli");
+
+  asm volatile ("c.slli %0,1" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r1) : "r" (val));
+  asm volatile ("c.slli %0,2" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r2) : "r" (val));
+  asm volatile ("c.slli %0,3" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r3) : "r" (val));
+  asm volatile ("c.slli %0,4" : "+r" (val));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (r1, -10);
+  L_CHECK (r2, -40);
+  L_CHECK (r3, -320);
+  L_CHECK (val, -5120);
+#else
+  I_CHECK (r1, -10);
+  I_CHECK (r2, -40);
+  I_CHECK (r3, -320);
+  I_CHECK (val, -5120);
+#endif
+
+  val = 5;
+  asm volatile ("c.slli %0,1" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r1) : "r" (val));
+  asm volatile ("c.slli %0,2" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r2) : "r" (val));
+  asm volatile ("c.slli %0,3" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r3) : "r" (val));
+  asm volatile ("c.slli %0,4" : "+r" (val));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (r1, 10);
+  L_CHECK (r2, 40);
+  L_CHECK (r3, 320);
+  L_CHECK (val, 5120);
+#else
+  I_CHECK (r1, 10);
+  I_CHECK (r2, 40);
+  I_CHECK (r3, 320);
+  I_CHECK (val, 5120);
+#endif
+}
+
+void
+test_c_srli ()
+{
+  volatile riscv_reg_t val = -105, r1, r2, r3;
+
+  info ("Testing c.srli");
+
+  asm volatile ("c.srli %0,1" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r1) : "r" (val));
+  asm volatile ("c.srli %0,2" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r2) : "r" (val));
+  asm volatile ("c.srli %0,3" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r3) : "r" (val));
+  asm volatile ("c.srli %0,4" : "+r" (val));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (r1, 0x7fffffffffffffcbul);
+  L_CHECK (r2, 0x1ffffffffffffff2ul);
+  L_CHECK (r3, 0x3fffffffffffffeul);
+  L_CHECK (val, 0x3ffffffffffffful);
+#else
+  I_CHECK (r1, 0x7fffffcb);
+  I_CHECK (r2, 0x1ffffff2);
+  I_CHECK (r3, 0x3fffffe);
+  I_CHECK (val, 0x3fffff);
+#endif
+
+  val = 105;
+  asm volatile ("c.srli %0,1" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r1) : "r" (val));
+  asm volatile ("c.srli %0,2" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r2) : "r" (val));
+  asm volatile ("c.srli %0,3" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r3) : "r" (val));
+  asm volatile ("c.srli %0,4" : "+r" (val));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (r1, 52);
+  L_CHECK (r2, 13);
+  L_CHECK (r3, 1);
+  L_CHECK (val, 0);
+#else
+  I_CHECK (r1, 52);
+  I_CHECK (r2, 13);
+  I_CHECK (r3, 1);
+  I_CHECK (val, 0);
+#endif
+}
+
+void
+test_c_srai ()
+{
+  volatile riscv_reg_t val = -105, r1, r2, r3;
+
+  info ("Testing c.srai");
+
+  asm volatile ("c.srai %0,1" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r1) : "r" (val));
+  asm volatile ("c.srai %0,2" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r2) : "r" (val));
+  asm volatile ("c.srai %0,3" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r3) : "r" (val));
+  asm volatile ("c.srai %0,4" : "+r" (val));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (r1, -53);
+  L_CHECK (r2, -14);
+  L_CHECK (r3, -2);
+  L_CHECK (val, -1);
+#else
+  I_CHECK (r1, -53);
+  I_CHECK (r2, -14);
+  I_CHECK (r3, -2);
+  I_CHECK (val, -1);
+#endif
+
+  val = 105;
+  asm volatile ("c.srai %0,1" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r1) : "r" (val));
+  asm volatile ("c.srai %0,2" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r2) : "r" (val));
+  asm volatile ("c.srai %0,3" : "+r" (val));
+  asm volatile ("c.mv %0,%1" : "=r" (r3) : "r" (val));
+  asm volatile ("c.srai %0,4" : "+r" (val));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (r1, 52);
+  L_CHECK (r2, 13);
+  L_CHECK (r3, 1);
+  L_CHECK (val, 0);
+#else
+  I_CHECK (r1, 52);
+  I_CHECK (r2, 13);
+  I_CHECK (r3, 1);
+  I_CHECK (val, 0);
+#endif
+}
+
+void
+test_c_andi ()
+{
+  riscv_reg_t val1 = -1, val2 = 0x101;
+
+  info ("Testing c.andi");
+
+  asm volatile ("c.andi %0,5" : "+r" (val1));
+  asm volatile ("c.andi %0,7" : "+r" (val2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (val1, 5);
+  L_CHECK (val2, 1);
+#else
+  I_CHECK (val1, 5);
+  I_CHECK (val2, 1);
+#endif
+}
+
+void
+test_c_add ()
+{
+  riscv_reg_t dst, rs2;
+
+  info ("Testing c.add");
+
+  dst = -1;
+  rs2 = 1;
+  asm volatile ("c.add %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (dst, 0);
+  L_CHECK (rs2, 1);
+#else
+  I_CHECK (dst, 0);
+  I_CHECK (rs2, 1);
+#endif
+
+  dst = -1;
+  rs2 = 0;
+  asm volatile ("c.add %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (dst, -1);
+  L_CHECK (rs2, 0);
+#else
+  I_CHECK (dst, -1);
+  I_CHECK (rs2, 0);
+#endif
+}
+
+void
+test_c_and ()
+{
+  riscv_reg_t dst, rs2;
+
+  info ("Testing c.and");
+
+  dst = -1;
+  rs2 = 1;
+  asm volatile ("c.and %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (dst, 1);
+  L_CHECK (rs2, 1);
+#else
+  I_CHECK (dst, 1);
+  I_CHECK (rs2, 1);
+#endif
+
+  dst = -1;
+  rs2 = 0;
+  asm volatile ("c.and %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (dst, 0);
+  L_CHECK (rs2, 0);
+#else
+  I_CHECK (dst, 0);
+  I_CHECK (rs2, 0);
+#endif
+}
+
+void
+test_c_or ()
+{
+  riscv_reg_t dst, rs2;
+
+  info ("Testing c.or");
+
+  dst = -3;
+  rs2 = 2;
+  asm volatile ("c.or %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (dst, -1);
+  L_CHECK (rs2, 2);
+#else
+  I_CHECK (dst, -1);
+  I_CHECK (rs2, 2);
+#endif
+
+  dst = 0x7ffffffd;
+  rs2 = 0x80000002;
+  asm volatile ("c.or %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (dst, 0xffffffff);
+  L_CHECK (rs2, 0x80000002);
+#else
+  I_CHECK (dst, 0xffffffff);
+  I_CHECK (rs2, 0x80000002);
+#endif
+}
+
+void
+test_c_xor ()
+{
+  riscv_reg_t dst, rs2;
+
+  info ("Testing c.xor");
+
+  dst = -3;
+  rs2 = -3;
+  asm volatile ("c.xor %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (dst, 0);
+  L_CHECK (rs2, -3);
+#else
+  I_CHECK (dst, 0);
+  I_CHECK (rs2, -3);
+#endif
+
+  dst = 0x7ffffffd;
+  rs2 = 0x80000002;
+  asm volatile ("c.xor %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (dst, 0xffffffff);
+  L_CHECK (rs2, 0x80000002);
+#else
+  I_CHECK (dst, 0xffffffff);
+  I_CHECK (rs2, 0x80000002);
+#endif
+}
+
+void
+test_c_sub ()
+{
+  riscv_reg_t dst, rs2;
+
+  info ("Testing c.sub");
+
+  dst = -1;
+  rs2 = 1;
+  asm volatile ("c.sub %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (dst, -2);
+  L_CHECK (rs2, 1);
+#else
+  I_CHECK (dst, -2);
+  I_CHECK (rs2, 1);
+#endif
+
+  dst = 0;
+  rs2 = -1;
+  asm volatile ("c.sub %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (dst, 1);
+  L_CHECK (rs2, -1);
+#else
+  I_CHECK (dst, 1);
+  I_CHECK (rs2, -1);
+#endif
+}
+
+void
+test_c_addw ()
+{
+#if defined (SKIP_c_addw)
+  info ("--- Disable c.addw");
+#else
+
+  riscv_reg_t dst, rs2;
+
+  info ("Testing c.addw");
+
+  dst = -1;
+  rs2 = 1;
+  asm volatile ("c.addw %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (dst, 0);
+  L_CHECK (rs2, 1);
+#else
+  I_CHECK (dst, 0);
+  I_CHECK (rs2, 1);
+#endif
+
+  dst = -1;
+  rs2 = 0;
+  asm volatile ("c.addw %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (dst, -1);
+  L_CHECK (rs2, 0);
+#else
+  I_CHECK (dst, -1);
+  I_CHECK (rs2, 0);
+#endif
+#endif
+}
+
+void
+test_c_subw ()
+{
+#if defined (SKIP_c_subw)
+  info ("--- Disable c.subw");
+#else
+  riscv_reg_t dst, rs2;
+
+  info ("Testing c.subw");
+
+  dst = -1;
+  rs2 = 1;
+  asm volatile ("c.subw %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (dst, -2);
+  L_CHECK (rs2, 1);
+#else
+  I_CHECK (dst, -2);
+  I_CHECK (rs2, 1);
+#endif
+
+  dst = 0;
+  rs2 = -1;
+  asm volatile ("c.subw %0,%1" : "+r" (dst) : "r" (rs2));
+
+#if (__riscv_xlen >= 64)
+  L_CHECK (dst, 1);
+  L_CHECK (rs2, -1);
+#else
+  I_CHECK (dst, 1);
+  I_CHECK (rs2, -1);
+#endif
+#endif
+}
+
+int
+main ()
+{
+  test_c_lwsp ();
+  test_c_ldsp ();
+  test_c_flwsp ();
+  test_c_fldsp ();
+  test_c_swsp ();
+  test_c_sdsp ();
+  test_c_fswsp ();
+  test_c_fsdsp ();
+  test_c_lw ();
+  test_c_ld ();
+  test_c_flw ();
+  test_c_fld ();
+  test_c_sw ();
+  test_c_sd ();
+  test_c_fsw ();
+  test_c_fsd ();
+  test_c_j ();
+  test_c_jal ();
+  test_c_jr ();
+  test_c_jalr ();
+  test_c_beqz ();
+  test_c_bnez ();
+  test_c_li ();
+  test_c_lui ();
+  test_c_addi ();
+  test_c_addiw ();
+  test_c_addi16sp ();
+  test_c_addi4spn ();
+  test_c_slli ();
+  test_c_srli ();
+  test_c_srai ();
+  test_c_andi ();
+  test_c_add ();
+  test_c_and ();
+  test_c_or ();
+  test_c_xor ();
+  test_c_sub ();
+  test_c_addw ();
+  test_c_subw ();
+
+  if (num_fail == 0)
+    {
+      print ("*** All tests pass\n");
+    }
+  else
+    {
+      print ("*** Not all tests pass\n");
+    }
+
+  return num_fail;
+}
diff --git a/gdb/testsuite/gdb.arch/riscv-insn-simulation.exp b/gdb/testsuite/gdb.arch/riscv-insn-simulation.exp
new file mode 100755
index 00000000000..0191f84015c
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/riscv-insn-simulation.exp
@@ -0,0 +1,31 @@
+# Copyright 2023 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Tests to check instruction simulation of RISC-V instructions.
+
+require {istarget "riscv*-*-*"}
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
+	  {debug quiet}] } {
+    return -1
+}
+
+if { ![runto_main] } {
+    return -1
+}
+
+gdb_continue_to_end "continue parent to end" "continue" 1
diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
index 1b374323aa7..61aae39dbb4 100644
--- a/sim/riscv/sim-main.c
+++ b/sim/riscv/sim-main.c
@@ -26,7 +26,11 @@
 
 #include <inttypes.h>
 #include <time.h>
+#include <sys/stat.h>
+#include <sys/unistd.h>
+#include <sys/fcntl.h>
 
+#include "bfd.h"
 #include "sim-main.h"
 #include "sim-signal.h"
 #include "sim-syscall.h"
@@ -66,6 +70,66 @@ static const struct riscv_opcode *riscv_hash[OP_MASK_OP + 1];
       } \
   } while (0)
 
+/* Semihosting operations.  */
+#define SEMIHOST_open 0x01
+#define SEMIHOST_close 0x02
+#define SEMIHOST_writec 0x03
+#define SEMIHOST_write0 0x04
+#define SEMIHOST_write 0x05
+#define SEMIHOST_read 0x06
+#define SEMIHOST_readc 0x07
+#define SEMIHOST_iserror 0x08
+#define SEMIHOST_istty 0x09
+#define SEMIHOST_seek 0x0A
+#define SEMIHOST_flen 0x0C
+#define SEMIHOST_tmpnam 0x0D
+#define SEMIHOST_remove 0x0E
+#define SEMIHOST_rename 0x0F
+#define SEMIHOST_clock 0x10
+#define SEMIHOST_time 0x11
+#define SEMIHOST_system 0x12
+#define SEMIHOST_errno 0x13
+#define SEMIHOST_get_cmdline 0x15
+#define SEMIHOST_heapinfo 0x16
+#define SEMIHOST_exit 0x18
+#define SEMIHOST_exit_extended 0x20
+#define SEMIHOST_elapsed 0x30
+#define SEMIHOST_tickfreq 0x31
+
+/* Used in SEMIHOST_exit call to indicate successful execution of the
+   application.  */
+#define APPLICATION_EXIT 0x20026
+
+/* The low level file open function receives these flags.  It then converts
+   these flags into modes (defined below) and calls SEMIHOST_open.
+   F_RDONLY -> SEMIHOST_MODE_R
+   F_WRONLY | F_CREAT | F_TRUNC -> SEMIHOST_MODE_W
+   F_WRONLY | F_CREAT | F_APPEND -> SEMIHOST_MODE_A
+   F_RDWR | F_CREAT | F_TRUNC -> SEMIHOST_MODE_WPLUS
+   F_RDWR | F_CREAT | F_APPEND -> SEMIHOST_MODE_APLUS
+   F_RDWR  -> SEMIHOST_MODE_RPLUS.  */
+#define F_RDONLY 0x000
+#define F_WRONLY 0x001
+#define F_RDWR 0x002
+#define F_APPEND 0x008
+#define F_CREAT 0x200
+#define F_TRUNC 0x400
+
+/* File open modes received by SEMIHOST_open function.  These modes are then
+   converted back to flags before calling open function of the host.  */
+#define SEMIHOST_MODE_R 0
+#define SEMIHOST_MODE_RPLUS 2
+#define SEMIHOST_MODE_W 4
+#define SEMIHOST_MODE_WPLUS 6
+#define SEMIHOST_MODE_A 8
+#define SEMIHOST_MODE_APLUS 10
+
+/* Maximum length of the file name in SEMIHOST_open.  */
+#define MAX_FILE_NAME_LENGTH 1024
+
+/* Used in SEMIHOST_elapsed to measure elapsed clock ticks.  */
+static clock_t clock_start = 0;
+
 static INLINE void
 store_rd (SIM_CPU *cpu, int rd, unsigned_word val)
 {
@@ -151,6 +215,723 @@ ashiftrt64 (unsigned_word val, unsigned_word shift)
   return (val >> shift) | sign;
 }
 
+/* Read 4 or 8 bytes of data from the core memory.  The ADDR and (INDEX * XLEN)
+   form the base address.  4-byte values are zero extended.  */
+static unsigned_word
+get_core_data (SIM_CPU *cpu, unsigned_word addr, unsigned_word index)
+{
+  unsigned_word param;
+  int xlen = RISCV_XLEN (cpu);
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+  if (xlen == 64)
+    param = sim_core_read_unaligned_8 (cpu, riscv_cpu->pc, read_map,
+				       addr + (index * 8));
+  else
+    param = EXTEND32 (sim_core_read_unaligned_4 (cpu, riscv_cpu->pc, read_map,
+						 addr + (index * 4)));
+
+  return param;
+}
+
+/* Write string in HOST_BUF at CORE_ADDR.  The length of string is LEN.  */
+static void
+set_core_string (SIM_CPU *cpu, unsigned_word core_addr, char *host_buf,
+		 int len)
+{
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  for (int i = 0; i < len; i++)
+    sim_core_write_unaligned_1 (cpu, riscv_cpu->pc, write_map,
+				core_addr + i, host_buf[i]);
+}
+
+/* Read string of length LEN at address ADDR.  */
+static char *
+get_core_string_with_len (SIM_CPU *cpu, unsigned_word addr,
+			  unsigned_word len)
+{
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  char * str;
+  str = (char *) xmalloc (len + 1);
+
+  for (int i = 0; i < len; i++)
+    str[i] = sim_core_read_unaligned_1 (cpu, riscv_cpu->pc, read_map,
+					addr + i);
+  str[len] = '\0';
+
+  return str;
+}
+
+/* Write VALUE at specified address.  ADDR and (INDEX * XLEN) form the base
+   address.  */
+static void
+set_core_data (SIM_CPU *cpu, unsigned_word addr, unsigned_word index,
+	       unsigned_word value)
+{
+  int xlen = RISCV_XLEN (cpu);
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+  if (xlen == 64)
+    sim_core_write_unaligned_8 (cpu, riscv_cpu->pc, read_map,
+				addr + (index * 8), value);
+  else
+    sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, read_map,
+				addr + (index * 4), (uint32_t) value);
+}
+
+/* Read data of length SLEN and address ADDR.  */
+static char *
+get_core_string (SIM_CPU *cpu, unsigned_word addr, int *slen)
+{
+  int len = 0;
+  char * str;
+  const int chunk_size = 128;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+  str = (char *) xmalloc (chunk_size);
+
+  while (1)
+    {
+      uint8_t ch = sim_core_read_unaligned_1 (cpu, riscv_cpu->pc, read_map,
+					      addr + len);
+      str[len] = ch;
+      if (ch == 0)
+	break;
+      len++;
+      if ((len % chunk_size) == 0)
+	str = (char *) xrealloc (str, len + chunk_size);
+    }
+
+  *slen = len;
+  return str;
+}
+
+/* Find address of the symbol in SYMNAME.  */
+static unsigned_word
+get_symbol_value (SIM_CPU *cpu, const char *symname)
+{
+  struct bfd *abfd = STATE_PROG_BFD (CPU_STATE (cpu));
+  static asymbol **symbol_table = NULL;
+  static long number_of_symbols = 0;
+
+  if (symbol_table == NULL)
+    {
+      long storage_needed;
+      storage_needed = bfd_get_symtab_upper_bound (abfd);
+      if (storage_needed <= 0)
+	return 0;
+      symbol_table = (asymbol **) xmalloc (storage_needed);
+      if (symbol_table == NULL)
+	return 0;
+      number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table);
+      if (number_of_symbols <= 0)
+	return 0;
+    }
+
+  for (long i = 0; i < number_of_symbols; i++)
+    {
+      asymbol *sym = symbol_table[i];
+      if (!strcmp (sym->name, symname))
+	return bfd_asymbol_value (sym);
+    }
+
+  return 0;
+}
+
+/* Register a1 points to a buffer containing:
+   Index 0: Pointer: Address of the file name string.
+   Index 1: Integer: File open flags.
+   Index 2: Integer: Length of the file name string.
+   File handle is returned through register a0.  */
+static void
+semihosting_open (SIM_CPU *cpu)
+{
+  int flags;
+  char *name;
+  unsigned_word fname_addr, mode, fname_len;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+  fname_addr = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 0);
+  mode = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+  fname_len = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 2);
+
+  if (fname_len <= 0 && fname_len > MAX_FILE_NAME_LENGTH)
+    {
+      riscv_cpu->a0 = -1;
+      return;
+    }
+
+  /* By default open in SEMIHOST_MODE_R mode.  */
+  if (mode == SEMIHOST_MODE_RPLUS)
+    flags = F_RDWR;
+  else if (mode == SEMIHOST_MODE_W)
+    flags = F_WRONLY | F_CREAT | F_TRUNC;
+  else if (mode == SEMIHOST_MODE_WPLUS)
+    flags = F_RDWR | F_CREAT | F_TRUNC;
+  else if (mode == SEMIHOST_MODE_A)
+    flags = F_WRONLY | F_CREAT | F_APPEND;
+  else if (mode == SEMIHOST_MODE_APLUS)
+    flags = F_RDWR | F_CREAT | F_APPEND;
+  else
+    flags = F_RDONLY;
+
+  name = get_core_string_with_len (cpu, fname_addr, fname_len);
+  riscv_cpu->a0 = sim_io_open (CPU_STATE (cpu), name, flags);
+  free (name);
+}
+
+/* In case of RV32, register a1 holds the application stop reason and the exit
+   code is decided based on it.  Otherwise, register a1 points to a buffer
+   containing:
+   Index 0: Integer: Application stop reason (ignored)
+   Index 1: Integer: Exit code.  */
+static void
+semihosting_exit (SIM_CPU *cpu)
+{
+  unsigned_word app_code, exit_code;
+  SIM_DESC sd = CPU_STATE (cpu);
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  if (RISCV_XLEN (cpu) == 32)
+    {
+      app_code = riscv_cpu->a1;
+      if (app_code == APPLICATION_EXIT)
+	exit_code = 0;
+      else
+	exit_code = 1;
+    }
+  else
+    exit_code = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+  riscv_cpu->a0 = exit_code;
+  sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_exited, exit_code);
+}
+
+/* Write command line arguments to a buffer.  Arguments passed to "run" command
+   are stored in STATE_PROG_ARGV member of SIM_DESC.  Register a1 points to a
+   buffer containing:
+   Index 0: Pointer: Buffer to store command line arguments
+   Index 1: Integer: Maximum length of the buffer.  */
+static void
+semihosting_get_cmdline (SIM_CPU *cpu)
+{
+  int i = 0, len = 0, total_len = 0;
+  unsigned_word buf_addr, max_buf_len;
+  SIM_DESC sd = CPU_STATE (cpu);
+  char *space = " ";
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+  char **prog_argv = STATE_PROG_ARGV (sd);
+  if (prog_argv == NULL)
+    {
+      /* Return non-zero to indicate error.  */
+      riscv_cpu->a0 = 1;
+      return;
+    }
+
+  buf_addr = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 0);
+  max_buf_len = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+
+  while (prog_argv[i])
+    {
+      len = strlen (prog_argv[i]);
+      if ((total_len + len) > max_buf_len)
+	break;
+      set_core_string (cpu, buf_addr, prog_argv[i], len);
+      /* Add space at the end.  */
+      set_core_string (cpu, buf_addr + len, space, 1);
+      len++;
+      buf_addr += len;
+      total_len += len;
+      i++;
+    }
+  riscv_cpu->a0 = 0;
+}
+
+/* Register a1 points to a buffer containing:
+   Index 0: Integer: File handle.
+   The file size in bytes is returned through register a0.  */
+static void
+semihosting_flen (SIM_CPU *cpu)
+{
+  int fd;
+  struct stat sb;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+  fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+
+  if (fd > STDERR_FILENO)
+    {
+      fstat (fd, &sb);
+      riscv_cpu->a0 = sb.st_size;
+    }
+  else
+    riscv_cpu->a0 = 0;
+}
+
+/* Write data to a file.  Register a1 points to a buffer containing:
+   Index 0: Integer: File handle.
+   Index 1: Pointer: Pointer to buffer.
+   Index 2: Integer: Number of bytes to write.
+   Number of bytes written is returned through register a0.  */
+static void
+semihosting_write (SIM_CPU *cpu)
+{
+  int i, fd;
+  char *str;
+  unsigned_word buf, count;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+  fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+  buf = get_core_data (cpu, riscv_cpu->a1, 1);
+  count = get_core_data (cpu, riscv_cpu->a1, 2);
+
+  if (count <= 0)
+    {
+      riscv_cpu->a0 = -1;
+      return;
+    }
+
+  str = get_core_string_with_len (cpu, buf, count);
+  riscv_cpu->a0 = sim_io_write (CPU_STATE (cpu), fd, str, count);
+  free (str);
+}
+
+/* Register a1 points to a buffer containing:
+   Index 0: Integer: Character to write to console.
+   Number of bytes written is returned through register a0.  */
+static void
+semihosting_writec (SIM_CPU *cpu)
+{
+  char ch;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  ch = (char) get_core_data (cpu, riscv_cpu->a1, 0);
+  riscv_cpu->a0 = sim_io_write_stdout (CPU_STATE (cpu), &ch, 1);
+}
+
+/* Register a1 points to a buffer containing:
+   Index 0: Integer: Null terminated string.
+   Number of bytes written is returned through register a0.  */
+static void
+semihosting_write0 (SIM_CPU *cpu)
+{
+  int len;
+  char *str;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  str = get_core_string (cpu, riscv_cpu->a1, &len);
+  riscv_cpu->a0 = sim_io_write_stdout (CPU_STATE (cpu), str, len);
+}
+
+/* Read data from a file.  Register a1 points to a buffer containing:
+   Index 0: Integer: File handle.
+   Index 1: Pointer: Pointer to destination buffer.
+   Index 2: Integer: Number of bytes to read.
+   Number of bytes read is returned through register a0.  */
+static void
+semihosting_read (SIM_CPU *cpu)
+{
+  int i, fd, read_len;
+  unsigned_word dst_buf, count;
+  char *host_buf;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+  fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+  dst_buf = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+  count = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 2);
+
+  if (count <= 0)
+    {
+      riscv_cpu->a0 = 0;
+      return;
+    }
+
+  host_buf = (char *) xmalloc (count);
+  read_len = sim_io_read (CPU_STATE (cpu), fd, host_buf, count);
+  if (read_len > 0)
+    set_core_string (cpu, dst_buf, host_buf, read_len);
+  riscv_cpu->a0 = read_len;
+  free (host_buf);
+}
+
+/* Read a char from console and return it through register a0.  */
+static void
+semihosting_readc (SIM_CPU *cpu)
+{
+  char ch;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  sim_io_read_stdin (CPU_STATE (cpu), &ch, 1);
+  riscv_cpu->a0 = ch;
+}
+
+/* Close a file.  Register a1 points to a buffer containing:
+   Index 0: Integer: File handle.
+   Return status in register a0.  */
+static void
+semihosting_close (SIM_CPU *cpu)
+{
+  int fd;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+  riscv_cpu->a0 = sim_io_close (CPU_STATE (cpu), fd);
+}
+
+/* Seek in a file.  Register a1 points to a buffer containing:
+   Index 0: Integer: File handle.
+   Index 1: Integer: Number of bytes to seek in the file.
+   Return status in register a0.  */
+static void
+semihosting_seek (SIM_CPU *cpu)
+{
+  int fd;
+  unsigned_word pos;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+  pos = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+  riscv_cpu->a0 = sim_io_lseek (CPU_STATE (cpu), fd, pos, 0);
+}
+
+/* Register a1 points to a buffer containing:
+   Index 0: Integer: Application code.
+   Index 1: Integer: Exit code.  */
+static void
+semihosting_exit_extended (SIM_CPU *cpu)
+{
+  int ret;
+  unsigned_word app_code, exit_status;
+  SIM_DESC sd = CPU_STATE (cpu);
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  app_code = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 0);
+  exit_status = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+  if (app_code == APPLICATION_EXIT)
+    ret = exit_status;
+  else
+    ret = 1;
+  riscv_cpu->a0 = ret;
+  sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_exited, ret);
+}
+
+/* Register a1 points to a buffer containing:
+   Index 0: Integer: Status code.  */
+static void
+semihosting_iserror (SIM_CPU *cpu)
+{
+  signed_word status;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  status = (signed_word) get_core_data (cpu, riscv_cpu->a1, 0);
+  riscv_cpu->a0 = (status < 0);
+}
+
+/* Register a1 points to a buffer containing:
+   Index 0: Integer: File handle.  */
+static void
+semihosting_istty (SIM_CPU *cpu)
+{
+  int fd;
+  SIM_DESC sd = CPU_STATE (cpu);
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+  riscv_cpu->a0 = sim_io_isatty (sd, fd);
+}
+
+/* Register a1 points to a buffer containing:
+   Index 0: Pointer: Destination buffer address.
+   Index 1: Integer: ID (ignored).
+   Index 2: Integer: Maximum length of the buffer.
+   Pointer to temporary name is returned through a0.  */
+static void
+semihosting_tmpnam (SIM_CPU *cpu)
+{
+  unsigned_word t_pname;
+  int len, fd, maxpath;
+  char pname[] = "/tmp/fileXXXXXX";
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+  t_pname = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 0);
+  maxpath = (int) get_core_data (cpu, riscv_cpu->a1, 2);
+
+  fd = mkstemp (pname);
+
+  if (fd == -1)
+    {
+      riscv_cpu->a0 = 0;
+      return;
+    }
+
+  len = strlen (pname);
+  if (maxpath > len)
+    {
+      riscv_cpu->a0 = 0;
+      return;
+    }
+
+  set_core_string (cpu, t_pname, pname, len + 1);
+  riscv_cpu->a0 = t_pname;
+  close (fd);
+}
+
+/* Register a1 points to a buffer containing:
+   Index 0: Pointer: Name of the file to remove.
+   Index 1: Integer: Length of the file name.  */
+static void
+semihosting_remove (SIM_CPU *cpu)
+{
+  unsigned_word t_pname;
+  int len;
+  char *pname;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  t_pname = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 0);
+  len = (int) get_core_data (cpu, riscv_cpu->a1, 1);
+  pname = get_core_string_with_len (cpu, t_pname, len);
+  riscv_cpu->a0 = sim_io_unlink (CPU_STATE (cpu), pname);
+  free (pname);
+}
+
+/* Register a1 points to a buffer containing:
+   Index 0: Pointer: Old file path.
+   Index 1: Integer: Length of the old file name.
+   Index 2: Pointer: New file name.
+   Index 3: Integer: Length of the new file name.  */
+static void
+semihosting_rename (SIM_CPU *cpu)
+{
+  unsigned_word old_name_addr, new_name_addr;
+  unsigned_word old_len, new_len;
+  char *old_host_name, *new_host_name;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  old_name_addr = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 0);
+  old_len = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+  new_name_addr = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 2);
+  new_len = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 3);
+  old_host_name = get_core_string_with_len (cpu, old_name_addr, old_len);
+  new_host_name = get_core_string_with_len (cpu, new_name_addr, new_len);
+  riscv_cpu->a0 = sim_io_rename (CPU_STATE (cpu), old_host_name,
+				 new_host_name);
+  free (old_host_name);
+  free (new_host_name);
+}
+
+/* Register a1 points to a buffer containing:
+   Index 0: Pointer: Command string.
+   Index 1: Integer: Length of the command string.
+   Status of the command is returned through a0.  */
+static void
+semihosting_system (SIM_CPU *cpu)
+{
+  unsigned_word cmd_addr;
+  unsigned_word cmd_len;
+  char *cmd_host;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  cmd_addr = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 0);
+  cmd_len = (unsigned_word) get_core_data (cpu, riscv_cpu->a1, 1);
+  cmd_host = get_core_string_with_len (cpu, cmd_addr, cmd_len);
+  riscv_cpu->a0 = sim_io_system (CPU_STATE (cpu), cmd_host);
+  free (cmd_host);
+}
+
+/* Return the elapsed seconds in a buffer pointed by register a1.  */
+static void
+semihosting_elapsed (SIM_CPU *cpu)
+{
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  clock_t elapsed = clock () - clock_start;
+  if (RISCV_XLEN (cpu) == 32)
+    {
+      sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map,
+	riscv_cpu->a1, (uint32_t) elapsed);
+      sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map,
+	riscv_cpu->a1 + 4, (uint32_t) (elapsed >> 32));
+    }
+  else
+    sim_core_write_unaligned_8 (cpu, riscv_cpu->pc, write_map, riscv_cpu->a1,
+				elapsed);
+}
+
+/* Return the heap start, heap end, stack start and stack end in a buffer
+   pointed by register a1.  */
+static void
+semihosting_heapinfo (SIM_CPU *cpu)
+{
+  static unsigned_word heap_base = 0, heap_limit = 0,
+	stack_base = 0, stack_limit = 0, stack_size = 0;
+  static bool have_heap = false, have_stack = false;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+  if (have_heap == false)
+    {
+      heap_base = get_symbol_value (cpu, "__heap_start");
+      heap_limit = get_symbol_value (cpu, "__heap_end");
+      have_heap = true;
+    }
+
+  if (have_stack == false)
+    {
+      stack_base = get_symbol_value (cpu, "__stack");
+      stack_size = get_symbol_value (cpu, "__stack_size");
+      stack_limit = stack_base + stack_size;
+      have_stack = true;
+    }
+
+  set_core_data (cpu, riscv_cpu->a1, 0, heap_base);
+  set_core_data (cpu, riscv_cpu->a1, 1, heap_limit);
+  set_core_data (cpu, riscv_cpu->a1, 2, stack_base);
+  set_core_data (cpu, riscv_cpu->a1, 3, stack_limit);
+}
+
+/* Handle semi-hosting calls.  Register a0 contains semi-hosting call number
+   and a1 contains pointer to a buffer containing additional arguments.  Return
+   1 (true) to indicate a call was handled.  */
+static int
+do_semihosting (SIM_CPU *cpu)
+{
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+  switch (riscv_cpu->a0)
+    {
+    case SEMIHOST_open:
+      semihosting_open (cpu);
+      break;
+    case SEMIHOST_get_cmdline:
+      semihosting_get_cmdline (cpu);
+      break;
+    case SEMIHOST_exit:
+      semihosting_exit (cpu);
+      break;
+    case SEMIHOST_close:
+      semihosting_close (cpu);
+      break;
+    case SEMIHOST_writec:
+      semihosting_writec (cpu);
+      break;
+    case SEMIHOST_write0:
+      semihosting_write0 (cpu);
+      break;
+    case SEMIHOST_write:
+      semihosting_write (cpu);
+      break;
+    case SEMIHOST_read:
+      semihosting_read (cpu);
+      break;
+    case SEMIHOST_readc:
+      semihosting_readc (cpu);
+      break;
+    case SEMIHOST_iserror:
+      semihosting_iserror (cpu);
+      break;
+    case SEMIHOST_istty:
+      semihosting_istty (cpu);
+      break;
+    case SEMIHOST_seek:
+      semihosting_seek (cpu);
+      break;
+    case SEMIHOST_flen:
+      semihosting_flen (cpu);
+      break;
+    case SEMIHOST_tmpnam:
+      semihosting_tmpnam (cpu);
+      break;
+    case SEMIHOST_remove:
+      semihosting_remove (cpu);
+      break;
+    case SEMIHOST_rename:
+      semihosting_rename (cpu);
+      break;
+    case SEMIHOST_clock:
+      riscv_cpu->a0 = (clock () / (CLOCKS_PER_SEC / 100));
+      break;
+    case SEMIHOST_time:
+      riscv_cpu->a0 = sim_io_time (CPU_STATE (cpu));
+      break;
+    case SEMIHOST_system:
+      semihosting_system (cpu);
+      break;
+    case SEMIHOST_errno:
+      riscv_cpu->a0 = sim_io_get_errno (CPU_STATE (cpu));
+      break;
+    case SEMIHOST_heapinfo:
+      semihosting_heapinfo (cpu);
+      break;
+    case SEMIHOST_exit_extended:
+      semihosting_exit_extended (cpu);
+      break;
+    case SEMIHOST_elapsed:
+      semihosting_elapsed (cpu);
+      break;
+    case SEMIHOST_tickfreq:
+      riscv_cpu->a0 = 1000000000;
+      break;
+    default:
+      /* Semi-hosting call not supported.  */
+      return 0;
+    }
+
+  return 1;
+}
+
+/* The instruction at *PC_PTR is assumed to be an ebreak instruction.
+   Check if this ebreak is surrounded by the required slli/srai
+   instructions that indicate this is a semihosting call.
+
+   If this is a semihosting call then extract the argument data and
+   perform the syscall, return 1 (true) to indicate a call was handled.  In
+   this case, *PC_PTR is updated to point at the next instruction to
+   execute, this will be the instruction after the srai.
+
+   If no semihosting call was handled then return 0 (false).  */
+static int
+check_and_handle_riscv_semihosting (SIM_CPU *cpu, sim_cia pc)
+{
+  int iw_len;
+  unsigned_word insn;
+  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+  /* If ebreak is at PC 0 then do not check for semi-hosting.  */
+  if (pc == 0)
+    return 0;
+
+  /* RISC-V semi-hosting call is flagged using these three instructions
+
+     slli zero,zero,0x1f
+     ebreak
+     srai zero,zero,0x7
+
+     Register a0 holds the system call number and a1 holds the
+     pointer to parameter buffer.  */
+
+  insn = sim_core_read_aligned_4 (cpu, pc - 4, exec_map, pc - 4);
+  iw_len = riscv_insn_length (insn);
+  if (iw_len != 4)
+    return 0;
+
+/* Instruction encoding of slli zero,zero,0x1f instruction.  */
+#define SEMIHOST_SLLI_INSN 0x01f01013
+
+/* Instruction encoding of srai zero,zero,0x7 instruction.  */
+#define SEMIHOST_SRAI_INSN 0x40705013
+
+  if (insn == SEMIHOST_SLLI_INSN)
+    {
+      insn = sim_core_read_aligned_4 (cpu, pc + 4, exec_map, pc + 4);
+      iw_len = riscv_insn_length (insn);
+      if (iw_len != 4)
+	return 0;
+
+      if (insn == SEMIHOST_SRAI_INSN)
+	{
+	  TRACE_INSN (cpu, "semi-hosting a0=%lx,a1=%lx;", riscv_cpu->a0,
+		      riscv_cpu->a1);
+
+	  if (do_semihosting (cpu))
+	    return 1;
+	  else
+	    {
+	      /* Invalid semi-hosting call.  */
+	      SIM_DESC sd = CPU_STATE (cpu);
+	      TRACE_INSN (cpu, "ebreak;");
+	      sim_engine_halt (sd, cpu, NULL, pc, sim_stopped, SIM_SIGTRAP);
+	    }
+	}
+    }
+
+  return 0;
+}
+
 static sim_cia
 execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
 {
@@ -623,10 +1404,15 @@ execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
       TRACE_INSN (cpu, "fence.i;");
       break;
     case MATCH_EBREAK:
+      if (check_and_handle_riscv_semihosting (cpu, riscv_cpu->pc))
+	{
+	  pc = pc + 4;
+	  break;
+	}
+
+      /* Not a RISC-V semihosting syscall.  */
       TRACE_INSN (cpu, "ebreak;");
-      /* GDB expects us to step over EBREAK.  */
-      sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc + 4, sim_stopped,
-		       SIM_SIGTRAP);
+      sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_stopped, SIM_SIGTRAP);
       break;
     case MATCH_ECALL:
       TRACE_INSN (cpu, "ecall;");
@@ -1517,6 +2303,7 @@ initialize_cpu (SIM_DESC sd, SIM_CPU *cpu, int mhartid)
 
   riscv_cpu->csr.mimpid = 0x8000;
   riscv_cpu->csr.mhartid = mhartid;
+  clock_start = clock ();
 }
 \f
 /* Some utils don't like having a NULL environ.  */
-- 
2.25.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 1/3] [sim/riscv] Fix crash during instruction decoding
  2023-12-17  6:52 ` [PATCH v3 1/3] [sim/riscv] Fix crash during instruction decoding jaydeep.patil
@ 2023-12-18 16:26   ` Mike Frysinger
  2023-12-19  6:14     ` [EXTERNAL] " Jaydeep Patil
  0 siblings, 1 reply; 10+ messages in thread
From: Mike Frysinger @ 2023-12-18 16:26 UTC (permalink / raw)
  To: jaydeep.patil; +Cc: gdb-patches, aburgess, joseph.faulls, bhushan.attarde

[-- Attachment #1: Type: text/plain, Size: 359 bytes --]

On 17 Dec 2023 06:52, jaydeep.patil@imgtec.com wrote:
> The match_never() function has been removed and thus step_once() crashes
> during instruction decoding. Fixed it by checking for null pointer before
> invoking function attached to match_func member of riscv_opcode structure.

the opcodes code is still doing:
  if (! (op->match_func) (op, word))
-mike

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 2/3] [sim/riscv] Add support for compressed integer instruction set simulation
  2023-12-17  6:52 ` [PATCH v3 2/3] [sim/riscv] Add support for compressed integer instruction set simulation jaydeep.patil
@ 2023-12-18 16:29   ` Mike Frysinger
  2023-12-19  6:15     ` [EXTERNAL] " Jaydeep Patil
  0 siblings, 1 reply; 10+ messages in thread
From: Mike Frysinger @ 2023-12-18 16:29 UTC (permalink / raw)
  To: jaydeep.patil; +Cc: gdb-patches, aburgess, joseph.faulls, bhushan.attarde

[-- Attachment #1: Type: text/plain, Size: 568 bytes --]

On 17 Dec 2023 06:52, jaydeep.patil@imgtec.com wrote:
> Added support for compressed integer instruction set ("c").

you haven't responded to feedback in previous patches.  please do that
before posting new ones, or clearly document in the patch the feedback
you've addressed as is standard git behavior.

> --- a/sim/riscv/sim-main.c
> +++ b/sim/riscv/sim-main.c
>  
> +/* Return address register.  */
> +#define REG_RA 1
> +
> +/* Stack pointer register.  */
> +#define REG_SP 2

we already have SIM_RISCV_xxx_REGNUM.  don't invent new ones.
-mike

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 3/3] [sim/riscv] Add semi-hosting support
  2023-12-17  6:52 ` [PATCH v3 3/3] [sim/riscv] Add semi-hosting support jaydeep.patil
@ 2023-12-18 16:30   ` Mike Frysinger
  2023-12-19  6:16     ` [EXTERNAL] " Jaydeep Patil
  0 siblings, 1 reply; 10+ messages in thread
From: Mike Frysinger @ 2023-12-18 16:30 UTC (permalink / raw)
  To: jaydeep.patil; +Cc: gdb-patches, aburgess, joseph.faulls, bhushan.attarde

[-- Attachment #1: Type: text/plain, Size: 70 bytes --]

sorry, but NAK until you follow up to existing feedback/threads
-mike

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: [EXTERNAL] Re: [PATCH v3 1/3] [sim/riscv] Fix crash during instruction decoding
  2023-12-18 16:26   ` Mike Frysinger
@ 2023-12-19  6:14     ` Jaydeep Patil
  0 siblings, 0 replies; 10+ messages in thread
From: Jaydeep Patil @ 2023-12-19  6:14 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: gdb-patches, aburgess, Joseph Faulls, Bhushan Attarde

Mike Frysinger <vapier@gentoo.org> writes:

>the opcodes code is still doing:
>  if (! (op->match_func) (op, word))

Will address this in next revision.

Regards,
Jaydeep


^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: [EXTERNAL] Re: [PATCH v3 2/3] [sim/riscv] Add support for compressed integer instruction set simulation
  2023-12-18 16:29   ` Mike Frysinger
@ 2023-12-19  6:15     ` Jaydeep Patil
  0 siblings, 0 replies; 10+ messages in thread
From: Jaydeep Patil @ 2023-12-19  6:15 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: gdb-patches, aburgess, Joseph Faulls, Bhushan Attarde

Mike Frysinger <vapier@gentoo.org> writes:

>you haven't responded to feedback in previous patches.  please do that before posting new ones, or clearly document in the patch the feedback you've addressed as is standard git behavior.

I have replied to your questions.

>we already have SIM_RISCV_xxx_REGNUM.  don't invent new ones

Will address this in next revision

Regards,
Jaydeep

^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: [EXTERNAL] Re: [PATCH v3 3/3] [sim/riscv] Add semi-hosting support
  2023-12-18 16:30   ` Mike Frysinger
@ 2023-12-19  6:16     ` Jaydeep Patil
  0 siblings, 0 replies; 10+ messages in thread
From: Jaydeep Patil @ 2023-12-19  6:16 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: gdb-patches, aburgess, Joseph Faulls, Bhushan Attarde

Mike Frysinger <vapier@gentoo.org> writes:

>sorry, but NAK until you follow up to existing feedback/threads -mike
I have replied to your questions.

Regards,
Jaydeep


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2023-12-19  6:16 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-17  6:52 [PATCH v3 0/3] sim: riscv: Compressed instruction simulation and semi-hosting support jaydeep.patil
2023-12-17  6:52 ` [PATCH v3 1/3] [sim/riscv] Fix crash during instruction decoding jaydeep.patil
2023-12-18 16:26   ` Mike Frysinger
2023-12-19  6:14     ` [EXTERNAL] " Jaydeep Patil
2023-12-17  6:52 ` [PATCH v3 2/3] [sim/riscv] Add support for compressed integer instruction set simulation jaydeep.patil
2023-12-18 16:29   ` Mike Frysinger
2023-12-19  6:15     ` [EXTERNAL] " Jaydeep Patil
2023-12-17  6:52 ` [PATCH v3 3/3] [sim/riscv] Add semi-hosting support jaydeep.patil
2023-12-18 16:30   ` Mike Frysinger
2023-12-19  6:16     ` [EXTERNAL] " Jaydeep Patil

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).