Ruby: mjit.h Source File

Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)

mjit.h
Go to the documentation of this file.
1#ifndef RUBY_MJIT_H
2#define RUBY_MJIT_H 1
3/**********************************************************************
4
5 mjit.h - Interface to MRI method JIT compiler for Ruby's main thread
6
7 Copyright (C) 2017 Vladimir Makarov <vmakarov@redhat.com>.
8
9**********************************************************************/
10
11#include "ruby/internal/config.h" // defines USE_MJIT
12#include "ruby/internal/stdbool.h"
13#include "vm_core.h"
14
15# if USE_MJIT
16
17#include "debug_counter.h"
18#include "ruby.h"
19
20// Special address values of a function generated from the
21// corresponding iseq by MJIT:
22enum rb_mjit_iseq_func {
23 // ISEQ was not queued yet for the machine code generation
24 NOT_ADDED_JIT_ISEQ_FUNC = 0,
25 // ISEQ is already queued for the machine code generation but the
26 // code is not ready yet for the execution
27 NOT_READY_JIT_ISEQ_FUNC = 1,
28 // ISEQ included not compilable insn, some internal assertion failed
29 // or the unit is unloaded
30 NOT_COMPILED_JIT_ISEQ_FUNC = 2,
31 // End mark
32 LAST_JIT_ISEQ_FUNC = 3
33};
34
35// MJIT options which can be defined on the MRI command line.
36struct mjit_options {
37 // Converted from "jit" feature flag to tell the enablement
38 // information to ruby_show_version().
39 char on;
40 // Save temporary files after MRI finish. The temporary files
41 // include the pre-compiled header, C code file generated for ISEQ,
42 // and the corresponding object file.
43 char save_temps;
44 // Print MJIT warnings to stderr.
45 char warnings;
46 // Disable compiler optimization and add debug symbols. It can be
47 // very slow.
48 char debug;
49 // Add arbitrary cflags.
50 char* debug_flags;
51 // If not 0, all ISeqs are synchronously compiled. For testing.
52 unsigned int wait;
53 // Number of calls to trigger JIT compilation. For testing.
54 unsigned int min_calls;
55 // Force printing info about MJIT work of level VERBOSE or
56 // less. 0=silence, 1=medium, 2=verbose.
57 int verbose;
58 // Maximal permitted number of iseq JIT codes in a MJIT memory
59 // cache.
60 int max_cache_size;
61};
62
63// State of optimization switches
64struct rb_mjit_compile_info {
65 // Disable getinstancevariable/setinstancevariable optimizations based on inline cache (T_OBJECT)
66 bool disable_ivar_cache;
67 // Disable getinstancevariable/setinstancevariable optimizations based on inline cache (FL_EXIVAR)
68 bool disable_exivar_cache;
69 // Disable send/opt_send_without_block optimizations based on inline cache
70 bool disable_send_cache;
71 // Disable method inlining
72 bool disable_inlining;
73 // Disable opt_getinlinecache inlining
74 bool disable_const_cache;
75};
76
77typedef VALUE (*mjit_func_t)(rb_execution_context_t *, rb_control_frame_t *);
78
79RUBY_SYMBOL_EXPORT_BEGIN
80 RUBY_EXTERN struct mjit_options mjit_opts;
81 RUBY_EXTERN bool mjit_call_p;
82
83extern void rb_mjit_add_iseq_to_process(const rb_iseq_t *iseq);
84extern VALUE rb_mjit_wait_call(rb_execution_context_t *ec, struct rb_iseq_constant_body *body);
85extern struct rb_mjit_compile_info* rb_mjit_iseq_compile_info(const struct rb_iseq_constant_body *body);
86extern void rb_mjit_recompile_send(const rb_iseq_t *iseq);
87extern void rb_mjit_recompile_ivar(const rb_iseq_t *iseq);
88extern void rb_mjit_recompile_exivar(const rb_iseq_t *iseq);
89extern void rb_mjit_recompile_inlining(const rb_iseq_t *iseq);
90extern void rb_mjit_recompile_const(const rb_iseq_t *iseq);
91RUBY_SYMBOL_EXPORT_END
92
93extern bool mjit_compile(FILE *f, const rb_iseq_t *iseq, const char *funcname, int id);
94extern void mjit_init(const struct mjit_options *opts);
95extern void mjit_gc_start_hook(void);
96extern void mjit_gc_exit_hook(void);
97extern void mjit_free_iseq(const rb_iseq_t *iseq);
98extern void mjit_update_references(const rb_iseq_t *iseq);
99extern void mjit_mark(void);
100extern struct mjit_cont *mjit_cont_new(rb_execution_context_t *ec);
101extern void mjit_cont_free(struct mjit_cont *cont);
102extern void mjit_add_class_serial(rb_serial_t class_serial);
103extern void mjit_remove_class_serial(rb_serial_t class_serial);
104extern void mjit_mark_cc_entries(const struct rb_iseq_constant_body *const body);
105
106// A threshold used to reject long iseqs from JITting as such iseqs
107// takes too much time to be compiled.
108#define JIT_ISEQ_SIZE_THRESHOLD 1000
109
110// Return TRUE if given ISeq body should be compiled by MJIT
111static inline int
112mjit_target_iseq_p(struct rb_iseq_constant_body *body)
113{
114 return (body->type == ISEQ_TYPE_METHOD || body->type == ISEQ_TYPE_BLOCK)
115 && !body->builtin_inline_p
116 && body->iseq_size < JIT_ISEQ_SIZE_THRESHOLD;
117}
118
119# ifdef MJIT_HEADER
120 NOINLINE(static COLDFUNC VALUE mjit_exec_slowpath(rb_execution_context_t *ec, const rb_iseq_t *iseq, struct rb_iseq_constant_body *body));
121# else
122static inline VALUE mjit_exec_slowpath(rb_execution_context_t *ec, const rb_iseq_t *iseq, struct rb_iseq_constant_body *body);
123# endif
124static VALUE
125mjit_exec_slowpath(rb_execution_context_t *ec, const rb_iseq_t *iseq, struct rb_iseq_constant_body *body)
126{
127 uintptr_t func_i = (uintptr_t)(body->jit_func);
128 ASSUME(func_i <= LAST_JIT_ISEQ_FUNC);
129 switch ((enum rb_mjit_iseq_func)func_i) {
130 case NOT_ADDED_JIT_ISEQ_FUNC:
131 RB_DEBUG_COUNTER_INC(mjit_exec_not_added);
132 if (body->total_calls == mjit_opts.min_calls && mjit_target_iseq_p(body)) {
133 rb_mjit_add_iseq_to_process(iseq);
134 if (UNLIKELY(mjit_opts.wait)) {
135 return rb_mjit_wait_call(ec, body);
136 }
137 }
138 break;
139 case NOT_READY_JIT_ISEQ_FUNC:
140 RB_DEBUG_COUNTER_INC(mjit_exec_not_ready);
141 break;
142 case NOT_COMPILED_JIT_ISEQ_FUNC:
143 RB_DEBUG_COUNTER_INC(mjit_exec_not_compiled);
144 break;
145 default: // to avoid warning with LAST_JIT_ISEQ_FUNC
146 break;
147 }
148 return Qundef;
149}
150
151// Try to execute the current iseq in ec. Use JIT code if it is ready.
152// If it is not, add ISEQ to the compilation queue and return Qundef.
153static inline VALUE
154mjit_exec(rb_execution_context_t *ec)
155{
156 const rb_iseq_t *iseq;
157 struct rb_iseq_constant_body *body;
158
159 if (!mjit_call_p)
160 return Qundef;
161 RB_DEBUG_COUNTER_INC(mjit_exec);
162
163 iseq = ec->cfp->iseq;
164 body = iseq->body;
165 body->total_calls++;
166
167 mjit_func_t func = body->jit_func;
168 if (UNLIKELY((uintptr_t)func <= LAST_JIT_ISEQ_FUNC)) {
169# ifdef MJIT_HEADER
170 RB_DEBUG_COUNTER_INC(mjit_frame_JT2VM);
171# else
172 RB_DEBUG_COUNTER_INC(mjit_frame_VM2VM);
173# endif
174 return mjit_exec_slowpath(ec, iseq, body);
175 }
176
177# ifdef MJIT_HEADER
178 RB_DEBUG_COUNTER_INC(mjit_frame_JT2JT);
179# else
180 RB_DEBUG_COUNTER_INC(mjit_frame_VM2JT);
181# endif
182 RB_DEBUG_COUNTER_INC(mjit_exec_call_func);
183 return func(ec, ec->cfp);
184}
185
186void mjit_child_after_fork(void);
187
188# ifdef MJIT_HEADER
189#define mjit_enabled true
190# else // MJIT_HEADER
191extern bool mjit_enabled;
192# endif // MJIT_HEADER
193 VALUE mjit_pause(bool wait_p);
194 VALUE mjit_resume(void);
195void mjit_finish(bool close_handle_p);
196
197# else // USE_MJIT
198
199static inline struct mjit_cont *mjit_cont_new(rb_execution_context_t *ec){return NULL;}
200static inline void mjit_cont_free(struct mjit_cont *cont){}
201static inline void mjit_gc_start_hook(void){}
202static inline void mjit_gc_exit_hook(void){}
203static inline void mjit_free_iseq(const rb_iseq_t *iseq){}
204static inline void mjit_mark(void){}
205static inline void mjit_add_class_serial(rb_serial_t class_serial){}
206static inline void mjit_remove_class_serial(rb_serial_t class_serial){}
207static inline VALUE mjit_exec(rb_execution_context_t *ec) { return Qundef; /* unreachable */ }
208static inline void mjit_child_after_fork(void){}
209
210#define mjit_enabled false
211static inline VALUE mjit_pause(bool wait_p){ return Qnil; } // unreachable
212static inline VALUE mjit_resume(void){ return Qnil; } // unreachable
213static inline void mjit_finish(bool close_handle_p){}
214
215# endif // USE_MJIT
216#endif // RUBY_MJIT_H
COLDFUNC
#define COLDFUNC
Definition: attributes.h:110
NOINLINE
#define NOINLINE(x)
Definition: attributes.h:82
ASSUME
#define ASSUME
Definition: assume.h:29
debug_counter.h
RB_DEBUG_COUNTER_INC
#define RB_DEBUG_COUNTER_INC(type)
Definition: debug_counter.h:444
RUBY_EXTERN
#define RUBY_EXTERN
Definition: dllexport.h:36
UNLIKELY
#define UNLIKELY(x)
Definition: ffi_common.h:126
config.h
Thin wrapper to ruby/config.h.
mjit_enabled
#define mjit_enabled
Definition: mjit.h:210
mjit_opts
struct mjit_options mjit_opts
Definition: mjit_worker.c:199
mjit_call_p
bool mjit_call_p
Definition: mjit_worker.c:205
debug
#define debug(lvl, x...)
Definition: ffi.c:52
NULL
#define NULL
Definition: regenc.h:69
rb_serial_t
unsigned LONG_LONG rb_serial_t
Definition: serial.h:19
Qundef
#define Qundef
Definition: special_consts.h:53
Qnil
#define Qnil
Definition: special_consts.h:51
f
#define f
stdbool.h
C99 shim for <stdbool.h>
__sFILE
Definition: vsnprintf.c:169
mjit_cont
Definition: mjit_worker.c:1336
mjit_cont::ec
rb_execution_context_t * ec
Definition: mjit_worker.c:1337
rb_control_frame_struct
Definition: vm_core.h:769
rb_control_frame_struct::iseq
const rb_iseq_t * iseq
Definition: vm_core.h:772
rb_execution_context_struct
Definition: vm_core.h:854
rb_execution_context_struct::cfp
rb_control_frame_t * cfp
Definition: vm_core.h:858
rb_iseq_constant_body
Definition: vm_core.h:305
rb_iseq_constant_body::type
enum rb_iseq_constant_body::iseq_type type
rb_iseq_constant_body::builtin_inline_p
bool builtin_inline_p
Definition: vm_core.h:430
rb_iseq_constant_body::iseq_size
unsigned int iseq_size
Definition: vm_core.h:318
rb_iseq_struct
Definition: vm_core.h:444
rb_iseq_struct::body
struct rb_iseq_constant_body * body
Definition: vm_core.h:448
VALUE
unsigned long VALUE
Definition: value.h:38
vm_core.h
wait
int wait(int *status)
Definition: win32.c:5245
uintptr_t
unsigned int uintptr_t
Definition: win32.h:106