Skip to content

Commit f5c439c

Browse files
committed
ZJIT: Add support for putspecialobject
1 parent 90ba2f4 commit f5c439c

5 files changed

Lines changed: 104 additions & 2 deletions

File tree

test/ruby/test_zjit.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,17 @@ def test() = @foo = 1
568568
}
569569
end
570570

571+
def test_putspecialobject_method
572+
assert_compiles 'nil', %q{
573+
def test
574+
alias bar __callee__
575+
end
576+
577+
test
578+
bar
579+
}, insns: [:putspecialobject]
580+
end
581+
571582
# tool/ruby_vm/views/*.erb relies on the zjit instructions a) being contiguous and
572583
# b) being reliably ordered after all the other instructions.
573584
def test_instruction_order

vm_insnhelper.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5550,6 +5550,13 @@ vm_get_special_object(const VALUE *const reg_ep,
55505550
}
55515551
}
55525552

5553+
// ZJIT implementation is using the C function
5554+
// and needs to call a non-static function
5555+
VALUE rb_vm_get_special_object(const VALUE *reg_ep, enum vm_special_object_type type)
5556+
{
5557+
return vm_get_special_object(reg_ep, type);
5558+
}
5559+
55535560
static VALUE
55545561
vm_concat_array(VALUE ary1, VALUE ary2st)
55555562
{

zjit/src/codegen.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::state::ZJITState;
77
use crate::{asm::CodeBlock, cruby::*, options::debug, virtualmem::CodePtr};
88
use crate::invariants::{iseq_escapes_ep, track_no_ep_escape_assumption};
99
use crate::backend::lir::{self, asm_comment, Assembler, Opnd, Target, CFP, C_ARG_OPNDS, C_RET_OPND, EC, SP};
10-
use crate::hir::{iseq_to_hir, Block, BlockId, BranchEdge, CallInfo, RangeType, SELF_PARAM_IDX};
10+
use crate::hir::{iseq_to_hir, Block, BlockId, BranchEdge, CallInfo, RangeType, SELF_PARAM_IDX, SpecialObjectType};
1111
use crate::hir::{Const, FrameState, Function, Insn, InsnId};
1212
use crate::hir_type::{types::Fixnum, Type};
1313
use crate::options::get_option;
@@ -276,6 +276,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
276276
Insn::CCall { cfun, args, name: _, return_type: _, elidable: _ } => gen_ccall(jit, asm, *cfun, args)?,
277277
Insn::GetIvar { self_val, id, state: _ } => gen_getivar(asm, opnd!(self_val), *id),
278278
Insn::SetIvar { self_val, id, val, state: _ } => gen_setivar(asm, opnd!(self_val), *id, opnd!(val)),
279+
Insn::PutSpecialObject { value_type } => gen_putspecialobject(asm, *value_type),
279280
_ => {
280281
debug!("ZJIT: gen_function: unexpected insn {:?}", insn);
281282
return None;
@@ -317,6 +318,20 @@ fn gen_setivar(asm: &mut Assembler, recv: Opnd, id: ID, val: Opnd) -> Opnd {
317318
)
318319
}
319320

321+
/// Emit a special object lookup
322+
fn gen_putspecialobject(asm: &mut Assembler, value_type: SpecialObjectType) -> Opnd {
323+
asm_comment!(asm, "call rb_vm_get_special_object");
324+
325+
// Get the EP of the current CFP and load it into a register
326+
let ep_opnd = Opnd::mem(64, CFP, RUBY_OFFSET_CFP_EP);
327+
let ep_reg = asm.load(ep_opnd);
328+
329+
asm.ccall(
330+
rb_vm_get_special_object as *const u8,
331+
vec![ep_reg, Opnd::UImm(value_type as u32 as u64)],
332+
)
333+
}
334+
320335
/// Compile an interpreter entry block to be inserted into an ISEQ
321336
fn gen_entry_prologue(asm: &mut Assembler, iseq: IseqPtr) {
322337
asm_comment!(asm, "ZJIT entry point: {}", iseq_get_location(iseq, 0));

zjit/src/cruby.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ unsafe extern "C" {
133133
pub fn rb_str_setbyte(str: VALUE, index: VALUE, value: VALUE) -> VALUE;
134134
pub fn rb_vm_splat_array(flag: VALUE, ary: VALUE) -> VALUE;
135135
pub fn rb_vm_concat_array(ary1: VALUE, ary2st: VALUE) -> VALUE;
136+
pub fn rb_vm_get_special_object(reg_ep: *const VALUE, value_type: vm_special_object_type) -> VALUE;
136137
pub fn rb_vm_concat_to_array(ary1: VALUE, ary2st: VALUE) -> VALUE;
137138
pub fn rb_vm_defined(
138139
ec: EcPtr,
@@ -213,6 +214,7 @@ pub use rb_vm_ci_flag as vm_ci_flag;
213214
pub use rb_vm_ci_kwarg as vm_ci_kwarg;
214215
pub use rb_METHOD_ENTRY_VISI as METHOD_ENTRY_VISI;
215216
pub use rb_RCLASS_ORIGIN as RCLASS_ORIGIN;
217+
pub use rb_vm_get_special_object as vm_get_special_object;
216218

217219
/// Helper so we can get a Rust string for insn_name()
218220
pub fn insn_name(opcode: usize) -> String {

zjit/src/hir.rs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,40 @@ impl Invariant {
138138
}
139139
}
140140

141+
#[derive(Debug, Clone, Copy, PartialEq)]
142+
pub enum SpecialObjectType {
143+
VMCore = 1,
144+
CBase = 2,
145+
ConstBase = 3,
146+
}
147+
148+
impl From<u32> for SpecialObjectType {
149+
fn from(value: u32) -> Self {
150+
match value {
151+
1 => SpecialObjectType::VMCore,
152+
2 => SpecialObjectType::CBase,
153+
3 => SpecialObjectType::ConstBase,
154+
_ => panic!("Invalid special object type: {}", value),
155+
}
156+
}
157+
}
158+
159+
impl From<SpecialObjectType> for u32 {
160+
fn from(special_type: SpecialObjectType) -> Self {
161+
special_type as u32
162+
}
163+
}
164+
165+
impl std::fmt::Display for SpecialObjectType {
166+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
167+
match self {
168+
SpecialObjectType::VMCore => write!(f, "VMCore"),
169+
SpecialObjectType::CBase => write!(f, "CBase"),
170+
SpecialObjectType::ConstBase => write!(f, "ConstBase"),
171+
}
172+
}
173+
}
174+
141175
/// Print adaptor for [`Invariant`]. See [`PtrPrintMap`].
142176
pub struct InvariantPrinter<'a> {
143177
inner: Invariant,
@@ -365,6 +399,9 @@ pub enum Insn {
365399
StringCopy { val: InsnId },
366400
StringIntern { val: InsnId },
367401

402+
/// Put special object (VMCORE, CBASE, etc.) based on value_type
403+
PutSpecialObject { value_type: SpecialObjectType },
404+
368405
/// Call `to_a` on `val` if the method is defined, or make a new array `[val]` otherwise.
369406
ToArray { val: InsnId, state: InsnId },
370407
/// Call `to_a` on `val` if the method is defined, or make a new array `[val]` otherwise. If we
@@ -482,6 +519,7 @@ impl Insn {
482519
match self {
483520
Insn::Const { .. } => false,
484521
Insn::Param { .. } => false,
522+
Insn::PutSpecialObject { .. } => false,
485523
Insn::StringCopy { .. } => false,
486524
Insn::NewArray { .. } => false,
487525
Insn::NewHash { .. } => false,
@@ -630,6 +668,9 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
630668
Insn::ArrayExtend { left, right, .. } => write!(f, "ArrayExtend {left}, {right}"),
631669
Insn::ArrayPush { array, val, .. } => write!(f, "ArrayPush {array}, {val}"),
632670
Insn::SideExit { .. } => write!(f, "SideExit"),
671+
Insn::PutSpecialObject { value_type } => {
672+
write!(f, "PutSpecialObject {}", value_type)
673+
}
633674
insn => { write!(f, "{insn:?}") }
634675
}
635676
}
@@ -943,6 +984,7 @@ impl Function {
943984
FixnumGe { left, right } => FixnumGe { left: find!(*left), right: find!(*right) },
944985
FixnumLt { left, right } => FixnumLt { left: find!(*left), right: find!(*right) },
945986
FixnumLe { left, right } => FixnumLe { left: find!(*left), right: find!(*right) },
987+
PutSpecialObject { value_type } => PutSpecialObject { value_type: *value_type },
946988
SendWithoutBlock { self_val, call_info, cd, args, state } => SendWithoutBlock {
947989
self_val: find!(*self_val),
948990
call_info: call_info.clone(),
@@ -1056,6 +1098,7 @@ impl Function {
10561098
Insn::FixnumLe { .. } => types::BoolExact,
10571099
Insn::FixnumGt { .. } => types::BoolExact,
10581100
Insn::FixnumGe { .. } => types::BoolExact,
1101+
Insn::PutSpecialObject { .. } => types::BasicObject,
10591102
Insn::SendWithoutBlock { .. } => types::BasicObject,
10601103
Insn::SendWithoutBlockDirect { .. } => types::BasicObject,
10611104
Insn::Send { .. } => types::BasicObject,
@@ -1542,7 +1585,8 @@ impl Function {
15421585
necessary[insn_id.0] = true;
15431586
match self.find(insn_id) {
15441587
Insn::Const { .. } | Insn::Param { .. }
1545-
| Insn::PatchPoint(..) | Insn::GetConstantPath { .. } =>
1588+
| Insn::PatchPoint(..) | Insn::GetConstantPath { .. }
1589+
| Insn::PutSpecialObject { .. } =>
15461590
{}
15471591
Insn::ArrayMax { elements, state }
15481592
| Insn::NewArray { elements, state } => {
@@ -2074,6 +2118,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
20742118
YARVINSN_nop => {},
20752119
YARVINSN_putnil => { state.stack_push(fun.push_insn(block, Insn::Const { val: Const::Value(Qnil) })); },
20762120
YARVINSN_putobject => { state.stack_push(fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) })); },
2121+
YARVINSN_putspecialobject => {
2122+
let value_type = SpecialObjectType::from(get_arg(pc, 0).as_u32());
2123+
state.stack_push(fun.push_insn(block, Insn::PutSpecialObject { value_type }));
2124+
}
20772125
YARVINSN_putstring | YARVINSN_putchilledstring => {
20782126
// TODO(max): Do something different for chilled string
20792127
let val = fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) });
@@ -3806,6 +3854,25 @@ mod tests {
38063854
"#]]);
38073855
}
38083856

3857+
#[test]
3858+
fn test_putspecialobject_callee() {
3859+
eval("
3860+
def test
3861+
alias aliased __callee__
3862+
end
3863+
");
3864+
assert_method_hir_with_opcode("test", YARVINSN_putspecialobject, expect![[r#"
3865+
fn test:
3866+
bb0(v0:BasicObject):
3867+
v2:BasicObject = PutSpecialObject VMCore
3868+
v3:BasicObject = PutSpecialObject CBase
3869+
v4:StaticSymbol[VALUE(0x1000)] = Const Value(VALUE(0x1000))
3870+
v5:StaticSymbol[VALUE(0x1008)] = Const Value(VALUE(0x1008))
3871+
v7:BasicObject = SendWithoutBlock v2, :core#set_method_alias, v3, v4, v5
3872+
Return v7
3873+
"#]]);
3874+
}
3875+
38093876
#[test]
38103877
fn test_branchnil() {
38113878
eval("

0 commit comments

Comments
 (0)