Skip to content

Commit 48b9001

Browse files
authored
Merge pull request #2 from sammer1107/computed_goto
Use computed-goto to lower instruction dispatch overhead
2 parents a8e5828 + 4af77cd commit 48b9001

File tree

2 files changed

+115
-25
lines changed

2 files changed

+115
-25
lines changed

Makefile

+16-1
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

+99-24
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,121 @@ 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 // ENABLE_COMPUTED_GOTO = false
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 quit; \
813+
/* fetch the next instruction */ \
814+
inst = rv->io.mem_ifetch(rv, rv->PC); \
815+
/* standard uncompressed instruction */ \
816+
if ((inst & 3) == 3) { \
817+
index = (inst & INST_6_2) >> 2; \
818+
goto *jump_table[index]; \
819+
} else { \
820+
/* TODO: compressed instruction*/ \
821+
assert(!"Unreachable"); \
822+
} \
823+
}
824+
825+
#define EXEC(instr) \
826+
{ \
827+
/* dispatch this opcode */ \
828+
if (!op_##instr(rv, inst)) \
829+
goto quit; \
830+
/* increment the cycles csr*/ \
831+
rv->csr_cycle++; \
832+
}
833+
834+
#define TARGET(instr) \
835+
op_##instr : EXEC(instr); \
836+
DISPATCH();
837+
838+
DISPATCH();
839+
840+
// main loop
841+
TARGET(load)
842+
TARGET(op_imm)
843+
TARGET(auipc)
844+
TARGET(store)
845+
TARGET(op)
846+
TARGET(lui)
847+
TARGET(branch)
848+
TARGET(jalr)
849+
TARGET(jal)
850+
TARGET(system)
851+
#ifdef ENABLE_Zifencei
852+
TARGET(misc_mem)
853+
#endif
854+
#ifdef ENABLE_RV32A
855+
TARGET(amo)
856+
#endif
857+
TARGET(unimp)
858+
859+
quit:
860+
return;
791861

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

796870
// standard uncompressed instruction
797871
if ((inst & 3) == 3) {
798-
const uint32_t index = (inst & INST_6_2) >> 2;
872+
index = (inst & INST_6_2) >> 2;
799873

800-
// dispatch this opcode
801-
const opcode_t op = opcodes[index];
874+
// dispatch this opcode
875+
TABLE_TYPE op = jump_table[index];
802876
assert(op);
803877
if (!op(rv, inst))
804878
break;
@@ -810,6 +884,7 @@ void rv_step(struct riscv_t *rv, int32_t cycles)
810884
assert(!"Unreachable");
811885
}
812886
}
887+
#endif // ENABLE_COMPUTED_GOTO
813888
}
814889

815890
riscv_user_t rv_userdata(struct riscv_t *rv)

0 commit comments

Comments
 (0)