From 06f4b7525e3888d7d7be4ec1d5fc2df00bc02924 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Tue, 1 Nov 2022 11:47:34 +0000 Subject: [PATCH] Add option to configure LLVM JIT threshold --- src/jit/Makemodule.am | 2 +- src/jit/jit-interp.c | 22 ---------- src/jit/jit-llvm.c | 99 +++++++++++++++++++++++++++++++++---------- src/jit/jit-llvm.h | 32 ++++++++++++++ src/nvc.c | 4 +- src/opt.c | 1 + src/opt.h | 1 + src/prim.h | 4 ++ src/util.c | 11 ++--- test/jitperf.c | 17 +++----- 10 files changed, 131 insertions(+), 62 deletions(-) create mode 100644 src/jit/jit-llvm.h diff --git a/src/jit/Makemodule.am b/src/jit/Makemodule.am index 1e27fa58..2db70b83 100644 --- a/src/jit/Makemodule.am +++ b/src/jit/Makemodule.am @@ -11,5 +11,5 @@ lib_libnvc_a_SOURCES += \ src/jit/jit-ffi.c if ENABLE_LLVM -lib_libcgen_a_SOURCES += src/jit/jit-llvm.c +lib_libcgen_a_SOURCES += src/jit/jit-llvm.c src/jit/jit-llvm.h endif diff --git a/src/jit/jit-interp.c b/src/jit/jit-interp.c index 9db82d8f..4931ac9e 100644 --- a/src/jit/jit-interp.c +++ b/src/jit/jit-interp.c @@ -406,24 +406,6 @@ static void interp_store(jit_interp_t *state, jit_ir_t *ir) } } -#ifdef DEBUG -static void interp_check_poison(jit_interp_t *state, jit_reg_t reg) -{ - bool all_ff = true; - for (int i = 56; i >= 0; i -= 8) { - const uint8_t byte = (state->regs[reg].integer >> i) & 0xff; - if (all_ff && byte == 0xff && i > 0) - continue; - else if (byte != 0xde) - return; - all_ff = false; - } - - interp_dump(state); - warnf("loaded poison value in R%d", reg); -} -#endif - static void interp_uload(jit_interp_t *state, jit_ir_t *ir) { jit_scalar_t arg1 = interp_get_value(state, ir->arg1); @@ -448,8 +430,6 @@ static void interp_uload(jit_interp_t *state, jit_ir_t *ir) case JIT_SZ_UNSPEC: break; } - - DEBUG_ONLY(interp_check_poison(state, ir->result)); } static void interp_load(jit_interp_t *state, jit_ir_t *ir) @@ -476,8 +456,6 @@ static void interp_load(jit_interp_t *state, jit_ir_t *ir) case JIT_SZ_UNSPEC: break; } - - DEBUG_ONLY(interp_check_poison(state, ir->result)); } static void interp_cmp(jit_interp_t *state, jit_ir_t *ir) diff --git a/src/jit/jit-llvm.c b/src/jit/jit-llvm.c index 3d6a30bc..e6a10d2d 100644 --- a/src/jit/jit-llvm.c +++ b/src/jit/jit-llvm.c @@ -1510,8 +1510,6 @@ static void cgen_module(cgen_req_t *req) jit_cfg_t *cfg = req->cfg = jit_get_cfg(req->func); cgen_basic_blocks(req, cfg); - jit_dump(req->func); - cgen_block_t *cgb = req->blocks; int maxin = 0; @@ -1640,6 +1638,26 @@ static void cgen_optimise(cgen_req_t *req) cgen_dump_module(req, "final"); } +static LLVMTargetMachineRef llvm_target_machine(void) +{ + static __thread LLVMTargetMachineRef tm_ref = NULL; + if (tm_ref == NULL) { + char *def_triple = LLVMGetDefaultTargetTriple(); + char *error; + LLVMTargetRef target_ref; + if (LLVMGetTargetFromTriple(def_triple, &target_ref, &error)) + fatal("failed to get LLVM target for %s: %s", def_triple, error); + + tm_ref = LLVMCreateTargetMachine(target_ref, def_triple, "", "", + LLVMCodeGenLevelDefault, + LLVMRelocDefault, + LLVMCodeModelJITDefault); + LLVMDisposeMessage(def_triple); + } + + return tm_ref; +} + static void *jit_llvm_init(void) { lljit_state_t *state = xcalloc(sizeof(lljit_state_t)); @@ -1676,31 +1694,14 @@ static void jit_llvm_cgen(jit_t *j, jit_handle_t handle, void *context) if (only != NULL && !icmp(f->name, only)) return; - printf("LLVM compile %s\n", istr(f->name)); - const uint64_t start_us = get_timestamp_us(); - static __thread LLVMTargetMachineRef tm_ref = NULL; - if (tm_ref == NULL) { - char *def_triple = LLVMGetDefaultTargetTriple(); - char *error; - LLVMTargetRef target_ref; - if (LLVMGetTargetFromTriple(def_triple, &target_ref, &error)) - fatal("failed to get LLVM target for %s: %s", def_triple, error); - - tm_ref = LLVMCreateTargetMachine(target_ref, def_triple, "", "", - LLVMCodeGenLevelDefault, - LLVMRelocDefault, - LLVMCodeModelJITDefault); - LLVMDisposeMessage(def_triple); - } - LOCAL_TEXT_BUF tb = tb_new(); tb_istr(tb, f->name); cgen_req_t req = { .context = LLVMOrcThreadSafeContextGetContext(state->context), - .target = tm_ref, + .target = llvm_target_machine(), .name = tb_claim(tb), .func = f, .textbuf = tb_new(), @@ -1716,7 +1717,10 @@ static void jit_llvm_cgen(jit_t *j, jit_handle_t handle, void *context) LLVM_CHECK(LLVMOrcLLJITLookup, state->jit, &addr, req.name); const uint64_t end_us = get_timestamp_us(); - debugf("%s at %p [%"PRIi64" us]", req.name, (void *)addr, end_us - start_us); + static __thread uint64_t slowest = 0; + if (end_us - start_us > slowest) + debugf("%s at %p [%"PRIi64" us]", req.name, (void *)addr, + (slowest = end_us - start_us)); atomic_store(&f->entry, (jit_entry_fn_t)addr); @@ -1734,10 +1738,61 @@ static void jit_llvm_cleanup(void *context) free(state); } -const jit_plugin_t jit_llvm = { +static const jit_plugin_t jit_llvm = { .init = jit_llvm_init, .cgen = jit_llvm_cgen, .cleanup = jit_llvm_cleanup }; +void jit_register_llvm_plugin(jit_t *j) +{ + const int threshold = opt_get_int(OPT_JIT_THRESHOLD); + if (threshold > 0) { + extern const jit_plugin_t jit_llvm; + jit_add_tier(j, threshold, &jit_llvm); + } + else if (threshold < 0) + warnf("invalid NVC_JIT_THRESOLD setting %d", threshold); +} + +static inline LLVMContextRef llvm_context(void) +{ + static __thread LLVMContextRef context = NULL; + return context ?: (context = LLVMContextCreate()); +} + +LLVMModuleRef jit_llvm_for_aot(jit_t *j, jit_handle_t handle) +{ + jit_func_t *f = jit_get_func(j, handle); + if (f->irbuf == NULL) + jit_irgen(f); + + const uint64_t start_us = get_timestamp_us(); + + LOCAL_TEXT_BUF tb = tb_new(); + tb_istr(tb, f->name); + + cgen_req_t req = { + .context = llvm_context(), + .target = llvm_target_machine(), + .name = tb_claim(tb), + .textbuf = tb_new(), + .func = f, + }; + + cgen_module(&req); + cgen_optimise(&req); + + const uint64_t end_us = get_timestamp_us(); + static __thread uint64_t slowest = 0; + if (end_us - start_us > slowest) + debugf("compiled %s [%"PRIi64" us]", req.name, + (slowest = end_us - start_us)); + + tb_free(req.textbuf); + free(req.name); + + return req.module; +} + #endif // LLVM_HAS_LLJIT diff --git a/src/jit/jit-llvm.h b/src/jit/jit-llvm.h new file mode 100644 index 00000000..96cd2390 --- /dev/null +++ b/src/jit/jit-llvm.h @@ -0,0 +1,32 @@ +// +// Copyright (C) 2022 Nick Gasson +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef _JIT_LLVM_H +#define _JIT_LLVM_H + +#include "prim.h" +#include "jit/jit.h" + +#if defined LLVM_HAS_LLJIT +void jit_register_llvm_plugin(jit_t *j); +#endif + +typedef struct LLVMOpaqueModule *LLVMModuleRef; + +LLVMModuleRef jit_llvm_for_aot(jit_t *j, jit_handle_t handle); + +#endif // _JIT_LLVM_H diff --git a/src/nvc.c b/src/nvc.c index d174d32f..e224cc8d 100644 --- a/src/nvc.c +++ b/src/nvc.c @@ -20,6 +20,7 @@ #include "diag.h" #include "eval.h" #include "jit/jit.h" +#include "jit/jit-llvm.h" #include "lib.h" #include "opt.h" #include "phase.h" @@ -524,8 +525,7 @@ static int run(int argc, char **argv) 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); + jit_register_llvm_plugin(jit); #endif _std_standard_init(); diff --git a/src/opt.c b/src/opt.c index ed14898e..fc3582ba 100644 --- a/src/opt.c +++ b/src/opt.c @@ -139,4 +139,5 @@ void set_default_options(void) opt_set_int(OPT_WARN_HIDDEN, 0); opt_set_int(OPT_NO_SAVE, 0); opt_set_str(OPT_LLVM_VERBOSE, getenv("NVC_LLVM_VERBOSE")); + opt_set_int(OPT_JIT_THRESHOLD, atoi(getenv("NVC_JIT_THRESHOLD") ?: "100")); } diff --git a/src/opt.h b/src/opt.h index e8d4964d..c382f955 100644 --- a/src/opt.h +++ b/src/opt.h @@ -56,6 +56,7 @@ typedef enum { OPT_WARN_HIDDEN, OPT_NO_SAVE, OPT_LLVM_VERBOSE, + OPT_JIT_THRESHOLD, OPT_LAST_NAME } opt_name_t; diff --git a/src/prim.h b/src/prim.h index aec96a48..0d33217b 100644 --- a/src/prim.h +++ b/src/prim.h @@ -18,6 +18,10 @@ #ifndef _PRIM_H #define _PRIM_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/src/util.c b/src/util.c index 3ade4d93..a14e6171 100644 --- a/src/util.c +++ b/src/util.c @@ -806,9 +806,9 @@ static void signal_handler(int sig, siginfo_t *info, void *context) if (sig != SIGUSR1) _exit(2); -#endif -} #endif // !__SANITIZE_THREAD__ +} +#endif // ! __MINGW32__ void register_signal_handlers(void) { @@ -816,10 +816,11 @@ void register_signal_handlers(void) SetUnhandledExceptionFilter(win32_exception_handler); #else - struct sigaction sa; - sa.sa_sigaction = signal_handler; + struct sigaction sa = { + .sa_sigaction = signal_handler, + .sa_flags = SA_RESTART | SA_SIGINFO + }; sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART | SA_SIGINFO; #ifndef __SANITIZE_THREAD__ sigaction(SIGSEGV, &sa, NULL); diff --git a/test/jitperf.c b/test/jitperf.c index d48ab2c9..6a2ffee7 100644 --- a/test/jitperf.c +++ b/test/jitperf.c @@ -20,6 +20,7 @@ #include "eval.h" #include "ident.h" #include "jit/jit.h" +#include "jit/jit-llvm.h" #include "lib.h" #include "opt.h" #include "phase.h" @@ -50,7 +51,7 @@ static void print_result(double ops_sec, double usec_op) printf("%.1f ops/s; %.1f us/op\n", ops_sec, usec_op); } -static void run_benchmark(tree_t pack, tree_t proc, bool interpret) +static void run_benchmark(tree_t pack, tree_t proc) { color_printf("$!magenta$## %s$$\n\n", istr(tree_ident(proc))); @@ -59,10 +60,7 @@ static void run_benchmark(tree_t pack, tree_t proc, bool interpret) jit_t *j = jit_new(); #ifdef LLVM_HAS_LLJIT - if (!interpret) { - extern const jit_plugin_t jit_llvm; - jit_add_tier(j, 100, &jit_llvm); - } + jit_register_llvm_plugin(j); #endif jit_handle_t hpack = jit_compile(j, tree_ident(pack)); @@ -105,7 +103,7 @@ static void run_benchmark(tree_t pack, tree_t proc, bool interpret) jit_free(j); } -static void find_benchmarks(tree_t pack, const char *filter, bool interpret) +static void find_benchmarks(tree_t pack, const char *filter) { ident_t test_i = ident_new("TEST_"); @@ -118,7 +116,7 @@ static void find_benchmarks(tree_t pack, const char *filter, bool interpret) ident_t id = tree_ident(d); if (ident_starts_with(id, test_i) && (filter == NULL || strcasestr(istr(id), filter) != NULL)) - run_benchmark(pack, d, interpret); + run_benchmark(pack, d); } } @@ -158,7 +156,6 @@ int main(int argc, char **argv) opterr = 0; - bool interpret = false; const char *filter = NULL; int c, index = 0; const char *spec = "L:hf:i"; @@ -177,7 +174,7 @@ int main(int argc, char **argv) filter = optarg; break; case 'i': - interpret = true; + opt_set_int(OPT_JIT_THRESHOLD, 0); break; default: if (optopt == 0) @@ -225,7 +222,7 @@ int main(int argc, char **argv) if (pack == NULL) fatal("no package found in %s", argv[i]); - find_benchmarks(pack, filter, interpret); + find_benchmarks(pack, filter); } eval_free(eval); -- 2.39.2