Skip to content

Commit bb33557

Browse files
authored
ZJIT: Split Insn::Const from Insn::GetBlockParamProxy (ruby#14583)
* ZJIT: Split Insn::Const from Insn::GetBlockParamProxy * Print [BlockParamProxy] * Link a TODO comment to a Shopify/ruby issue
1 parent c995faa commit bb33557

3 files changed

Lines changed: 29 additions & 28 deletions

File tree

‎zjit/src/codegen.rs‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -393,14 +393,14 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
393393
Insn::GuardType { val, guard_type, state } => gen_guard_type(jit, asm, opnd!(val), *guard_type, &function.frame_state(*state)),
394394
Insn::GuardTypeNot { val, guard_type, state } => gen_guard_type_not(jit, asm, opnd!(val), *guard_type, &function.frame_state(*state)),
395395
Insn::GuardBitEquals { val, expected, state } => gen_guard_bit_equals(jit, asm, opnd!(val), *expected, &function.frame_state(*state)),
396+
&Insn::GuardBlockParamProxy { level, state } => no_output!(gen_guard_block_param_proxy(jit, asm, level, &function.frame_state(state))),
396397
Insn::PatchPoint { invariant, state } => no_output!(gen_patch_point(jit, asm, invariant, &function.frame_state(*state))),
397398
Insn::CCall { cfun, args, name: _, return_type: _, elidable: _ } => gen_ccall(asm, *cfun, opnds!(args)),
398399
Insn::GetIvar { self_val, id, state: _ } => gen_getivar(asm, opnd!(self_val), *id),
399400
Insn::SetGlobal { id, val, state } => no_output!(gen_setglobal(jit, asm, *id, opnd!(val), &function.frame_state(*state))),
400401
Insn::GetGlobal { id, state } => gen_getglobal(jit, asm, *id, &function.frame_state(*state)),
401402
&Insn::GetLocal { ep_offset, level } => gen_getlocal_with_ep(asm, ep_offset, level),
402403
&Insn::SetLocal { val, ep_offset, level } => no_output!(gen_setlocal_with_ep(asm, opnd!(val), function.type_of(val), ep_offset, level)),
403-
&Insn::GetBlockParamProxy { level, state } => gen_get_block_param_proxy(jit, asm, level, &function.frame_state(state)),
404404
Insn::GetConstantPath { ic, state } => gen_get_constant_path(jit, asm, *ic, &function.frame_state(*state)),
405405
Insn::SetIvar { self_val, id, val, state: _ } => no_output!(gen_setivar(asm, opnd!(self_val), *id, opnd!(val))),
406406
Insn::SideExit { state, reason } => no_output!(gen_side_exit(jit, asm, reason, &function.frame_state(*state))),
@@ -551,7 +551,7 @@ fn gen_setlocal_with_ep(asm: &mut Assembler, val: Opnd, val_type: Type, local_ep
551551
}
552552
}
553553

554-
fn gen_get_block_param_proxy(jit: &JITState, asm: &mut Assembler, level: u32, state: &FrameState) -> lir::Opnd {
554+
fn gen_guard_block_param_proxy(jit: &JITState, asm: &mut Assembler, level: u32, state: &FrameState) {
555555
// Bail out if the `&block` local variable has been modified
556556
let ep = gen_get_ep(asm, level);
557557
let flags = Opnd::mem(64, ep, SIZEOF_VALUE_I32 * (VM_ENV_DATA_INDEX_FLAGS as i32));
@@ -569,9 +569,6 @@ fn gen_get_block_param_proxy(jit: &JITState, asm: &mut Assembler, level: u32, st
569569
let block_handler = asm.load(Opnd::mem(64, ep, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL));
570570
asm.test(block_handler, 0x1.into());
571571
asm.jz(side_exit(jit, state, SideExitReason::BlockParamProxyNotIseqOrIfunc));
572-
573-
// Return the rb_block_param_proxy instance (GC root, so put as a number to avoid unnecessary GC tracing)
574-
unsafe { rb_block_param_proxy }.as_u64().into()
575572
}
576573

577574
fn gen_get_constant_path(jit: &JITState, asm: &mut Assembler, ic: *const iseq_inline_constant_cache, state: &FrameState) -> Opnd {

‎zjit/src/hir.rs‎

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -595,9 +595,6 @@ pub enum Insn {
595595
GetLocal { level: u32, ep_offset: u32 },
596596
/// Set a local variable in a higher scope or the heap
597597
SetLocal { level: u32, ep_offset: u32, val: InsnId },
598-
/// Get a special singleton instance `rb_block_param_proxy` if the block
599-
/// handler for the EP specified by `level` is an ISEQ or an ifunc.
600-
GetBlockParamProxy { level: u32, state: InsnId },
601598
GetSpecialSymbol { symbol_type: SpecialBackrefSymbol, state: InsnId },
602599
GetSpecialNumber { nth: u64, state: InsnId },
603600

@@ -679,6 +676,9 @@ pub enum Insn {
679676
GuardBitEquals { val: InsnId, expected: VALUE, state: InsnId },
680677
/// Side-exit if val doesn't have the expected shape.
681678
GuardShape { val: InsnId, shape: ShapeId, state: InsnId },
679+
/// Side-exit if the block param has been modified or the block handler for the frame
680+
/// is neither ISEQ nor ifunc, which makes it incompatible with rb_block_param_proxy.
681+
GuardBlockParamProxy { level: u32, state: InsnId },
682682

683683
/// Generate no code (or padding if necessary) and insert a patch point
684684
/// that can be rewritten to a side exit when the Invariant is broken.
@@ -704,7 +704,7 @@ impl Insn {
704704
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::ArrayExtend { .. }
705705
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetGlobal { .. }
706706
| Insn::SetLocal { .. } | Insn::Throw { .. } | Insn::IncrCounter(_)
707-
| Insn::CheckInterrupts { .. } => false,
707+
| Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } => false,
708708
_ => true,
709709
}
710710
}
@@ -921,6 +921,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
921921
Insn::GuardTypeNot { val, guard_type, .. } => { write!(f, "GuardTypeNot {val}, {}", guard_type.print(self.ptr_map)) },
922922
Insn::GuardBitEquals { val, expected, .. } => { write!(f, "GuardBitEquals {val}, {}", expected.print(self.ptr_map)) },
923923
&Insn::GuardShape { val, shape, .. } => { write!(f, "GuardShape {val}, {:p}", self.ptr_map.map_shape(shape)) },
924+
Insn::GuardBlockParamProxy { level, .. } => write!(f, "GuardBlockParamProxy l{level}"),
924925
Insn::PatchPoint { invariant, .. } => { write!(f, "PatchPoint {}", invariant.print(self.ptr_map)) },
925926
Insn::GetConstantPath { ic, .. } => { write!(f, "GetConstantPath {:p}", self.ptr_map.map_ptr(ic)) },
926927
Insn::CCall { cfun, args, name, return_type: _, elidable: _ } => {
@@ -956,7 +957,6 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
956957
Insn::SetGlobal { id, val, .. } => write!(f, "SetGlobal :{}, {val}", id.contents_lossy()),
957958
Insn::GetLocal { level, ep_offset } => write!(f, "GetLocal l{level}, EP@{ep_offset}"),
958959
Insn::SetLocal { val, level, ep_offset } => write!(f, "SetLocal l{level}, EP@{ep_offset}, {val}"),
959-
Insn::GetBlockParamProxy { level, .. } => write!(f, "GetBlockParamProxy l{level}"),
960960
Insn::GetSpecialSymbol { symbol_type, .. } => write!(f, "GetSpecialSymbol {symbol_type:?}"),
961961
Insn::GetSpecialNumber { nth, .. } => write!(f, "GetSpecialNumber {nth}"),
962962
Insn::ToArray { val, .. } => write!(f, "ToArray {val}"),
@@ -1349,6 +1349,7 @@ impl Function {
13491349
&GuardTypeNot { val, guard_type, state } => GuardTypeNot { val: find!(val), guard_type, state },
13501350
&GuardBitEquals { val, expected, state } => GuardBitEquals { val: find!(val), expected, state },
13511351
&GuardShape { val, shape, state } => GuardShape { val: find!(val), shape, state },
1352+
&GuardBlockParamProxy { level, state } => GuardBlockParamProxy { level, state: find!(state) },
13521353
&FixnumAdd { left, right, state } => FixnumAdd { left: find!(left), right: find!(right), state },
13531354
&FixnumSub { left, right, state } => FixnumSub { left: find!(left), right: find!(right), state },
13541355
&FixnumMult { left, right, state } => FixnumMult { left: find!(left), right: find!(right), state },
@@ -1424,7 +1425,6 @@ impl Function {
14241425
&NewRange { low, high, flag, state } => NewRange { low: find!(low), high: find!(high), flag, state: find!(state) },
14251426
&NewRangeFixnum { low, high, flag, state } => NewRangeFixnum { low: find!(low), high: find!(high), flag, state: find!(state) },
14261427
&ArrayMax { ref elements, state } => ArrayMax { elements: find_vec!(elements), state: find!(state) },
1427-
&GetBlockParamProxy { level, state } => GetBlockParamProxy { level, state: find!(state) },
14281428
&SetGlobal { id, val, state } => SetGlobal { id, val: find!(val), state },
14291429
&GetIvar { self_val, id, state } => GetIvar { self_val: find!(self_val), id, state },
14301430
&LoadIvarEmbedded { self_val, id, index } => LoadIvarEmbedded { self_val: find!(self_val), id, index },
@@ -1465,7 +1465,7 @@ impl Function {
14651465
| Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::Return { .. } | Insn::Throw { .. }
14661466
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::ArrayExtend { .. }
14671467
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. } | Insn::IncrCounter(_)
1468-
| Insn::CheckInterrupts { .. } =>
1468+
| Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } =>
14691469
panic!("Cannot infer type of instruction with no output: {}", self.insns[insn.0]),
14701470
Insn::Const { val: Const::Value(val) } => Type::from_value(*val),
14711471
Insn::Const { val: Const::CBool(val) } => Type::from_cbool(*val),
@@ -1537,7 +1537,6 @@ impl Function {
15371537
Insn::ObjToString { .. } => types::BasicObject,
15381538
Insn::AnyToString { .. } => types::String,
15391539
Insn::GetLocal { .. } => types::BasicObject,
1540-
Insn::GetBlockParamProxy { .. } => types::BasicObject,
15411540
// The type of Snapshot doesn't really matter; it's never materialized. It's used only
15421541
// as a reference for FrameState, which we use to generate side-exit code.
15431542
Insn::Snapshot { .. } => types::Any,
@@ -2370,7 +2369,7 @@ impl Function {
23702369
| &Insn::LoadIvarExtended { self_val, .. } => {
23712370
worklist.push_back(self_val);
23722371
}
2373-
&Insn::GetBlockParamProxy { state, .. } |
2372+
&Insn::GuardBlockParamProxy { state, .. } |
23742373
&Insn::GetGlobal { state, .. } |
23752374
&Insn::GetSpecialSymbol { state, .. } |
23762375
&Insn::GetSpecialNumber { state, .. } |
@@ -3515,7 +3514,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
35153514
YARVINSN_getblockparamproxy => {
35163515
let level = get_arg(pc, 1).as_u32();
35173516
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state });
3518-
state.stack_push(fun.push_insn(block, Insn::GetBlockParamProxy { level, state: exit_id }));
3517+
fun.push_insn(block, Insn::GuardBlockParamProxy { level, state: exit_id });
3518+
// TODO(Shopify/ruby#753): GC root, so we should be able to avoid unnecessary GC tracing
3519+
state.stack_push(fun.push_insn(block, Insn::Const { val: Const::Value(unsafe { rb_block_param_proxy }) }));
35193520
}
35203521
YARVINSN_pop => { state.stack_pop()?; }
35213522
YARVINSN_dup => { state.stack_push(state.stack_top()?); }
@@ -5942,19 +5943,20 @@ mod tests {
59425943
v5:NilClass = Const Value(nil)
59435944
v10:BasicObject = InvokeBuiltin dir_s_open, v0, v1, v2
59445945
PatchPoint NoEPEscape(open)
5945-
v16:BasicObject = GetBlockParamProxy l0
5946+
GuardBlockParamProxy l0
5947+
v17:BasicObject[BlockParamProxy] = Const Value(VALUE(0x1000))
59465948
CheckInterrupts
5947-
v19:CBool = Test v16
5948-
IfFalse v19, bb1(v0, v1, v2, v3, v4, v10)
5949+
v20:CBool = Test v17
5950+
IfFalse v20, bb1(v0, v1, v2, v3, v4, v10)
59495951
PatchPoint NoEPEscape(open)
5950-
v26:BasicObject = InvokeBlock, v10
5951-
v30:BasicObject = InvokeBuiltin dir_s_close, v0, v10
5952+
v27:BasicObject = InvokeBlock, v10
5953+
v31:BasicObject = InvokeBuiltin dir_s_close, v0, v10
59525954
CheckInterrupts
5953-
Return v26
5954-
bb1(v36:BasicObject, v37:BasicObject, v38:BasicObject, v39:BasicObject, v40:BasicObject, v41:BasicObject):
5955+
Return v27
5956+
bb1(v37:BasicObject, v38:BasicObject, v39:BasicObject, v40:BasicObject, v41:BasicObject, v42:BasicObject):
59555957
PatchPoint NoEPEscape(open)
59565958
CheckInterrupts
5957-
Return v41
5959+
Return v42
59585960
");
59595961
}
59605962

@@ -8158,11 +8160,12 @@ mod opt_tests {
81588160
assert_snapshot!(hir_string("test"), @r"
81598161
fn test@<compiled>:2:
81608162
bb0(v0:BasicObject, v1:BasicObject):
8161-
v6:BasicObject = GetBlockParamProxy l0
8162-
v8:BasicObject = Send v0, 0x1000, :tap, v6
8163-
v9:BasicObject = GetLocal l0, EP@3
8163+
GuardBlockParamProxy l0
8164+
v7:BasicObject[BlockParamProxy] = Const Value(VALUE(0x1000))
8165+
v9:BasicObject = Send v0, 0x1008, :tap, v7
8166+
v10:BasicObject = GetLocal l0, EP@3
81648167
CheckInterrupts
8165-
Return v8
8168+
Return v9
81668169
");
81678170
}
81688171

‎zjit/src/hir_type/mod.rs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! High-level intermediate representation types.
22
33
#![allow(non_upper_case_globals)]
4-
use crate::cruby::{Qfalse, Qnil, Qtrue, VALUE, RUBY_T_ARRAY, RUBY_T_STRING, RUBY_T_HASH, RUBY_T_CLASS, RUBY_T_MODULE};
4+
use crate::cruby::{rb_block_param_proxy, Qfalse, Qnil, Qtrue, RUBY_T_ARRAY, RUBY_T_CLASS, RUBY_T_HASH, RUBY_T_MODULE, RUBY_T_STRING, VALUE};
55
use crate::cruby::{rb_cInteger, rb_cFloat, rb_cArray, rb_cHash, rb_cString, rb_cSymbol, rb_cObject, rb_cTrueClass, rb_cFalseClass, rb_cNilClass, rb_cRange, rb_cSet, rb_cRegexp, rb_cClass, rb_cModule, rb_zjit_singleton_class_p};
66
use crate::cruby::ClassRelationship;
77
use crate::cruby::get_class_name;
@@ -75,6 +75,7 @@ fn write_spec(f: &mut std::fmt::Formatter, printer: &TypePrinter) -> std::fmt::R
7575
match ty.spec {
7676
Specialization::Any | Specialization::Empty => { Ok(()) },
7777
Specialization::Object(val) if val == unsafe { rb_mRubyVMFrozenCore } => write!(f, "[VMFrozenCore]"),
78+
Specialization::Object(val) if val == unsafe { rb_block_param_proxy } => write!(f, "[BlockParamProxy]"),
7879
Specialization::Object(val) if ty.is_subtype(types::Symbol) => write!(f, "[:{}]", ruby_sym_to_rust_string(val)),
7980
Specialization::Object(val) => write!(f, "[{}]", val.print(printer.ptr_map)),
8081
// TODO(max): Ensure singleton classes never have Type specialization

0 commit comments

Comments
 (0)