Skip to content

Commit 7087c7f

Browse files
committed
Use computed goto to speed up main loop
+ The old implementation of rv_step can be enabled through environment variable NO_COMPUTED_GOTO. + An op_unimp handler is also added in order to acheive this. This also allows better handling of unimplemented opcodes instead of jumping into NULL.
1 parent 179d878 commit 7087c7f

File tree

2 files changed

+107
-24
lines changed

2 files changed

+107
-24
lines changed

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ CFLAGS += -D ENABLE_SDL
1313
CFLAGS += `sdl2-config --cflags`
1414
LDFLAGS += `sdl2-config --libs`
1515

16+
# Whether to enable computed goto in riscv.c
17+
ifeq ("$(NO_COMPUTED_GOTO)", "")
18+
riscv.o: CFLAGS += -fno-gcse -fno-crossjumping -D ENABLE_COMPUTED_GOTO
19+
endif
20+
1621
# Control the build verbosity
1722
ifeq ("$(VERBOSE)","1")
1823
Q :=

riscv.c

Lines changed: 102 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ static bool op_misc_mem(struct riscv_t *rv, uint32_t inst UNUSED)
138138
return true;
139139
}
140140
#else
141-
#define op_misc_mem NULL
141+
#define op_misc_mem OP_UNIMP
142142
#endif // ENABLE_Zifencei
143143

144144
static bool op_op_imm(struct riscv_t *rv, uint32_t inst)
@@ -758,47 +758,124 @@ static bool op_amo(struct riscv_t *rv, uint32_t inst)
758758
return true;
759759
}
760760
#else
761-
#define op_amo NULL
761+
#define op_amo OP_UNIMP
762762
#endif // ENABLE_RV32A
763763

764764
/* No RV32F support */
765-
#define op_load_fp NULL
766-
#define op_store_fp NULL
767-
#define op_fp NULL
768-
#define op_madd NULL
769-
#define op_msub NULL
770-
#define op_nmsub NULL
771-
#define op_nmadd NULL
765+
#define op_load_fp OP_UNIMP
766+
#define op_store_fp OP_UNIMP
767+
#define op_fp OP_UNIMP
768+
#define op_madd OP_UNIMP
769+
#define op_msub OP_UNIMP
770+
#define op_nmsub OP_UNIMP
771+
#define op_nmadd OP_UNIMP
772+
773+
// handler for all unimplemented opcodes
774+
static bool op_unimp(struct riscv_t *rv, uint32_t inst UNUSED)
775+
{
776+
rv_except_illegal_inst(rv);
777+
return false;
778+
}
772779

773780
// opcode handler type
774781
typedef bool (*opcode_t)(struct riscv_t *rv, uint32_t inst);
775782

776-
// clang-format off
777-
// opcode dispatch table
778-
static const opcode_t opcodes[] = {
779-
// 000 001 010 011 100 101 110 111
780-
op_load, op_load_fp, NULL, op_misc_mem, op_op_imm, op_auipc, NULL, NULL, // 00
781-
op_store, op_store_fp, NULL, op_amo, op_op, op_lui, NULL, NULL, // 01
782-
op_madd, op_msub, op_nmsub, op_nmadd, op_fp, NULL, NULL, NULL, // 10
783-
op_branch, op_jalr, NULL, op_jal, op_system, NULL, NULL, NULL, // 11
784-
};
785-
// clang-format on
786-
787783
void rv_step(struct riscv_t *rv, int32_t cycles)
788784
{
789785
assert(rv);
790786
const uint64_t cycles_target = rv->csr_cycle + cycles;
787+
uint32_t inst, index;
788+
789+
#define OP_UNIMP op_unimp
790+
#ifdef ENABLE_COMPUTED_GOTO
791+
#define OP(instr) &&op_##instr
792+
#define TABLE_TYPE const void *
793+
#else
794+
#define OP(instr) op_##instr
795+
#define TABLE_TYPE const opcode_t
796+
#endif
797+
798+
// clang-format off
799+
TABLE_TYPE jump_table[] = {
800+
// 000 001 010 011 100 101 110 111
801+
OP(load), OP(load_fp), OP(unimp), OP(misc_mem), OP(op_imm), OP(auipc), OP(unimp), OP(unimp), // 00
802+
OP(store), OP(store_fp), OP(unimp), OP(amo), OP(op), OP(lui), OP(unimp), OP(unimp), // 01
803+
OP(madd), OP(msub), OP(nmsub), OP(nmadd), OP(fp), OP(unimp), OP(unimp), OP(unimp), // 10
804+
OP(branch), OP(jalr), OP(unimp), OP(jal), OP(system), OP(unimp), OP(unimp), OP(unimp), // 11
805+
};
806+
// clang-format on
807+
808+
#ifdef ENABLE_COMPUTED_GOTO
809+
#define DISPATCH() \
810+
{ \
811+
if (rv->csr_cycle >= cycles_target || rv->halt) \
812+
goto exit; \
813+
else { \
814+
/* fetch the next instruction */ \
815+
inst = rv->io.mem_ifetch(rv, rv->PC); \
816+
/* standard uncompressed instruction */ \
817+
if ((inst & 3) == 3) { \
818+
index = (inst & INST_6_2) >> 2; \
819+
goto *jump_table[index]; \
820+
} else { \
821+
/* TODO: compressed instruction*/ \
822+
assert(!"Unreachable"); \
823+
} \
824+
} \
825+
}
826+
827+
#define EXEC(instr) \
828+
{ \
829+
/* dispatch this opcode */ \
830+
if (!op_##instr(rv, inst)) \
831+
goto exit; \
832+
/* increment the cycles csr*/ \
833+
rv->csr_cycle++; \
834+
}
835+
836+
#define TARGET(instr) \
837+
op_##instr : \
838+
EXEC(instr); \
839+
DISPATCH();
840+
841+
DISPATCH();
842+
843+
// main loop
844+
TARGET(load)
845+
TARGET(op_imm)
846+
TARGET(auipc)
847+
TARGET(store)
848+
TARGET(op)
849+
TARGET(lui)
850+
TARGET(branch)
851+
TARGET(jalr)
852+
TARGET(jal)
853+
TARGET(system)
854+
#ifdef ENABLE_Zifencei
855+
TARGET(misc_mem)
856+
#endif
857+
#ifdef ENABLE_RV32A
858+
TARGET(amo)
859+
#endif
860+
TARGET(unimp)
861+
862+
exit:
863+
return;
791864

865+
#undef DISPATCH
866+
#undef EXEC
867+
#undef TARGET
868+
#else // ENABLE_COMPUTED_GOTO = 0
792869
while (rv->csr_cycle < cycles_target && !rv->halt) {
793870
// fetch the next instruction
794-
const uint32_t inst = rv->io.mem_ifetch(rv, rv->PC);
871+
inst = rv->io.mem_ifetch(rv, rv->PC);
795872

796873
// standard uncompressed instruction
797874
if ((inst & 3) == 3) {
798-
const uint32_t index = (inst & INST_6_2) >> 2;
875+
index = (inst & INST_6_2) >> 2;
799876

800-
// dispatch this opcode
801-
const opcode_t op = opcodes[index];
877+
// dispatch this opcode
878+
TABLE_TYPE op = jump_table[index];
802879
assert(op);
803880
if (!op(rv, inst))
804881
break;
@@ -810,6 +887,7 @@ void rv_step(struct riscv_t *rv, int32_t cycles)
810887
assert(!"Unreachable");
811888
}
812889
}
890+
#endif // ENABLE_COMPUTED_GOTO
813891
}
814892

815893
riscv_user_t rv_userdata(struct riscv_t *rv)

0 commit comments

Comments
 (0)