mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
ZJIT: throw
to HIR
This commit is contained in:
parent
565ab3ef57
commit
ddb8de1f5f
1 changed files with 50 additions and 5 deletions
|
@ -488,6 +488,8 @@ pub enum Insn {
|
||||||
|
|
||||||
/// Control flow instructions
|
/// Control flow instructions
|
||||||
Return { val: InsnId },
|
Return { val: InsnId },
|
||||||
|
/// Non-local control flow. See the throw YARV instruction
|
||||||
|
Throw { throw_state: u32, val: InsnId },
|
||||||
|
|
||||||
/// Fixnum +, -, *, /, %, ==, !=, <, <=, >, >=
|
/// Fixnum +, -, *, /, %, ==, !=, <, <=, >, >=
|
||||||
FixnumAdd { left: InsnId, right: InsnId, state: InsnId },
|
FixnumAdd { left: InsnId, right: InsnId, state: InsnId },
|
||||||
|
@ -527,7 +529,7 @@ impl Insn {
|
||||||
| 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 { .. }
|
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetGlobal { .. }
|
||||||
| Insn::SetLocal { .. } => false,
|
| Insn::SetLocal { .. } | Insn::Throw { .. } => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -535,7 +537,7 @@ impl Insn {
|
||||||
/// Return true if the instruction ends a basic block and false otherwise.
|
/// Return true if the instruction ends a basic block and false otherwise.
|
||||||
pub fn is_terminator(&self) -> bool {
|
pub fn is_terminator(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Insn::Jump(_) | Insn::Return { .. } | Insn::SideExit { .. } => true,
|
Insn::Jump(_) | Insn::Return { .. } | Insn::SideExit { .. } | Insn::Throw { .. } => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -713,8 +715,25 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
|
||||||
Insn::ObjToString { val, .. } => { write!(f, "ObjToString {val}") },
|
Insn::ObjToString { val, .. } => { write!(f, "ObjToString {val}") },
|
||||||
Insn::AnyToString { val, str, .. } => { write!(f, "AnyToString {val}, str: {str}") },
|
Insn::AnyToString { val, str, .. } => { write!(f, "AnyToString {val}, str: {str}") },
|
||||||
Insn::SideExit { .. } => write!(f, "SideExit"),
|
Insn::SideExit { .. } => write!(f, "SideExit"),
|
||||||
Insn::PutSpecialObject { value_type } => {
|
Insn::PutSpecialObject { value_type } => write!(f, "PutSpecialObject {value_type}"),
|
||||||
write!(f, "PutSpecialObject {}", value_type)
|
Insn::Throw { throw_state, val } => {
|
||||||
|
let mut state_string = match throw_state & VM_THROW_STATE_MASK {
|
||||||
|
RUBY_TAG_NONE => "TAG_NONE".to_string(),
|
||||||
|
RUBY_TAG_RETURN => "TAG_RETURN".to_string(),
|
||||||
|
RUBY_TAG_BREAK => "TAG_BREAK".to_string(),
|
||||||
|
RUBY_TAG_NEXT => "TAG_NEXT".to_string(),
|
||||||
|
RUBY_TAG_RETRY => "TAG_RETRY".to_string(),
|
||||||
|
RUBY_TAG_REDO => "TAG_REDO".to_string(),
|
||||||
|
RUBY_TAG_RAISE => "TAG_RAISE".to_string(),
|
||||||
|
RUBY_TAG_THROW => "TAG_THROW".to_string(),
|
||||||
|
RUBY_TAG_FATAL => "TAG_FATAL".to_string(),
|
||||||
|
tag => format!("{tag}")
|
||||||
|
};
|
||||||
|
if throw_state & VM_THROW_NO_ESCAPE_FLAG != 0 {
|
||||||
|
use std::fmt::Write;
|
||||||
|
write!(state_string, "|NO_ESCAPE")?;
|
||||||
|
}
|
||||||
|
write!(f, "Throw {state_string}, {val}")
|
||||||
}
|
}
|
||||||
insn => { write!(f, "{insn:?}") }
|
insn => { write!(f, "{insn:?}") }
|
||||||
}
|
}
|
||||||
|
@ -1015,6 +1034,7 @@ impl Function {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Return { val } => Return { val: find!(*val) },
|
Return { val } => Return { val: find!(*val) },
|
||||||
|
&Throw { throw_state, val } => Throw { throw_state, val: find!(val) },
|
||||||
StringCopy { val, chilled } => StringCopy { val: find!(*val), chilled: *chilled },
|
StringCopy { val, chilled } => StringCopy { val: find!(*val), chilled: *chilled },
|
||||||
StringIntern { val } => StringIntern { val: find!(*val) },
|
StringIntern { val } => StringIntern { val: find!(*val) },
|
||||||
Test { val } => Test { val: find!(*val) },
|
Test { val } => Test { val: find!(*val) },
|
||||||
|
@ -1119,7 +1139,7 @@ impl Function {
|
||||||
match &self.insns[insn.0] {
|
match &self.insns[insn.0] {
|
||||||
Insn::Param { .. } => unimplemented!("params should not be present in block.insns"),
|
Insn::Param { .. } => unimplemented!("params should not be present in block.insns"),
|
||||||
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::Throw { .. }
|
||||||
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::ArrayExtend { .. }
|
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::ArrayExtend { .. }
|
||||||
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. } =>
|
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. } =>
|
||||||
panic!("Cannot infer type of instruction with no output: {}", self.insns[insn.0]),
|
panic!("Cannot infer type of instruction with no output: {}", self.insns[insn.0]),
|
||||||
|
@ -1755,6 +1775,7 @@ impl Function {
|
||||||
Insn::StringCopy { val, .. }
|
Insn::StringCopy { val, .. }
|
||||||
| Insn::StringIntern { val }
|
| Insn::StringIntern { val }
|
||||||
| Insn::Return { val }
|
| Insn::Return { val }
|
||||||
|
| Insn::Throw { val, .. }
|
||||||
| Insn::Defined { v: val, .. }
|
| Insn::Defined { v: val, .. }
|
||||||
| Insn::Test { val }
|
| Insn::Test { val }
|
||||||
| Insn::SetLocal { val, .. }
|
| Insn::SetLocal { val, .. }
|
||||||
|
@ -2710,6 +2731,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
||||||
fun.push_insn(block, Insn::Return { val: state.stack_pop()? });
|
fun.push_insn(block, Insn::Return { val: state.stack_pop()? });
|
||||||
break; // Don't enqueue the next block as a successor
|
break; // Don't enqueue the next block as a successor
|
||||||
}
|
}
|
||||||
|
YARVINSN_throw => {
|
||||||
|
fun.push_insn(block, Insn::Throw { throw_state: get_arg(pc, 0).as_u32(), val: state.stack_pop()? });
|
||||||
|
break; // Don't enqueue the next block as a successor
|
||||||
|
}
|
||||||
|
|
||||||
// These are opt_send_without_block and all the opt_* instructions
|
// These are opt_send_without_block and all the opt_* instructions
|
||||||
// specialized to a certain method that could also be serviced
|
// specialized to a certain method that could also be serviced
|
||||||
|
@ -4620,6 +4645,26 @@ mod tests {
|
||||||
SideExit
|
SideExit
|
||||||
"#]]);
|
"#]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn throw() {
|
||||||
|
eval("
|
||||||
|
define_method(:throw_return) { return 1 }
|
||||||
|
define_method(:throw_break) { break 2 }
|
||||||
|
");
|
||||||
|
assert_method_hir_with_opcode("throw_return", YARVINSN_throw, expect![[r#"
|
||||||
|
fn block in <compiled>:
|
||||||
|
bb0(v0:BasicObject):
|
||||||
|
v2:Fixnum[1] = Const Value(1)
|
||||||
|
Throw TAG_RETURN, v2
|
||||||
|
"#]]);
|
||||||
|
assert_method_hir_with_opcode("throw_break", YARVINSN_throw, expect![[r#"
|
||||||
|
fn block in <compiled>:
|
||||||
|
bb0(v0:BasicObject):
|
||||||
|
v2:Fixnum[2] = Const Value(2)
|
||||||
|
Throw TAG_BREAK, v2
|
||||||
|
"#]]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue