summaryrefslogtreecommitdiff
path: root/zjit.c
diff options
context:
space:
mode:
Diffstat (limited to 'zjit.c')
-rw-r--r--zjit.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/zjit.c b/zjit.c
new file mode 100644
index 0000000000..f1a02864af
--- /dev/null
+++ b/zjit.c
@@ -0,0 +1,255 @@
+#include "internal.h"
+#include "internal/sanitizers.h"
+#include "internal/string.h"
+#include "internal/hash.h"
+#include "internal/variable.h"
+#include "internal/compile.h"
+#include "internal/class.h"
+#include "internal/fixnum.h"
+#include "internal/numeric.h"
+#include "internal/gc.h"
+#include "internal/vm.h"
+#include "yjit.h"
+#include "vm_core.h"
+#include "vm_callinfo.h"
+#include "builtin.h"
+#include "insns.inc"
+#include "insns_info.inc"
+#include "zjit.h"
+#include "vm_insnhelper.h"
+#include "probes.h"
+#include "probes_helper.h"
+#include "constant.h"
+#include "iseq.h"
+#include "ruby/debug.h"
+#include "internal/cont.h"
+
+// This build config impacts the pointer tagging scheme and we only want to
+// support one scheme for simplicity.
+STATIC_ASSERT(pointer_tagging_scheme, USE_FLONUM);
+
+enum zjit_struct_offsets {
+ ISEQ_BODY_OFFSET_PARAM = offsetof(struct rb_iseq_constant_body, param)
+};
+
+// Special JITFrame used by all C method calls. We don't control the native
+// stack layout for C frames, so cfp->jit_return points at this static frame
+// via the ZJIT_JIT_RETURN_C_FRAME sentinel instead of a per-call allocation.
+const zjit_jit_frame_t rb_zjit_c_frame = (zjit_jit_frame_t) {
+ .pc = 0,
+ .iseq = 0,
+ .materialize_block_code = false,
+};
+
+void rb_zjit_profile_disable(const rb_iseq_t *iseq);
+
+void
+rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception)
+{
+ RB_VM_LOCKING() {
+ rb_vm_barrier();
+
+ // Compile a block version starting at the current instruction
+ uint8_t *rb_zjit_iseq_gen_entry_point(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception); // defined in Rust
+ uintptr_t code_ptr = (uintptr_t)rb_zjit_iseq_gen_entry_point(iseq, ec, jit_exception);
+
+ if (jit_exception) {
+ iseq->body->jit_exception = (rb_jit_func_t)code_ptr;
+ }
+ else {
+ iseq->body->jit_entry = (rb_jit_func_t)code_ptr;
+ }
+ }
+}
+
+extern VALUE *rb_vm_base_ptr(struct rb_control_frame_struct *cfp);
+
+bool
+rb_zjit_constcache_shareable(const struct iseq_inline_constant_cache_entry *ice)
+{
+ return (ice->flags & IMEMO_CONST_CACHE_SHAREABLE) != 0;
+}
+
+// Convert a given ISEQ's instructions to zjit_* instructions
+void
+rb_zjit_profile_enable(const rb_iseq_t *iseq)
+{
+ // This table encodes an opcode into the instruction's address
+ const void *const *insn_table = rb_vm_get_insns_address_table();
+
+ unsigned int insn_idx = 0;
+ while (insn_idx < iseq->body->iseq_size) {
+ int insn = rb_vm_insn_addr2opcode((void *)iseq->body->iseq_encoded[insn_idx]);
+ int zjit_insn = vm_bare_insn_to_zjit_insn(insn);
+ if (insn != zjit_insn) {
+ iseq->body->iseq_encoded[insn_idx] = (VALUE)insn_table[zjit_insn];
+ }
+ insn_idx += insn_len(insn);
+ }
+}
+
+// Convert a given ISEQ's ZJIT instructions to bare instructions
+void
+rb_zjit_profile_disable(const rb_iseq_t *iseq)
+{
+ // This table encodes an opcode into the instruction's address
+ const void *const *insn_table = rb_vm_get_insns_address_table();
+
+ unsigned int insn_idx = 0;
+ while (insn_idx < iseq->body->iseq_size) {
+ int insn = rb_vm_insn_addr2opcode((void *)iseq->body->iseq_encoded[insn_idx]);
+ int bare_insn = vm_zjit_insn_to_bare_insn(insn);
+ if (insn != bare_insn) {
+ iseq->body->iseq_encoded[insn_idx] = (VALUE)insn_table[bare_insn];
+ }
+ insn_idx += insn_len(insn);
+ }
+}
+
+// Update a YARV instruction to a given opcode (to disable ZJIT profiling).
+void
+rb_zjit_iseq_insn_set(const rb_iseq_t *iseq, unsigned int insn_idx, enum ruby_vminsn_type bare_insn)
+{
+#if RUBY_DEBUG
+ int insn = rb_vm_insn_addr2opcode((void *)iseq->body->iseq_encoded[insn_idx]);
+ RUBY_ASSERT(vm_zjit_insn_to_bare_insn(insn) == (int)bare_insn);
+#endif
+ const void *const *insn_table = rb_vm_get_insns_address_table();
+ iseq->body->iseq_encoded[insn_idx] = (VALUE)insn_table[bare_insn];
+}
+
+// Get profiling information for ISEQ
+void *
+rb_iseq_get_zjit_payload(const rb_iseq_t *iseq)
+{
+ RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(iseq, imemo_iseq));
+ if (iseq->body) {
+ return iseq->body->zjit_payload;
+ }
+ else {
+ // Body is NULL when constructing the iseq.
+ return NULL;
+ }
+}
+
+// Set profiling information for ISEQ
+void
+rb_iseq_set_zjit_payload(const rb_iseq_t *iseq, void *payload)
+{
+ RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(iseq, imemo_iseq));
+ RUBY_ASSERT_ALWAYS(iseq->body);
+ RUBY_ASSERT_ALWAYS(NULL == iseq->body->zjit_payload);
+ iseq->body->zjit_payload = payload;
+}
+
+void
+rb_zjit_print_exception(void)
+{
+ VALUE exception = rb_errinfo();
+ rb_set_errinfo(Qnil);
+ assert(RTEST(exception));
+ rb_warn("Ruby error: %"PRIsVALUE"", rb_funcall(exception, rb_intern("full_message"), 0));
+}
+
+bool
+rb_zjit_singleton_class_p(VALUE klass)
+{
+ return RCLASS_SINGLETON_P(klass);
+}
+
+VALUE
+rb_zjit_defined_ivar(VALUE obj, ID id, VALUE pushval)
+{
+ VALUE result = rb_ivar_defined(obj, id);
+ return result ? pushval : Qnil;
+}
+
+bool
+rb_zjit_method_tracing_currently_enabled(void)
+{
+ rb_event_flag_t tracing_events;
+ if (rb_multi_ractor_p()) {
+ tracing_events = ruby_vm_event_enabled_global_flags;
+ }
+ else {
+ // At the time of writing, events are never removed from
+ // ruby_vm_event_enabled_global_flags so always checking using it would
+ // mean we don't compile even after tracing is disabled.
+ tracing_events = rb_ec_ractor_hooks(GET_EC())->events;
+ }
+
+ return tracing_events & (RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN);
+}
+
+// Check if any ISEQ trace events are currently enabled.
+// Used to prevent ZJIT from compiling while tracing is active, since ZJIT's
+// send fallback (rb_vm_opt_send_without_block) uses VM_EXEC which sets
+// VM_FRAME_FLAG_FINISH on the callee frame, changing exception handling
+// semantics for throw TAG_RETURN (e.g. return from rescue).
+bool
+rb_zjit_iseq_tracing_currently_enabled(void)
+{
+ rb_event_flag_t tracing_events;
+ if (rb_multi_ractor_p()) {
+ tracing_events = ruby_vm_event_enabled_global_flags;
+ }
+ else {
+ tracing_events = rb_ec_ractor_hooks(GET_EC())->events;
+ }
+
+ return tracing_events & ISEQ_TRACE_EVENTS;
+}
+
+bool
+rb_zjit_insn_leaf(int insn, const VALUE *opes)
+{
+ return insn_leaf(insn, opes);
+}
+
+ID
+rb_zjit_local_id(const rb_iseq_t *iseq, unsigned idx)
+{
+ return ISEQ_BODY(iseq)->local_table[idx];
+}
+
+bool rb_zjit_cme_is_cfunc(const rb_callable_method_entry_t *me, const void *func);
+
+const struct rb_callable_method_entry_struct *
+rb_zjit_vm_search_method(VALUE cd_owner, struct rb_call_data *cd, VALUE recv);
+
+bool
+rb_zjit_class_initialized_p(VALUE klass)
+{
+ return RCLASS_INITIALIZED_P(klass);
+}
+
+rb_alloc_func_t rb_zjit_class_get_alloc_func(VALUE klass);
+
+VALUE rb_class_allocate_instance(VALUE klass);
+
+bool
+rb_zjit_class_has_default_allocator(VALUE klass)
+{
+ assert(RCLASS_INITIALIZED_P(klass));
+ assert(!RCLASS_SINGLETON_P(klass));
+ rb_alloc_func_t alloc = rb_zjit_class_get_alloc_func(klass);
+ return alloc == rb_class_allocate_instance;
+}
+
+
+VALUE rb_vm_untag_block_handler(VALUE block_handler);
+VALUE rb_vm_get_untagged_block_handler(rb_control_frame_t *reg_cfp);
+
+// Primitives used by zjit.rb. Don't put other functions below, which wouldn't use them.
+VALUE rb_zjit_enable(rb_execution_context_t *ec, VALUE self);
+VALUE rb_zjit_assert_compiles(rb_execution_context_t *ec, VALUE self);
+VALUE rb_zjit_stats(rb_execution_context_t *ec, VALUE self, VALUE target_key);
+VALUE rb_zjit_reset_stats_bang(rb_execution_context_t *ec, VALUE self);
+VALUE rb_zjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self);
+VALUE rb_zjit_print_stats_p(rb_execution_context_t *ec, VALUE self);
+VALUE rb_zjit_get_stats_file_path_p(rb_execution_context_t *ec, VALUE self);
+VALUE rb_zjit_trace_exit_locations_enabled_p(rb_execution_context_t *ec, VALUE self);
+VALUE rb_zjit_get_exit_locations(rb_execution_context_t *ec, VALUE self);
+
+// Preprocessed zjit.rb generated during build
+#include "zjit.rbinc"