From 998bdb0a2f53313d0c81191b08e0d668e80cd9f1 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Mon, 24 Oct 2022 22:23:48 +0100 Subject: [PATCH] Add configure option to enable experimental LLVM JIT compilation --- configure.ac | 13 +++++ src/jit/jit-core.c | 2 + src/jit/jit-exits.c | 18 +++++++ src/jit/jit-llvm.c | 129 +++++++++++++++++++++++++++++++++++++++++--- src/nvc.c | 12 +++-- src/symbols.txt | 2 + src/util.h | 14 +++++ 7 files changed, 180 insertions(+), 10 deletions(-) diff --git a/configure.ac b/configure.ac index 17624160..55c57f0b 100644 --- a/configure.ac +++ b/configure.ac @@ -141,6 +141,19 @@ else AM_CONDITIONAL([LLVM_STATIC], [false]) fi +AC_ARG_ENABLE([jit], + [AS_HELP_STRING([--enable-jit], [Enable JIT complilation with LLVM (EXPERIMENTAL)])], + [enable_llvm=$enableval], + [enable_llvm=no]) + +if test "$enable_jit" = "yes"; then + if test "$enable_llvm" != "yes"; then + AC_MSG_ERROR([JIT compilation depends on LLVM]) + fi + + AC_DEFINE_UNQUOTED([ENABLE_JIT], [1], [JIT compilation enabled]) +fi + PKG_CHECK_MODULES([check], [check >= 0.9.4], [], [AC_MSG_WARN(libcheck not found - unit tests will not run)]) diff --git a/src/jit/jit-core.c b/src/jit/jit-core.c index 1e5e37e3..a63e7621 100644 --- a/src/jit/jit-core.c +++ b/src/jit/jit-core.c @@ -919,6 +919,8 @@ void jit_tier_up(jit_func_t *f) void jit_add_tier(jit_t *j, int threshold, const jit_plugin_t *plugin) { + assert(threshold > 0); + jit_tier_t *t = xcalloc(sizeof(jit_tier_t)); t->next = j->tiers; t->threshold = threshold; diff --git a/src/jit/jit-exits.c b/src/jit/jit-exits.c index 4ea17910..b95cb900 100644 --- a/src/jit/jit-exits.c +++ b/src/jit/jit-exits.c @@ -1427,3 +1427,21 @@ jit_handle_t __nvc_get_handle(const char *func, ffi_spec_t spec) return handle; } + +DLLEXPORT +void *__nvc_getpriv(jit_handle_t handle) +{ + jit_t *j = jit_thread_local()->jit; + jit_func_t *f = jit_get_func(j, handle); + + return jit_get_privdata(j, f); +} + +DLLEXPORT +void __nvc_putpriv(jit_handle_t handle, void *data) +{ + jit_t *j = jit_thread_local()->jit; + jit_func_t *f = jit_get_func(j, handle); + + jit_put_privdata(j, f, data); +} diff --git a/src/jit/jit-llvm.c b/src/jit/jit-llvm.c index 44b9365b..a5bb6131 100644 --- a/src/jit/jit-llvm.c +++ b/src/jit/jit-llvm.c @@ -88,6 +88,9 @@ typedef enum { LLVM_MUL_OVERFLOW_U64, LLVM_DO_EXIT, + LLVM_GETPRIV, + LLVM_PUTPRIV, + LLVM_MSPACE_ALLOC, LLVM_LAST_FN, } llvm_fn_t; @@ -341,6 +344,45 @@ static LLVMValueRef cgen_get_fn(cgen_req_t *req, llvm_fn_t which) } break; + case LLVM_GETPRIV: + { + LLVMTypeRef args[] = { req->types[LLVM_INT32] }; + req->fntypes[which] = LLVMFunctionType(req->types[LLVM_PTR], args, + ARRAY_LEN(args), false); + + fn = LLVMAddFunction(req->module, "__nvc_getpriv", + req->fntypes[which]); + } + break; + + case LLVM_PUTPRIV: + { + LLVMTypeRef args[] = { + req->types[LLVM_INT32], + req->types[LLVM_PTR] + }; + req->fntypes[which] = LLVMFunctionType(req->types[LLVM_VOID], args, + ARRAY_LEN(args), false); + + fn = LLVMAddFunction(req->module, "__nvc_putpriv", + req->fntypes[which]); + } + break; + + case LLVM_MSPACE_ALLOC: + { + LLVMTypeRef args[] = { + req->types[LLVM_INT32], + req->types[LLVM_INT32] + }; + req->fntypes[which] = LLVMFunctionType(req->types[LLVM_PTR], args, + ARRAY_LEN(args), false); + + fn = LLVMAddFunction(req->module, "__nvc_mspace_alloc", + req->fntypes[which]); + } + break; + default: fatal_trace("cannot generate prototype for function %d", which); } @@ -410,6 +452,7 @@ static LLVMValueRef cgen_get_value(cgen_req_t *req, cgen_block_t *cgb, return (jit_scalar_t){ .integer = value.label }; */ case JIT_VALUE_EXIT: + case JIT_VALUE_HANDLE: return llvm_int32(req, value.exit); default: fatal_trace("cannot handle value kind %d", value.kind); @@ -428,13 +471,18 @@ static void cgen_coerece(cgen_req_t *req, jit_size_t size, LLVMValueRef *arg1, LLVMTypeRef type1 = LLVMTypeOf(*arg1); LLVMTypeRef type2 = LLVMTypeOf(*arg2); - const int bits1 = LLVMGetIntTypeWidth(type1); - const int bits2 = LLVMGetIntTypeWidth(type2); + if (LLVMGetTypeKind(type1) == LLVMPointerTypeKind) + *arg2 = LLVMBuildIntToPtr(req->builder, *arg2, + req->types[LLVM_PTR], ""); + else { + const int bits1 = LLVMGetIntTypeWidth(type1); + const int bits2 = LLVMGetIntTypeWidth(type2); - if (bits1 < bits2) - *arg1 = LLVMBuildSExt(req->builder, *arg1, type2, ""); - else - *arg2 = LLVMBuildSExt(req->builder, *arg2, type1, ""); + if (bits1 < bits2) + *arg1 = LLVMBuildSExt(req->builder, *arg1, type2, ""); + else + *arg2 = LLVMBuildSExt(req->builder, *arg2, type1, ""); + } } } @@ -621,6 +669,12 @@ static void cgen_op_mul(cgen_req_t *req, cgen_block_t *cgb, jit_ir_t *ir) cgb->outregs[ir->result] = result; } +static void cgen_op_not(cgen_req_t *req, cgen_block_t *cgb, jit_ir_t *ir) +{ + LLVMValueRef arg1 = cgen_get_value(req, cgb, ir->arg1); + cgb->outregs[ir->result] = LLVMBuildNot(req->builder, arg1, ""); +} + static void cgen_op_ret(cgen_req_t *req, jit_ir_t *ir) { LLVMBuildRetVoid(req->builder); @@ -767,6 +821,48 @@ static void cgen_macro_exit(cgen_req_t *req, cgen_block_t *cgb, jit_ir_t *ir) args, ARRAY_LEN(args), ""); } +static void cgen_macro_galloc(cgen_req_t *req, cgen_block_t *cgb, jit_ir_t *ir) +{ + // TODO: use TLAB + + LLVMValueRef fn = cgen_get_fn(req, LLVM_MSPACE_ALLOC); + LLVMValueRef size = cgen_get_value(req, cgb, ir->arg1); + + LLVMValueRef args[] = { + LLVMBuildTrunc(req->builder, size, req->types[LLVM_INT32], ""), + llvm_int32(req, 1), + }; + cgb->outregs[ir->result] = LLVMBuildCall2(req->builder, + req->fntypes[LLVM_MSPACE_ALLOC], + fn, args, ARRAY_LEN(args), ""); +} + +static void cgen_macro_getpriv(cgen_req_t *req, cgen_block_t *cgb, jit_ir_t *ir) +{ + // TODO: this needs some kind of fast-path + + LLVMValueRef fn = cgen_get_fn(req, LLVM_GETPRIV); + + LLVMValueRef args[] = { + cgen_get_value(req, cgb, ir->arg1) + }; + cgb->outregs[ir->result] = LLVMBuildCall2(req->builder, + req->fntypes[LLVM_GETPRIV], + fn, args, ARRAY_LEN(args), ""); +} + +static void cgen_macro_putpriv(cgen_req_t *req, cgen_block_t *cgb, jit_ir_t *ir) +{ + LLVMValueRef fn = cgen_get_fn(req, LLVM_PUTPRIV); + + LLVMValueRef args[] = { + cgen_get_value(req, cgb, ir->arg1), + cgen_get_value(req, cgb, ir->arg2), + }; + LLVMBuildCall2(req->builder, req->fntypes[LLVM_PUTPRIV], + fn, args, ARRAY_LEN(args), ""); +} + static void cgen_ir(cgen_req_t *req, cgen_block_t *cgb, jit_ir_t *ir) { switch (ir->op) { @@ -792,6 +888,9 @@ static void cgen_ir(cgen_req_t *req, cgen_block_t *cgb, jit_ir_t *ir) case J_MUL: cgen_op_mul(req, cgb, ir); break; + case J_NOT: + cgen_op_not(req, cgb, ir); + break; case J_RET: cgen_op_ret(req, ir); break; @@ -827,8 +926,18 @@ static void cgen_ir(cgen_req_t *req, cgen_block_t *cgb, jit_ir_t *ir) case MACRO_EXIT: cgen_macro_exit(req, cgb, ir); break; + case MACRO_GALLOC: + cgen_macro_galloc(req, cgb, ir); + break; + case MACRO_GETPRIV: + cgen_macro_getpriv(req, cgb, ir); + break; + case MACRO_PUTPRIV: + cgen_macro_putpriv(req, cgb, ir); + break; default: - warnf("cannot generate LLVM for %s", jit_op_name(ir->op)); + jit_dump_with_mark(req->func, ir - req->func->irbuf, false); + fatal("cannot generate LLVM for %s", jit_op_name(ir->op)); } } @@ -947,6 +1056,10 @@ static void cgen_reg_types(cgen_req_t *req) cgen_hint_value_size(req, ir->arg2, ir->size, LLVM_INTPTR); break; + case J_NOT: + cgen_force_reg_type(req, ir->result, req->regtypes[ir->arg1.reg]); + break; + case J_CSET: cgen_force_reg_type(req, ir->result, LLVM_INT1); break; @@ -966,6 +1079,7 @@ static void cgen_reg_types(cgen_req_t *req) case J_LEA: case MACRO_GETPRIV: + case MACRO_GALLOC: cgen_force_reg_type(req, ir->result, LLVM_PTR); break; @@ -984,6 +1098,7 @@ static void cgen_reg_types(cgen_req_t *req) case MACRO_EXIT: case MACRO_COPY: + case MACRO_PUTPRIV: break; default: diff --git a/src/nvc.c b/src/nvc.c index 562de409..0a6e0d76 100644 --- a/src/nvc.c +++ b/src/nvc.c @@ -301,14 +301,14 @@ static int elaborate(int argc, char **argv) return EXIT_FAILURE; lib_t work = lib_work(); - NOT_LLVM_ONLY(lib_put_vcode(work, top, vu)); + NOT_AOT_ONLY(lib_put_vcode(work, top, vu)); if (!opt_get_int(OPT_NO_SAVE)) { lib_save(work); progress("saving library"); } - LLVM_ONLY(cgen(top, vu, cover)); + AOT_ONLY(cgen(top, vu, cover)); argc -= next_cmd - 1; argv += next_cmd - 1; @@ -520,7 +520,13 @@ static int run(int argc, char **argv) jit_t *jit = jit_new(); jit_enable_runtime(jit, true); - jit_load_dll(jit, tree_ident(top)); + + AOT_ONLY(jit_load_dll(jit, tree_ident(top))); + +#if defined ENABLE_JIT && defined LLVM_HAS_LLJIT + extern const jit_plugin_t jit_llvm; + jit_add_tier(jit, 1, &jit_llvm); +#endif _std_standard_init(); _std_env_init(); diff --git a/src/symbols.txt b/src/symbols.txt index 15b020cf..3bc81e0e 100644 --- a/src/symbols.txt +++ b/src/symbols.txt @@ -42,6 +42,7 @@ __nvc_flush; __nvc_force; __nvc_get_handle; + __nvc_getpriv; __nvc_index_fail; __nvc_length_fail; __nvc_map_const; @@ -51,6 +52,7 @@ __nvc_overflow; __nvc_pop_scope; __nvc_push_scope; + __nvc_putpriv; __nvc_range_fail; __nvc_release; __nvc_report; diff --git a/src/util.h b/src/util.h index 592d6802..96c345a3 100644 --- a/src/util.h +++ b/src/util.h @@ -89,6 +89,20 @@ #define NOT_LLVM_ONLY(x) x #endif +#ifdef ENABLE_JIT +#define JIT_ONLY(x) x +#else +#define JIT_ONLY(x) +#endif + +#if defined ENABLE_LLVM && !defined ENABLE_JIT +#define AOT_ONLY(x) x +#define NOT_AOT_ONLY(x) +#else +#define AOT_ONLY(x) +#define NOT_AOT_ONLY(x) x +#endif + #define UNUSED __attribute__((unused)) #define LCOV_EXCL_LINE -- 2.39.2