Add zjit_* instructions to profile the interpreter (https://github.com/Shopify/zjit/pull/16)
* Add zjit_* instructions to profile the interpreter * Rename FixnumPlus to FixnumAdd * Update a comment about Invalidate * Rename Guard to GuardType * Rename Invalidate to PatchPoint * Drop unneeded debug!() * Plan on profiling the types * Use the output of GuardType as type refined outputs
This commit is contained in:
parent
30db473389
commit
0a543daf15
Notes:
git
2025-04-18 13:48:27 +00:00
@ -1194,10 +1194,12 @@ $(srcs_vpath)insns.inc: $(tooldir)/ruby_vm/views/insns.inc.erb $(inc_common_head
|
||||
$(srcs_vpath)insns_info.inc: $(tooldir)/ruby_vm/views/insns_info.inc.erb $(inc_common_headers) \
|
||||
$(tooldir)/ruby_vm/views/_insn_type_chars.erb $(tooldir)/ruby_vm/views/_insn_name_info.erb \
|
||||
$(tooldir)/ruby_vm/views/_insn_len_info.erb $(tooldir)/ruby_vm/views/_insn_operand_info.erb \
|
||||
$(tooldir)/ruby_vm/views/_attributes.erb $(tooldir)/ruby_vm/views/_comptime_insn_stack_increase.erb
|
||||
$(tooldir)/ruby_vm/views/_attributes.erb $(tooldir)/ruby_vm/views/_comptime_insn_stack_increase.erb \
|
||||
$(tooldir)/ruby_vm/views/_zjit_helpers.erb
|
||||
$(srcs_vpath)vmtc.inc: $(tooldir)/ruby_vm/views/vmtc.inc.erb $(inc_common_headers)
|
||||
$(srcs_vpath)vm.inc: $(tooldir)/ruby_vm/views/vm.inc.erb $(inc_common_headers) \
|
||||
$(tooldir)/ruby_vm/views/_insn_entry.erb $(tooldir)/ruby_vm/views/_trace_instruction.erb
|
||||
$(tooldir)/ruby_vm/views/_insn_entry.erb $(tooldir)/ruby_vm/views/_trace_instruction.erb \
|
||||
$(tooldir)/ruby_vm/views/_zjit_instruction.erb
|
||||
|
||||
BUILTIN_RB_SRCS = \
|
||||
$(srcdir)/ast.rb \
|
||||
@ -19568,6 +19570,7 @@ vm.$(OBJEXT): {$(VPATH)}vm_sync.h
|
||||
vm.$(OBJEXT): {$(VPATH)}vmtc.inc
|
||||
vm.$(OBJEXT): {$(VPATH)}yjit.h
|
||||
vm.$(OBJEXT): {$(VPATH)}yjit_hook.rbinc
|
||||
vm.$(OBJEXT): {$(VPATH)}zjit.h
|
||||
vm_backtrace.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
|
||||
vm_backtrace.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
||||
vm_backtrace.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
@ -21175,4 +21178,5 @@ zjit.$(OBJEXT): {$(VPATH)}vm_insnhelper.h
|
||||
zjit.$(OBJEXT): {$(VPATH)}vm_opts.h
|
||||
zjit.$(OBJEXT): {$(VPATH)}vm_sync.h
|
||||
zjit.$(OBJEXT): {$(VPATH)}zjit.c
|
||||
zjit.$(OBJEXT): {$(VPATH)}zjit.h
|
||||
# AUTOGENERATED DEPENDENCIES END
|
||||
|
@ -1265,6 +1265,7 @@ opt_plus
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
// attr bool zjit_profile = true;
|
||||
{
|
||||
val = vm_opt_plus(recv, obj);
|
||||
|
||||
|
@ -640,6 +640,7 @@ $(INSNS): $(srcdir)/insns.def vm_opts.h \
|
||||
$(tooldir)/ruby_vm/models/operands_unifications.rb \
|
||||
$(tooldir)/ruby_vm/models/trace_instructions.rb \
|
||||
$(tooldir)/ruby_vm/models/typemap.rb \
|
||||
$(tooldir)/ruby_vm/models/zjit_instructions.rb \
|
||||
$(tooldir)/ruby_vm/scripts/converter.rb \
|
||||
$(tooldir)/ruby_vm/scripts/insns2vm.rb \
|
||||
$(tooldir)/ruby_vm/views/_attributes.erb \
|
||||
@ -655,6 +656,7 @@ $(INSNS): $(srcdir)/insns.def vm_opts.h \
|
||||
$(tooldir)/ruby_vm/views/_notice.erb \
|
||||
$(tooldir)/ruby_vm/views/_sp_inc_helpers.erb \
|
||||
$(tooldir)/ruby_vm/views/_trace_instruction.erb \
|
||||
$(tooldir)/ruby_vm/views/_zjit_instruction.erb \
|
||||
$(tooldir)/ruby_vm/views/insns.inc.erb \
|
||||
$(tooldir)/ruby_vm/views/insns_info.inc.erb \
|
||||
$(tooldir)/ruby_vm/views/optinsn.inc.erb \
|
||||
|
@ -148,6 +148,10 @@ class RubyVM::BareInstructions
|
||||
@variables.find { |_, var_info| var_info[:type] == 'CALL_DATA' }
|
||||
end
|
||||
|
||||
def zjit_profile?
|
||||
@attrs.fetch('zjit_profile').expr.expr != 'false;'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_attribute_consistency
|
||||
@ -186,6 +190,7 @@ class RubyVM::BareInstructions
|
||||
generate_attribute 'rb_snum_t', 'sp_inc', rets.size - pops.size
|
||||
generate_attribute 'bool', 'handles_sp', default_definition_of_handles_sp
|
||||
generate_attribute 'bool', 'leaf', default_definition_of_leaf
|
||||
generate_attribute 'bool', 'zjit_profile', false
|
||||
end
|
||||
|
||||
def default_definition_of_handles_sp
|
||||
|
@ -17,5 +17,6 @@ RubyVM::Instructions = RubyVM::BareInstructions.to_a + \
|
||||
RubyVM::OperandsUnifications.to_a + \
|
||||
RubyVM::InstructionsUnifications.to_a
|
||||
|
||||
require_relative 'zjit_instructions'
|
||||
require_relative 'trace_instructions'
|
||||
RubyVM::Instructions.freeze
|
||||
|
58
tool/ruby_vm/models/zjit_instructions.rb
Normal file
58
tool/ruby_vm/models/zjit_instructions.rb
Normal file
@ -0,0 +1,58 @@
|
||||
require_relative '../helpers/c_escape'
|
||||
require_relative 'bare_instructions'
|
||||
|
||||
# Profile YARV instructions to optimize code generated by ZJIT
|
||||
class RubyVM::ZJITInstructions
|
||||
include RubyVM::CEscape
|
||||
|
||||
attr_reader :name
|
||||
|
||||
def initialize(orig)
|
||||
@orig = orig
|
||||
@name = as_tr_cpp "zjit @ #{@orig.name}"
|
||||
end
|
||||
|
||||
def pretty_name
|
||||
return sprintf "%s(...)(...)(...)", @name
|
||||
end
|
||||
|
||||
def jump_destination
|
||||
return @orig.name
|
||||
end
|
||||
|
||||
def bin
|
||||
return sprintf "BIN(%s)", @name
|
||||
end
|
||||
|
||||
def width
|
||||
return @orig.width
|
||||
end
|
||||
|
||||
def operands_info
|
||||
return @orig.operands_info
|
||||
end
|
||||
|
||||
def rets
|
||||
return ['...']
|
||||
end
|
||||
|
||||
def pops
|
||||
return ['...']
|
||||
end
|
||||
|
||||
def attributes
|
||||
return []
|
||||
end
|
||||
|
||||
def has_attribute?(*)
|
||||
return false
|
||||
end
|
||||
|
||||
@instances = RubyVM::Instructions.filter(&:zjit_profile?).map {|i| new(i) }
|
||||
|
||||
def self.to_a
|
||||
@instances
|
||||
end
|
||||
|
||||
RubyVM::Instructions.push(*to_a)
|
||||
end
|
27
tool/ruby_vm/views/_insn_sp_pc_dependency.erb
Normal file
27
tool/ruby_vm/views/_insn_sp_pc_dependency.erb
Normal file
@ -0,0 +1,27 @@
|
||||
%# -*- C -*-
|
||||
%# Copyright (c) 2019 Takashi Kokubun. All rights reserved.
|
||||
%#
|
||||
%# This file is a part of the programming language Ruby. Permission is hereby
|
||||
%# granted, to either redistribute and/or modify this file, provided that the
|
||||
%# conditions mentioned in the file COPYING are met. Consult the file for
|
||||
%# details.
|
||||
%#
|
||||
PUREFUNC(MAYBE_UNUSED(static bool insn_may_depend_on_sp_or_pc(int insn, const VALUE *opes)));
|
||||
|
||||
static bool
|
||||
insn_may_depend_on_sp_or_pc(int insn, const VALUE *opes)
|
||||
{
|
||||
switch (insn) {
|
||||
% RubyVM::Instructions.each do |insn|
|
||||
% # handles_sp?: If true, it requires to move sp in JIT
|
||||
% # always_leaf?: If false, it may call an arbitrary method. pc should be moved
|
||||
% # before the call, and the method may refer to caller's pc (lineno).
|
||||
% unless !insn.is_a?(RubyVM::TraceInstructions) && !insn.is_a?(RubyVM::ZJITInstructions) && !insn.handles_sp? && insn.always_leaf?
|
||||
case <%= insn.bin %>:
|
||||
% end
|
||||
% end
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
14
tool/ruby_vm/views/_zjit_helpers.erb
Normal file
14
tool/ruby_vm/views/_zjit_helpers.erb
Normal file
@ -0,0 +1,14 @@
|
||||
MAYBE_UNUSED(static int vm_insn_to_zjit_insn(int insn));
|
||||
|
||||
static int
|
||||
vm_insn_to_zjit_insn(int insn)
|
||||
{
|
||||
switch (insn) {
|
||||
% RubyVM::ZJITInstructions.to_a.each do |insn|
|
||||
case BIN(<%= insn.jump_destination %>):
|
||||
return <%= insn.bin %>;
|
||||
% end
|
||||
default:
|
||||
return insn;
|
||||
}
|
||||
}
|
8
tool/ruby_vm/views/_zjit_instruction.erb
Normal file
8
tool/ruby_vm/views/_zjit_instruction.erb
Normal file
@ -0,0 +1,8 @@
|
||||
/* insn <%= insn.pretty_name %> */
|
||||
INSN_ENTRY(<%= insn.name %>)
|
||||
{
|
||||
START_OF_ORIGINAL_INSN(<%= insn.name %>);
|
||||
rb_zjit_profile_insn(BIN(<%= insn.jump_destination %>), ec);
|
||||
DISPATCH_ORIGINAL_INSN(<%= insn.jump_destination %>);
|
||||
END_INSN(<%= insn.name %>);
|
||||
}
|
@ -12,6 +12,9 @@
|
||||
edit: __FILE__,
|
||||
} -%>
|
||||
|
||||
#ifndef INSNS_INC
|
||||
#define INSNS_INC 1
|
||||
|
||||
/* BIN : Basic Instruction Name */
|
||||
#define BIN(n) YARVINSN_##n
|
||||
|
||||
@ -24,3 +27,5 @@ enum ruby_vminsn_type {
|
||||
|
||||
#define ASSERT_VM_INSTRUCTION_SIZE(array) \
|
||||
STATIC_ASSERT(numberof_##array, numberof(array) == VM_INSTRUCTION_SIZE)
|
||||
|
||||
#endif
|
||||
|
@ -17,5 +17,6 @@
|
||||
<%= render 'insn_operand_info' %>
|
||||
<%= render 'leaf_helpers' %>
|
||||
<%= render 'sp_inc_helpers' %>
|
||||
<%= render 'zjit_helpers' %>
|
||||
<%= render 'attributes' %>
|
||||
<%= render 'comptime_insn_stack_increase' %>
|
||||
|
14
tool/ruby_vm/views/lib/ruby_vm/rjit/instruction.rb.erb
Normal file
14
tool/ruby_vm/views/lib/ruby_vm/rjit/instruction.rb.erb
Normal file
@ -0,0 +1,14 @@
|
||||
module RubyVM::RJIT # :nodoc: all
|
||||
Instruction = Data.define(:name, :bin, :len, :operands)
|
||||
|
||||
INSNS = {
|
||||
% RubyVM::Instructions.each_with_index do |insn, i|
|
||||
<%= i %> => Instruction.new(
|
||||
name: :<%= insn.name %>,
|
||||
bin: <%= i %>, # BIN(<%= insn.name %>)
|
||||
len: <%= insn.width %>, # insn_len
|
||||
operands: <%= (insn.operands unless insn.name.start_with?(/trace_|zjit_/)).inspect %>,
|
||||
),
|
||||
% end
|
||||
}
|
||||
end
|
@ -25,6 +25,10 @@
|
||||
<%= render 'insn_entry', locals: { insn: insn } -%>
|
||||
% end
|
||||
%
|
||||
% RubyVM::ZJITInstructions.to_a.each do |insn|
|
||||
<%= render 'zjit_instruction', locals: { insn: insn } -%>
|
||||
% end
|
||||
%
|
||||
% RubyVM::TraceInstructions.to_a.each do |insn|
|
||||
<%= render 'trace_instruction', locals: { insn: insn } -%>
|
||||
% end
|
||||
|
18
vm.c
18
vm.c
@ -44,6 +44,8 @@
|
||||
#include "ractor_core.h"
|
||||
#include "vm_sync.h"
|
||||
#include "shape.h"
|
||||
#include "insns.inc"
|
||||
#include "zjit.h"
|
||||
|
||||
#include "builtin.h"
|
||||
|
||||
@ -434,18 +436,26 @@ jit_compile(rb_execution_context_t *ec)
|
||||
const rb_iseq_t *iseq = ec->cfp->iseq;
|
||||
struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
|
||||
|
||||
// Increment the ISEQ's call counter and trigger JIT compilation if not compiled
|
||||
#if USE_ZJIT
|
||||
extern bool rb_zjit_enabled_p;
|
||||
extern uint64_t rb_zjit_call_threshold;
|
||||
// Number of calls used to profile a YARV instruction for ZJIT
|
||||
#define ZJIT_PROFILE_COUNT 1
|
||||
|
||||
if (body->jit_entry == NULL && rb_zjit_enabled_p) {
|
||||
body->jit_entry_calls++;
|
||||
|
||||
// At call-threshold - ZJIT_PROFILE_COUNT, rewrite some of the YARV
|
||||
// instructions to zjit_* instructions to profile these instructions.
|
||||
if (body->jit_entry_calls + ZJIT_PROFILE_COUNT == rb_zjit_call_threshold) {
|
||||
rb_zjit_profile_iseq(iseq);
|
||||
}
|
||||
|
||||
// At call-threshold, compile the ISEQ with ZJIT.
|
||||
if (body->jit_entry_calls == rb_zjit_call_threshold) {
|
||||
extern void rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception);
|
||||
rb_zjit_compile_iseq(iseq, ec, false);
|
||||
}
|
||||
}
|
||||
#elif USE_YJIT
|
||||
// Increment the ISEQ's call counter and trigger JIT compilation if not compiled
|
||||
if (body->jit_entry == NULL && rb_yjit_enabled_p) {
|
||||
body->jit_entry_calls++;
|
||||
if (rb_yjit_threshold_hit(iseq, body->jit_entry_calls)) {
|
||||
|
@ -318,7 +318,7 @@ fn main() {
|
||||
|
||||
// From yjit.c
|
||||
.allowlist_function("rb_object_shape_count")
|
||||
.allowlist_function("rb_iseq_(get|set)_yjit_payload")
|
||||
.allowlist_function("rb_iseq_(get|set)_zjit_payload")
|
||||
.allowlist_function("rb_iseq_pc_at_idx")
|
||||
.allowlist_function("rb_iseq_opcode_at_pc")
|
||||
.allowlist_function("rb_(yjit|zjit)_reserve_addr_space")
|
||||
|
43
zjit.c
43
zjit.c
@ -22,6 +22,7 @@
|
||||
#include "iseq.h"
|
||||
#include "ruby/debug.h"
|
||||
#include "internal/cont.h"
|
||||
#include "zjit.h"
|
||||
|
||||
// For mmapp(), sysconf()
|
||||
#ifndef _WIN32
|
||||
@ -634,3 +635,45 @@ rb_RCLASS_ORIGIN(VALUE c)
|
||||
{
|
||||
return RCLASS_ORIGIN(c);
|
||||
}
|
||||
|
||||
// Convert a given ISEQ's instructions to zjit_* instructions
|
||||
void
|
||||
rb_zjit_profile_iseq(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_decode(iseq->body->iseq_encoded[insn_idx]);
|
||||
int zjit_insn = vm_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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
19
zjit.h
Normal file
19
zjit.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef ZJIT_H
|
||||
#define ZJIT_H 1
|
||||
//
|
||||
// This file contains definitions ZJIT exposes to the CRuby codebase
|
||||
//
|
||||
|
||||
#if USE_ZJIT
|
||||
extern bool rb_zjit_enabled_p;
|
||||
extern uint64_t rb_zjit_call_threshold;
|
||||
void rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception);
|
||||
void rb_zjit_profile_insn(enum ruby_vminsn_type insn, rb_execution_context_t *ec);
|
||||
void rb_zjit_profile_iseq(const rb_iseq_t *iseq);
|
||||
#else
|
||||
void rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception) {}
|
||||
void rb_zjit_profile_insn(enum ruby_vminsn_type insn, rb_execution_context_t *ec) {}
|
||||
void rb_zjit_profile_iseq(const rb_iseq_t *iseq) {}
|
||||
#endif // #if USE_YJIT
|
||||
|
||||
#endif // #ifndef ZJIT_H
|
@ -597,117 +597,119 @@ pub const YARVINSN_setlocal_WC_0: ruby_vminsn_type = 106;
|
||||
pub const YARVINSN_setlocal_WC_1: ruby_vminsn_type = 107;
|
||||
pub const YARVINSN_putobject_INT2FIX_0_: ruby_vminsn_type = 108;
|
||||
pub const YARVINSN_putobject_INT2FIX_1_: ruby_vminsn_type = 109;
|
||||
pub const YARVINSN_trace_nop: ruby_vminsn_type = 110;
|
||||
pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 111;
|
||||
pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 112;
|
||||
pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 113;
|
||||
pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 114;
|
||||
pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 115;
|
||||
pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 116;
|
||||
pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 117;
|
||||
pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 118;
|
||||
pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 119;
|
||||
pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 120;
|
||||
pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 121;
|
||||
pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 122;
|
||||
pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 123;
|
||||
pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 124;
|
||||
pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 125;
|
||||
pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 126;
|
||||
pub const YARVINSN_trace_putnil: ruby_vminsn_type = 127;
|
||||
pub const YARVINSN_trace_putself: ruby_vminsn_type = 128;
|
||||
pub const YARVINSN_trace_putobject: ruby_vminsn_type = 129;
|
||||
pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 130;
|
||||
pub const YARVINSN_trace_putstring: ruby_vminsn_type = 131;
|
||||
pub const YARVINSN_trace_putchilledstring: ruby_vminsn_type = 132;
|
||||
pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 133;
|
||||
pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 134;
|
||||
pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 135;
|
||||
pub const YARVINSN_trace_intern: ruby_vminsn_type = 136;
|
||||
pub const YARVINSN_trace_newarray: ruby_vminsn_type = 137;
|
||||
pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 138;
|
||||
pub const YARVINSN_trace_duparray: ruby_vminsn_type = 139;
|
||||
pub const YARVINSN_trace_duphash: ruby_vminsn_type = 140;
|
||||
pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 141;
|
||||
pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 142;
|
||||
pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 143;
|
||||
pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 144;
|
||||
pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 145;
|
||||
pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 146;
|
||||
pub const YARVINSN_trace_newhash: ruby_vminsn_type = 147;
|
||||
pub const YARVINSN_trace_newrange: ruby_vminsn_type = 148;
|
||||
pub const YARVINSN_trace_pop: ruby_vminsn_type = 149;
|
||||
pub const YARVINSN_trace_dup: ruby_vminsn_type = 150;
|
||||
pub const YARVINSN_trace_dupn: ruby_vminsn_type = 151;
|
||||
pub const YARVINSN_trace_swap: ruby_vminsn_type = 152;
|
||||
pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 153;
|
||||
pub const YARVINSN_trace_topn: ruby_vminsn_type = 154;
|
||||
pub const YARVINSN_trace_setn: ruby_vminsn_type = 155;
|
||||
pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 156;
|
||||
pub const YARVINSN_trace_defined: ruby_vminsn_type = 157;
|
||||
pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 158;
|
||||
pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 159;
|
||||
pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 160;
|
||||
pub const YARVINSN_trace_checktype: ruby_vminsn_type = 161;
|
||||
pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 162;
|
||||
pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 163;
|
||||
pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 164;
|
||||
pub const YARVINSN_trace_send: ruby_vminsn_type = 165;
|
||||
pub const YARVINSN_trace_sendforward: ruby_vminsn_type = 166;
|
||||
pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 167;
|
||||
pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 168;
|
||||
pub const YARVINSN_trace_opt_ary_freeze: ruby_vminsn_type = 169;
|
||||
pub const YARVINSN_trace_opt_hash_freeze: ruby_vminsn_type = 170;
|
||||
pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 171;
|
||||
pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 172;
|
||||
pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 173;
|
||||
pub const YARVINSN_trace_opt_duparray_send: ruby_vminsn_type = 174;
|
||||
pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 175;
|
||||
pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 176;
|
||||
pub const YARVINSN_trace_invokesuperforward: ruby_vminsn_type = 177;
|
||||
pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 178;
|
||||
pub const YARVINSN_trace_leave: ruby_vminsn_type = 179;
|
||||
pub const YARVINSN_trace_throw: ruby_vminsn_type = 180;
|
||||
pub const YARVINSN_trace_jump: ruby_vminsn_type = 181;
|
||||
pub const YARVINSN_trace_branchif: ruby_vminsn_type = 182;
|
||||
pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 183;
|
||||
pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 184;
|
||||
pub const YARVINSN_trace_once: ruby_vminsn_type = 185;
|
||||
pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 186;
|
||||
pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 187;
|
||||
pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 188;
|
||||
pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 189;
|
||||
pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 190;
|
||||
pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 191;
|
||||
pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 192;
|
||||
pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 193;
|
||||
pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 194;
|
||||
pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 195;
|
||||
pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 196;
|
||||
pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 197;
|
||||
pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 198;
|
||||
pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 199;
|
||||
pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 200;
|
||||
pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 201;
|
||||
pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 202;
|
||||
pub const YARVINSN_trace_opt_aset_with: ruby_vminsn_type = 203;
|
||||
pub const YARVINSN_trace_opt_aref_with: ruby_vminsn_type = 204;
|
||||
pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 205;
|
||||
pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 206;
|
||||
pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 207;
|
||||
pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 208;
|
||||
pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 209;
|
||||
pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 210;
|
||||
pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 211;
|
||||
pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 212;
|
||||
pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 213;
|
||||
pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 214;
|
||||
pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 215;
|
||||
pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 216;
|
||||
pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 217;
|
||||
pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 218;
|
||||
pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 219;
|
||||
pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 220;
|
||||
pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 110;
|
||||
pub const YARVINSN_trace_nop: ruby_vminsn_type = 111;
|
||||
pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 112;
|
||||
pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 113;
|
||||
pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 114;
|
||||
pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 115;
|
||||
pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 116;
|
||||
pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 117;
|
||||
pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 118;
|
||||
pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 119;
|
||||
pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 120;
|
||||
pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 121;
|
||||
pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 122;
|
||||
pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 123;
|
||||
pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 124;
|
||||
pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 125;
|
||||
pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 126;
|
||||
pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 127;
|
||||
pub const YARVINSN_trace_putnil: ruby_vminsn_type = 128;
|
||||
pub const YARVINSN_trace_putself: ruby_vminsn_type = 129;
|
||||
pub const YARVINSN_trace_putobject: ruby_vminsn_type = 130;
|
||||
pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 131;
|
||||
pub const YARVINSN_trace_putstring: ruby_vminsn_type = 132;
|
||||
pub const YARVINSN_trace_putchilledstring: ruby_vminsn_type = 133;
|
||||
pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 134;
|
||||
pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 135;
|
||||
pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 136;
|
||||
pub const YARVINSN_trace_intern: ruby_vminsn_type = 137;
|
||||
pub const YARVINSN_trace_newarray: ruby_vminsn_type = 138;
|
||||
pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 139;
|
||||
pub const YARVINSN_trace_duparray: ruby_vminsn_type = 140;
|
||||
pub const YARVINSN_trace_duphash: ruby_vminsn_type = 141;
|
||||
pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 142;
|
||||
pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 143;
|
||||
pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 144;
|
||||
pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 145;
|
||||
pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 146;
|
||||
pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 147;
|
||||
pub const YARVINSN_trace_newhash: ruby_vminsn_type = 148;
|
||||
pub const YARVINSN_trace_newrange: ruby_vminsn_type = 149;
|
||||
pub const YARVINSN_trace_pop: ruby_vminsn_type = 150;
|
||||
pub const YARVINSN_trace_dup: ruby_vminsn_type = 151;
|
||||
pub const YARVINSN_trace_dupn: ruby_vminsn_type = 152;
|
||||
pub const YARVINSN_trace_swap: ruby_vminsn_type = 153;
|
||||
pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 154;
|
||||
pub const YARVINSN_trace_topn: ruby_vminsn_type = 155;
|
||||
pub const YARVINSN_trace_setn: ruby_vminsn_type = 156;
|
||||
pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 157;
|
||||
pub const YARVINSN_trace_defined: ruby_vminsn_type = 158;
|
||||
pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 159;
|
||||
pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 160;
|
||||
pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 161;
|
||||
pub const YARVINSN_trace_checktype: ruby_vminsn_type = 162;
|
||||
pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 163;
|
||||
pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 164;
|
||||
pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 165;
|
||||
pub const YARVINSN_trace_send: ruby_vminsn_type = 166;
|
||||
pub const YARVINSN_trace_sendforward: ruby_vminsn_type = 167;
|
||||
pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 168;
|
||||
pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 169;
|
||||
pub const YARVINSN_trace_opt_ary_freeze: ruby_vminsn_type = 170;
|
||||
pub const YARVINSN_trace_opt_hash_freeze: ruby_vminsn_type = 171;
|
||||
pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 172;
|
||||
pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 173;
|
||||
pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 174;
|
||||
pub const YARVINSN_trace_opt_duparray_send: ruby_vminsn_type = 175;
|
||||
pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 176;
|
||||
pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 177;
|
||||
pub const YARVINSN_trace_invokesuperforward: ruby_vminsn_type = 178;
|
||||
pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 179;
|
||||
pub const YARVINSN_trace_leave: ruby_vminsn_type = 180;
|
||||
pub const YARVINSN_trace_throw: ruby_vminsn_type = 181;
|
||||
pub const YARVINSN_trace_jump: ruby_vminsn_type = 182;
|
||||
pub const YARVINSN_trace_branchif: ruby_vminsn_type = 183;
|
||||
pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 184;
|
||||
pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 185;
|
||||
pub const YARVINSN_trace_once: ruby_vminsn_type = 186;
|
||||
pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 187;
|
||||
pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 188;
|
||||
pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 189;
|
||||
pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 190;
|
||||
pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 191;
|
||||
pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 192;
|
||||
pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 193;
|
||||
pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 194;
|
||||
pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 195;
|
||||
pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 196;
|
||||
pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 197;
|
||||
pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 198;
|
||||
pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 199;
|
||||
pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 200;
|
||||
pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 201;
|
||||
pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 202;
|
||||
pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 203;
|
||||
pub const YARVINSN_trace_opt_aset_with: ruby_vminsn_type = 204;
|
||||
pub const YARVINSN_trace_opt_aref_with: ruby_vminsn_type = 205;
|
||||
pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 206;
|
||||
pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 207;
|
||||
pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 208;
|
||||
pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 209;
|
||||
pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 210;
|
||||
pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 211;
|
||||
pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 212;
|
||||
pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 213;
|
||||
pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 214;
|
||||
pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 215;
|
||||
pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 216;
|
||||
pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 217;
|
||||
pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 218;
|
||||
pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 219;
|
||||
pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 220;
|
||||
pub const YARVINSN_trace_zjit_opt_plus: ruby_vminsn_type = 221;
|
||||
pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 222;
|
||||
pub type ruby_vminsn_type = u32;
|
||||
pub type rb_iseq_callback = ::std::option::Option<
|
||||
unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void),
|
||||
@ -996,4 +998,6 @@ unsafe extern "C" {
|
||||
line: ::std::os::raw::c_int,
|
||||
);
|
||||
pub fn rb_RCLASS_ORIGIN(c: VALUE) -> VALUE;
|
||||
pub fn rb_iseq_get_zjit_payload(iseq: *const rb_iseq_t) -> *mut ::std::os::raw::c_void;
|
||||
pub fn rb_iseq_set_zjit_payload(iseq: *const rb_iseq_t, payload: *mut ::std::os::raw::c_void);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
use crate::{
|
||||
cruby::*,
|
||||
get_option,
|
||||
options::DumpHIR
|
||||
options::DumpHIR, profile::{get_or_create_iseq_payload, InsnProfile}
|
||||
};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
@ -77,6 +77,18 @@ pub struct CallInfo {
|
||||
name: String,
|
||||
}
|
||||
|
||||
/// Invalidation reasons
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Invariant {
|
||||
/// Basic operation is redefined
|
||||
BOPRedefined {
|
||||
/// {klass}_REDEFINED_OP_FLAG
|
||||
klass: RedefinitionFlag,
|
||||
/// BOP_{bop}
|
||||
bop: ruby_basic_operators,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Insn {
|
||||
PutSelf,
|
||||
@ -125,6 +137,17 @@ pub enum Insn {
|
||||
|
||||
// Control flow instructions
|
||||
Return { val: InsnId },
|
||||
|
||||
/// Fixnum + Fixnum
|
||||
FixnumAdd { recv: InsnId, obj: InsnId },
|
||||
|
||||
/// Side-exist if val doesn't have the expected type.
|
||||
// TODO: Replace is_fixnum with the type lattice
|
||||
GuardType { val: InsnId, is_fixnum: bool },
|
||||
|
||||
/// Generate no code (or padding if necessary) and insert a patch point
|
||||
/// that can be rewritten to a side exit when the Invariant is broken.
|
||||
PatchPoint(Invariant),
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
@ -515,6 +538,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
||||
let mut visited = HashSet::new();
|
||||
|
||||
let iseq_size = unsafe { get_iseq_encoded_size(iseq) };
|
||||
let payload = get_or_create_iseq_payload(iseq);
|
||||
while let Some((incoming_state, block, mut insn_idx)) = queue.pop_front() {
|
||||
if visited.contains(&block) { continue; }
|
||||
visited.insert(block);
|
||||
@ -657,10 +681,19 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
||||
state.setn(n, top);
|
||||
}
|
||||
|
||||
YARVINSN_opt_plus => {
|
||||
YARVINSN_opt_plus | YARVINSN_zjit_opt_plus => {
|
||||
let right = state.pop()?;
|
||||
let left = state.pop()?;
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "+".into() }, args: vec![right] }));
|
||||
if let Some(InsnProfile::OptPlus { recv_is_fixnum: true, obj_is_fixnum: true }) = payload.get_insn_profile(insn_idx as usize) {
|
||||
state.push(fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_PLUS })));
|
||||
let left_fixnum = fun.push_insn(block, Insn::GuardType { val: left, is_fixnum: true });
|
||||
state.push(left_fixnum);
|
||||
let right_fixnum = fun.push_insn(block, Insn::GuardType { val: right, is_fixnum: true });
|
||||
state.push(right_fixnum);
|
||||
state.push(fun.push_insn(block, Insn::FixnumAdd { recv: left_fixnum, obj: right_fixnum }));
|
||||
} else {
|
||||
state.push(fun.push_insn(block, Insn::Send { self_val: left, call_info: CallInfo { name: "+".into() }, args: vec![right] }));
|
||||
}
|
||||
}
|
||||
YARVINSN_opt_div => {
|
||||
let right = state.pop()?;
|
||||
|
@ -13,6 +13,7 @@ mod backend;
|
||||
#[cfg(feature = "disasm")]
|
||||
mod disasm;
|
||||
mod options;
|
||||
mod profile;
|
||||
|
||||
use codegen::gen_function;
|
||||
use options::{debug, get_option, Options};
|
||||
|
@ -5,7 +5,7 @@ use std::{ffi::CStr, os::raw::c_char};
|
||||
// Threshold==1 means compile on first execution
|
||||
#[unsafe(no_mangle)]
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub static mut rb_zjit_call_threshold: u64 = 1;
|
||||
pub static mut rb_zjit_call_threshold: u64 = 2;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Options {
|
||||
|
122
zjit/src/profile.rs
Normal file
122
zjit/src/profile.rs
Normal file
@ -0,0 +1,122 @@
|
||||
// We use the YARV bytecode constants which have a CRuby-style name
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
use core::ffi::c_void;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::cruby::*;
|
||||
|
||||
/// Ephemeral state for profiling runtime information
|
||||
struct Profiler {
|
||||
cfp: CfpPtr,
|
||||
iseq: IseqPtr,
|
||||
insn_idx: usize,
|
||||
}
|
||||
|
||||
impl Profiler {
|
||||
fn new(ec: EcPtr) -> Self {
|
||||
let cfp = unsafe { get_ec_cfp(ec) };
|
||||
let iseq = unsafe { get_cfp_iseq(cfp) };
|
||||
Profiler {
|
||||
cfp,
|
||||
iseq,
|
||||
insn_idx: unsafe { get_cfp_pc(cfp).offset_from(get_iseq_body_iseq_encoded(iseq)) as usize },
|
||||
}
|
||||
}
|
||||
|
||||
// Peek at the nth topmost value on the Ruby stack.
|
||||
// Returns the topmost value when n == 0.
|
||||
fn peek_at_stack(&self, n: isize) -> VALUE {
|
||||
unsafe {
|
||||
let sp: *mut VALUE = get_cfp_sp(self.cfp);
|
||||
*(sp.offset(-1 - n))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// API called from zjit_* instruction. opcode is the bare (non-zjit_*) instruction.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn rb_zjit_profile_insn(opcode: ruby_vminsn_type, ec: EcPtr) {
|
||||
with_vm_lock(src_loc!(), || {
|
||||
let mut profiler = Profiler::new(ec);
|
||||
profile_insn(&mut profiler, opcode);
|
||||
});
|
||||
}
|
||||
|
||||
/// Profile a YARV instruction
|
||||
fn profile_insn(profiler: &mut Profiler, opcode: ruby_vminsn_type) {
|
||||
match opcode {
|
||||
YARVINSN_opt_plus => profile_opt_plus(profiler),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Profile opt_plus instruction
|
||||
fn profile_opt_plus(profiler: &mut Profiler) {
|
||||
let recv = profiler.peek_at_stack(1);
|
||||
let obj = profiler.peek_at_stack(0);
|
||||
|
||||
let payload = get_or_create_iseq_payload(profiler.iseq);
|
||||
payload.insns.insert(profiler.insn_idx, InsnProfile::OptPlus {
|
||||
// TODO: profile the type and union it with past results
|
||||
recv_is_fixnum: recv.fixnum_p(),
|
||||
obj_is_fixnum: obj.fixnum_p(),
|
||||
});
|
||||
}
|
||||
|
||||
/// Profiling information for each YARV instruction
|
||||
pub enum InsnProfile {
|
||||
// TODO: Change it to { recv: Type, obj: Type } once the type lattice is merged
|
||||
OptPlus { recv_is_fixnum: bool, obj_is_fixnum: bool },
|
||||
}
|
||||
|
||||
/// This is all the data YJIT stores on an iseq. This will be dynamically allocated by C code
|
||||
/// C code should pass an &mut IseqPayload to us when calling into ZJIT.
|
||||
#[derive(Default)]
|
||||
pub struct IseqPayload {
|
||||
/// Profiling information for each YARV instruction, indexed by the instruction index
|
||||
insns: HashMap<usize, InsnProfile>,
|
||||
}
|
||||
|
||||
impl IseqPayload {
|
||||
/// Get the instruction profile for a given instruction index
|
||||
pub fn get_insn_profile(&self, insn_idx: usize) -> Option<&InsnProfile> {
|
||||
self.insns.get(&insn_idx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the payload for an iseq. For safety it's up to the caller to ensure the returned `&mut`
|
||||
/// upholds aliasing rules and that the argument is a valid iseq.
|
||||
pub fn get_iseq_payload(iseq: IseqPtr) -> Option<&'static mut IseqPayload> {
|
||||
let payload = unsafe { rb_iseq_get_zjit_payload(iseq) };
|
||||
let payload: *mut IseqPayload = payload.cast();
|
||||
unsafe { payload.as_mut() }
|
||||
}
|
||||
|
||||
/// Get the payload object associated with an iseq. Create one if none exists.
|
||||
pub fn get_or_create_iseq_payload(iseq: IseqPtr) -> &'static mut IseqPayload {
|
||||
type VoidPtr = *mut c_void;
|
||||
|
||||
let payload_non_null = unsafe {
|
||||
let payload = rb_iseq_get_zjit_payload(iseq);
|
||||
if payload.is_null() {
|
||||
// Allocate a new payload with Box and transfer ownership to the GC.
|
||||
// We drop the payload with Box::from_raw when the GC frees the iseq and calls us.
|
||||
// NOTE(alan): Sometimes we read from an iseq without ever writing to it.
|
||||
// We allocate in those cases anyways.
|
||||
let new_payload = IseqPayload::default();
|
||||
let new_payload = Box::into_raw(Box::new(new_payload));
|
||||
rb_iseq_set_zjit_payload(iseq, new_payload as VoidPtr);
|
||||
|
||||
new_payload
|
||||
} else {
|
||||
payload as *mut IseqPayload
|
||||
}
|
||||
};
|
||||
|
||||
// SAFETY: we should have the VM lock and all other Ruby threads should be asleep. So we have
|
||||
// exclusive mutable access.
|
||||
// Hmm, nothing seems to stop calling this on the same
|
||||
// iseq twice, though, which violates aliasing rules.
|
||||
unsafe { payload_non_null.as_mut() }.unwrap()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user