diff --git a/Makefile b/Makefile index 3aa1e73d9..55b831b2a 100644 --- a/Makefile +++ b/Makefile @@ -88,6 +88,27 @@ gdbstub-test: $(BIN) $(Q).ci/gdbstub-test.sh && $(call notice, [OK]) endif +ENABLE_JIT ?= 0 +$(call set-feature, JIT) +ifeq ($(call has, JIT), 1) +CFLAGS += -I./src/mir +LDFLAGS += src/mir/libmir.a -lpthread +OBJS_EXT += compile.o + +src/jit_template.c: + $(Q)tools/gen-jit-template.py $(CFLAGS) > $@ + +src/mir/GNUmakefile: + git submodule update --init $(dir $@) + +src/mir/libmir.a: src/mir/GNUmakefile + $(MAKE) --quiet -C src/mir + +$(OUT)/compile.o: src/compile.c src/mir/libmir.a src/jit_template.c + $(VECHO) " CC\t$@\n" + $(Q)$(CC) -o $@ $(CFLAGS) -c -MMD -MF $@.d $< +endif + # For tail-call elimination, we need a specific set of build flags applied. # FIXME: On macOS + Apple Silicon, -fno-stack-protector might have a negative impact. $(OUT)/emulate.o: CFLAGS += $(CFLAGS_NO_CET) -fomit-frame-pointer -fno-stack-check -fno-stack-protector @@ -182,7 +203,7 @@ endif endif clean: - $(RM) $(BIN) $(OBJS) $(HIST_BIN) $(HIST_OBJS) $(deps) $(CACHE_OUT) + $(RM) $(BIN) $(OBJS) $(HIST_BIN) $(HIST_OBJS) $(deps) $(CACHE_OUT) src/jit_template.c distclean: clean -$(RM) $(DOOM_DATA) $(QUAKE_DATA) $(RM) -r $(OUT)/id1 diff --git a/src/cache.c b/src/cache.c index 3875d7d90..b08913737 100644 --- a/src/cache.c +++ b/src/cache.c @@ -11,16 +11,20 @@ #include "cache.h" #include "mpool.h" +#include "utils.h" #define MIN(a, b) ((a < b) ? a : b) -#define GOLDEN_RATIO_32 0x61C88647 #define HASH(val) \ (((val) * (GOLDEN_RATIO_32)) >> (32 - (cache_size_bits))) & (cache_size - 1) /* THRESHOLD is set to identify hot spots. Once the frequency of use for a block * exceeds the THRESHOLD, the JIT compiler flow is triggered. */ -#define THRESHOLD 1000 +#define THRESHOLD 32768 +#if RV32_HAS(JIT) +#define sys_icache_invalidate(addr, size) \ + __builtin___clear_cache((char *) (addr), (char *) (addr) + (size)); +#endif static uint32_t cache_size, cache_size_bits; static struct mpool *cache_mp; @@ -541,3 +545,37 @@ void cache_free(cache_t *cache, void (*callback)(void *)) free(cache->map); free(cache); } + +#if RV32_HAS(JIT) +bool cache_hot(struct cache *cache, uint32_t key) +{ + if (!cache->capacity || hlist_empty(&cache->map->ht_list_head[HASH(key)])) + return false; +#if RV32_HAS(ARC) + arc_entry_t *entry = NULL; +#ifdef __HAVE_TYPEOF + hlist_for_each_entry (entry, &cache->map->ht_list_head[HASH(key)], ht_list) +#else + hlist_for_each_entry (entry, &cache->map->ht_list_head[HASH(key)], ht_list, + arc_entry_t) +#endif + { + if (entry->key == key && entry->frequency == THRESHOLD) + return true; + } +#else + lfu_entry_t *entry = NULL; +#ifdef __HAVE_TYPEOF + hlist_for_each_entry (entry, &cache->map->ht_list_head[HASH(key)], ht_list) +#else + hlist_for_each_entry (entry, &cache->map->ht_list_head[HASH(key)], ht_list, + lfu_entry_t) +#endif + { + if (entry->key == key && entry->frequency == THRESHOLD) + return true; + } +#endif + return false; +} +#endif \ No newline at end of file diff --git a/src/cache.h b/src/cache.h index 6460cd282..e8928e461 100644 --- a/src/cache.h +++ b/src/cache.h @@ -5,6 +5,7 @@ #pragma once +#include #include struct cache; @@ -38,3 +39,13 @@ void *cache_put(struct cache *cache, uint32_t key, void *value); * @callback: a function for freeing cache entry completely */ void cache_free(struct cache *cache, void (*callback)(void *)); + +#if RV32_HAS(JIT) +/** + * cache_hot - check whether the frequency of the cache entry exceeds the + * threshold or not + * @cache: a pointer points to target cache + * @key: the key of the specified entry + */ +bool cache_hot(struct cache *cache, uint32_t key); +#endif diff --git a/src/compile.c b/src/compile.c new file mode 100644 index 000000000..aa747228e --- /dev/null +++ b/src/compile.c @@ -0,0 +1,596 @@ +/* + * rv32emu is freely redistributable under the MIT License. See the file + * "LICENSE" for information on usage and redistribution of this file. + */ + +#include +#include +#include +#include +#include + +#include "c2mir/c2mir.h" +#include "cache.h" +#include "compile.h" +#include "decode.h" +#include "mir-gen.h" +#include "mir.h" +#include "riscv_private.h" +#include "utils.h" + +/* Provide for c2mir to retrieve the generated code string. */ +typedef struct { + char *code; + size_t code_size; + size_t curr; /**< the current pointer to code string */ +} code_string_t; + +/* mir module */ +typedef struct { + MIR_context_t ctx; + struct c2mir_options *options; + uint8_t debug_level; + uint8_t optimize_level; +} riscv_jit_t; + +typedef struct { + char *name; + void *func; +} func_obj_t; + +#define SET_SIZE_BITS 10 +#define SET_SIZE 1 << SET_SIZE_BITS +#define SET_SLOTS_SIZE 32 +#define HASH(val) \ + (((val) * (GOLDEN_RATIO_32)) >> (32 - SET_SIZE_BITS)) & ((SET_SIZE) -1) +#define sys_icache_invalidate(addr, size) \ + __builtin___clear_cache((char *) (addr), (char *) (addr) + (size)); + +/* + * The set consists of SET_SIZE buckets, with each bucket containing + * SET_SLOTS_SIZE slots. + */ +typedef struct { + uint32_t table[SET_SIZE][SET_SLOTS_SIZE]; +} set_t; + +/** + * set_reset - clear a set + * @set: a pointer points to target set + */ +static inline void set_reset(set_t *set) +{ + memset(set, 0, sizeof(set_t)); +} + +/** + * set_add - insert a new element into the set + * @set: a pointer points to target set + * @key: the key of the inserted entry + */ +static bool set_add(set_t *set, uint32_t key) +{ + const uint32_t index = HASH(key); + uint8_t count = 0; + while (set->table[index][count]) { + if (set->table[index][count++] == key) + return false; + } + + set->table[index][count] = key; + return true; +} + +/** + * set_has - check whether the element exist in the set or not + * @set: a pointer points to target set + * @key: the key of the inserted entry + */ +static bool set_has(set_t *set, uint32_t key) +{ + const uint32_t index = HASH(key); + for (uint8_t count = 0; set->table[index][count]; count++) { + if (set->table[index][count] == key) + return true; + } + return false; +} + +static bool insn_is_branch(uint8_t opcode) +{ + switch (opcode) { +#define _(inst, can_branch, insn_len, reg_mask) \ + IIF(can_branch)(case rv_insn_##inst:, ) + RV_INSN_LIST +#undef _ + return true; + } + return false; +} + +static uint8_t insn_len_table[] = { +#define _(inst, can_branch, insn_len, reg_mask) [rv_insn_##inst] = insn_len, + RV_INSN_LIST +#undef _ +}; + +typedef void (*gen_func_t)(riscv_t *, rv_insn_t *, char *); +static gen_func_t dispatch_table[N_TOTAL_INSNS]; +static char funcbuf[128] = {0}; +#define GEN(...) \ + sprintf(funcbuf, __VA_ARGS__); \ + strcat(gencode, funcbuf); +#define UPDATE_PC(inc) GEN(" PC += %d;\n", inc) +#define NEXT_INSN(target) GEN(" goto insn_%x;\n", target) +#define END_INSN \ + GEN(" rv->csr_cycle = cycle;\n"); \ + GEN(" rv->PC = PC;\n"); \ + GEN(" return true;\n"); +#define RVOP(inst, code) \ + static void gen_##inst(riscv_t *rv UNUSED, rv_insn_t *ir, char *gencode) \ + { \ + GEN("insn_%x:\n" \ + " cycle++;\n", \ + (ir->pc)); \ + code; \ + if (!insn_is_branch(ir->opcode)) { \ + GEN(" PC += %d;\n", insn_len_table[ir->opcode]); \ + NEXT_INSN(ir->pc + insn_len_table[ir->opcode]); \ + } \ + } + +#include "jit_template.c" +/* + * In the decoding and emulation stage, specific information is stored in the + * IR, such as register numbers and immediates. We can leverage this information + * to generate more efficient code instead of relying on the original source + * code. + */ +RVOP(jal, { + GEN(" pc = PC;\n"); + UPDATE_PC(ir->imm); + if (ir->rd) { + GEN(" rv->X[%u] = pc + 4;\n", ir->rd); + } + if (ir->branch_taken) { + NEXT_INSN(ir->pc + ir->imm); + } else { + END_INSN; + } +}) + +RVOP(jalr, { + GEN(" pc = PC;\n"); + GEN(" PC = (rv->X[%u] + %d) & ~1U;\n", ir->rs1, ir->imm); + if (ir->rd) { + GEN(" rv->X[%u] = pc + 4;\n", ir->rd); + } + END_INSN; +}) + +#define BRNACH_FUNC(type, comp) \ + GEN(" if ((%s) rv->X[%u] %s (%s) rv->X[%u]) {\n", #type, ir->rs1, #comp, \ + #type, ir->rs2); \ + UPDATE_PC(ir->imm); \ + if (ir->branch_taken) { \ + NEXT_INSN(ir->pc + ir->imm); \ + } else { \ + END_INSN; \ + } \ + GEN(" }\n"); \ + UPDATE_PC(4); \ + if (ir->branch_untaken) { \ + NEXT_INSN(ir->pc + 4); \ + } else { \ + END_INSN; \ + } + +RVOP(beq, { BRNACH_FUNC(uint32_t, ==); }) + +RVOP(bne, { BRNACH_FUNC(uint32_t, !=); }) + +RVOP(blt, { BRNACH_FUNC(int32_t, <); }) + +RVOP(bge, { BRNACH_FUNC(int32_t, >=); }) + +RVOP(bltu, { BRNACH_FUNC(uint32_t, <); }) + +RVOP(bgeu, { BRNACH_FUNC(uint32_t, >=); }) + +RVOP(lb, { + GEN(" addr = rv->X[%u] + %d;\n", ir->rs1, ir->imm); + GEN(" rv->X[%u] = sign_extend_b(*((const uint8_t *) (m->mem_base + " + "addr)));\n", + ir->rd); +}) + +RVOP(lh, { + GEN(" addr = rv->X[%u] + %d;\n", ir->rs1, ir->imm); + GEN(" rv->X[%u] = sign_extend_h(*((const uint16_t *) (m->mem_base + " + "addr)));\n", + ir->rd); +}) + +#define MEMORY_FUNC(type, IO) \ + GEN(" addr = rv->X[%u] + %d;\n", ir->rs1, ir->imm); \ + IIF(IO) \ + (GEN(" rv->X[%u] = *((const %s *) (m->mem_base + addr));\n", ir->rd, \ + #type), \ + GEN(" *((%s *) (m->mem_base + addr)) = (%s) rv->X[%u];\n", #type, #type, \ + ir->rs2)); + +RVOP(lw, {MEMORY_FUNC(uint32_t, 1)}) + +RVOP(lbu, {MEMORY_FUNC(uint8_t, 1)}) + +RVOP(lhu, {MEMORY_FUNC(uint16_t, 1)}) + +RVOP(sb, {MEMORY_FUNC(uint8_t, 0)}) + +RVOP(sh, {MEMORY_FUNC(uint16_t, 0)}) + +RVOP(sw, {MEMORY_FUNC(uint32_t, 0)}) + +FORCE_INLINE void gen_shift_func(const rv_insn_t *ir, char *gencode) +{ + switch (ir->opcode) { + case rv_insn_slli: + GEN(" rv->X[%u] = rv->X[%u] << %d;", ir->rd, ir->rs1, (ir->imm & 0x1f)); + break; + case rv_insn_srli: + GEN(" rv->X[%u] = rv->X[%u] >> %d;", ir->rd, ir->rs1, (ir->imm & 0x1f)); + break; + case rv_insn_srai: + GEN(" rv->X[%u] = ((int32_t) rv->X[%u]) >> %d;", ir->rd, ir->rs1, + (ir->imm & 0x1f)); + break; + default: + __UNREACHABLE; + break; + } +}; + +RVOP(slli, { gen_shift_func(ir, gencode); }) + +RVOP(srli, { gen_shift_func(ir, gencode); }) + +RVOP(srai, { gen_shift_func(ir, gencode); }) + +#if RV32_HAS(EXT_F) +RVOP(flw, { + GEN(" addr = rv->X[%u] + %d;\n", ir->rs1, ir->imm); + GEN(" rv->F_int[%u] = *((const uint32_t *) (m->mem_base + addr));\n", + ir->rd); +}) + +/* FSW */ +RVOP(fsw, { + GEN(" addr = rv->X[%u] + %d;\n", ir->rs1, ir->imm); + GEN(" *((uint32_t *) (m->mem_base + addr)) = rv->F_int[%u];\n", ir->rs2); +}) +#endif + +#if RV32_HAS(EXT_C) +RVOP(clw, { + GEN(" addr = rv->X[%u] + %d;\n", ir->rs1, ir->imm); + GEN(" rv->X[%u] = *((const uint32_t *) (m->mem_base + addr));\n", ir->rd); +}) + +RVOP(csw, { + GEN(" addr = rv->X[%u] + %d;\n", ir->rs1, ir->imm); + GEN(" *((uint32_t *) (m->mem_base + addr)) = rv->X[%u];\n", ir->rs2); +}) + +RVOP(cjal, { + GEN(" rv->X[1] = PC + 2;\n"); + UPDATE_PC(ir->imm); + if (ir->branch_taken) { + NEXT_INSN(ir->pc + ir->imm); + } else { + END_INSN; + } +}) + +RVOP(cj, { + UPDATE_PC(ir->imm); + if (ir->branch_taken) { + NEXT_INSN(ir->pc + ir->imm); + } else { + END_INSN; + } +}) + +RVOP(cjr, { + GEN(" rv->csr_cycle = cycle;\n"); + GEN(" rv->PC = rv->X[%d];\n", ir->rs1); + GEN(" return true;\n"); +}) + +RVOP(cjalr, { + GEN(" rv->X[rv_reg_ra] = PC + 2;"); + GEN(" rv->csr_cycle = cycle;\n"); + GEN(" rv->PC = rv->X[%u];\n", ir->rs1); + GEN(" return true;\n"); +}) + +RVOP(cbeqz, { + GEN(" if (!rv->X[%u]){\n", ir->rs1); + UPDATE_PC(ir->imm); + if (ir->branch_taken) { + NEXT_INSN(ir->pc + ir->imm); + } else { + END_INSN; + } + GEN(" }\n"); + UPDATE_PC(2); + if (ir->branch_untaken) { + NEXT_INSN(ir->pc + 2); + } else { + END_INSN; + } +}) + +RVOP(cbnez, { + GEN(" if (rv->X[%u]){\n", ir->rs1); + UPDATE_PC(ir->imm); + if (ir->branch_taken) { + NEXT_INSN(ir->pc + ir->imm); + } else { + END_INSN; + } + GEN(" }\n"); + UPDATE_PC(2); + if (ir->branch_untaken) { + NEXT_INSN(ir->pc + 2); + } else { + END_INSN; + } +}) + +RVOP(clwsp, { + GEN("addr = rv->X[rv_reg_sp] + %d;\n", ir->imm); + GEN(" rv->X[%u] = *((const uint32_t *) (m->mem_base + addr));\n", ir->rd); +}) + +RVOP(cswsp, { + GEN("addr = rv->X[rv_reg_sp] + %d;\n", ir->imm); + GEN(" *((uint32_t *) (m->mem_base + addr)) = rv->X[%u];\n", ir->rs2); +}) +#endif + + +static void gen_fuse1(riscv_t *rv UNUSED, rv_insn_t *ir, char *gencode) +{ + GEN("insn_%x:\n" + " csr_cycle += %d;\n", + (ir->pc), (ir->imm2)); + opcode_fuse_t *fuse = ir->fuse; + for (int i = 0; i < ir->imm2; i++) { + GEN(" rv->X[%u] = %d;\n", fuse[i].rd, fuse[i].imm); + } + UPDATE_PC(ir->imm2 * 4); + NEXT_INSN(ir->pc + ir->imm2 * 4); +} + +static void gen_fuse2(riscv_t *rv UNUSED, rv_insn_t *ir, char *gencode) +{ + GEN("insn_%x:\n" + " csr_cycle += 2;\n", + (ir->pc)); + GEN(" rv->X[%u] = %d;\n", ir->rd, ir->imm); + GEN(" rv->X[%u] = rv->X[%u] + rv->X[%u];\n", ir->rs2, ir->rd, ir->rs1); + UPDATE_PC(8); + NEXT_INSN(ir->pc + 8); +} + +static void gen_fuse3(riscv_t *rv UNUSED, rv_insn_t *ir, char *gencode) +{ + GEN("insn_%x:\n" + " csr_cycle += %u;\n", + (ir->pc), ir->imm2); + opcode_fuse_t *fuse = ir->fuse; + for (int i = 0; i < ir->imm2; i++) { + GEN(" addr = rv->X[%u] + %d;\n", fuse[i].rs1, fuse[i].imm); + GEN(" *((uint32_t *) (m->mem_base + addr)) = rv->X[%u];\n", + fuse[i].rs2) + } + UPDATE_PC(ir->imm2 * 4); + NEXT_INSN(ir->pc + ir->imm2 * 4); +} + +static void gen_fuse4(riscv_t *rv UNUSED, rv_insn_t *ir, char *gencode) +{ + GEN("insn_%x:\n" + " csr_cycle += %u;\n", + (ir->pc), ir->imm2); + opcode_fuse_t *fuse = ir->fuse; + for (int i = 0; i < ir->imm2; i++) { + GEN(" addr = rv->X[%u] + %d;\n", fuse[i].rs1, fuse[i].imm); + GEN(" rv->X[%u] = *((const uint32_t *) (m->mem_base + addr));\n", + fuse[i].rd); + } + UPDATE_PC(ir->imm2 * 4); + NEXT_INSN(ir->pc + ir->imm2 * 4); +} + +static void gen_fuse5(riscv_t *rv UNUSED, rv_insn_t *ir, char *gencode) +{ + GEN("insn_%x:\n" + " rv->csr_cycle += 2;\n", + (ir->pc)); + GEN(" memset((char *) m->mem_base + rv->X[rv_reg_a0], rv->X[rv_reg_a1], " + "rv->X[rv_reg_a2]); "); + END_INSN; +} + +static void gen_fuse6(riscv_t *rv UNUSED, rv_insn_t *ir, char *gencode) +{ + GEN("insn_%x:\n" + " rv->X[0] = 0;\n" + " rv->csr_cycle += 2;\n", + (ir->pc)); + GEN(" memcpy((char *) m->mem_base + rv->X[rv_reg_a0], (char *) " + "m->mem_base + rv->X[rv_reg_a1], rv->X[rv_reg_a2]);"); + END_INSN; +} + +static void gen_fuse7(riscv_t *rv UNUSED, rv_insn_t *ir, char *gencode) +{ + GEN("insn_%x:\n" + " csr_cycle += %u;\n", + (ir->pc), ir->imm2); + opcode_fuse_t *fuse = ir->fuse; + for (int i = 0; i < ir->imm2; i++) + gen_shift_func((const rv_insn_t *) (&fuse[i]), gencode); + UPDATE_PC(ir->imm2 * 4); + NEXT_INSN(ir->pc + ir->imm2 * 4); +} +#undef RVOP + +static void trace_ebb(riscv_t *rv, char *gencode, rv_insn_t *ir, set_t *set) +{ + while (1) { + if (set_add(set, ir->pc)) + dispatch_table[ir->opcode](rv, ir, gencode); + + if (!ir->next) + break; + ir = ir->next; + } + if (ir->branch_untaken && + !set_has(set, ir->pc + insn_len_table[ir->opcode])) { + block_t *block = + cache_get(rv->block_cache, ir->pc + insn_len_table[ir->opcode]); + if (block) { + if (ir->branch_untaken->pc != ir->pc + insn_len_table[ir->opcode]) + ir->branch_untaken = block->ir_head; + trace_ebb(rv, gencode, ir->branch_untaken, set); + } + } + if (ir->branch_taken && !set_has(set, ir->pc + ir->imm)) { + block_t *block = cache_get(rv->block_cache, ir->pc + ir->imm); + if (block) { + if (ir->branch_taken->pc != ir->pc + ir->imm) + ir->branch_taken = block->ir_head; + trace_ebb(rv, gencode, ir->branch_taken, set); + } + } +} + +#define EPILOGUE "}" + +static void trace_and_gencode(riscv_t *rv, char *gencode) +{ +#define _(inst, can_branch, insn_len, reg_mask) \ + dispatch_table[rv_insn_##inst] = &gen_##inst; + RV_INSN_LIST +#undef _ +#define _(inst) dispatch_table[rv_insn_##inst] = &gen_##inst; + FUSE_INSN_LIST +#undef _ + set_t set; + strcat(gencode, PROLOGUE); + set_reset(&set); + block_t *block = cache_get(rv->block_cache, rv->PC); + trace_ebb(rv, gencode, block->ir_head, &set); + strcat(gencode, EPILOGUE); +} + +static int get_string_func(void *data) +{ + code_string_t *codestr = data; + return codestr->curr >= codestr->code_size ? EOF + : codestr->code[codestr->curr++]; +} + +#define DLIST_ITEM_FOREACH(modules, item) \ + for (item = DLIST_HEAD(MIR_item_t, modules->items); item; \ + item = DLIST_NEXT(MIR_item_t, item)) + +/* parse the funcitons are not defined in mir */ +static void *import_resolver(const char *name) +{ + func_obj_t func_list[] = { + {"sign_extend_b", sign_extend_b}, + {"sign_extend_h", sign_extend_h}, +#if RV32_HAS(Zicsr) + {"csr_csrrw", csr_csrrw}, + {"csr_csrrs", csr_csrrs}, + {"csr_csrrc", csr_csrrc}, +#endif +#if RV32_HAS(EXT_F) + {"isnanf", isnanf}, + {"isinff", isinff}, + {"sqrtf", sqrtf}, + {"calc_fclass", calc_fclass}, + {"is_nan", is_nan}, + {"is_snan", is_snan}, +#endif + {NULL, NULL}, + }; + for (int i = 0; func_list[i].name; i++) { + if (!strcmp(name, func_list[i].name)) + return func_list[i].func; + } + return NULL; +} + +static riscv_jit_t *jit = NULL; +static code_string_t *jit_code_string = NULL; + +/* TODO: fix the segmentation fault error that occurs when invoking a function + * compiled by mir while running on Apple M1 MacOS. + */ +static uint8_t *compile(riscv_t *rv) +{ + char func_name[25]; + snprintf(func_name, 25, "jit_func_%d", rv->PC); + c2mir_init(jit->ctx); + size_t gen_num = 0; + MIR_gen_init(jit->ctx, gen_num); + MIR_gen_set_optimize_level(jit->ctx, gen_num, jit->optimize_level); + if (!c2mir_compile(jit->ctx, jit->options, get_string_func, jit_code_string, + func_name, NULL)) { + perror("Compile failure"); + exit(EXIT_FAILURE); + } + MIR_module_t module = + DLIST_TAIL(MIR_module_t, *MIR_get_module_list(jit->ctx)); + MIR_load_module(jit->ctx, module); + MIR_link(jit->ctx, MIR_set_gen_interface, import_resolver); + MIR_item_t func = DLIST_HEAD(MIR_item_t, module->items); + uint8_t *code = NULL; + size_t func_len = DLIST_LENGTH(MIR_item_t, module->items); + for (size_t i = 0; i < func_len; i++, func = DLIST_NEXT(MIR_item_t, func)) { + if (func->item_type == MIR_func_item) + code = func->addr; + } + + MIR_gen_finish(jit->ctx); + c2mir_finish(jit->ctx); + return code; +} + +uint8_t *block_compile(riscv_t *rv) +{ + if (!jit) { + jit = calloc(1, sizeof(riscv_jit_t)); + jit->options = calloc(1, sizeof(struct c2mir_options)); + jit->ctx = MIR_init(); + jit->optimize_level = 1; + } + + if (!jit_code_string) { + jit_code_string = malloc(sizeof(code_string_t)); + jit_code_string->code = calloc(1, 1024 * 1024); + } else { + memset(jit_code_string->code, 0, 1024 * 1024); + } + jit_code_string->curr = 0; + trace_and_gencode(rv, jit_code_string->code); + jit_code_string->code_size = strlen(jit_code_string->code); + FILE *ff = fopen("a.c", "w"); + fwrite(jit_code_string->code, 1, jit_code_string->code_size, ff); + fclose(ff); + return compile(rv); +} diff --git a/src/compile.h b/src/compile.h new file mode 100644 index 000000000..5a5a5ee2a --- /dev/null +++ b/src/compile.h @@ -0,0 +1,11 @@ +/* + * rv32emu is freely redistributable under the MIT License. See the file + * "LICENSE" for information on usage and redistribution of this file. + */ + +#pragma once +#include + +#include "riscv.h" + +uint8_t *block_compile(riscv_t *rv); diff --git a/src/decode.h b/src/decode.h index 874d8fe47..7bb4e698c 100644 --- a/src/decode.h +++ b/src/decode.h @@ -179,13 +179,31 @@ enum op_field { ) /* clang-format on */ +/* Macro operation fusion */ + +/* macro operation fusion: convert specific RISC-V instruction patterns + * into faster and equivalent code + */ +#define FUSE_INSN_LIST \ + _(fuse1) \ + _(fuse2) \ + _(fuse3) \ + _(fuse4) \ + _(fuse5) \ + _(fuse6) \ + _(fuse7) + /* clang-format off */ /* IR list */ enum { #define _(inst, can_branch, insn_len, reg_mask) rv_insn_##inst, RV_INSN_LIST #undef _ - N_RV_INSNS + N_RV_INSNS, +#define _(inst) rv_insn_##inst, + FUSE_INSN_LIST +#undef _ + N_TOTAL_INSNS, }; /* clang-format on */ diff --git a/src/emulate.c b/src/emulate.c index 6820d0550..a3d9d648e 100644 --- a/src/emulate.c +++ b/src/emulate.c @@ -13,17 +13,6 @@ #if RV32_HAS(EXT_F) #include #include "softfloat.h" - -#if defined(__APPLE__) -static inline int isinff(float x) -{ - return __builtin_fabsf(x) == __builtin_inff(); -} -static inline int isnanf(float x) -{ - return x != x; -} -#endif #endif /* RV32_HAS(EXT_F) */ #if RV32_HAS(GDBSTUB) @@ -36,6 +25,10 @@ extern struct target_ops gdbstub_ops; #include "riscv_private.h" #include "state.h" #include "utils.h" +#if RV32_HAS(JIT) +#include "cache.h" +#include "compile.h" +#endif /* Shortcuts for comparing each field of specified RISC-V instruction */ #define IF_insn(i, o) (i->opcode == rv_insn_##o) @@ -122,136 +115,6 @@ RV_EXCEPTION_LIST return false; \ } -/* get current time in microsecnds and update csr_time register */ -static inline void update_time(riscv_t *rv) -{ - struct timeval tv; - rv_gettimeofday(&tv); - - uint64_t t = (uint64_t) tv.tv_sec * 1e6 + (uint32_t) tv.tv_usec; - rv->csr_time[0] = t & 0xFFFFFFFF; - rv->csr_time[1] = t >> 32; -} - -#if RV32_HAS(Zicsr) -/* get a pointer to a CSR */ -static uint32_t *csr_get_ptr(riscv_t *rv, uint32_t csr) -{ - /* csr & 0xFFF prevent sign-extension in decode stage */ - switch (csr & 0xFFF) { - case CSR_MSTATUS: /* Machine Status */ - return (uint32_t *) (&rv->csr_mstatus); - case CSR_MTVEC: /* Machine Trap Handler */ - return (uint32_t *) (&rv->csr_mtvec); - case CSR_MISA: /* Machine ISA and Extensions */ - return (uint32_t *) (&rv->csr_misa); - - /* Machine Trap Handling */ - case CSR_MSCRATCH: /* Machine Scratch Register */ - return (uint32_t *) (&rv->csr_mscratch); - case CSR_MEPC: /* Machine Exception Program Counter */ - return (uint32_t *) (&rv->csr_mepc); - case CSR_MCAUSE: /* Machine Exception Cause */ - return (uint32_t *) (&rv->csr_mcause); - case CSR_MTVAL: /* Machine Trap Value */ - return (uint32_t *) (&rv->csr_mtval); - case CSR_MIP: /* Machine Interrupt Pending */ - return (uint32_t *) (&rv->csr_mip); - - /* Machine Counter/Timers */ - case CSR_CYCLE: /* Cycle counter for RDCYCLE instruction */ - return (uint32_t *) &rv->csr_cycle; - case CSR_CYCLEH: /* Upper 32 bits of cycle */ - return &((uint32_t *) &rv->csr_cycle)[1]; - - /* TIME/TIMEH - very roughly about 1 ms per tick */ - case CSR_TIME: /* Timer for RDTIME instruction */ - update_time(rv); - return &rv->csr_time[0]; - case CSR_TIMEH: /* Upper 32 bits of time */ - update_time(rv); - return &rv->csr_time[1]; - case CSR_INSTRET: /* Number of Instructions Retired Counter */ - return (uint32_t *) (&rv->csr_cycle); -#if RV32_HAS(EXT_F) - case CSR_FFLAGS: - return (uint32_t *) (&rv->csr_fcsr); - case CSR_FCSR: - return (uint32_t *) (&rv->csr_fcsr); -#endif - default: - return NULL; - } -} - -FORCE_INLINE bool csr_is_writable(uint32_t csr) -{ - return csr < 0xc00; -} - -/* CSRRW (Atomic Read/Write CSR) instruction atomically swaps values in the - * CSRs and integer registers. CSRRW reads the old value of the CSR, - * zero-extends the value to XLEN bits, and then writes it to register rd. - * The initial value in rs1 is written to the CSR. - * If rd == x0, then the instruction shall not read the CSR and shall not cause - * any of the side effects that might occur on a CSR read. - */ -static uint32_t csr_csrrw(riscv_t *rv, uint32_t csr, uint32_t val) -{ - uint32_t *c = csr_get_ptr(rv, csr); - if (!c) - return 0; - - uint32_t out = *c; -#if RV32_HAS(EXT_F) - if (csr == CSR_FFLAGS) - out &= FFLAG_MASK; -#endif - if (csr_is_writable(csr)) - *c = val; - - return out; -} - -/* perform csrrs (atomic read and set) */ -static uint32_t csr_csrrs(riscv_t *rv, uint32_t csr, uint32_t val) -{ - uint32_t *c = csr_get_ptr(rv, csr); - if (!c) - return 0; - - uint32_t out = *c; -#if RV32_HAS(EXT_F) - if (csr == CSR_FFLAGS) - out &= FFLAG_MASK; -#endif - if (csr_is_writable(csr)) - *c |= val; - - return out; -} - -/* perform csrrc (atomic read and clear) - * Read old value of CSR, zero-extend to XLEN bits, write to rd. - * Read value from rs1, use as bit mask to clear bits in CSR. - */ -static uint32_t csr_csrrc(riscv_t *rv, uint32_t csr, uint32_t val) -{ - uint32_t *c = csr_get_ptr(rv, csr); - if (!c) - return 0; - - uint32_t out = *c; -#if RV32_HAS(EXT_F) - if (csr == CSR_FFLAGS) - out &= FFLAG_MASK; -#endif - if (csr_is_writable(csr)) - *c &= ~val; - return out; -} -#endif - #if RV32_HAS(GDBSTUB) void rv_debug(riscv_t *rv) { @@ -277,8 +140,9 @@ void rv_debug(riscv_t *rv) } #endif /* RV32_HAS(GDBSTUB) */ +#if !RV32_HAS(JIT) /* hash function is used when mapping address into the block map */ -static inline uint32_t hash(size_t k) +FORCE_INLINE uint32_t hash(size_t k) { k ^= k << 21; k ^= k >> 17; @@ -288,6 +152,7 @@ static inline uint32_t hash(size_t k) #endif return k; } +#endif /* allocate a basic block */ static block_t *block_alloc(riscv_t *rv) @@ -296,9 +161,13 @@ static block_t *block_alloc(riscv_t *rv) assert(block); block->n_insn = 0; block->predict = NULL; +#if RV32_HAS(JIT) + block->hot = false; +#endif return block; } +#if !RV32_HAS(JIT) /* insert a block into block map */ static void block_insert(block_map_t *map, const block_t *block) { @@ -334,6 +203,7 @@ static block_t *block_find(const block_map_t *map, const uint32_t addr) } return NULL; } +#endif FORCE_INLINE bool insn_is_misaligned(uint32_t pc) { @@ -374,6 +244,12 @@ static bool branch_taken = false; /* record the program counter of the previous block */ static uint32_t last_pc = 0; +#if RV32_HAS(JIT) +/* record whether the block is replaced by cache. If so, clear the EBB + * information */ +static bool clear_flag = false; +#endif + /* Interpreter-based execution path */ #define RVOP(inst, code) \ static bool do_##inst(riscv_t *rv, const rv_insn_t *ir, uint64_t cycle, \ @@ -397,27 +273,6 @@ static uint32_t last_pc = 0; /* FIXME: Add JIT-based execution path */ -/* Macro operation fusion */ - -/* macro operation fusion: convert specific RISC-V instruction patterns - * into faster and equivalent code - */ -#define FUSE_INSN_LIST \ - _(fuse1) \ - _(fuse2) \ - _(fuse3) \ - _(fuse4) \ - _(fuse5) \ - _(fuse6) \ - _(fuse7) - -enum { - rv_insn_fuse0 = N_RV_INSNS, -#define _(inst) rv_insn_##inst, - FUSE_INSN_LIST -#undef _ -}; - /* multiple lui */ static bool do_fuse1(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, uint32_t PC) { @@ -939,31 +794,52 @@ static void optimize_constant(riscv_t *rv UNUSED, block_t *block) static block_t *prev = NULL; static block_t *block_find_or_translate(riscv_t *rv) { +#if !RV32_HAS(JIT) block_map_t *map = &rv->block_map; /* lookup the next block in the block map */ block_t *next = block_find(map, rv->PC); +#else + /* lookup the next block in the block cache */ + block_t *next = (block_t *) cache_get(rv->block_cache, rv->PC); +#endif if (!next) { +#if !RV32_HAS(JIT) if (map->size * 1.25 > map->block_capacity) { block_map_clear(rv); prev = NULL; } - +#endif /* allocate a new block */ next = block_alloc(rv); block_translate(rv, next); - if (!libc_substitute(rv, next)) { - optimize_constant(rv, next); -#if RV32_HAS(GDBSTUB) - if (likely(!rv->debug_mode)) -#endif - /* macro operation fusion */ - match_pattern(rv, next); - } + // if (!libc_substitute(rv, next)) { + // optimize_constant(rv, next); +// #if RV32_HAS(GDBSTUB) +// if (likely(!rv->debug_mode)) +// #endif +// /* macro operation fusion */ +// match_pattern(rv, next); +// } +#if !RV32_HAS(JIT) /* insert the block into block map */ block_insert(&rv->block_map, next); - +#else + /* insert the block into block cache */ + block_t *delete_target = cache_put(rv->block_cache, rv->PC, &(*next)); + if (delete_target) { + uint32_t idx; + rv_insn_t *ir, *next; + for (idx = 0, ir = delete_target->ir_head; + idx < delete_target->n_insn; idx++, ir = next) { + free(ir->fuse); + next = ir->next; + mpool_free(rv->block_ir_mp, ir); + } + mpool_free(rv->block_mp, delete_target); + } +#endif /* update the block prediction. * When translating a new block, the block predictor may benefit, * but updating it after finding a particular block may penalize @@ -976,6 +852,10 @@ static block_t *block_find_or_translate(riscv_t *rv) return next; } +#if RV32_HAS(JIT) +typedef bool (*exec_block_func_t)(riscv_t *rv, uint64_t, uint32_t); +#endif + void rv_step(riscv_t *rv, int32_t cycles) { assert(rv); @@ -1007,30 +887,72 @@ void rv_step(riscv_t *rv, int32_t cycles) if (prev) { /* update previous block */ if (prev->pc_start != last_pc) +#if !RV32_HAS(JIT) prev = block_find(&rv->block_map, last_pc); +#else + prev = cache_get(rv->block_cache, last_pc); +#endif + if (prev) { + rv_insn_t *last_ir = prev->ir_tail; +#if RV32_HAS(JIT) + if (clear_flag) { + if (branch_taken) + last_ir->branch_taken = NULL; + else + last_ir->branch_untaken = NULL; - rv_insn_t *last_ir = prev->ir_tail; - /* chain block */ - if (!insn_is_unconditional_branch(last_ir->opcode)) { - if (branch_taken && !last_ir->branch_taken) - last_ir->branch_taken = block->ir_head; - else if (!last_ir->branch_untaken) - last_ir->branch_untaken = block->ir_head; - } else if (IF_insn(last_ir, jal) + clear_flag = false; + } +#endif + /* chain block */ + if (!insn_is_unconditional_branch(last_ir->opcode)) { + if (branch_taken && !last_ir->branch_taken) + last_ir->branch_taken = block->ir_head; + else if (!branch_taken && !last_ir->branch_untaken) + last_ir->branch_untaken = block->ir_head; + } else if (IF_insn(last_ir, jal) #if RV32_HAS(EXT_C) - || IF_insn(last_ir, cj) || IF_insn(last_ir, cjal) + || IF_insn(last_ir, cj) || IF_insn(last_ir, cjal) #endif - ) { - if (!last_ir->branch_taken) - last_ir->branch_taken = block->ir_head; + ) { + if (!last_ir->branch_taken) + last_ir->branch_taken = block->ir_head; + } } } last_pc = rv->PC; - - /* execute the block */ +#if RV32_HAS(JIT) + /* execute the block by JIT compiler */ + exec_block_func_t code = NULL; + if (block->hot) + code = (exec_block_func_t) cache_get(rv->code_cache, rv->PC); + if (!code) { + /* check if using frequency of block exceed threshold */ + if ((block->hot = cache_hot(rv->block_cache, block->pc_start))) { + // printf("rv->PC1 = %#x\n", rv->PC); + code = (exec_block_func_t) block_compile(rv); + assert(code); + cache_put(rv->code_cache, rv->PC, code); + assert(cache_get(rv->code_cache, rv->PC)); + } + } + if (code) { + /* execute machine code */ + // printf("rv->PC1 = %#x\n", rv->PC); + code(rv, rv->csr_cycle, rv->PC); + // printf("rv->PC2 = %#x\n", rv->PC); + /* block should not be extended if execution mode is jit */ + prev = NULL; + continue; + } +#endif + /* execute the block by interpreter */ const rv_insn_t *ir = block->ir_head; - if (unlikely(!ir->impl(rv, ir, rv->csr_cycle, rv->PC))) + if (unlikely(!ir->impl(rv, ir, rv->csr_cycle, rv->PC))) { + /* block should not be extended if execption handler invoked */ + prev = NULL; break; + } prev = block; } } diff --git a/src/feature.h b/src/feature.h index 36fb4ac4b..3a74c0a7b 100644 --- a/src/feature.h +++ b/src/feature.h @@ -52,5 +52,10 @@ #define RV32_FEATURE_ARC 0 #endif +/* Just-in-time compiler */ +#ifndef RV32_FEATURE_JIT +#define RV32_FEATURE_JIT 0 +#endif + /* Feature test macro */ #define RV32_HAS(x) RV32_FEATURE_##x diff --git a/src/jit_template.c b/src/jit_template.c new file mode 100644 index 000000000..9217a487b --- /dev/null +++ b/src/jit_template.c @@ -0,0 +1,515 @@ +#define PROLOGUE \ +"#include \n"\ +"#include \n"\ +"enum {rv_reg_zero,rv_reg_ra,rv_reg_sp,rv_reg_gp,rv_reg_tp,rv_reg_t0,rv_reg_t1,rv_reg_t2,rv_reg_s0,rv_reg_s1,rv_reg_a0,rv_reg_a1,rv_reg_a2,rv_reg_a3,rv_reg_a4,rv_reg_a5,rv_reg_a6,rv_reg_a7,rv_reg_s2,rv_reg_s3,rv_reg_s4,rv_reg_s5,rv_reg_s6,rv_reg_s7,rv_reg_s8,rv_reg_s9,rv_reg_s10,rv_reg_s11,rv_reg_t3,rv_reg_t4,rv_reg_t5,rv_reg_t6,N_RV_REGS };"\ +"typedef struct riscv_internal riscv_t;"\ +"typedef void *riscv_user_t;"\ +""\ +"typedef uint32_t riscv_word_t;"\ +"typedef uint16_t riscv_half_t;"\ +"typedef uint8_t riscv_byte_t;"\ +"typedef uint32_t riscv_exception_t;"\ +"typedef float riscv_float_t;"\ +""\ +"/* memory read handlers */"\ +"typedef riscv_word_t (*riscv_mem_ifetch)(riscv_word_t addr);"\ +"typedef riscv_word_t (*riscv_mem_read_w)(riscv_word_t addr);"\ +"typedef riscv_half_t (*riscv_mem_read_s)(riscv_word_t addr);"\ +"typedef riscv_byte_t (*riscv_mem_read_b)(riscv_word_t addr);"\ +""\ +"/* memory write handlers */"\ +"typedef void (*riscv_mem_write_w)(riscv_word_t addr, riscv_word_t data);"\ +"typedef void (*riscv_mem_write_s)(riscv_word_t addr, riscv_half_t data);"\ +"typedef void (*riscv_mem_write_b)(riscv_word_t addr, riscv_byte_t data);"\ +""\ +"/* system instruction handlers */"\ +"typedef void (*riscv_on_ecall)(riscv_t *rv);"\ +"typedef void (*riscv_on_ebreak)(riscv_t *rv);"\ +""\ +"/* RISC-V emulator I/O interface */"\ +"typedef struct {"\ +" /* memory read interface */"\ +" riscv_mem_ifetch mem_ifetch;"\ +" riscv_mem_read_w mem_read_w;"\ +" riscv_mem_read_s mem_read_s;"\ +" riscv_mem_read_b mem_read_b;"\ +""\ +" /* memory write interface */"\ +" riscv_mem_write_w mem_write_w;"\ +" riscv_mem_write_s mem_write_s;"\ +" riscv_mem_write_b mem_write_b;"\ +""\ +" /* system */"\ +" riscv_on_ecall on_ecall;"\ +" riscv_on_ebreak on_ebreak;"\ +""\ +" /* enable misaligned memory access */"\ +" bool allow_misalign;"\ +"} riscv_io_t;"\ +"enum {"\ +" FMASK_SIGN = 0b10000000000000000000000000000000,"\ +" FMASK_EXPN = 0b01111111100000000000000000000000,"\ +" FMASK_FRAC = 0b00000000011111111111111111111111,"\ +" FMASK_QNAN = 0b00000000010000000000000000000000,"\ +" FFLAG_MASK = 0b00000000000000000000000000011111,"\ +" FFLAG_INVALID_OP = 0b00000000000000000000000000010000,"\ +" FFLAG_DIV_BY_ZERO = 0b00000000000000000000000000001000,"\ +" FFLAG_OVERFLOW = 0b00000000000000000000000000000100,"\ +" FFLAG_UNDERFLOW = 0b00000000000000000000000000000010,"\ +" FFLAG_INEXACT = 0b00000000000000000000000000000001,"\ +" RV_NAN = 0b01111111110000000000000000000000"\ +"};"\ +"struct riscv_internal {"\ +" bool halt; "\ +" riscv_io_t io;"\ +""\ +" riscv_word_t X[N_RV_REGS];"\ +" riscv_word_t PC;"\ +""\ +" riscv_user_t userdata;"\ +""\ +""\ +" union {"\ +" riscv_float_t F[N_RV_REGS];"\ +" uint32_t F_int[N_RV_REGS]; };"\ +" uint32_t csr_fcsr;"\ +""\ +" uint64_t csr_cycle; uint32_t csr_time[2]; uint32_t csr_mstatus; uint32_t csr_mtvec; uint32_t csr_misa; uint32_t csr_mtval; uint32_t csr_mcause; uint32_t csr_mscratch; uint32_t csr_mepc; uint32_t csr_mip; uint32_t csr_mbadaddr;"\ +""\ +" bool compressed;};"\ +"typedef struct {"\ +" uint8_t *mem_base;"\ +" uint64_t mem_size;"\ +"} memory_t;"\ +"typedef struct {"\ +" memory_t *mem;"\ +""\ +" riscv_word_t break_addr;"\ +""\ +" "\ +"} state_t;"\ +"bool start(riscv_t *rv, uint64_t cycle, uint32_t PC) {"\ +" uint32_t pc, addr, udividend, udivisor, tmp, data, mask, ures, "\ +"a, b, jump_to;"\ +" int32_t dividend, divisor, res;"\ +" int64_t multiplicand, multiplier;"\ +" uint64_t umultiplier;"\ +" memory_t *m = ((state_t *)rv->userdata)->mem;" +RVOP(nop, { +strcat(gencode, " rv->X[rv_reg_zero] = 0; \n"); +}) +RVOP(lui, { +GEN(" rv->X[%u] = %u; \n", ir->rd, ir->imm); +}) +RVOP(auipc, { +GEN(" rv->X[%u] = %u + PC; \n", ir->rd, ir->imm); +}) +RVOP(addi, { +GEN(" rv->X[%u] = (int32_t) (rv->X[%u]) + %u; \n", ir->rd, ir->rs1, ir->imm); +}) +RVOP(slti, { +GEN(" rv->X[%u] = ((int32_t) (rv->X[%u]) < %u) ? 1 : 0; \n", ir->rd, ir->rs1, ir->imm); +}) +RVOP(sltiu, { +GEN(" rv->X[%u] = (rv->X[%u] < (uint32_t) %u) ? 1 : 0; \n", ir->rd, ir->rs1, ir->imm); +}) +RVOP(xori, { +GEN(" rv->X[%u] = rv->X[%u] ^ %u; \n", ir->rd, ir->rs1, ir->imm); +}) +RVOP(ori, { +GEN(" rv->X[%u] = rv->X[%u] | %u; \n", ir->rd, ir->rs1, ir->imm); +}) +RVOP(andi, { +GEN(" rv->X[%u] = rv->X[%u] & %u; \n", ir->rd, ir->rs1, ir->imm); +}) +RVOP(add, { +GEN(" rv->X[%u] = (int32_t) (rv->X[%u]) + (int32_t) (rv->X[%u]);\n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(sub, { +GEN(" rv->X[%u] = (int32_t) (rv->X[%u]) - (int32_t) (rv->X[%u]);\n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(sll, { +GEN(" rv->X[%u] = rv->X[%u] << (rv->X[%u] & 0x1f); \n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(slt, { +GEN(" rv->X[%u] =\n", ir->rd); +GEN(" ((int32_t) (rv->X[%u]) < (int32_t) (rv->X[%u])) ? 1 : 0;\n", ir->rs1, ir->rs2); +}) +RVOP(sltu, { +GEN(" rv->X[%u] = (rv->X[%u] < rv->X[%u]) ? 1 : 0; \n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(xor, { +GEN(" rv->X[%u] = rv->X[%u] ^ rv->X[%u];\n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(srl, { +GEN(" rv->X[%u] = rv->X[%u] >> (rv->X[%u] & 0x1f); \n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(sra, { +GEN(" rv->X[%u] = ((int32_t) rv->X[%u]) >> (rv->X[%u] & 0x1f); \n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(or, { +GEN(" rv->X[%u] = rv->X[%u] | rv->X[%u]; \n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(and, { +GEN(" rv->X[%u] = rv->X[%u] & rv->X[%u]; \n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(ecall, { +strcat(gencode, " rv->compressed = false;\n"); +strcat(gencode, " rv->csr_cycle = cycle;\n"); +strcat(gencode, " rv->PC = PC;\n"); +strcat(gencode, " rv->io.on_ecall(rv);\n"); +strcat(gencode, " return true;\n"); +}) +RVOP(ebreak, { +strcat(gencode, " rv->compressed = false;\n"); +strcat(gencode, " rv->csr_cycle = cycle;\n"); +strcat(gencode, " rv->PC = PC;\n"); +strcat(gencode, " rv->io.on_ebreak(rv);\n"); +strcat(gencode, " return true;\n"); +}) +RVOP(wfi, { +strcat(gencode, " return false;\n"); +}) +RVOP(uret, { +strcat(gencode, " return false;\n"); +}) +RVOP(sret, { +strcat(gencode, " return false;\n"); +}) +RVOP(hret, { +strcat(gencode, " return false;\n"); +}) +RVOP(mret, { +strcat(gencode, " rv->csr_mstatus = MSTATUS_MPIE;\n"); +strcat(gencode, " rv->PC = rv->csr_mepc;\n"); +strcat(gencode, " return true;\n"); +}) +RVOP(fencei, { +strcat(gencode, " PC += 4;\n"); +strcat(gencode, " rv->csr_cycle = cycle;\n"); +strcat(gencode, " rv->PC = PC;\n"); +strcat(gencode, " return true;\n"); +}) +RVOP(csrrw, { +GEN(" tmp = csr_csrrw(rv, %u, rv->X[%u]);\n", ir->imm, ir->rs1); +GEN(" rv->X[%u] = %u ? tmp : rv->X[%u];\n", ir->rd, ir->rd, ir->rd); +}) +RVOP(csrrs, { +strcat(gencode, " tmp =\n"); +GEN(" csr_csrrs(rv, %u, (%u == rv_reg_zero) ? 0U : rv->X[%u]);\n", ir->imm, ir->rs1, ir->rs1); +GEN(" rv->X[%u] = %u ? tmp : rv->X[%u];\n", ir->rd, ir->rd, ir->rd); +}) +RVOP(csrrc, { +strcat(gencode, " tmp =\n"); +GEN(" csr_csrrc(rv, %u, (%u == rv_reg_zero) ? ~0U : rv->X[%u]);\n", ir->imm, ir->rs1, ir->rs1); +GEN(" rv->X[%u] = %u ? tmp : rv->X[%u];\n", ir->rd, ir->rd, ir->rd); +}) +RVOP(csrrwi, { +GEN(" tmp = csr_csrrw(rv, %u, %u);\n", ir->imm, ir->rs1); +GEN(" rv->X[%u] = %u ? tmp : rv->X[%u];\n", ir->rd, ir->rd, ir->rd); +}) +RVOP(csrrsi, { +GEN(" tmp = csr_csrrs(rv, %u, %u);\n", ir->imm, ir->rs1); +GEN(" rv->X[%u] = %u ? tmp : rv->X[%u];\n", ir->rd, ir->rd, ir->rd); +}) +RVOP(csrrci, { +GEN(" tmp = csr_csrrc(rv, %u, %u);\n", ir->imm, ir->rs1); +GEN(" rv->X[%u] = %u ? tmp : rv->X[%u];\n", ir->rd, ir->rd, ir->rd); +}) +RVOP(mul, { +GEN(" rv->X[%u] = (int32_t) rv->X[%u] * (int32_t) rv->X[%u]; \n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(mulh, { +GEN(" multiplicand = (int32_t) rv->X[%u];\n", ir->rs1); +GEN(" multiplier = (int32_t) rv->X[%u];\n", ir->rs2); +GEN(" rv->X[%u] = ((uint64_t) (multiplicand * multiplier)) >> 32;\n", ir->rd); +}) +RVOP(mulhsu, { +GEN(" multiplicand = (int32_t) rv->X[%u];\n", ir->rs1); +GEN(" umultiplier = rv->X[%u];\n", ir->rs2); +GEN(" rv->X[%u] = ((uint64_t) (multiplicand * umultiplier)) >> 32;\n", ir->rd); +}) +RVOP(mulhu, { +GEN(" rv->X[%u] =\n", ir->rd); +GEN(" ((uint64_t) rv->X[%u] * (uint64_t) rv->X[%u]) >> 32;\n", ir->rs1, ir->rs2); +}) +RVOP(div, { +GEN(" dividend = (int32_t) rv->X[%u];\n", ir->rs1); +GEN(" divisor = (int32_t) rv->X[%u];\n", ir->rs2); +GEN(" rv->X[%u] = !divisor ? ~0U\n", ir->rd); +GEN(" : (divisor == -1 && rv->X[%u] == 0x80000000U)\n", ir->rs1); +GEN(" ? rv->X[%u] : (unsigned int) (dividend / divisor);\n", ir->rs1); +}) +RVOP(divu, { +GEN(" udividend = rv->X[%u];\n", ir->rs1); +GEN(" udivisor = rv->X[%u];\n", ir->rs2); +GEN(" rv->X[%u] = !udivisor ? ~0U : udividend / udivisor;\n", ir->rd); +}) +RVOP(rem, { +GEN(" dividend = rv->X[%u];\n", ir->rs1); +GEN(" divisor = rv->X[%u];\n", ir->rs2); +GEN(" rv->X[%u] = !divisor ? dividend\n", ir->rd); +GEN(" : (divisor == -1 && rv->X[%u] == 0x80000000U)\n", ir->rs1); +strcat(gencode, " ? 0 : (dividend \n"); +strcat(gencode, " % divisor);\n"); +}) +RVOP(remu, { +GEN(" udividend = rv->X[%u];\n", ir->rs1); +GEN(" udivisor = rv->X[%u];\n", ir->rs2); +GEN(" rv->X[%u] = !udivisor ? udividend : udividend \n", ir->rd); +strcat(gencode, " % udivisor;\n"); +}) +RVOP(lrw, { +GEN(" rv->X[%u] = rv->io.mem_read_w(rv->X[%u]);\n", ir->rd, ir->rs1); +strcat(gencode, " \n"); +}) +RVOP(scw, { +GEN(" rv->io.mem_write_w(rv->X[%u], rv->X[%u]);\n", ir->rs1, ir->rs2); +GEN(" rv->X[%u] = 0;\n", ir->rd); +}) +RVOP(amoswapw, { +GEN(" rv->X[%u] = rv->io.mem_read_w(%u);\n", ir->rd, ir->rs1); +GEN(" rv->io.mem_write_s(%u, rv->X[%u]);\n", ir->rs1, ir->rs2); +}) +RVOP(amoaddw, { +GEN(" rv->X[%u] = rv->io.mem_read_w(%u);\n", ir->rd, ir->rs1); +GEN(" res = (int32_t) rv->X[%u] + (int32_t) rv->X[%u];\n", ir->rd, ir->rs2); +GEN(" rv->io.mem_write_s(%u, res);\n", ir->rs1); +}) +RVOP(amoxorw, { +GEN(" rv->X[%u] = rv->io.mem_read_w(%u);\n", ir->rd, ir->rs1); +GEN(" res = rv->X[%u] ^ rv->X[%u];\n", ir->rd, ir->rs2); +GEN(" rv->io.mem_write_s(%u, res);\n", ir->rs1); +}) +RVOP(amoandw, { +GEN(" rv->X[%u] = rv->io.mem_read_w(%u);\n", ir->rd, ir->rs1); +GEN(" res = rv->X[%u] & rv->X[%u];\n", ir->rd, ir->rs2); +GEN(" rv->io.mem_write_s(%u, res);\n", ir->rs1); +}) +RVOP(amoorw, { +GEN(" rv->X[%u] = rv->io.mem_read_w(%u);\n", ir->rd, ir->rs1); +GEN(" res = rv->X[%u] | rv->X[%u];\n", ir->rd, ir->rs2); +GEN(" rv->io.mem_write_s(%u, res);\n", ir->rs1); +}) +RVOP(amominw, { +GEN(" rv->X[%u] = rv->io.mem_read_w(%u);\n", ir->rd, ir->rs1); +strcat(gencode, " res =\n"); +GEN(" rv->X[%u] < rv->X[%u] ? rv->X[%u] : rv->X[%u];\n", ir->rd, ir->rs2, ir->rd, ir->rs2); +GEN(" rv->io.mem_write_s(%u, res);\n", ir->rs1); +}) +RVOP(amomaxw, { +GEN(" rv->X[%u] = rv->io.mem_read_w(%u);\n", ir->rd, ir->rs1); +strcat(gencode, " res =\n"); +GEN(" rv->X[%u] > rv->X[%u] ? rv->X[%u] : rv->X[%u];\n", ir->rd, ir->rs2, ir->rd, ir->rs2); +GEN(" rv->io.mem_write_s(%u, res);\n", ir->rs1); +}) +RVOP(amominuw, { +GEN(" rv->X[%u] = rv->io.mem_read_w(%u);\n", ir->rd, ir->rs1); +strcat(gencode, " ures =\n"); +GEN(" rv->X[%u] < rv->X[%u] ? rv->X[%u] : rv->X[%u];\n", ir->rd, ir->rs2, ir->rd, ir->rs2); +GEN(" rv->io.mem_write_s(%u, ures);\n", ir->rs1); +}) +RVOP(amomaxuw, { +GEN(" rv->X[%u] = rv->io.mem_read_w(%u);\n", ir->rd, ir->rs1); +strcat(gencode, " ures =\n"); +GEN(" rv->X[%u] > rv->X[%u] ? rv->X[%u] : rv->X[%u];\n", ir->rd, ir->rs2, ir->rd, ir->rs2); +GEN(" rv->io.mem_write_s(%u, ures);\n", ir->rs1); +}) +RVOP(fmadds, { +GEN(" rv->F[%u] = rv->F[%u] * rv->F[%u] + rv->F[%u]; \n", ir->rd, ir->rs1, ir->rs2, ir->rs3); +}) +RVOP(fmsubs, { +GEN(" rv->F[%u] = rv->F[%u] * rv->F[%u] - rv->F[%u]; \n", ir->rd, ir->rs1, ir->rs2, ir->rs3); +}) +RVOP(fnmsubs, { +GEN(" rv->F[%u] = rv->F[%u] - (rv->F[%u] * rv->F[%u]); \n", ir->rd, ir->rs3, ir->rs1, ir->rs2); +}) +RVOP(fnmadds, { +GEN(" rv->F[%u] = -(rv->F[%u] * rv->F[%u]) - rv->F[%u]; \n", ir->rd, ir->rs1, ir->rs2, ir->rs3); +}) +RVOP(fadds, { +GEN(" if (isnanf(rv->F[%u]) || isnanf(rv->F[%u]) ||\n", ir->rs1, ir->rs2); +GEN(" isnanf(rv->F[%u] + rv->F[%u])) {\n", ir->rs1, ir->rs2); +GEN(" rv->F_int[%u] = RV_NAN; rv->csr_fcsr |= FFLAG_INVALID_OP;\n", ir->rd); +strcat(gencode, " } else {\n"); +GEN(" rv->F[%u] = rv->F[%u] + rv->F[%u];\n", ir->rd, ir->rs1, ir->rs2); +strcat(gencode, " }\n"); +GEN(" if (isinff(rv->F[%u])) {\n", ir->rd); +strcat(gencode, " rv->csr_fcsr |= FFLAG_OVERFLOW;\n"); +strcat(gencode, " rv->csr_fcsr |= FFLAG_INEXACT;\n"); +strcat(gencode, " }\n"); +}) +RVOP(fsubs, { +GEN(" if (isnanf(rv->F[%u]) || isnanf(rv->F[%u])) {\n", ir->rs1, ir->rs2); +GEN(" rv->F_int[%u] = RV_NAN;\n", ir->rd); +strcat(gencode, " } else {\n"); +GEN(" rv->F[%u] = rv->F[%u] - rv->F[%u];\n", ir->rd, ir->rs1, ir->rs2); +strcat(gencode, " }\n"); +}) +RVOP(fmuls, { +GEN(" rv->F[%u] = rv->F[%u] * rv->F[%u]; \n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(fdivs, { +GEN(" rv->F[%u] = rv->F[%u] / rv->F[%u]; \n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(fsqrts, { +GEN(" rv->F[%u] = sqrtf(rv->F[%u]); \n", ir->rd, ir->rs1); +}) +RVOP(fsgnjs, { +GEN(" ures = (((uint32_t) rv->F_int[%u]) & ~FMASK_SIGN) |\n", ir->rs1); +GEN(" (((uint32_t) rv->F_int[%u]) & FMASK_SIGN);\n", ir->rs2); +GEN(" rv->F_int[%u] = ures;\n", ir->rd); +}) +RVOP(fsgnjns, { +GEN(" ures = (((uint32_t) rv->F_int[%u]) & ~FMASK_SIGN) |\n", ir->rs1); +GEN(" (~((uint32_t) rv->F_int[%u]) & FMASK_SIGN);\n", ir->rs2); +GEN(" rv->F_int[%u] = ures;\n", ir->rd); +}) +RVOP(fsgnjxs, { +GEN(" ures = ((uint32_t) rv->F_int[%u]) ^\n", ir->rs1); +GEN(" (((uint32_t) rv->F_int[%u]) & FMASK_SIGN);\n", ir->rs2); +GEN(" rv->F_int[%u] = ures;\n", ir->rd); +}) +RVOP(fmins, { +GEN(" a = rv->F_int[%u];\n", ir->rs1); +GEN(" b = rv->F_int[%u];\n", ir->rs2); +strcat(gencode, " if (is_nan(a) || is_nan(b)) {\n"); +strcat(gencode, " if (is_snan(a) || is_snan(b))\n"); +strcat(gencode, " rv->csr_fcsr |= FFLAG_INVALID_OP;\n"); +strcat(gencode, " if (is_nan(a) && !is_nan(b)) {\n"); +GEN(" rv->F[%u] = rv->F[%u];\n", ir->rd, ir->rs2); +strcat(gencode, " } else if (!is_nan(a) && is_nan(b)) {\n"); +GEN(" rv->F[%u] = rv->F[%u];\n", ir->rd, ir->rs1); +strcat(gencode, " } else {\n"); +GEN(" rv->F_int[%u] = RV_NAN;\n", ir->rd); +strcat(gencode, " }\n"); +strcat(gencode, " } else {\n"); +strcat(gencode, " a_sign = a & FMASK_SIGN;\n"); +strcat(gencode, " b_sign = b & FMASK_SIGN;\n"); +strcat(gencode, " if (a_sign != b_sign) {\n"); +GEN(" rv->F[%u] = a_sign ? rv->F[%u] : rv->F[%u];\n", ir->rd, ir->rs1, ir->rs2); +strcat(gencode, " } else {\n"); +GEN(" rv->F[%u] = (rv->F[%u] < rv->F[%u]) ? rv->F[%u]\n", ir->rd, ir->rs1, ir->rs2, ir->rs1); +GEN(" : rv->F[%u];\n", ir->rs2); +strcat(gencode, " }\n"); +strcat(gencode, " }\n"); +}) +RVOP(fmaxs, { +GEN(" a = rv->F_int[%u];\n", ir->rs1); +GEN(" b = rv->F_int[%u];\n", ir->rs2); +strcat(gencode, " if (is_nan(a) || is_nan(b)) {\n"); +strcat(gencode, " if (is_snan(a) || is_snan(b))\n"); +strcat(gencode, " rv->csr_fcsr |= FFLAG_INVALID_OP;\n"); +strcat(gencode, " if (is_nan(a) && !is_nan(b)) {\n"); +GEN(" rv->F[%u] = rv->F[%u];\n", ir->rd, ir->rs2); +strcat(gencode, " } else if (!is_nan(a) && is_nan(b)) {\n"); +GEN(" rv->F[%u] = rv->F[%u];\n", ir->rd, ir->rs1); +strcat(gencode, " } else {\n"); +GEN(" rv->F_int[%u] = RV_NAN;\n", ir->rd); +strcat(gencode, " }\n"); +strcat(gencode, " } else {\n"); +strcat(gencode, " a_sign = a & FMASK_SIGN;\n"); +strcat(gencode, " b_sign = b & FMASK_SIGN;\n"); +strcat(gencode, " if (a_sign != b_sign) {\n"); +GEN(" rv->F[%u] = a_sign ? rv->F[%u] : rv->F[%u];\n", ir->rd, ir->rs2, ir->rs1); +strcat(gencode, " } else {\n"); +GEN(" rv->F[%u] = (rv->F[%u] > rv->F[%u]) ? rv->F[%u]\n", ir->rd, ir->rs1, ir->rs2, ir->rs1); +GEN(" : rv->F[%u];\n", ir->rs2); +strcat(gencode, " }\n"); +strcat(gencode, " }\n"); +}) +RVOP(fcvtws, { +GEN(" rv->X[%u] = (int32_t) rv->F[%u]; \n", ir->rd, ir->rs1); +}) +RVOP(fcvtwus, { +GEN(" rv->X[%u] = (uint32_t) rv->F[%u]; \n", ir->rd, ir->rs1); +}) +RVOP(fmvxw, { +GEN(" rv->X[%u] = rv->F_int[%u]; \n", ir->rd, ir->rs1); +}) +RVOP(feqs, { +GEN(" rv->X[%u] = (rv->F[%u] == rv->F[%u]) ? 1 : 0;\n", ir->rd, ir->rs1, ir->rs2); +GEN(" if (is_snan(rv->F_int[%u]) || is_snan(rv->F_int[%u]))\n", ir->rs1, ir->rs2); +strcat(gencode, " rv->csr_fcsr |= FFLAG_INVALID_OP;\n"); +}) +RVOP(flts, { +GEN(" rv->X[%u] = (rv->F[%u] < rv->F[%u]) ? 1 : 0;\n", ir->rd, ir->rs1, ir->rs2); +GEN(" if (is_nan(rv->F_int[%u]) || is_nan(rv->F_int[%u]))\n", ir->rs1, ir->rs2); +strcat(gencode, " rv->csr_fcsr |= FFLAG_INVALID_OP;\n"); +}) +RVOP(fles, { +GEN(" rv->X[%u] = (rv->F[%u] <= rv->F[%u]) ? 1 : 0;\n", ir->rd, ir->rs1, ir->rs2); +GEN(" if (is_nan(rv->F_int[%u]) || is_nan(rv->F_int[%u]))\n", ir->rs1, ir->rs2); +strcat(gencode, " rv->csr_fcsr |= FFLAG_INVALID_OP;\n"); +}) +RVOP(fclasss, { +GEN(" bits = rv->F_int[%u];\n", ir->rs1); +GEN(" rv->X[%u] = calc_fclass(bits);\n", ir->rd); +}) +RVOP(fcvtsw, { +GEN(" rv->F[%u] = (int32_t) rv->X[%u]; \n", ir->rd, ir->rs1); +}) +RVOP(fcvtswu, { +GEN(" rv->F[%u] = rv->X[%u]; \n", ir->rd, ir->rs1); +}) +RVOP(fmvwx, { +GEN(" rv->F_int[%u] = rv->X[%u]; \n", ir->rd, ir->rs1); +}) +RVOP(caddi4spn, { +GEN(" rv->X[%u] = rv->X[rv_reg_sp] + (uint16_t) %u; \n", ir->rd, ir->imm); +}) +RVOP(cnop, { +strcat(gencode, "/* no operation */\n"); +}) +RVOP(caddi, { +GEN(" rv->X[%u] += (int16_t) %u; \n", ir->rd, ir->imm); +}) +RVOP(cli, { +GEN(" rv->X[%u] = %u; \n", ir->rd, ir->imm); +}) +RVOP(caddi16sp, { +GEN(" rv->X[%u] += %u; \n", ir->rd, ir->imm); +}) +RVOP(clui, { +GEN(" rv->X[%u] = %u; \n", ir->rd, ir->imm); +}) +RVOP(csrli, { +GEN(" rv->X[%u] >>= %u; \n", ir->rs1, ir->shamt); +}) +RVOP(csrai, { +GEN(" mask = 0x80000000 & rv->X[%u];\n", ir->rs1); +GEN(" rv->X[%u] >>= %u;\n", ir->rs1, ir->shamt); +GEN(" for (unsigned int i = 0; i < %u; ++i)\n", ir->shamt); +GEN(" rv->X[%u] |= mask >> i;\n", ir->rs1); +}) +RVOP(candi, { +GEN(" rv->X[%u] &= %u; \n", ir->rs1, ir->imm); +}) +RVOP(csub, { +GEN(" rv->X[%u] = rv->X[%u] - rv->X[%u]; \n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(cxor, { +GEN(" rv->X[%u] = rv->X[%u] ^ rv->X[%u]; \n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(cor, { +GEN(" rv->X[%u] = rv->X[%u] | rv->X[%u]; \n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(cand, { +GEN(" rv->X[%u] = rv->X[%u] & rv->X[%u]; \n", ir->rd, ir->rs1, ir->rs2); +}) +RVOP(cslli, { +GEN(" rv->X[%u] <<= (uint8_t) %u; \n", ir->rd, ir->imm); +}) +RVOP(cmv, { +GEN(" rv->X[%u] = rv->X[%u]; \n", ir->rd, ir->rs2); +}) +RVOP(cebreak, { +strcat(gencode, " rv->compressed = true;\n"); +strcat(gencode, " rv->csr_cycle = cycle;\n"); +strcat(gencode, " rv->PC = PC;\n"); +strcat(gencode, " rv->io.on_ebreak(rv);\n"); +strcat(gencode, " return true;\n"); +}) +RVOP(cadd, { +GEN(" rv->X[%u] = rv->X[%u] + rv->X[%u]; \n", ir->rd, ir->rs1, ir->rs2); +}) diff --git a/src/riscv.c b/src/riscv.c index 332fff48c..b084f8932 100644 --- a/src/riscv.c +++ b/src/riscv.c @@ -10,10 +10,12 @@ #include "mpool.h" #include "riscv_private.h" #include "state.h" +#include "utils.h" #define BLOCK_MAP_CAPACITY_BITS 10 #define BLOCK_IR_MAP_CAPACITY_BITS 10 +#if !RV32_HAS(JIT) /* initialize the block map */ static void block_map_init(block_map_t *map, const uint8_t bits) { @@ -52,6 +54,7 @@ static void block_map_destroy(riscv_t *rv) mpool_destroy(rv->block_mp); mpool_destroy(rv->block_ir_mp); } +#endif riscv_user_t rv_userdata(riscv_t *rv) { @@ -119,8 +122,13 @@ riscv_t *rv_create(const riscv_io_t *io, rv->block_ir_mp = mpool_create( sizeof(rv_insn_t) << BLOCK_IR_MAP_CAPACITY_BITS, sizeof(rv_insn_t)); +#if !RV32_HAS(JIT) /* initialize the block map */ block_map_init(&rv->block_map, 10); +#else + rv->block_cache = cache_create(10); + rv->code_cache = cache_create(10); +#endif /* reset */ rv_reset(rv, 0U, argc, args); @@ -143,10 +151,15 @@ bool rv_enables_to_output_exit_code(riscv_t *rv) return rv->output_exit_code; } + void rv_delete(riscv_t *rv) { assert(rv); +#if !RV32_HAS(JIT) block_map_destroy(rv); +#else + +#endif free(rv); } @@ -274,3 +287,133 @@ void rv_reset(riscv_t *rv, riscv_word_t pc, int argc, char **args) rv->halt = false; } + +/* get current time in microsecnds and update csr_time register */ +FORCE_INLINE void update_time(riscv_t *rv) +{ + struct timeval tv; + rv_gettimeofday(&tv); + + uint64_t t = (uint64_t) tv.tv_sec * 1e6 + (uint32_t) tv.tv_usec; + rv->csr_time[0] = t & 0xFFFFFFFF; + rv->csr_time[1] = t >> 32; +} + +#if RV32_HAS(Zicsr) +/* get a pointer to a CSR */ +static uint32_t *csr_get_ptr(riscv_t *rv, uint32_t csr) +{ + /* csr & 0xFFF prevent sign-extension in decode stage */ + switch (csr & 0xFFF) { + case CSR_MSTATUS: /* Machine Status */ + return (uint32_t *) (&rv->csr_mstatus); + case CSR_MTVEC: /* Machine Trap Handler */ + return (uint32_t *) (&rv->csr_mtvec); + case CSR_MISA: /* Machine ISA and Extensions */ + return (uint32_t *) (&rv->csr_misa); + + /* Machine Trap Handling */ + case CSR_MSCRATCH: /* Machine Scratch Register */ + return (uint32_t *) (&rv->csr_mscratch); + case CSR_MEPC: /* Machine Exception Program Counter */ + return (uint32_t *) (&rv->csr_mepc); + case CSR_MCAUSE: /* Machine Exception Cause */ + return (uint32_t *) (&rv->csr_mcause); + case CSR_MTVAL: /* Machine Trap Value */ + return (uint32_t *) (&rv->csr_mtval); + case CSR_MIP: /* Machine Interrupt Pending */ + return (uint32_t *) (&rv->csr_mip); + + /* Machine Counter/Timers */ + case CSR_CYCLE: /* Cycle counter for RDCYCLE instruction */ + return (uint32_t *) &rv->csr_cycle; + case CSR_CYCLEH: /* Upper 32 bits of cycle */ + return &((uint32_t *) &rv->csr_cycle)[1]; + + /* TIME/TIMEH - very roughly about 1 ms per tick */ + case CSR_TIME: /* Timer for RDTIME instruction */ + update_time(rv); + return &rv->csr_time[0]; + case CSR_TIMEH: /* Upper 32 bits of time */ + update_time(rv); + return &rv->csr_time[1]; + case CSR_INSTRET: /* Number of Instructions Retired Counter */ + return (uint32_t *) (&rv->csr_cycle); +#if RV32_HAS(EXT_F) + case CSR_FFLAGS: + return (uint32_t *) (&rv->csr_fcsr); + case CSR_FCSR: + return (uint32_t *) (&rv->csr_fcsr); +#endif + default: + return NULL; + } +} + +FORCE_INLINE bool csr_is_writable(uint32_t csr) +{ + return csr < 0xc00; +} + +/* CSRRW (Atomic Read/Write CSR) instruction atomically swaps values in the + * CSRs and integer registers. CSRRW reads the old value of the CSR, + * zero-extends the value to XLEN bits, and then writes it to register rd. + * The initial value in rs1 is written to the CSR. + * If rd == x0, then the instruction shall not read the CSR and shall not cause + * any of the side effects that might occur on a CSR read. + */ +uint32_t csr_csrrw(riscv_t *rv, uint32_t csr, uint32_t val) +{ + uint32_t *c = csr_get_ptr(rv, csr); + if (!c) + return 0; + + uint32_t out = *c; +#if RV32_HAS(EXT_F) + if (csr == CSR_FFLAGS) + out &= FFLAG_MASK; +#endif + if (csr_is_writable(csr)) + *c = val; + + return out; +} + +/* perform csrrs (atomic read and set) */ +uint32_t csr_csrrs(riscv_t *rv, uint32_t csr, uint32_t val) +{ + uint32_t *c = csr_get_ptr(rv, csr); + if (!c) + return 0; + + uint32_t out = *c; +#if RV32_HAS(EXT_F) + if (csr == CSR_FFLAGS) + out &= FFLAG_MASK; +#endif + if (csr_is_writable(csr)) + *c |= val; + + return out; +} + +/* perform csrrc (atomic read and clear) + * Read old value of CSR, zero-extend to XLEN bits, write to rd. + * Read value from rs1, use as bit mask to clear bits in CSR. + */ +uint32_t csr_csrrc(riscv_t *rv, uint32_t csr, uint32_t val) +{ + uint32_t *c = csr_get_ptr(rv, csr); + if (!c) + return 0; + + uint32_t out = *c; +#if RV32_HAS(EXT_F) + if (csr == CSR_FFLAGS) + out &= FFLAG_MASK; +#endif + if (csr_is_writable(csr)) + *c &= ~val; + return out; +} +#endif diff --git a/src/riscv_private.h b/src/riscv_private.h index defe09b1b..64b88ca59 100644 --- a/src/riscv_private.h +++ b/src/riscv_private.h @@ -12,6 +12,9 @@ #endif #include "decode.h" #include "riscv.h" +#if RV32_HAS(JIT) +#include "cache.h" +#endif /* CSRs */ enum { @@ -59,6 +62,9 @@ typedef struct block { struct block *predict; /**< block prediction */ rv_insn_t *ir_head, *ir_tail; /**< the first and last ir for this block */ +#if RV32_HAS(JIT) + bool hot; /**< Determine the block is hotspot or not */ +#endif } block_t; typedef struct { @@ -83,20 +89,6 @@ struct riscv_internal { /* user provided data */ riscv_user_t userdata; -#if RV32_HAS(GDBSTUB) - /* gdbstub instance */ - gdbstub_t gdbstub; - - bool debug_mode; - - /* GDB instruction breakpoint */ - breakpoint_map_t breakpoint_map; - - /* The flag to notify interrupt from GDB client: it should - * be accessed by atomic operation when starting the GDBSTUB. */ - bool is_interrupted; -#endif - #if RV32_HAS(EXT_F) /* float registers */ union { @@ -119,11 +111,30 @@ struct riscv_internal { uint32_t csr_mip; /* Machine interrupt pending */ uint32_t csr_mbadaddr; - bool compressed; /**< current instruction is compressed or not */ + bool compressed; /**< current instruction is compressed or not */ +#if !RV32_HAS(JIT) block_map_t block_map; /**< basic block map */ +#else + struct cache *block_cache; + struct cache *code_cache; +#endif struct mpool *block_mp, *block_ir_mp; /* print exit code on syscall_exit */ bool output_exit_code; + +#if RV32_HAS(GDBSTUB) + /* gdbstub instance */ + gdbstub_t gdbstub; + + bool debug_mode; + + /* GDB instruction breakpoint */ + breakpoint_map_t breakpoint_map; + + /* The flag to notify interrupt from GDB client: it should + * be accessed by atomic operation when starting the GDBSTUB. */ + bool is_interrupted; +#endif }; /* sign extend a 16 bit value */ @@ -143,3 +154,39 @@ FORCE_INLINE bool is_compressed(uint32_t insn) { return (insn & FC_OPCODE) != 3; } + +#if RV32_HAS(EXT_F) +#include +#include "softfloat.h" + +#if defined(__APPLE__) +static inline int isinff(float x) +{ + return __builtin_fabsf(x) == __builtin_inff(); +} +static inline int isnanf(float x) +{ + return x != x; +} +#endif +#endif /* RV32_HAS(EXT_F) */ + +#if RV32_HAS(Zicsr) +/* CSRRW (Atomic Read/Write CSR) instruction atomically swaps values in the + * CSRs and integer registers. CSRRW reads the old value of the CSR, + * zero-extends the value to XLEN bits, and then writes it to register rd. + * The initial value in rs1 is written to the CSR. + * If rd == x0, then the instruction shall not read the CSR and shall not cause + * any of the side effects that might occur on a CSR read. + */ +uint32_t csr_csrrw(riscv_t *rv, uint32_t csr, uint32_t val); + +/* perform csrrs (atomic read and set) */ +uint32_t csr_csrrs(riscv_t *rv, uint32_t csr, uint32_t val); + +/* perform csrrc (atomic read and clear) + * Read old value of CSR, zero-extend to XLEN bits, write to rd. + * Read value from rs1, use as bit mask to clear bits in CSR. + */ +uint32_t csr_csrrc(riscv_t *rv, uint32_t csr, uint32_t val); +#endif diff --git a/src/rv32_template.c b/src/rv32_template.c index 93131c98a..a4389b109 100644 --- a/src/rv32_template.c +++ b/src/rv32_template.c @@ -1,6 +1,7 @@ /* RV32I Base Instruction Set */ /* Internal */ +#include RVOP(nop, { rv->X[rv_reg_zero] = 0; }) /* LUI is used to build 32-bit constants and uses the U-type format. LUI @@ -30,8 +31,17 @@ RVOP(jal, { rv->X[ir->rd] = pc + 4; /* check instruction misaligned */ RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); - if (ir->branch_taken) + if (ir->branch_taken) { +#if !RV32_HAS(JIT) return ir->branch_taken->impl(rv, ir->branch_taken, cycle, PC); +#else + if (!cache_get(rv->block_cache, PC)) { + clear_flag = true; + goto end_insn; + } +#endif + } +end_insn: rv->csr_cycle = cycle; rv->PC = PC; return true; @@ -53,9 +63,11 @@ RVOP(jalr, { rv->X[ir->rd] = pc + 4; /* check instruction misaligned */ RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); +#if !RV32_HAS(JIT) block_t *block = block_find(&rv->block_map, PC); if (block) return block->ir_head->impl(rv, block->ir_head, cycle, PC); +#endif rv->csr_cycle = cycle; rv->PC = PC; return true; @@ -64,10 +76,17 @@ RVOP(jalr, { /* clang-format off */ #define BRANCH_FUNC(type, cond) \ const uint32_t pc = PC; \ - if ((type) rv->X[ir->rs1] cond (type)rv->X[ir->rs2]) { \ + if ((type)rv->X[ir->rs1] cond (type)rv->X[ir->rs2]) { \ branch_taken = false; \ if (!ir->branch_untaken) \ goto nextop; \ + assert(ir->pc== PC);\ + IIF(RV32_HAS(JIT)) \ + ( \ + if (!cache_get(rv->block_cache, PC + 4)) { \ + clear_flag = true; \ + goto nextop; \ + }, ); \ PC += 4; \ last_pc = PC; \ return ir->branch_untaken->impl(rv, ir->branch_untaken, cycle, PC); \ @@ -76,10 +95,18 @@ RVOP(jalr, { PC += ir->imm; \ /* check instruction misaligned */ \ RV_EXC_MISALIGN_HANDLER(pc, insn, false, 0); \ + assert(ir->pc + ir->imm == PC);\ if (ir->branch_taken) { \ - last_pc = PC; \ + IIF(RV32_HAS(JIT)) \ + ( \ + if (!cache_get(rv->block_cache, PC)) { \ + clear_flag = true; \ + goto end_insn;\ + }, );\ + last_pc = PC; \ return ir->branch_taken->impl(rv, ir->branch_taken, cycle, PC); \ } \ +end_insn:\ rv->csr_cycle = cycle; \ rv->PC = PC; \ return true; @@ -370,9 +397,9 @@ RVOP(mul, * cast to i64 sign-extends the register values. */ RVOP(mulh, { - const int64_t a = (int32_t) rv->X[ir->rs1]; - const int64_t b = (int32_t) rv->X[ir->rs2]; - rv->X[ir->rd] = ((uint64_t) (a * b)) >> 32; + const int64_t multiplicand = (int32_t) rv->X[ir->rs1]; + const int64_t multiplier = (int32_t) rv->X[ir->rs2]; + rv->X[ir->rd] = ((uint64_t) (multiplicand * multiplier)) >> 32; }) /* MULHSU: Multiply High Signed Unsigned */ @@ -381,9 +408,9 @@ RVOP(mulh, { * Additionally, rs2 should not undergo sign extension. */ RVOP(mulhsu, { - const int64_t a = (int32_t) rv->X[ir->rs1]; - const uint64_t b = rv->X[ir->rs2]; - rv->X[ir->rd] = ((uint64_t) (a * b)) >> 32; + const int64_t multiplicand = (int32_t) rv->X[ir->rs1]; + const uint64_t umultiplier = rv->X[ir->rs2]; + rv->X[ir->rd] = ((uint64_t) (multiplicand * umultiplier)) >> 32; }) /* MULHU: Multiply High Unsigned Unsigned */ @@ -417,11 +444,12 @@ RVOP(div, { * +------------------------+-----------+----------+----------+ */ RVOP(divu, { - const uint32_t dividend = rv->X[ir->rs1]; - const uint32_t divisor = rv->X[ir->rs2]; - rv->X[ir->rd] = !divisor ? ~0U : dividend / divisor; + const uint32_t udividend = rv->X[ir->rs1]; + const uint32_t udivisor = rv->X[ir->rs2]; + rv->X[ir->rd] = !udivisor ? ~0U : udividend / udivisor; }) +/* clang-format off */ /* REM: Remainder Signed */ /* +------------------------+-----------+----------+---------+ * | Condition | Dividend | Divisor | REM[W] | @@ -435,8 +463,8 @@ RVOP(rem, { const int32_t divisor = rv->X[ir->rs2]; rv->X[ir->rd] = !divisor ? dividend : (divisor == -1 && rv->X[ir->rs1] == 0x80000000U) - ? 0 /* overflow */ - : (dividend % divisor); + ? 0 : (dividend + % divisor); }) /* REMU: Remainder Unsigned */ @@ -447,10 +475,12 @@ RVOP(rem, { * +------------------------+-----------+----------+----------+ */ RVOP(remu, { - const uint32_t dividend = rv->X[ir->rs1]; - const uint32_t divisor = rv->X[ir->rs2]; - rv->X[ir->rd] = !divisor ? dividend : dividend % divisor; + const uint32_t udividend = rv->X[ir->rs1]; + const uint32_t udivisor = rv->X[ir->rs2]; + rv->X[ir->rd] = !udivisor ? udividend : udividend + % udivisor; }) +/* clang-format on */ #endif /* RV32A Standard Extension */ @@ -531,37 +561,33 @@ RVOP(amoorw, { /* AMOMIN.W: Atomic MIN */ RVOP(amominw, { rv->X[ir->rd] = rv->io.mem_read_w(ir->rs1); - const int32_t a = rv->X[ir->rd]; - const int32_t b = rv->X[ir->rs2]; - const int32_t res = a < b ? a : b; + const int32_t res = + rv->X[ir->rd] < rv->X[ir->rs2] ? rv->X[ir->rd] : rv->X[ir->rs2]; rv->io.mem_write_s(ir->rs1, res); }) /* AMOMAX.W: Atomic MAX */ RVOP(amomaxw, { rv->X[ir->rd] = rv->io.mem_read_w(ir->rs1); - const int32_t a = rv->X[ir->rd]; - const int32_t b = rv->X[ir->rs2]; - const int32_t res = a > b ? a : b; + const int32_t res = + rv->X[ir->rd] > rv->X[ir->rs2] ? rv->X[ir->rd] : rv->X[ir->rs2]; rv->io.mem_write_s(ir->rs1, res); }) /* AMOMINU.W */ RVOP(amominuw, { rv->X[ir->rd] = rv->io.mem_read_w(ir->rs1); - const uint32_t a = rv->X[ir->rd]; - const uint32_t b = rv->X[ir->rs2]; - const uint32_t res = a < b ? a : b; - rv->io.mem_write_s(ir->rs1, res); + const uint32_t ures = + rv->X[ir->rd] < rv->X[ir->rs2] ? rv->X[ir->rd] : rv->X[ir->rs2]; + rv->io.mem_write_s(ir->rs1, ures); }) /* AMOMAXU.W */ RVOP(amomaxuw, { rv->X[ir->rd] = rv->io.mem_read_w(ir->rs1); - const uint32_t a = rv->X[ir->rd]; - const uint32_t b = rv->X[ir->rs2]; - const uint32_t res = a > b ? a : b; - rv->io.mem_write_s(ir->rs1, res); + const uint32_t ures = + rv->X[ir->rd] > rv->X[ir->rs2] ? rv->X[ir->rd] : rv->X[ir->rs2]; + rv->io.mem_write_s(ir->rs1, ures); }) #endif /* RV32_HAS(EXT_A) */ @@ -634,29 +660,23 @@ RVOP(fsqrts, { rv->F[ir->rd] = sqrtf(rv->F[ir->rs1]); }) /* FSGNJ.S */ RVOP(fsgnjs, { - uint32_t f1 = rv->F_int[ir->rs1]; - uint32_t f2 = rv->F_int[ir->rs2]; - uint32_t res; - res = (f1 & ~FMASK_SIGN) | (f2 & FMASK_SIGN); - rv->F_int[ir->rd] = res; + const uint32_t ures = (((uint32_t) rv->F_int[ir->rs1]) & ~FMASK_SIGN) | + (((uint32_t) rv->F_int[ir->rs2]) & FMASK_SIGN); + rv->F_int[ir->rd] = ures; }) /* FSGNJN.S */ RVOP(fsgnjns, { - uint32_t f1 = rv->F_int[ir->rs1]; - uint32_t f2 = rv->F_int[ir->rs2]; - uint32_t res; - res = (f1 & ~FMASK_SIGN) | (~f2 & FMASK_SIGN); - rv->F_int[ir->rd] = res; + const uint32_t ures = (((uint32_t) rv->F_int[ir->rs1]) & ~FMASK_SIGN) | + (~((uint32_t) rv->F_int[ir->rs2]) & FMASK_SIGN); + rv->F_int[ir->rd] = ures; }) /* FSGNJX.S */ RVOP(fsgnjxs, { - uint32_t f1 = rv->F_int[ir->rs1]; - uint32_t f2 = rv->F_int[ir->rs2]; - uint32_t res; - res = f1 ^ (f2 & FMASK_SIGN); - rv->F_int[ir->rd] = res; + const uint32_t ures = ((uint32_t) rv->F_int[ir->rs1]) ^ + (((uint32_t) rv->F_int[ir->rs2]) & FMASK_SIGN); + rv->F_int[ir->rd] = ures; }) /* FMIN.S @@ -680,10 +700,8 @@ RVOP(fmins, { rv->F_int[ir->rd] = RV_NAN; } } else { - uint32_t a_sign; - uint32_t b_sign; - a_sign = a & FMASK_SIGN; - b_sign = b & FMASK_SIGN; + uint32_t a_sign = a & FMASK_SIGN; + uint32_t b_sign = b & FMASK_SIGN; if (a_sign != b_sign) { rv->F[ir->rd] = a_sign ? rv->F[ir->rs1] : rv->F[ir->rs2]; } else { @@ -708,10 +726,8 @@ RVOP(fmaxs, { rv->F_int[ir->rd] = RV_NAN; } } else { - uint32_t a_sign; - uint32_t b_sign; - a_sign = a & FMASK_SIGN; - b_sign = b & FMASK_SIGN; + uint32_t a_sign = a & FMASK_SIGN; + uint32_t b_sign = b & FMASK_SIGN; if (a_sign != b_sign) { rv->F[ir->rd] = a_sign ? rv->F[ir->rs2] : rv->F[ir->rs1]; } else { @@ -823,8 +839,17 @@ RVOP(cjal, { rv->X[rv_reg_ra] = PC + 2; PC += ir->imm; RV_EXC_MISALIGN_HANDLER(PC, insn, true, 0); - if (ir->branch_taken) + if (ir->branch_taken) { +#if !RV32_HAS(JIT) return ir->branch_taken->impl(rv, ir->branch_taken, cycle, PC); +#else + if (!cache_get(rv->block_cache, PC)) { + clear_flag = true; + goto end_insn; + } +#endif + } +end_insn: rv->csr_cycle = cycle; rv->PC = PC; return true; @@ -893,8 +918,17 @@ RVOP(cand, { rv->X[ir->rd] = rv->X[ir->rs1] & rv->X[ir->rs2]; }) RVOP(cj, { PC += ir->imm; RV_EXC_MISALIGN_HANDLER(PC, insn, true, 0); - if (ir->branch_taken) + if (ir->branch_taken) { +#if !RV32_HAS(JIT) return ir->branch_taken->impl(rv, ir->branch_taken, cycle, PC); +#else + if (!cache_get(rv->block_cache, PC)) { + clear_flag = true; + goto end_insn; + } +#endif + } +end_insn: rv->csr_cycle = cycle; rv->PC = PC; return true; @@ -910,16 +944,31 @@ RVOP(cbeqz, { branch_taken = false; if (!ir->branch_untaken) goto nextop; + assert(ir->pc == PC); +#if RV32_HAS(JIT) + if (!cache_get(rv->block_cache, PC + 2)) { + clear_flag = true; + goto nextop; + } +#endif PC += 2; last_pc = PC; return ir->branch_untaken->impl(rv, ir->branch_untaken, cycle, PC); } branch_taken = true; - PC += (uint32_t) ir->imm; + PC += ir->imm; + assert(ir->pc + ir->imm == PC); if (ir->branch_taken) { +#if RV32_HAS(JIT) + if (!cache_get(rv->block_cache, PC)) { + clear_flag = true; + goto end_insn; + } +#endif last_pc = PC; return ir->branch_taken->impl(rv, ir->branch_taken, cycle, PC); } +end_insn: rv->csr_cycle = cycle; rv->PC = PC; return true; @@ -931,16 +980,31 @@ RVOP(cbnez, { branch_taken = false; if (!ir->branch_untaken) goto nextop; + assert(ir->pc == PC); +#if RV32_HAS(JIT) + if (!cache_get(rv->block_cache, PC + 2)) { + clear_flag = true; + goto nextop; + } +#endif PC += 2; last_pc = PC; return ir->branch_untaken->impl(rv, ir->branch_untaken, cycle, PC); } branch_taken = true; - PC += (uint32_t) ir->imm; + PC += ir->imm; + assert(ir->pc + ir->imm == PC); if (ir->branch_taken) { +#if RV32_HAS(JIT) + if (!cache_get(rv->block_cache, PC)) { + clear_flag = true; + goto end_insn; + } +#endif last_pc = PC; return ir->branch_taken->impl(rv, ir->branch_taken, cycle, PC); } +end_insn: rv->csr_cycle = cycle; rv->PC = PC; return true; @@ -962,9 +1026,11 @@ RVOP(clwsp, { /* C.JR */ RVOP(cjr, { PC = rv->X[ir->rs1]; +#if !RV32_HAS(JIT) block_t *block = block_find(&rv->block_map, PC); if (block) return block->ir_head->impl(rv, block->ir_head, cycle, PC); +#endif rv->csr_cycle = cycle; rv->PC = PC; return true; @@ -989,9 +1055,11 @@ RVOP(cjalr, { rv->X[rv_reg_ra] = PC + 2; PC = jump_to; RV_EXC_MISALIGN_HANDLER(PC, insn, true, 0); +#if !RV32_HAS(JIT) block_t *block = block_find(&rv->block_map, PC); if (block) return block->ir_head->impl(rv, block->ir_head, cycle, PC); +#endif rv->csr_cycle = cycle; rv->PC = PC; return true; diff --git a/src/utils.h b/src/utils.h index 1611b2249..9642fcd96 100644 --- a/src/utils.h +++ b/src/utils.h @@ -3,6 +3,8 @@ #include #include +#define GOLDEN_RATIO_32 0x61C88647 + /* Obtain the system 's notion of the current Greenwich time. * TODO: manipulate current time zone. */ diff --git a/tools/gen-jit-template.py b/tools/gen-jit-template.py new file mode 100755 index 000000000..e2947e585 --- /dev/null +++ b/tools/gen-jit-template.py @@ -0,0 +1,258 @@ +#!/usr/bin/env python3 + +''' +This script serves as a code generator for creating JIT code templates +based on existing code files in the 'src' directory, eliminating the need +for writing duplicated code. +''' + +import re +import sys + +INSN = { + "Zifencei": ["fencei"], + "Zicsr": [ + "csrrw", + "csrrs", + "csrrc", + "csrrw", + "csrrsi", + "csrrci"], + "EXT_M": [ + "mul", + "mulh", + "mulhsu", + "mulhu", + "div", + "divu", + "rem", + "remu"], + "EXT_A": [ + "lrw", + "scw", + "amoswapw", + "amoaddw", + "amoxorw", + "amoandw", + "amoorw", + "amominw", + "amomaxw", + "amominuw", + "amomaxuw"], + "EXT_F": [ + "flw", + "fsw", + "fmadds", + "fmsubs", + "fnmsubs", + "fnmadds", + "fadds", + "fsubs", + "fmuls", + "fdivs", + "fsqrts", + "fsgnjs", + "fsgnjns", + "fsgnjxs", + "fmins", + "fmaxs", + "fcvtws", + "fcvtwus", + "fmvxw", + "feqs", + "flts", + "fles", + "fclasss", + "fcvtsw", + "fcvtswu", + "fmvwx"], + "EXT_C": [ + "caddi4spn", + "clw", + "csw", + "cnop", + "caddi", + "cjal", + "cli", + "caddi16sp", + "clui", + "csrli", + "csrai", + "candi", + "csub", + "cxor", + "cor", + "cand", + "cj", + "cbeqz", + "cbnez", + "cslli", + "clwsp", + "cjr", + "cmv", + "cebreak", + "cjalr", + "cadd", + "cswsp", + ], +} +EXT_LIST = ["Zifencei", "Zicsr", "EXT_M", "EXT_A", "EXT_F", "EXT_C"] +SKIPLIST = [ + "jal", + "jalr", + "beq", + "bne", + "blt", + "bge", + "bltu", + "bgeu", + "lb", + "lh", + "lw", + "lbu", + "lhu", + "sb", + "sh", + "sw", + "slli", + "srli", + "srai", + "flw", + "fsw", + "clw", + "csw", + "cjal", + "cj", + "cjalr", + "cjr", + "cbeqz", + "cbnez", + "clwsp", + "cswsp" +] +# check enabled extension in Makefile + + +def parse_argv(EXT_LIST, SKIPLIST): + for argv in sys.argv: + if argv.find("RV32_FEATURE_") != -1: + ext = argv[argv.find("RV32_FEATURE_") + 13:-2] + if argv[-1:] == "1" and EXT_LIST.count(ext): + EXT_LIST.remove(ext) + for ext in EXT_LIST: + SKIPLIST += INSN[ext] + + +def remove_comment(str): + str = re.sub(r'//[\s|\S]+?\n', "", str) + return re.sub(r'/\*[\s|\S]+?\*/\n', "", str) + + +parse_argv(EXT_LIST, SKIPLIST) +# prepare PROLOGUE +output = '''#define PROLOGUE \\ +\"#include \\n\"\\ +\"#include \\n\"\\ +''' +f = open('src/riscv.h', 'r') +lines = f.read() +reg_info = re.findall(r'#define RV_REGS_LIST[\S|\s]+?};', lines)[0] +reg_info = re.sub(r"/\*[\S|\s]+?\*/", "", reg_info) +reg_list = re.findall(r"_\([\S|\s]+?\)", reg_info) +reg_info = "enum {" +for i in range(32): + reg_info = reg_info + "rv_reg_" + reg_list[i][2:-1] + "," +reg_info += "N_RV_REGS };" +output = output + "\"" + \ + re.sub("\n", "\"\\\n\"", reg_info) + "\"\\\n" +output = output + "\"" + \ + re.sub("\n", "\"\\\n\"", re.findall( + r'typedef[\S|\s]+?riscv_io_t;', lines)[0]) + "\"\\\n" +if sys.argv.count("RV32_FEATURE_EXT_F=1"): + f = open('src/softfloat.h', 'r') + lines = f.read() + lines = remove_comment(lines) + output = output + "\"" + \ + re.sub("\n", "\"\\\n\"", re.findall( + r'enum[\S|\s]+?};', lines)[0]) + "\"\\\n" +f = open('src/riscv_private.h', 'r') +lines = f.read() +lines = remove_comment(lines) +lines = re.sub(r'#if RV32_HAS\(GDBSTUB\)[\s|\S]+?#endif\n', "", lines) +lines = re.sub(r'#if !RV32_HAS\(JIT\)[\s|\S]+?#endif\n', "", lines) +lines = re.sub(r"#if RV32_HAS\(EXT_F\)", "", lines) +lines = re.sub('#endif\n', "", lines) +output = output + "\"" + \ + re.sub("\n", "\"\\\n\"", re.findall( + r'struct riscv_internal[\S|\s]+?bool compressed;', lines)[0] + "};") + "\"\\\n" +f = open('src/io.h', 'r') +lines = f.read() +lines = remove_comment(lines) +output = output + "\"" + \ + re.sub("\n", "\"\\\n\"", re.findall( + r'typedef[\S|\s]+?memory_t;', lines)[0]) + "\"\\\n" +f = open('src/state.h', 'r') +lines = f.read() +lines = remove_comment(lines) +lines = re.sub('map_t fd_map;', "", lines) +output = output + "\"" + \ + re.sub("\n", "\"\\\n\"", re.findall( + r'typedef[\S|\s]+?state_t;', lines)[0]) + "\"\\\n" +# f = open('src/decode.h', 'r') +# lines = f.read() +# lines = remove_comment(lines) +# lines = re.sub(r'#if[\s|\S]+?\)\n', "", lines) +# lines = re.sub('#endif\n', "", lines) +# output = output + "\"" + \ +# re.sub("\n", "\"\\\n\"", re.findall( +# r'typedef[\S|\s]+?rv_insn_t;', lines)[0]) + "\"\\\n" +output += "\"bool start(riscv_t *rv, uint64_t cycle, uint32_t PC) {\"\\\n" +output += "\" uint32_t pc, addr, udividend, udivisor, tmp, data, mask, ures, \"\\\n" +output += "\"a, b, jump_to;\"\\\n" +output += "\" int32_t dividend, divisor, res;\"\\\n" +output += "\" int64_t multiplicand, multiplier;\"\\\n" +output += "\" uint64_t umultiplier;\"\\\n" +output += "\" memory_t *m = ((state_t *)rv->userdata)->mem;\"\n" + +f = open('src/rv32_template.c', 'r') +lines = f.read() +# remove exception handler +lines = re.sub(r'RV_EXC[\S]+?\([\S|\s]+?\);\s', "", lines) +# replace variable +lines = re.sub(r'const [u]*int[32|64]+_t', "", lines) +lines = re.sub("uint32_t tmp", "tmp", lines) +lines = re.sub("uint32_t a", "a", lines) +lines = re.sub("uint32_t b", "b", lines) +lines = re.sub("uint32_t a_sign", "a_sign", lines) +lines = re.sub("uint32_t b_sign", "b_sign", lines) +lines = re.sub("uint32_t data", "data", lines) +lines = re.sub("uint32_t bits", "bits", lines) +str2 = re.findall(r'RVOP\([\s|\S]+?}\)', lines) +op = [] +impl = [] +for i in range(len(str2)): + tmp = remove_comment(str2[i]) + op.append(tmp[5:tmp.find(',')]) + impl.append(tmp[tmp.find('{') + 1:-2]) + +f.close() +# generate jit template +for i in range(len(str2)): + if (not SKIPLIST.count(op[i])): + output = output + "RVOP(" + op[i] + ", {\n" + substr = impl[i].split("\n") + for str in substr: + IRs = re.findall( + r'ir->[rd|rs1|rs2|rs3|imm|imm2|insn_len|shamt]+', str) + str = re.sub( + r'ir->[rd|rs1|rs2|rs3|imm|imm2|insn_len|shamt]+', "%u", str) + if (str != ""): + if (len(IRs)): + output = output + 'GEN(\"' + str + '\\n\"' + for IR in IRs: + output = output + ", " + IR + else: + output = output + 'strcat(gencode, \"' + str + '\\n\"' + output = output + ');\n' + output = output + "})\n" +sys.stdout.write(output)