Skip to content

Commit 424485d

Browse files
committed
ZJIT: throw to HIR
1 parent f571c3f commit 424485d

1 file changed

Lines changed: 50 additions & 5 deletions

File tree

zjit/src/hir.rs

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,8 @@ pub enum Insn {
488488

489489
/// Control flow instructions
490490
Return { val: InsnId },
491+
/// Non-local control flow. See the throw YARV instruction
492+
Throw { throw_state: u32, val: InsnId },
491493

492494
/// Fixnum +, -, *, /, %, ==, !=, <, <=, >, >=
493495
FixnumAdd { left: InsnId, right: InsnId, state: InsnId },
@@ -527,15 +529,15 @@ impl Insn {
527529
| Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::Return { .. }
528530
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::ArrayExtend { .. }
529531
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetGlobal { .. }
530-
| Insn::SetLocal { .. } => false,
532+
| Insn::SetLocal { .. } | Insn::Throw { .. } => false,
531533
_ => true,
532534
}
533535
}
534536

535537
/// Return true if the instruction ends a basic block and false otherwise.
536538
pub fn is_terminator(&self) -> bool {
537539
match self {
538-
Insn::Jump(_) | Insn::Return { .. } | Insn::SideExit { .. } => true,
540+
Insn::Jump(_) | Insn::Return { .. } | Insn::SideExit { .. } | Insn::Throw { .. } => true,
539541
_ => false,
540542
}
541543
}
@@ -713,8 +715,25 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
713715
Insn::ObjToString { val, .. } => { write!(f, "ObjToString {val}") },
714716
Insn::AnyToString { val, str, .. } => { write!(f, "AnyToString {val}, str: {str}") },
715717
Insn::SideExit { .. } => write!(f, "SideExit"),
716-
Insn::PutSpecialObject { value_type } => {
717-
write!(f, "PutSpecialObject {}", value_type)
718+
Insn::PutSpecialObject { value_type } => write!(f, "PutSpecialObject {value_type}"),
719+
Insn::Throw { throw_state, val } => {
720+
let mut state_string = match throw_state & VM_THROW_STATE_MASK {
721+
RUBY_TAG_NONE => "TAG_NONE".to_string(),
722+
RUBY_TAG_RETURN => "TAG_RETURN".to_string(),
723+
RUBY_TAG_BREAK => "TAG_BREAK".to_string(),
724+
RUBY_TAG_NEXT => "TAG_NEXT".to_string(),
725+
RUBY_TAG_RETRY => "TAG_RETRY".to_string(),
726+
RUBY_TAG_REDO => "TAG_REDO".to_string(),
727+
RUBY_TAG_RAISE => "TAG_RAISE".to_string(),
728+
RUBY_TAG_THROW => "TAG_THROW".to_string(),
729+
RUBY_TAG_FATAL => "TAG_FATAL".to_string(),
730+
tag => format!("{tag}")
731+
};
732+
if throw_state & VM_THROW_NO_ESCAPE_FLAG != 0 {
733+
use std::fmt::Write;
734+
write!(state_string, "|NO_ESCAPE")?;
735+
}
736+
write!(f, "Throw {state_string}, {val}")
718737
}
719738
insn => { write!(f, "{insn:?}") }
720739
}
@@ -1015,6 +1034,7 @@ impl Function {
10151034
}
10161035
},
10171036
Return { val } => Return { val: find!(*val) },
1037+
&Throw { throw_state, val } => Throw { throw_state, val: find!(val) },
10181038
StringCopy { val, chilled } => StringCopy { val: find!(*val), chilled: *chilled },
10191039
StringIntern { val } => StringIntern { val: find!(*val) },
10201040
Test { val } => Test { val: find!(*val) },
@@ -1119,7 +1139,7 @@ impl Function {
11191139
match &self.insns[insn.0] {
11201140
Insn::Param { .. } => unimplemented!("params should not be present in block.insns"),
11211141
Insn::SetGlobal { .. } | Insn::ArraySet { .. } | Insn::Snapshot { .. } | Insn::Jump(_)
1122-
| Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::Return { .. }
1142+
| Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::Return { .. } | Insn::Throw { .. }
11231143
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::ArrayExtend { .. }
11241144
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. } =>
11251145
panic!("Cannot infer type of instruction with no output: {}", self.insns[insn.0]),
@@ -1755,6 +1775,7 @@ impl Function {
17551775
Insn::StringCopy { val, .. }
17561776
| Insn::StringIntern { val }
17571777
| Insn::Return { val }
1778+
| Insn::Throw { val, .. }
17581779
| Insn::Defined { v: val, .. }
17591780
| Insn::Test { val }
17601781
| Insn::SetLocal { val, .. }
@@ -2710,6 +2731,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
27102731
fun.push_insn(block, Insn::Return { val: state.stack_pop()? });
27112732
break; // Don't enqueue the next block as a successor
27122733
}
2734+
YARVINSN_throw => {
2735+
fun.push_insn(block, Insn::Throw { throw_state: get_arg(pc, 0).as_u32(), val: state.stack_pop()? });
2736+
break; // Don't enqueue the next block as a successor
2737+
}
27132738

27142739
// These are opt_send_without_block and all the opt_* instructions
27152740
// specialized to a certain method that could also be serviced
@@ -4620,6 +4645,26 @@ mod tests {
46204645
SideExit
46214646
"#]]);
46224647
}
4648+
4649+
#[test]
4650+
fn throw() {
4651+
eval("
4652+
define_method(:throw_return) { return 1 }
4653+
define_method(:throw_break) { break 2 }
4654+
");
4655+
assert_method_hir_with_opcode("throw_return", YARVINSN_throw, expect![[r#"
4656+
fn block in <compiled>:
4657+
bb0(v0:BasicObject):
4658+
v2:Fixnum[1] = Const Value(1)
4659+
Throw TAG_RETURN, v2
4660+
"#]]);
4661+
assert_method_hir_with_opcode("throw_break", YARVINSN_throw, expect![[r#"
4662+
fn block in <compiled>:
4663+
bb0(v0:BasicObject):
4664+
v2:Fixnum[2] = Const Value(2)
4665+
Throw TAG_BREAK, v2
4666+
"#]]);
4667+
}
46234668
}
46244669

46254670
#[cfg(test)]

0 commit comments

Comments
 (0)