mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 21:49:06 +02:00
ZJIT: getlocal
and setlocal
to HIR
This commit is contained in:
parent
fe9a3be296
commit
ff09cf199d
1 changed files with 74 additions and 3 deletions
|
@ -11,6 +11,7 @@ use std::{
|
||||||
collections::{HashMap, HashSet, VecDeque},
|
collections::{HashMap, HashSet, VecDeque},
|
||||||
ffi::{c_int, c_void, CStr},
|
ffi::{c_int, c_void, CStr},
|
||||||
mem::{align_of, size_of},
|
mem::{align_of, size_of},
|
||||||
|
num::NonZeroU32,
|
||||||
ptr,
|
ptr,
|
||||||
slice::Iter
|
slice::Iter
|
||||||
};
|
};
|
||||||
|
@ -447,6 +448,10 @@ pub enum Insn {
|
||||||
/// Check whether an instance variable exists on `self_val`
|
/// Check whether an instance variable exists on `self_val`
|
||||||
DefinedIvar { self_val: InsnId, id: ID, pushval: VALUE, state: InsnId },
|
DefinedIvar { self_val: InsnId, id: ID, pushval: VALUE, state: InsnId },
|
||||||
|
|
||||||
|
/// Get a local variable from a higher scope
|
||||||
|
GetLocal { level: NonZeroU32, ep_offset: u32 },
|
||||||
|
/// Set a local variable in a higher scope
|
||||||
|
SetLocal { level: NonZeroU32, ep_offset: u32, val: InsnId },
|
||||||
|
|
||||||
/// Own a FrameState so that instructions can look up their dominating FrameState when
|
/// Own a FrameState so that instructions can look up their dominating FrameState when
|
||||||
/// generating deopt side-exits and frame reconstruction metadata. Does not directly generate
|
/// generating deopt side-exits and frame reconstruction metadata. Does not directly generate
|
||||||
|
@ -521,7 +526,8 @@ impl Insn {
|
||||||
Insn::ArraySet { .. } | Insn::Snapshot { .. } | Insn::Jump(_)
|
Insn::ArraySet { .. } | Insn::Snapshot { .. } | Insn::Jump(_)
|
||||||
| Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::Return { .. }
|
| Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::Return { .. }
|
||||||
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::ArrayExtend { .. }
|
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::ArrayExtend { .. }
|
||||||
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetGlobal { .. } => false,
|
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetGlobal { .. }
|
||||||
|
| Insn::SetLocal { .. } => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -696,6 +702,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
|
||||||
Insn::SetIvar { self_val, id, val, .. } => write!(f, "SetIvar {self_val}, :{}, {val}", id.contents_lossy().into_owned()),
|
Insn::SetIvar { self_val, id, val, .. } => write!(f, "SetIvar {self_val}, :{}, {val}", id.contents_lossy().into_owned()),
|
||||||
Insn::GetGlobal { id, .. } => write!(f, "GetGlobal :{}", id.contents_lossy().into_owned()),
|
Insn::GetGlobal { id, .. } => write!(f, "GetGlobal :{}", id.contents_lossy().into_owned()),
|
||||||
Insn::SetGlobal { id, val, .. } => write!(f, "SetGlobal :{}, {val}", id.contents_lossy().into_owned()),
|
Insn::SetGlobal { id, val, .. } => write!(f, "SetGlobal :{}, {val}", id.contents_lossy().into_owned()),
|
||||||
|
Insn::GetLocal { level, ep_offset } => write!(f, "GetLocal l{level}, EP@{ep_offset}"),
|
||||||
|
Insn::SetLocal { val, level, ep_offset } => write!(f, "SetLocal l{level}, EP@{ep_offset}, {val}"),
|
||||||
Insn::ToArray { val, .. } => write!(f, "ToArray {val}"),
|
Insn::ToArray { val, .. } => write!(f, "ToArray {val}"),
|
||||||
Insn::ToNewArray { val, .. } => write!(f, "ToNewArray {val}"),
|
Insn::ToNewArray { val, .. } => write!(f, "ToNewArray {val}"),
|
||||||
Insn::ArrayExtend { left, right, .. } => write!(f, "ArrayExtend {left}, {right}"),
|
Insn::ArrayExtend { left, right, .. } => write!(f, "ArrayExtend {left}, {right}"),
|
||||||
|
@ -987,7 +995,7 @@ impl Function {
|
||||||
use Insn::*;
|
use Insn::*;
|
||||||
match &self.insns[insn_id.0] {
|
match &self.insns[insn_id.0] {
|
||||||
result@(Const {..} | Param {..} | GetConstantPath {..}
|
result@(Const {..} | Param {..} | GetConstantPath {..}
|
||||||
| PatchPoint {..}) => result.clone(),
|
| PatchPoint {..} | GetLocal {..}) => result.clone(),
|
||||||
Snapshot { state: FrameState { iseq, insn_idx, pc, stack, locals } } =>
|
Snapshot { state: FrameState { iseq, insn_idx, pc, stack, locals } } =>
|
||||||
Snapshot {
|
Snapshot {
|
||||||
state: FrameState {
|
state: FrameState {
|
||||||
|
@ -1076,6 +1084,7 @@ impl Function {
|
||||||
&SetGlobal { id, val, state } => SetGlobal { id, val: find!(val), state },
|
&SetGlobal { id, val, state } => SetGlobal { id, val: find!(val), state },
|
||||||
&GetIvar { self_val, id, state } => GetIvar { self_val: find!(self_val), id, state },
|
&GetIvar { self_val, id, state } => GetIvar { self_val: find!(self_val), id, state },
|
||||||
&SetIvar { self_val, id, val, state } => SetIvar { self_val: find!(self_val), id, val, state },
|
&SetIvar { self_val, id, val, state } => SetIvar { self_val: find!(self_val), id, val, state },
|
||||||
|
&SetLocal { val, ep_offset, level } => SetLocal { val: find!(val), ep_offset, level },
|
||||||
&ToArray { val, state } => ToArray { val: find!(val), state },
|
&ToArray { val, state } => ToArray { val: find!(val), state },
|
||||||
&ToNewArray { val, state } => ToNewArray { val: find!(val), state },
|
&ToNewArray { val, state } => ToNewArray { val: find!(val), state },
|
||||||
&ArrayExtend { left, right, state } => ArrayExtend { left: find!(left), right: find!(right), state },
|
&ArrayExtend { left, right, state } => ArrayExtend { left: find!(left), right: find!(right), state },
|
||||||
|
@ -1107,7 +1116,7 @@ impl Function {
|
||||||
Insn::SetGlobal { .. } | Insn::ArraySet { .. } | Insn::Snapshot { .. } | Insn::Jump(_)
|
Insn::SetGlobal { .. } | Insn::ArraySet { .. } | Insn::Snapshot { .. } | Insn::Jump(_)
|
||||||
| Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::Return { .. }
|
| Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::Return { .. }
|
||||||
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::ArrayExtend { .. }
|
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::ArrayExtend { .. }
|
||||||
| Insn::ArrayPush { .. } | Insn::SideExit { .. } =>
|
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. } =>
|
||||||
panic!("Cannot infer type of instruction with no output"),
|
panic!("Cannot infer type of instruction with no output"),
|
||||||
Insn::Const { val: Const::Value(val) } => Type::from_value(*val),
|
Insn::Const { val: Const::Value(val) } => Type::from_value(*val),
|
||||||
Insn::Const { val: Const::CBool(val) } => Type::from_cbool(*val),
|
Insn::Const { val: Const::CBool(val) } => Type::from_cbool(*val),
|
||||||
|
@ -1163,6 +1172,7 @@ impl Function {
|
||||||
Insn::ToArray { .. } => types::ArrayExact,
|
Insn::ToArray { .. } => types::ArrayExact,
|
||||||
Insn::ObjToString { .. } => types::BasicObject,
|
Insn::ObjToString { .. } => types::BasicObject,
|
||||||
Insn::AnyToString { .. } => types::String,
|
Insn::AnyToString { .. } => types::String,
|
||||||
|
Insn::GetLocal { .. } => types::BasicObject,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1714,6 +1724,7 @@ impl Function {
|
||||||
Insn::Const { .. }
|
Insn::Const { .. }
|
||||||
| Insn::Param { .. }
|
| Insn::Param { .. }
|
||||||
| Insn::PatchPoint(..)
|
| Insn::PatchPoint(..)
|
||||||
|
| Insn::GetLocal { .. }
|
||||||
| Insn::PutSpecialObject { .. } =>
|
| Insn::PutSpecialObject { .. } =>
|
||||||
{}
|
{}
|
||||||
Insn::GetConstantPath { ic: _, state } => {
|
Insn::GetConstantPath { ic: _, state } => {
|
||||||
|
@ -1741,6 +1752,7 @@ impl Function {
|
||||||
| Insn::Return { val }
|
| Insn::Return { val }
|
||||||
| Insn::Defined { v: val, .. }
|
| Insn::Defined { v: val, .. }
|
||||||
| Insn::Test { val }
|
| Insn::Test { val }
|
||||||
|
| Insn::SetLocal { val, .. }
|
||||||
| Insn::IsNil { val } =>
|
| Insn::IsNil { val } =>
|
||||||
worklist.push_back(val),
|
worklist.push_back(val),
|
||||||
Insn::SetGlobal { val, state, .. }
|
Insn::SetGlobal { val, state, .. }
|
||||||
|
@ -2174,6 +2186,7 @@ pub enum CallType {
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
StackUnderflow(FrameState),
|
StackUnderflow(FrameState),
|
||||||
|
MalformedIseq(u32), // insn_idx into iseq_encoded
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the number of locals in the current ISEQ (includes parameters)
|
/// Return the number of locals in the current ISEQ (includes parameters)
|
||||||
|
@ -2536,6 +2549,24 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
||||||
let val = state.stack_pop()?;
|
let val = state.stack_pop()?;
|
||||||
state.setlocal(ep_offset, val);
|
state.setlocal(ep_offset, val);
|
||||||
}
|
}
|
||||||
|
YARVINSN_getlocal_WC_1 => {
|
||||||
|
let ep_offset = get_arg(pc, 0).as_u32();
|
||||||
|
state.stack_push(fun.push_insn(block, Insn::GetLocal { ep_offset, level: NonZeroU32::new(1).unwrap() }));
|
||||||
|
}
|
||||||
|
YARVINSN_setlocal_WC_1 => {
|
||||||
|
let ep_offset = get_arg(pc, 0).as_u32();
|
||||||
|
fun.push_insn(block, Insn::SetLocal { val: state.stack_pop()?, ep_offset, level: NonZeroU32::new(1).unwrap() });
|
||||||
|
}
|
||||||
|
YARVINSN_getlocal => {
|
||||||
|
let ep_offset = get_arg(pc, 0).as_u32();
|
||||||
|
let level = NonZeroU32::try_from(get_arg(pc, 1).as_u32()).map_err(|_| ParseError::MalformedIseq(insn_idx))?;
|
||||||
|
state.stack_push(fun.push_insn(block, Insn::GetLocal { ep_offset, level }));
|
||||||
|
}
|
||||||
|
YARVINSN_setlocal => {
|
||||||
|
let ep_offset = get_arg(pc, 0).as_u32();
|
||||||
|
let level = NonZeroU32::try_from(get_arg(pc, 1).as_u32()).map_err(|_| ParseError::MalformedIseq(insn_idx))?;
|
||||||
|
fun.push_insn(block, Insn::SetLocal { val: state.stack_pop()?, ep_offset, level });
|
||||||
|
}
|
||||||
YARVINSN_pop => { state.stack_pop()?; }
|
YARVINSN_pop => { state.stack_pop()?; }
|
||||||
YARVINSN_dup => { state.stack_push(state.stack_top()?); }
|
YARVINSN_dup => { state.stack_push(state.stack_top()?); }
|
||||||
YARVINSN_dupn => {
|
YARVINSN_dupn => {
|
||||||
|
@ -3468,6 +3499,46 @@ mod tests {
|
||||||
"#]]);
|
"#]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nested_setlocal_getlocal() {
|
||||||
|
eval("
|
||||||
|
l3 = 3
|
||||||
|
_unused = _unused1 = nil
|
||||||
|
1.times do |l2|
|
||||||
|
_ = nil
|
||||||
|
l2 = 2
|
||||||
|
1.times do |l1|
|
||||||
|
l1 = 1
|
||||||
|
define_method(:test) do
|
||||||
|
l1 = l2
|
||||||
|
l2 = l1 + l2
|
||||||
|
l3 = l2 + l3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
");
|
||||||
|
assert_method_hir_with_opcodes(
|
||||||
|
"test",
|
||||||
|
&[YARVINSN_getlocal_WC_1, YARVINSN_setlocal_WC_1,
|
||||||
|
YARVINSN_getlocal, YARVINSN_setlocal],
|
||||||
|
expect![[r#"
|
||||||
|
fn block (3 levels) in <compiled>:
|
||||||
|
bb0(v0:BasicObject):
|
||||||
|
v2:BasicObject = GetLocal l2, 4
|
||||||
|
SetLocal l1, 3, v2
|
||||||
|
v4:BasicObject = GetLocal l1, 3
|
||||||
|
v5:BasicObject = GetLocal l2, 4
|
||||||
|
v7:BasicObject = SendWithoutBlock v4, :+, v5
|
||||||
|
SetLocal l2, 4, v7
|
||||||
|
v9:BasicObject = GetLocal l2, 4
|
||||||
|
v10:BasicObject = GetLocal l3, 5
|
||||||
|
v12:BasicObject = SendWithoutBlock v9, :+, v10
|
||||||
|
SetLocal l3, 5, v12
|
||||||
|
Return v12
|
||||||
|
"#]]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn defined_ivar() {
|
fn defined_ivar() {
|
||||||
eval("
|
eval("
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue