target/riscv: Implement dynamic establishment of custom decoder

In this patch, we modify the decoder to be a freely composable data
structure instead of a hardcoded one. It can be dynamically builded up
according to the extensions.
This approach has several benefits:
1. Provides support for heterogeneous cpu architectures. As we add decoder in
   RISCVCPU, each cpu can have their own decoder, and the decoders can be
   different due to cpu's features.
2. Improve the decoding efficiency. We run the guard_func to see if the decoder
   can be added to the dynamic_decoder when building up the decoder. Therefore,
   there is no need to run the guard_func when decoding each instruction. It can
   improve the decoding efficiency
3. For vendor or dynamic cpus, it allows them to customize their own decoder
   functions to improve decoding efficiency, especially when vendor-defined
   instruction sets increase. Because of dynamic building up, it can skip the other
   decoder guard functions when decoding.
4. Pre patch for allowing adding a vendor decoder before decode_insn32() with minimal
   overhead for users that don't need this particular vendor decoder.

Signed-off-by: Huang Tao <eric.huang@linux.alibaba.com>
Suggested-by: Christoph Muellner <christoph.muellner@vrull.eu>
Co-authored-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20240506023607.29544-1-eric.huang@linux.alibaba.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
Huang Tao 2024-05-06 10:36:07 +08:00 committed by Alistair Francis
parent ff33b7a969
commit 8c8a7cd647
5 changed files with 47 additions and 16 deletions

View File

@ -1134,6 +1134,7 @@ void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp)
error_propagate(errp, local_err);
return;
}
riscv_tcg_cpu_finalize_dynamic_decoder(cpu);
} else if (kvm_enabled()) {
riscv_kvm_cpu_finalize_features(cpu, &local_err);
if (local_err != NULL) {

View File

@ -455,6 +455,7 @@ struct ArchCPU {
uint32_t pmu_avail_ctrs;
/* Mapping of events to counters */
GHashTable *pmu_event_ctr_map;
const GPtrArray *decoders;
};
/**

View File

@ -863,6 +863,21 @@ void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp)
}
}
void riscv_tcg_cpu_finalize_dynamic_decoder(RISCVCPU *cpu)
{
GPtrArray *dynamic_decoders;
dynamic_decoders = g_ptr_array_sized_new(decoder_table_size);
for (size_t i = 0; i < decoder_table_size; ++i) {
if (decoder_table[i].guard_func &&
decoder_table[i].guard_func(&cpu->cfg)) {
g_ptr_array_add(dynamic_decoders,
(gpointer)decoder_table[i].riscv_cpu_decode_fn);
}
}
cpu->decoders = dynamic_decoders;
}
bool riscv_cpu_tcg_compatible(RISCVCPU *cpu)
{
return object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST) == NULL;

View File

@ -26,4 +26,19 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp);
void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp);
bool riscv_cpu_tcg_compatible(RISCVCPU *cpu);
struct DisasContext;
struct RISCVCPUConfig;
typedef struct RISCVDecoder {
bool (*guard_func)(const struct RISCVCPUConfig *);
bool (*riscv_cpu_decode_fn)(struct DisasContext *, uint32_t);
} RISCVDecoder;
typedef bool (*riscv_cpu_decode_fn)(struct DisasContext *, uint32_t);
extern const size_t decoder_table_size;
extern const RISCVDecoder decoder_table[];
void riscv_tcg_cpu_finalize_dynamic_decoder(RISCVCPU *cpu);
#endif

View File

@ -35,6 +35,8 @@
#include "exec/helper-info.c.inc"
#undef HELPER_H
#include "tcg/tcg-cpu.h"
/* global register indices */
static TCGv cpu_gpr[32], cpu_gprh[32], cpu_pc, cpu_vl, cpu_vstart;
static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */
@ -114,6 +116,7 @@ typedef struct DisasContext {
/* FRM is known to contain a valid value. */
bool frm_valid;
bool insn_start_updated;
const GPtrArray *decoders;
} DisasContext;
static inline bool has_ext(DisasContext *ctx, uint32_t ext)
@ -1123,21 +1126,16 @@ static inline int insn_len(uint16_t first_word)
return (first_word & 3) == 3 ? 4 : 2;
}
static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode)
{
/*
* A table with predicate (i.e., guard) functions and decoder functions
* that are tested in-order until a decoder matches onto the opcode.
*/
static const struct {
bool (*guard_func)(const RISCVCPUConfig *);
bool (*decode_func)(DisasContext *, uint32_t);
} decoders[] = {
const RISCVDecoder decoder_table[] = {
{ always_true_p, decode_insn32 },
{ has_xthead_p, decode_xthead},
{ has_XVentanaCondOps_p, decode_XVentanaCodeOps},
};
const size_t decoder_table_size = ARRAY_SIZE(decoder_table);
static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode)
{
ctx->virt_inst_excp = false;
ctx->cur_insn_len = insn_len(opcode);
/* Check for compressed insn */
@ -1158,9 +1156,9 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode)
ctx->base.pc_next + 2));
ctx->opcode = opcode32;
for (size_t i = 0; i < ARRAY_SIZE(decoders); ++i) {
if (decoders[i].guard_func(ctx->cfg_ptr) &&
decoders[i].decode_func(ctx, opcode32)) {
for (guint i = 0; i < ctx->decoders->len; ++i) {
riscv_cpu_decode_fn func = g_ptr_array_index(ctx->decoders, i);
if (func(ctx, opcode32)) {
return;
}
}
@ -1205,6 +1203,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER);
ctx->zero = tcg_constant_tl(0);
ctx->virt_inst_excp = false;
ctx->decoders = cpu->decoders;
}
static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu)