From 65fdaeffbff771b8eae7a1d3e332e702dd5e8c83 Mon Sep 17 00:00:00 2001 From: Vacantron Chen Date: Fri, 2 Aug 2024 00:47:59 +0800 Subject: [PATCH 1/2] Improve "JALR" execution with T2C JIT-cache Currently, the "JALR" indirect jump instruction turns the mode of rv32emu from T2C back to the interpreter. This commit introduces a "JIT-cache" table lookup to make it redirect to the T2C JIT-ed code entry and avoid the mode change. There are several scenarios benefitting from this approach, e.g. function pointer invocation and far-way function call. The former like "qsort" can be speeded up by two times, and the latter like "fibonacci", which compiled from the hand-written assembly for creating "JALR" instructions, can even reach x4.8 performance enhencement. --- build/fibonacci.elf | Bin 0 -> 76440 bytes src/jit.c | 1 + src/jit.h | 22 +++++++++++++- src/riscv.c | 5 +-- src/riscv_private.h | 1 + src/t2c.c | 72 +++++++++++++++++++++++++++++++++----------- src/t2c_template.c | 72 ++++++++++++++++++++++++++++++++++++++------ tests/fibonacci.s | 43 ++++++++++++++++++++++++++ 8 files changed, 187 insertions(+), 29 deletions(-) create mode 100644 build/fibonacci.elf create mode 100644 tests/fibonacci.s diff --git a/build/fibonacci.elf b/build/fibonacci.elf new file mode 100644 index 0000000000000000000000000000000000000000..a1b1107615cad54fd99f8a08114d0450e0e214e4 GIT binary patch literal 76440 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 literal 0 HcmV?d00001 diff --git a/src/jit.c b/src/jit.c index 62f1349c..c0a895bc 100644 --- a/src/jit.c +++ b/src/jit.c @@ -1864,6 +1864,7 @@ static void code_cache_flush(struct jit_state *state, riscv_t *rv) state->offset = state->org_size; state->n_blocks = 0; set_reset(&state->set); + jit_cache_clear(rv->jit_cache); clear_cache_hot(rv->block_cache, (clear_func_t) clear_hot); return; } diff --git a/src/jit.h b/src/jit.h index 8859822b..3967a1df 100644 --- a/src/jit.h +++ b/src/jit.h @@ -51,6 +51,26 @@ void jit_translate(riscv_t *rv, block_t *block); typedef void (*exec_block_func_t)(riscv_t *rv, uintptr_t); #if RV32_HAS(T2C) -void t2c_compile(block_t *block, uint64_t mem_base); +void t2c_compile(riscv_t *, block_t *); typedef void (*exec_t2c_func_t)(riscv_t *); + +/* The jit-cache records the program counters and the entries of executable + * instructions generated by T2C. Like hardware cache, the old jit-cache will be + * replaced by the new one which uses the same slot. + */ + +/* The size of jit-cache table should be the power of 2, thus, we can easily + * access the element by masking the program counter. + */ +#define N_JIT_CACHE_ENTRIES (1 << 12) + +struct jit_cache { + uint64_t pc; /* program counter, easy to build LLVM IR with 64-bit width */ + void *entry; /* entry of JIT-ed code */ +}; + +struct jit_cache *jit_cache_init(); +void jit_cache_exit(struct jit_cache *cache); +void jit_cache_update(struct jit_cache *cache, uint32_t pc, void *entry); +void jit_cache_clear(struct jit_cache *cache); #endif diff --git a/src/riscv.c b/src/riscv.c index 28103b23..231911f2 100644 --- a/src/riscv.c +++ b/src/riscv.c @@ -199,8 +199,7 @@ static void *t2c_runloop(void *arg) pthread_mutex_lock(&rv->wait_queue_lock); list_del_init(&entry->list); pthread_mutex_unlock(&rv->wait_queue_lock); - t2c_compile(entry->block, - (uint64_t) ((memory_t *) PRIV(rv)->mem)->mem_base); + t2c_compile(rv, entry->block); free(entry); } } @@ -291,6 +290,7 @@ riscv_t *rv_create(riscv_user_t rv_attr) mpool_create(sizeof(chain_entry_t) << BLOCK_IR_MAP_CAPACITY_BITS, sizeof(chain_entry_t)); rv->jit_state = jit_state_init(CODE_CACHE_SIZE); + rv->jit_cache = jit_cache_init(); rv->block_cache = cache_create(BLOCK_MAP_CAPACITY_BITS); assert(rv->block_cache); #if RV32_HAS(T2C) @@ -392,6 +392,7 @@ void rv_delete(riscv_t *rv) #endif mpool_destroy(rv->chain_entry_mp); jit_state_exit(rv->jit_state); + jit_cache_exit(rv->jit_cache); cache_free(rv->block_cache); #endif free(rv); diff --git a/src/riscv_private.h b/src/riscv_private.h index 33d8cb3e..5ca740a1 100644 --- a/src/riscv_private.h +++ b/src/riscv_private.h @@ -155,6 +155,7 @@ struct riscv_internal { struct mpool *block_mp, *block_ir_mp; void *jit_state; + void *jit_cache; #if RV32_HAS(GDBSTUB) /* gdbstub instance */ gdbstub_t gdbstub; diff --git a/src/t2c.c b/src/t2c.c index 36385356..17be7893 100644 --- a/src/t2c.c +++ b/src/t2c.c @@ -49,15 +49,15 @@ FORCE_INLINE LLVMBasicBlockRef t2c_block_map_search(struct LLVM_block_map *map, return NULL; } -#define T2C_OP(inst, code) \ - static void t2c_##inst( \ - LLVMBuilderRef *builder UNUSED, LLVMTypeRef *param_types UNUSED, \ - LLVMValueRef start UNUSED, LLVMBasicBlockRef *entry UNUSED, \ - LLVMBuilderRef *taken_builder UNUSED, \ - LLVMBuilderRef *untaken_builder UNUSED, uint64_t mem_base UNUSED, \ - rv_insn_t *ir UNUSED) \ - { \ - code; \ +#define T2C_OP(inst, code) \ + static void t2c_##inst( \ + LLVMBuilderRef *builder UNUSED, LLVMTypeRef *param_types UNUSED, \ + LLVMValueRef start UNUSED, LLVMBasicBlockRef *entry UNUSED, \ + LLVMBuilderRef *taken_builder UNUSED, \ + LLVMBuilderRef *untaken_builder UNUSED, riscv_t *rv UNUSED, \ + uint64_t mem_base UNUSED, rv_insn_t *ir UNUSED) \ + { \ + code; \ } #define T2C_LLVM_GEN_ADDR(reg, rv_member, ir_member) \ @@ -135,6 +135,9 @@ FORCE_INLINE void t2c_gen_call_io_func(LLVMValueRef start, &io_param, 1, ""); } +static LLVMTypeRef t2c_jit_cache_func_type; +static LLVMTypeRef t2c_jit_cache_struct_type; + #include "t2c_template.c" #undef T2C_OP @@ -174,6 +177,7 @@ typedef void (*t2c_codegen_block_func_t)(LLVMBuilderRef *builder UNUSED, LLVMBasicBlockRef *entry UNUSED, LLVMBuilderRef *taken_builder UNUSED, LLVMBuilderRef *untaken_builder UNUSED, + riscv_t *rv UNUSED, uint64_t mem_base UNUSED, rv_insn_t *ir UNUSED); @@ -181,7 +185,7 @@ static void t2c_trace_ebb(LLVMBuilderRef *builder, LLVMTypeRef *param_types UNUSED, LLVMValueRef start, LLVMBasicBlockRef *entry, - uint64_t mem_base, + riscv_t *rv, rv_insn_t *ir, set_t *set, struct LLVM_block_map *map) @@ -194,7 +198,8 @@ static void t2c_trace_ebb(LLVMBuilderRef *builder, while (1) { ((t2c_codegen_block_func_t) dispatch_table[ir->opcode])( - builder, param_types, start, entry, &tk, &utk, mem_base, ir); + builder, param_types, start, entry, &tk, &utk, rv, + (uint64_t) ((memory_t *) PRIV(rv)->mem)->mem_base, ir); if (!ir->next) break; ir = ir->next; @@ -214,8 +219,7 @@ static void t2c_trace_ebb(LLVMBuilderRef *builder, LLVMPositionBuilderAtEnd(untaken_builder, untaken_entry); LLVMBuildBr(utk, untaken_entry); t2c_trace_ebb(&untaken_builder, param_types, start, - &untaken_entry, mem_base, ir->branch_untaken, set, - map); + &untaken_entry, rv, ir->branch_untaken, set, map); } } if (ir->branch_taken) { @@ -230,13 +234,13 @@ static void t2c_trace_ebb(LLVMBuilderRef *builder, LLVMPositionBuilderAtEnd(taken_builder, taken_entry); LLVMBuildBr(tk, taken_entry); t2c_trace_ebb(&taken_builder, param_types, start, &taken_entry, - mem_base, ir->branch_taken, set, map); + rv, ir->branch_taken, set, map); } } } } -void t2c_compile(block_t *block, uint64_t mem_base) +void t2c_compile(riscv_t *rv, block_t *block) { LLVMModuleRef module = LLVMModuleCreateWithName("my_module"); LLVMTypeRef io_members[] = { @@ -254,6 +258,16 @@ void t2c_compile(block_t *block, uint64_t mem_base) LLVMTypeRef param_types[] = {LLVMPointerType(struct_rv, 0)}; LLVMValueRef start = LLVMAddFunction( module, "start", LLVMFunctionType(LLVMVoidType(), param_types, 1, 0)); + + LLVMTypeRef t2c_args[1] = {LLVMInt64Type()}; + t2c_jit_cache_func_type = + LLVMFunctionType(LLVMVoidType(), t2c_args, 1, false); + + /* Notice to the alignment */ + LLVMTypeRef jit_cache_memb[2] = {LLVMInt64Type(), + LLVMPointerType(LLVMVoidType(), 0)}; + t2c_jit_cache_struct_type = LLVMStructType(jit_cache_memb, 2, false); + LLVMBasicBlockRef first_block = LLVMAppendBasicBlock(start, "first_block"); LLVMBuilderRef first_builder = LLVMCreateBuilder(); LLVMPositionBuilderAtEnd(first_builder, first_block); @@ -266,8 +280,8 @@ void t2c_compile(block_t *block, uint64_t mem_base) struct LLVM_block_map map; map.count = 0; /* Translate custon IR into LLVM IR */ - t2c_trace_ebb(&builder, param_types, start, &entry, mem_base, - block->ir_head, &set, &map); + t2c_trace_ebb(&builder, param_types, start, &entry, rv, block->ir_head, + &set, &map); /* Offload LLVM IR to LLVM backend */ char *error = NULL, *triple = LLVMGetDefaultTargetTriple(); LLVMExecutionEngineRef engine; @@ -298,5 +312,29 @@ void t2c_compile(block_t *block, uint64_t mem_base) /* Return the function pointer of T2C generated machine code */ block->func = (exec_t2c_func_t) LLVMGetPointerToGlobal(engine, start); + jit_cache_update(rv->jit_cache, block->pc_start, block->func); block->hot2 = true; } + +struct jit_cache *jit_cache_init() +{ + return calloc(N_JIT_CACHE_ENTRIES, sizeof(struct jit_cache)); +} + +void jit_cache_exit(struct jit_cache *cache) +{ + free(cache); +} + +void jit_cache_update(struct jit_cache *cache, uint32_t pc, void *entry) +{ + uint32_t pos = pc & (N_JIT_CACHE_ENTRIES - 1); + + cache[pos].pc = pc; + cache[pos].entry = entry; +} + +void jit_cache_clear(struct jit_cache *cache) +{ + memset(cache, 0, N_JIT_CACHE_ENTRIES * sizeof(struct jit_cache)); +} diff --git a/src/t2c_template.c b/src/t2c_template.c index 864cb641..1b0737f2 100644 --- a/src/t2c_template.c +++ b/src/t2c_template.c @@ -32,6 +32,63 @@ T2C_OP(jal, { } }) +FORCE_INLINE void t2c_jit_cache_helper(LLVMBuilderRef *builder, + LLVMValueRef start, + LLVMValueRef addr, + riscv_t *rv, + rv_insn_t *ir) +{ + LLVMBasicBlockRef true_path = LLVMAppendBasicBlock(start, ""); + LLVMBuilderRef true_builder = LLVMCreateBuilder(); + LLVMPositionBuilderAtEnd(true_builder, true_path); + + LLVMBasicBlockRef false_path = LLVMAppendBasicBlock(start, ""); + LLVMBuilderRef false_builder = LLVMCreateBuilder(); + LLVMPositionBuilderAtEnd(false_builder, false_path); + + /* get jit-cache base address */ + LLVMValueRef base = LLVMConstIntToPtr( + LLVMConstInt(LLVMInt64Type(), (long) rv->jit_cache, false), + LLVMPointerType(t2c_jit_cache_struct_type, 0)); + + /* get index */ + LLVMValueRef hash = LLVMBuildAnd( + *builder, addr, + LLVMConstInt(LLVMInt32Type(), N_JIT_CACHE_ENTRIES - 1, false), ""); + + /* get jit_cache_t::pc */ + LLVMValueRef cast = + LLVMBuildIntCast2(*builder, hash, LLVMInt64Type(), false, ""); + LLVMValueRef element_ptr = LLVMBuildInBoundsGEP2( + *builder, t2c_jit_cache_struct_type, base, &cast, 1, ""); + LLVMValueRef pc_ptr = LLVMBuildStructGEP2( + *builder, t2c_jit_cache_struct_type, element_ptr, 0, ""); + LLVMValueRef pc = LLVMBuildLoad2(*builder, LLVMInt32Type(), pc_ptr, ""); + + /* compare with calculated destination */ + LLVMValueRef cmp = LLVMBuildICmp(*builder, LLVMIntEQ, pc, addr, ""); + + LLVMBuildCondBr(*builder, cmp, true_path, false_path); + + /* get jit_cache_t::entry */ + LLVMValueRef entry_ptr = LLVMBuildStructGEP2( + true_builder, t2c_jit_cache_struct_type, element_ptr, 1, ""); + + /* invoke T2C JIT-ed code */ + LLVMValueRef t2c_args[1] = { + LLVMConstInt(LLVMInt64Type(), (long) rv, false)}; + + LLVMBuildCall2(true_builder, t2c_jit_cache_func_type, + LLVMBuildLoad2(true_builder, LLVMInt64Type(), entry_ptr, ""), + t2c_args, 1, ""); + LLVMBuildRetVoid(true_builder); + + /* return to interpreter if cache-miss */ + LLVMBuildStore(false_builder, addr, + t2c_gen_PC_addr(start, &false_builder, ir)); + LLVMBuildRetVoid(false_builder); +} + T2C_OP(jalr, { if (ir->rd) T2C_LLVM_GEN_STORE_IMM32(*builder, ir->pc + 4, @@ -40,8 +97,7 @@ T2C_OP(jalr, { T2C_LLVM_GEN_LOAD_VMREG(rs1, 32, t2c_gen_rs1_addr(start, builder, ir)); val_rs1 = T2C_LLVM_GEN_ALU32_IMM(Add, val_rs1, ir->imm); val_rs1 = T2C_LLVM_GEN_ALU32_IMM(And, val_rs1, ~1U); - LLVMBuildStore(*builder, val_rs1, t2c_gen_PC_addr(start, builder, ir)); - LLVMBuildRetVoid(*builder); + t2c_jit_cache_helper(builder, start, val_rs1, rv, ir); }) #define BRANCH_FUNC(type, cond) \ @@ -672,8 +728,7 @@ T2C_OP(clwsp, { T2C_OP(cjr, { T2C_LLVM_GEN_LOAD_VMREG(rs1, 32, t2c_gen_rs1_addr(start, builder, ir)); - LLVMBuildStore(*builder, val_rs1, t2c_gen_PC_addr(start, builder, ir)); - LLVMBuildRetVoid(*builder); + t2c_jit_cache_helper(builder, start, val_rs1, rv, ir); }) T2C_OP(cmv, { @@ -692,8 +747,7 @@ T2C_OP(cjalr, { T2C_LLVM_GEN_STORE_IMM32(*builder, ir->pc + 2, t2c_gen_ra_addr(start, builder, ir)); T2C_LLVM_GEN_LOAD_VMREG(rs1, 32, t2c_gen_rs1_addr(start, builder, ir)); - LLVMBuildStore(*builder, val_rs1, t2c_gen_PC_addr(start, builder, ir)); - LLVMBuildRetVoid(*builder); + t2c_jit_cache_helper(builder, start, val_rs1, rv, ir); }) T2C_OP(cadd, { @@ -785,15 +839,15 @@ T2C_OP(fuse5, { switch (fuse[i].opcode) { case rv_insn_slli: t2c_slli(builder, param_types, start, entry, taken_builder, - untaken_builder, mem_base, (rv_insn_t *) (&fuse[i])); + untaken_builder, rv, mem_base, (rv_insn_t *) (&fuse[i])); break; case rv_insn_srli: t2c_srli(builder, param_types, start, entry, taken_builder, - untaken_builder, mem_base, (rv_insn_t *) (&fuse[i])); + untaken_builder, rv, mem_base, (rv_insn_t *) (&fuse[i])); break; case rv_insn_srai: t2c_srai(builder, param_types, start, entry, taken_builder, - untaken_builder, mem_base, (rv_insn_t *) (&fuse[i])); + untaken_builder, rv, mem_base, (rv_insn_t *) (&fuse[i])); break; default: __UNREACHABLE; diff --git a/tests/fibonacci.s b/tests/fibonacci.s new file mode 100644 index 00000000..65181b32 --- /dev/null +++ b/tests/fibonacci.s @@ -0,0 +1,43 @@ +fib: + li a5, 1 + bleu a0, a5, .L3 + addi sp, sp, -16 + sw ra, 12(sp) + sw s0, 8(sp) + sw s1, 4(sp) + mv s0, a0 + addi a0, a0, -1 + la t0, fib + jalr ra, 0(t0) + mv s1, a0 + addi a0, s0, -2 + la t0, fib + jalr ra, 0(t0) + add a0, s1, a0 + lw ra, 12(sp) + lw s0, 8(sp) + lw s1, 4(sp) + addi sp, sp, 16 + jr ra +.L3: + li a0, 1 + ret +.LC0: + .string "%d\n" + .text + .align 1 + .globl main + .type main, @function +main: + addi sp, sp, -16 + sw ra, 12(sp) + li a0, 42 + call fib + mv a1, a0 + lui a0, %hi(.LC0) + addi a0, a0, %lo(.LC0) + call printf + li a0, 0 + lw ra, 12(sp) + addi sp, sp, 16 + jr ra From f5d04fb7973fd9165253d82af224fac4290002f2 Mon Sep 17 00:00:00 2001 From: Vacantron Chen Date: Mon, 5 Aug 2024 01:10:10 +0800 Subject: [PATCH 2/2] CI: RISCOF workaround --- .ci/riscv-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/riscv-tests.sh b/.ci/riscv-tests.sh index 9cdab63f..ad11fda7 100755 --- a/.ci/riscv-tests.sh +++ b/.ci/riscv-tests.sh @@ -3,7 +3,7 @@ set -e -u -o pipefail # Install RISCOF -python3 -m pip install git+https://github.com/riscv/riscof +pip3 install git+https://github.com/riscv/riscof.git@d38859f85fe407bcacddd2efcd355ada4683aee4 set -x