Skip to content

Commit 3ecac53

Browse files
committed
Use computed-goto to lower instruction dispatch overhead
+ This commit applies the computed goto technique to the main loop. This modification is tested to have 15 to 23 percent improvement on performance. + The old implementation of rv_step can be enabled by setting the environment variable ENABLE_COMPUTED_GOTO to 0. + Only clang and gcc is supported by computed goto feature, ENABLE_COMPUTED_GOTO will be ignored if other compiler is used. + 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 3ecac53

File tree

2 files changed

+116
-25
lines changed

2 files changed

+116
-25
lines changed

Makefile

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
CFLAGS = -std=gnu99 -O2 -Wall -Wextra
1+
ifeq ("$(origin CC)", "default")
2+
CC = gcc
3+
endif
4+
5+
CFLAGS = -std=gnu99 -Wall -Wextra
26
CFLAGS += -include common.h
37

48
# Base configurations for RISC-V extensions
@@ -13,6 +17,17 @@ CFLAGS += -D ENABLE_SDL
1317
CFLAGS += `sdl2-config --cflags`
1418
LDFLAGS += `sdl2-config --libs`
1519

20+
# Whether to enable computed goto in riscv.c
21+
ENABLE_COMPUTED_GOTO ?= 1
22+
ifeq ("$(ENABLE_COMPUTED_GOTO)", "1")
23+
ifneq ($(filter $(CC), gcc clang),)
24+
riscv.o: CFLAGS += -D ENABLE_COMPUTED_GOTO
25+
ifeq ("$(CC)", "gcc")
26+
riscv.o: CFLAGS += -fno-gcse -fno-crossjumping
27+
endif
28+
endif
29+
endif
30+
1631
# Control the build verbosity
1732
ifeq ("$(VERBOSE)","1")
1833
Q :=

riscv.c

Lines changed: 100 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,122 @@ 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+
// clang-format off
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+
TABLE_TYPE jump_table[] = {
799+
// 000 001 010 011 100 101 110 111
800+
OP(load), OP(load_fp), OP(unimp), OP(misc_mem), OP(op_imm), OP(auipc), OP(unimp), OP(unimp), // 00
801+
OP(store), OP(store_fp), OP(unimp), OP(amo), OP(op), OP(lui), OP(unimp), OP(unimp), // 01
802+
OP(madd), OP(msub), OP(nmsub), OP(nmadd), OP(fp), OP(unimp), OP(unimp), OP(unimp), // 10
803+
OP(branch), OP(jalr), OP(unimp), OP(jal), OP(system), OP(unimp), OP(unimp), OP(unimp), // 11
804+
};
805+
// clang-format on
806+
807+
#ifdef ENABLE_COMPUTED_GOTO
808+
#define DISPATCH() \
809+
{ \
810+
if (rv->csr_cycle >= cycles_target || rv->halt) \
811+
goto exit; \
812+
/* fetch the next instruction */ \
813+
inst = rv->io.mem_ifetch(rv, rv->PC); \
814+
/* standard uncompressed instruction */ \
815+
if ((inst & 3) == 3) { \
816+
index = (inst & INST_6_2) >> 2; \
817+
goto *jump_table[index]; \
818+
} else { \
819+
/* TODO: compressed instruction*/ \
820+
assert(!"Unreachable"); \
821+
} \
822+
}
823+
824+
#define EXEC(instr) \
825+
{ \
826+
/* dispatch this opcode */ \
827+
if (!op_##instr(rv, inst)) \
828+
goto exit; \
829+
/* increment the cycles csr*/ \
830+
rv->csr_cycle++; \
831+
}
832+
// clang-format off
833+
#define TARGET(instr) \
834+
op_##instr : \
835+
EXEC(instr); \
836+
DISPATCH();
837+
// clang-format on
838+
839+
DISPATCH();
840+
841+
// main loop
842+
TARGET(load)
843+
TARGET(op_imm)
844+
TARGET(auipc)
845+
TARGET(store)
846+
TARGET(op)
847+
TARGET(lui)
848+
TARGET(branch)
849+
TARGET(jalr)
850+
TARGET(jal)
851+
TARGET(system)
852+
#ifdef ENABLE_Zifencei
853+
TARGET(misc_mem)
854+
#endif
855+
#ifdef ENABLE_RV32A
856+
TARGET(amo)
857+
#endif
858+
TARGET(unimp)
859+
860+
exit:
861+
return;
791862

863+
#undef DISPATCH
864+
#undef EXEC
865+
#undef TARGET
866+
#else // ENABLE_COMPUTED_GOTO = 0
792867
while (rv->csr_cycle < cycles_target && !rv->halt) {
793868
// fetch the next instruction
794-
const uint32_t inst = rv->io.mem_ifetch(rv, rv->PC);
869+
inst = rv->io.mem_ifetch(rv, rv->PC);
795870

796871
// standard uncompressed instruction
797872
if ((inst & 3) == 3) {
798-
const uint32_t index = (inst & INST_6_2) >> 2;
873+
index = (inst & INST_6_2) >> 2;
799874

800-
// dispatch this opcode
801-
const opcode_t op = opcodes[index];
875+
// dispatch this opcode
876+
TABLE_TYPE op = jump_table[index];
802877
assert(op);
803878
if (!op(rv, inst))
804879
break;
@@ -810,6 +885,7 @@ void rv_step(struct riscv_t *rv, int32_t cycles)
810885
assert(!"Unreachable");
811886
}
812887
}
888+
#endif // ENABLE_COMPUTED_GOTO
813889
}
814890

815891
riscv_user_t rv_userdata(struct riscv_t *rv)

0 commit comments

Comments
 (0)