summaryrefslogtreecommitdiff
path: root/mjit.h
diff options
context:
space:
mode:
Diffstat (limited to 'mjit.h')
-rw-r--r--mjit.h138
1 files changed, 138 insertions, 0 deletions
diff --git a/mjit.h b/mjit.h
new file mode 100644
index 0000000000..452110e6ec
--- /dev/null
+++ b/mjit.h
@@ -0,0 +1,138 @@
+/**********************************************************************
+
+ mjit.h - Interface to MRI method JIT compiler
+
+ Copyright (C) 2017 Vladimir Makarov <vmakarov@redhat.com>.
+
+**********************************************************************/
+
+#ifndef RUBY_MJIT_H
+#define RUBY_MJIT_H 1
+
+#include "ruby.h"
+
+/* Special address values of a function generated from the
+ corresponding iseq by MJIT: */
+enum rb_mjit_iseq_func {
+ /* ISEQ was not queued yet for the machine code generation */
+ NOT_ADDED_JIT_ISEQ_FUNC = 0,
+ /* ISEQ is already queued for the machine code generation but the
+ code is not ready yet for the execution */
+ NOT_READY_JIT_ISEQ_FUNC = 1,
+ /* ISEQ included not compilable insn or some assertion failed */
+ NOT_COMPILABLE_JIT_ISEQ_FUNC = 2,
+ /* End mark */
+ LAST_JIT_ISEQ_FUNC = 3,
+};
+
+/* C compiler used to generate native code. */
+enum rb_mjit_cc {
+ /* Not selected */
+ MJIT_CC_DEFAULT = 0,
+ /* GNU Compiler Collection */
+ MJIT_CC_GCC = 1,
+ /* LLVM/Clang */
+ MJIT_CC_CLANG = 2,
+};
+
+/* MJIT options which can be defined on the MRI command line. */
+struct mjit_options {
+ char on; /* flag of MJIT usage */
+ /* Default: clang for macOS, cl for Windows, gcc for others. */
+ enum rb_mjit_cc cc;
+ /* Save temporary files after MRI finish. The temporary files
+ include the pre-compiled header, C code file generated for ISEQ,
+ and the corresponding object file. */
+ char save_temps;
+ /* Print MJIT warnings to stderr. */
+ char warnings;
+ /* Disable compiler optimization and add debug symbols. It can be
+ very slow. */
+ char debug;
+ /* If not 0, all ISeqs are synchronously compiled. For testing. */
+ unsigned int wait;
+ /* Number of calls to trigger JIT compilation. For testing. */
+ unsigned int min_calls;
+ /* Force printing info about MJIT work of level VERBOSE or
+ less. 0=silence, 1=medium, 2=verbose. */
+ int verbose;
+ /* Maximal permitted number of iseq JIT codes in a MJIT memory
+ cache. */
+ int max_cache_size;
+};
+
+typedef VALUE (*mjit_func_t)(rb_execution_context_t *, rb_control_frame_t *);
+
+RUBY_SYMBOL_EXPORT_BEGIN
+extern struct mjit_options mjit_opts;
+extern int mjit_init_p;
+
+extern void mjit_add_iseq_to_process(const rb_iseq_t *iseq);
+extern mjit_func_t mjit_get_iseq_func(const struct rb_iseq_constant_body *body);
+RUBY_SYMBOL_EXPORT_END
+
+extern int mjit_compile(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname);
+extern void mjit_init(struct mjit_options *opts);
+extern void mjit_finish(void);
+extern void mjit_gc_start_hook(void);
+extern void mjit_gc_finish_hook(void);
+extern void mjit_free_iseq(const rb_iseq_t *iseq);
+extern void mjit_mark(void);
+extern struct mjit_cont *mjit_cont_new(rb_execution_context_t *ec);
+extern void mjit_cont_free(struct mjit_cont *cont);
+
+/* A threshold used to reject long iseqs from JITting as such iseqs
+ takes too much time to be compiled. */
+#define JIT_ISEQ_SIZE_THRESHOLD 1000
+
+/* Return TRUE if given ISeq body should be compiled by MJIT */
+static inline int
+mjit_target_iseq_p(struct rb_iseq_constant_body *body)
+{
+ return (body->type == ISEQ_TYPE_METHOD || body->type == ISEQ_TYPE_BLOCK)
+ && body->iseq_size < JIT_ISEQ_SIZE_THRESHOLD;
+}
+
+/* Try to execute the current iseq in ec. Use JIT code if it is ready.
+ If it is not, add ISEQ to the compilation queue and return Qundef. */
+static inline VALUE
+mjit_exec(rb_execution_context_t *ec)
+{
+ const rb_iseq_t *iseq;
+ struct rb_iseq_constant_body *body;
+ long unsigned total_calls;
+ mjit_func_t func;
+
+ if (!mjit_init_p)
+ return Qundef;
+
+ iseq = ec->cfp->iseq;
+ body = iseq->body;
+ total_calls = ++body->total_calls;
+
+ func = body->jit_func;
+ if (UNLIKELY(mjit_opts.wait && mjit_opts.min_calls == total_calls && mjit_target_iseq_p(body)
+ && (enum rb_mjit_iseq_func)func == NOT_ADDED_JIT_ISEQ_FUNC)) {
+ mjit_add_iseq_to_process(iseq);
+ func = mjit_get_iseq_func(body);
+ }
+
+ if (UNLIKELY((ptrdiff_t)func <= (ptrdiff_t)LAST_JIT_ISEQ_FUNC)) {
+ switch ((enum rb_mjit_iseq_func)func) {
+ case NOT_ADDED_JIT_ISEQ_FUNC:
+ if (total_calls == mjit_opts.min_calls && mjit_target_iseq_p(body)) {
+ mjit_add_iseq_to_process(iseq);
+ }
+ return Qundef;
+ case NOT_READY_JIT_ISEQ_FUNC:
+ case NOT_COMPILABLE_JIT_ISEQ_FUNC:
+ return Qundef;
+ default: /* to avoid warning with LAST_JIT_ISEQ_FUNC */
+ break;
+ }
+ }
+
+ return func(ec, ec->cfp);
+}
+
+#endif /* RUBY_MJIT_H */