Skip to content

Commit

Permalink
Use computed-goto to lower instruction dispatch overhead
Browse files Browse the repository at this point in the history
+ 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.
  • Loading branch information
sammer1107 committed Jan 5, 2021
1 parent 179d878 commit 3ecac53
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 25 deletions.
17 changes: 16 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
CFLAGS = -std=gnu99 -O2 -Wall -Wextra
ifeq ("$(origin CC)", "default")
CC = gcc
endif

CFLAGS = -std=gnu99 -Wall -Wextra
CFLAGS += -include common.h

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

# Whether to enable computed goto in riscv.c
ENABLE_COMPUTED_GOTO ?= 1
ifeq ("$(ENABLE_COMPUTED_GOTO)", "1")
ifneq ($(filter $(CC), gcc clang),)
riscv.o: CFLAGS += -D ENABLE_COMPUTED_GOTO
ifeq ("$(CC)", "gcc")
riscv.o: CFLAGS += -fno-gcse -fno-crossjumping
endif
endif
endif

# Control the build verbosity
ifeq ("$(VERBOSE)","1")
Q :=
Expand Down
124 changes: 100 additions & 24 deletions riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ static bool op_misc_mem(struct riscv_t *rv, uint32_t inst UNUSED)
return true;
}
#else
#define op_misc_mem NULL
#define op_misc_mem OP_UNIMP
#endif // ENABLE_Zifencei

static bool op_op_imm(struct riscv_t *rv, uint32_t inst)
Expand Down Expand Up @@ -758,47 +758,122 @@ static bool op_amo(struct riscv_t *rv, uint32_t inst)
return true;
}
#else
#define op_amo NULL
#define op_amo OP_UNIMP
#endif // ENABLE_RV32A

/* No RV32F support */
#define op_load_fp NULL
#define op_store_fp NULL
#define op_fp NULL
#define op_madd NULL
#define op_msub NULL
#define op_nmsub NULL
#define op_nmadd NULL
#define op_load_fp OP_UNIMP
#define op_store_fp OP_UNIMP
#define op_fp OP_UNIMP
#define op_madd OP_UNIMP
#define op_msub OP_UNIMP
#define op_nmsub OP_UNIMP
#define op_nmadd OP_UNIMP

// handler for all unimplemented opcodes
static bool op_unimp(struct riscv_t *rv, uint32_t inst UNUSED)
{
rv_except_illegal_inst(rv);
return false;
}

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

// clang-format off
// opcode dispatch table
static const opcode_t opcodes[] = {
// 000 001 010 011 100 101 110 111
op_load, op_load_fp, NULL, op_misc_mem, op_op_imm, op_auipc, NULL, NULL, // 00
op_store, op_store_fp, NULL, op_amo, op_op, op_lui, NULL, NULL, // 01
op_madd, op_msub, op_nmsub, op_nmadd, op_fp, NULL, NULL, NULL, // 10
op_branch, op_jalr, NULL, op_jal, op_system, NULL, NULL, NULL, // 11
};
// clang-format on

void rv_step(struct riscv_t *rv, int32_t cycles)
{
assert(rv);
const uint64_t cycles_target = rv->csr_cycle + cycles;
uint32_t inst, index;
// clang-format off
#define OP_UNIMP op_unimp
#ifdef ENABLE_COMPUTED_GOTO
#define OP(instr) &&op_##instr
#define TABLE_TYPE const void *
#else
#define OP(instr) op_##instr
#define TABLE_TYPE const opcode_t
#endif

TABLE_TYPE jump_table[] = {
// 000 001 010 011 100 101 110 111
OP(load), OP(load_fp), OP(unimp), OP(misc_mem), OP(op_imm), OP(auipc), OP(unimp), OP(unimp), // 00
OP(store), OP(store_fp), OP(unimp), OP(amo), OP(op), OP(lui), OP(unimp), OP(unimp), // 01
OP(madd), OP(msub), OP(nmsub), OP(nmadd), OP(fp), OP(unimp), OP(unimp), OP(unimp), // 10
OP(branch), OP(jalr), OP(unimp), OP(jal), OP(system), OP(unimp), OP(unimp), OP(unimp), // 11
};
// clang-format on

#ifdef ENABLE_COMPUTED_GOTO
#define DISPATCH() \
{ \
if (rv->csr_cycle >= cycles_target || rv->halt) \
goto exit; \
/* fetch the next instruction */ \
inst = rv->io.mem_ifetch(rv, rv->PC); \
/* standard uncompressed instruction */ \
if ((inst & 3) == 3) { \
index = (inst & INST_6_2) >> 2; \
goto *jump_table[index]; \
} else { \
/* TODO: compressed instruction*/ \
assert(!"Unreachable"); \
} \
}

#define EXEC(instr) \
{ \
/* dispatch this opcode */ \
if (!op_##instr(rv, inst)) \
goto exit; \
/* increment the cycles csr*/ \
rv->csr_cycle++; \
}
// clang-format off
#define TARGET(instr) \
op_##instr : \
EXEC(instr); \
DISPATCH();
// clang-format on

DISPATCH();

// main loop
TARGET(load)
TARGET(op_imm)
TARGET(auipc)
TARGET(store)
TARGET(op)
TARGET(lui)
TARGET(branch)
TARGET(jalr)
TARGET(jal)
TARGET(system)
#ifdef ENABLE_Zifencei
TARGET(misc_mem)
#endif
#ifdef ENABLE_RV32A
TARGET(amo)
#endif
TARGET(unimp)

exit:
return;

#undef DISPATCH
#undef EXEC
#undef TARGET
#else // ENABLE_COMPUTED_GOTO = 0
while (rv->csr_cycle < cycles_target && !rv->halt) {
// fetch the next instruction
const uint32_t inst = rv->io.mem_ifetch(rv, rv->PC);
inst = rv->io.mem_ifetch(rv, rv->PC);

// standard uncompressed instruction
if ((inst & 3) == 3) {
const uint32_t index = (inst & INST_6_2) >> 2;
index = (inst & INST_6_2) >> 2;

// dispatch this opcode
const opcode_t op = opcodes[index];
// dispatch this opcode
TABLE_TYPE op = jump_table[index];
assert(op);
if (!op(rv, inst))
break;
Expand All @@ -810,6 +885,7 @@ void rv_step(struct riscv_t *rv, int32_t cycles)
assert(!"Unreachable");
}
}
#endif // ENABLE_COMPUTED_GOTO
}

riscv_user_t rv_userdata(struct riscv_t *rv)
Expand Down

0 comments on commit 3ecac53

Please sign in to comment.