summaryrefslogtreecommitdiff
path: root/zjit/src
diff options
context:
space:
mode:
authorTakashi Kokubun <[email protected]>2025-03-03 13:46:53 -0800
committerTakashi Kokubun <[email protected]>2025-04-18 21:52:59 +0900
commit0a543daf15e995ad12b0884bf89ea89b6b480dd2 (patch)
tree8cadafeccd9da51ea8c11014c3b2ab8701848387 /zjit/src
parent30db473389ca5bb6c68bec72de49330a72a2541c (diff)
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
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/13131
Diffstat (limited to 'zjit/src')
-rw-r--r--zjit/src/cruby_bindings.inc.rs226
-rw-r--r--zjit/src/hir.rs39
-rw-r--r--zjit/src/lib.rs1
-rw-r--r--zjit/src/options.rs2
-rw-r--r--zjit/src/profile.rs122
5 files changed, 275 insertions, 115 deletions
diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs
index 1502ee6139..aa07e4d32e 100644
--- a/zjit/src/cruby_bindings.inc.rs
+++ b/zjit/src/cruby_bindings.inc.rs
@@ -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);
}
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs
index e112d4ccc7..1b9088a8b6 100644
--- a/zjit/src/hir.rs
+++ b/zjit/src/hir.rs
@@ -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()?;
diff --git a/zjit/src/lib.rs b/zjit/src/lib.rs
index e768bc74ce..cc2674f6fb 100644
--- a/zjit/src/lib.rs
+++ b/zjit/src/lib.rs
@@ -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};
diff --git a/zjit/src/options.rs b/zjit/src/options.rs
index 2c98cd140a..97622babb6 100644
--- a/zjit/src/options.rs
+++ b/zjit/src/options.rs
@@ -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 {
diff --git a/zjit/src/profile.rs b/zjit/src/profile.rs
new file mode 100644
index 0000000000..a66b2e6015
--- /dev/null
+++ b/zjit/src/profile.rs
@@ -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()
+}