summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Patterson <tenderlove@ruby-lang.org>2026-05-12 16:16:13 -0700
committerGitHub <noreply@github.com>2026-05-12 16:16:13 -0700
commitbe557259b71f70df6950f3140c25f251cde0c447 (patch)
treeca09fd7a27fe9e51ebe968ca5be12fa001dec51f
parentece14b61f505eea1ebefb3b8295df0fcf4d22567 (diff)
ZJIT: Convert ZJIT HIR Extended basic blocks to traditional basic blocks (#16888)
* Swap conditionals so HIR -> LIR is easier We're going to give HIR regular BBs which means each block can end with up to two jump instructions. IfTrue/IfFalse will always be followed by a Jump, so when we lower to LIR this change will make the lowering more natural. We'll only have to emit a jz/jnz for the IfTrue/IfFalse cases, and the Jump instructions will be naturally handled * Convert HIR EBB to regular BB This commit converts HIR EBB to regular BB. It ensures that all basic blocks in HIR end with 1 or 2 jump instructions, and that there are no jump instructions that appear mid-block. * implement condbranch * remove num_successors (we do not need it) * oops! * oops! * Do not use clone * fix insta * remove comment * check for terminator in rpo loop * peek at next instruction for new blocks in the jit entry * add comment
-rw-r--r--zjit/src/codegen.rs89
-rw-r--r--zjit/src/hir.rs491
-rw-r--r--zjit/src/hir/opt_tests.rs529
-rw-r--r--zjit/src/hir/tests.rs645
4 files changed, 909 insertions, 845 deletions
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs
index d89d3900fc..77ffae0cb2 100644
--- a/zjit/src/codegen.rs
+++ b/zjit/src/codegen.rs
@@ -172,7 +172,7 @@ pub extern "C" fn rb_zjit_iseq_gen_entry_point(iseq: IseqPtr, ec: EcPtr, jit_exc
// We assert only `jit_exception: false` cases until we support exception handlers.
if ZJITState::assert_compiles_enabled() && !jit_exception {
let iseq_location = iseq_get_location(iseq, 0);
- panic!("Failed to compile: {iseq_location}");
+ panic!("Failed to compile: {iseq_location}: {err:?}");
}
// For --zjit-stats, generate an entry that just increments exit_compilation_failure and exits
@@ -394,7 +394,7 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, version: IseqVersionRef, func
}
// Compile each basic block
- for (rpo_idx, &block_id) in reverse_post_order.iter().enumerate() {
+ for &block_id in reverse_post_order.iter() {
// Skip the entries superblock — it's an internal CFG artifact
if block_id == function.entries_block { continue; }
// Set the current block to the LIR block that corresponds to this
@@ -437,62 +437,27 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, version: IseqVersionRef, func
for (insn_idx, &insn_id) in block.insns().enumerate() {
let insn = function.find(insn_id);
- // IfTrue and IfFalse should never be terminators
- if matches!(insn, Insn::IfTrue {..} | Insn::IfFalse {..}) {
- assert!(!insn.is_terminator(), "IfTrue/IfFalse should not be terminators");
- }
-
match insn {
- Insn::IfFalse { val, target } => {
-
+ Insn::CondBranch { val, if_true, if_false } => {
let val_opnd = jit.get_opnd(val);
+ let true_target = hir_to_lir[if_true.target.0].unwrap();
+ let false_target = hir_to_lir[if_false.target.0].unwrap();
- let lir_target = hir_to_lir[target.target.0].unwrap();
-
- let fall_through_target = asm.new_block(block_id, false, rpo_idx);
-
- let branch_edge = lir::BranchEdge {
- target: lir_target,
- args: target.args.iter().map(|insn_id| jit.get_opnd(*insn_id)).collect()
+ let true_branch = lir::BranchEdge {
+ target: true_target,
+ args: if_true.args.iter().map(|insn_id| jit.get_opnd(*insn_id)).collect()
};
- let fall_through_edge = lir::BranchEdge {
- target: fall_through_target,
- args: vec![]
+ let false_branch = lir::BranchEdge {
+ target: false_target,
+ args: if_false.args.iter().map(|insn_id| jit.get_opnd(*insn_id)).collect()
};
- gen_if_false(&mut asm, val_opnd, branch_edge, fall_through_edge);
- assert!(asm.current_block().insns.last().unwrap().is_terminator());
-
- asm.set_current_block(fall_through_target);
-
- let label = jit.get_label(&mut asm, fall_through_target, block_id);
- asm.write_label(label);
- },
- Insn::IfTrue { val, target } => {
- let val_opnd = jit.get_opnd(val);
-
- let lir_target = hir_to_lir[target.target.0].unwrap();
-
- let fall_through_target = asm.new_block(block_id, false, rpo_idx);
+ asm.test(val_opnd, val_opnd);
+ asm.push_insn(lir::Insn::Jnz(Target::Block(true_branch)));
+ asm.jmp(Target::Block(false_branch));
- let branch_edge = lir::BranchEdge {
- target: lir_target,
- args: target.args.iter().map(|insn_id| jit.get_opnd(*insn_id)).collect()
- };
-
- let fall_through_edge = lir::BranchEdge {
- target: fall_through_target,
- args: vec![]
- };
-
- gen_if_true(&mut asm, val_opnd, branch_edge, fall_through_edge);
assert!(asm.current_block().insns.last().unwrap().is_terminator());
-
- asm.set_current_block(fall_through_target);
-
- let label = jit.get_label(&mut asm, fall_through_target, block_id);
- asm.write_label(label);
}
Insn::Jump(target) => {
let lir_target = hir_to_lir[target.target.0].unwrap();
@@ -500,7 +465,7 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, version: IseqVersionRef, func
target: lir_target,
args: target.args.iter().map(|insn_id| jit.get_opnd(*insn_id)).collect()
};
- gen_jump(&mut asm, branch_edge);
+ asm.jmp(Target::Block(branch_edge));
assert!(asm.current_block().insns.last().unwrap().is_terminator());
// Jump should always be the last instruction in an HIR block
@@ -779,7 +744,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
&Insn::ArrayMax { ref elements, state } => gen_array_max(jit, asm, opnds!(elements), &function.frame_state(state)),
&Insn::ArrayMin { ref elements, state } => gen_array_min(jit, asm, opnds!(elements), &function.frame_state(state)),
&Insn::Throw { state, .. } => return Err(state),
- &Insn::IfFalse { .. } | Insn::IfTrue { .. }
+ &Insn::CondBranch { .. }
| &Insn::Jump { .. } | Insn::Entries { .. } => unreachable!(),
};
@@ -1467,28 +1432,6 @@ fn gen_param(asm: &mut Assembler, _idx: usize) -> lir::Opnd {
vreg
}
-/// Compile a jump to a basic block
-fn gen_jump(asm: &mut Assembler, branch: lir::BranchEdge) {
- // Jump to the basic block
- asm.jmp(Target::Block(branch));
-}
-
-/// Compile a conditional branch to a basic block
-fn gen_if_true(asm: &mut Assembler, val: lir::Opnd, branch: lir::BranchEdge, fall_through: lir::BranchEdge) {
- // If val is zero, move on to the next instruction.
- asm.test(val, val);
- asm.push_insn(lir::Insn::Jz(Target::Block(fall_through)));
- asm.jmp(Target::Block(branch));
-}
-
-/// Compile a conditional branch to a basic block
-fn gen_if_false(asm: &mut Assembler, val: lir::Opnd, branch: lir::BranchEdge, fall_through: lir::BranchEdge) {
- // If val is not zero, move on to the next instruction.
- asm.test(val, val);
- asm.push_insn(lir::Insn::Jnz(Target::Block(fall_through)));
- asm.jmp(Target::Block(branch));
-}
-
/// Compile a dynamic dispatch with block
fn gen_send(
jit: &mut JITState,
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs
index 31fd50c16f..f3f6f73e3f 100644
--- a/zjit/src/hir.rs
+++ b/zjit/src/hir.rs
@@ -11,7 +11,7 @@ use crate::{
state,
};
use std::{
- cell::RefCell, collections::{BTreeSet, HashMap, HashSet, VecDeque}, ffi::{c_void, c_uint, c_int, CStr}, fmt::Display, mem::{align_of, size_of}, ptr, slice::Iter,
+ cell::RefCell, collections::{HashMap, HashSet, VecDeque}, ffi::{c_void, c_uint, c_int, CStr}, fmt::Display, mem::{align_of, size_of}, ptr, slice::Iter,
sync::atomic::Ordering,
};
use crate::hir_type::{Type, types};
@@ -952,9 +952,8 @@ pub enum Insn {
/// Unconditional jump
Jump(BranchEdge),
- /// Conditional branch instructions
- IfTrue { val: InsnId, target: BranchEdge },
- IfFalse { val: InsnId, target: BranchEdge },
+ /// Conditional branch
+ CondBranch { val: InsnId, if_true: BranchEdge, if_false: BranchEdge },
/// Call a C function without pushing a frame
/// `name` and `owner` are for printing purposes only
@@ -1328,10 +1327,10 @@ macro_rules! for_each_operand_impl {
Insn::Jump(BranchEdge { args, .. }) => {
$visit_many!(args);
}
- Insn::IfTrue { val, target: BranchEdge { args, .. } }
- | Insn::IfFalse { val, target: BranchEdge { args, .. } } => {
+ Insn::CondBranch { val, if_true: BranchEdge { args: true_args, .. }, if_false: BranchEdge { args: false_args, .. } } => {
$visit_one!(val);
- $visit_many!(args);
+ $visit_many!(true_args);
+ $visit_many!(false_args);
}
Insn::ArrayDup { val, state }
| Insn::Throw { val, state, .. }
@@ -1468,7 +1467,7 @@ impl Insn {
match self {
Insn::Jump(_)
| Insn::Entries { .. }
- | Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::EntryPoint { .. } | Insn::Return { .. }
+ | Insn::CondBranch { .. } | Insn::EntryPoint { .. } | Insn::Return { .. }
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. }
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetGlobal { .. }
| Insn::SetLocal { .. } | Insn::Throw { .. } | Insn::IncrCounter(_) | Insn::IncrCounterPtr { .. }
@@ -1482,7 +1481,7 @@ impl Insn {
/// Return true if the instruction ends a basic block and false otherwise.
pub fn is_terminator(&self) -> bool {
match self {
- Insn::Jump(_) | Insn::Entries { .. } | Insn::Return { .. } | Insn::SideExit { .. } | Insn::Throw { .. } => true,
+ Insn::Unreachable | Insn::CondBranch { .. } | Insn::Jump(_) | Insn::Entries { .. } | Insn::Return { .. } | Insn::SideExit { .. } | Insn::Throw { .. } => true,
_ => false,
}
}
@@ -1490,7 +1489,7 @@ impl Insn {
/// Return true if the instruction is a jump (has successor blocks in the CFG).
pub fn is_jump(&self) -> bool {
match self {
- Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::Jump(_) | Insn::Entries { .. } => true,
+ Insn::CondBranch { .. } | Insn::Jump(_) | Insn::Entries { .. } => true,
_ => false,
}
}
@@ -1616,8 +1615,7 @@ impl Insn {
Insn::GetBlockParam { .. } => effects::Any,
Insn::Snapshot { .. } => effects::Empty,
Insn::Jump(_) => effects::Any,
- Insn::IfTrue { .. } => effects::Any,
- Insn::IfFalse { .. } => effects::Any,
+ Insn::CondBranch { .. } => effects::Any,
Insn::CCall { elidable, .. } => {
if *elidable {
Effect::write(abstract_heaps::Allocator)
@@ -1949,8 +1947,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
Insn::UnboxFixnum { val } => write!(f, "UnboxFixnum {val}"),
Insn::FixnumAref { recv, index } => write!(f, "FixnumAref {recv}, {index}"),
Insn::Jump(target) => { write!(f, "Jump {target}") }
- Insn::IfTrue { val, target } => { write!(f, "IfTrue {val}, {target}") }
- Insn::IfFalse { val, target } => { write!(f, "IfFalse {val}, {target}") }
+ Insn::CondBranch { val, if_true, if_false } => { write!(f, "CondBranch {val}, {if_true}, {if_false}") },
Insn::SendDirect { recv, cd, iseq, args, block, .. } => {
let blockiseq = block.map(|bh| match bh { BlockHandler::BlockIseq(iseq) => iseq, BlockHandler::BlockArg => unreachable!() });
write!(f, "SendDirect {recv}, {:p}, :{} ({:?})", self.ptr_map.map_ptr(&blockiseq), ruby_call_method_name(*cd), self.ptr_map.map_ptr(iseq))?;
@@ -2690,6 +2687,23 @@ impl Function {
self.blocks.pop();
}
+ fn successors(&self, block: BlockId) -> Vec<BlockId> {
+ let insns = &self.blocks[block.0].insns;
+ let last = self.find(*insns.last().unwrap());
+ match last {
+ Insn::CondBranch { if_true, if_false, .. } => vec![if_true.target, if_false.target],
+ Insn::Jump(edge) => vec![edge.target],
+ Insn::Entries { targets } => targets,
+ Insn::Unreachable | Insn::Return { .. } | Insn::SideExit { .. } | Insn::Throw { .. } => vec![],
+ // Blocks that don't end with terminators are technically errors,
+ // every block in the CFG should end with a terminator. But we
+ // want to be able to iterate over poorly constructed CFG when
+ // debugging, so we'll return an empty vec. The validation
+ // routines check for terminators, so we should catch CFG errors there.
+ _ => vec![]
+ }
+ }
+
/// Return a reference to the Block at the given index.
pub fn block(&self, block_id: BlockId) -> &Block {
&self.blocks[block_id.0]
@@ -2829,7 +2843,7 @@ impl Function {
Insn::Param => unimplemented!("params should not be present in block.insns"),
Insn::LoadArg { val_type, .. } => *val_type,
Insn::SetGlobal { .. } | Insn::Jump(_) | Insn::Entries { .. } | Insn::EntryPoint { .. }
- | Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::Return { .. } | Insn::Throw { .. }
+ | Insn::CondBranch { .. } | Insn::Return { .. } | Insn::Throw { .. }
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. }
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. }
| Insn::IncrCounter(_) | Insn::IncrCounterPtr { .. }
@@ -3012,14 +3026,22 @@ impl Function {
// Assign `new_type` to `insn` if it differs from the recorded type.
// Returns `true` if a write actually happened, `false` if the type
- // was already equal.
- let set_type = |this: &mut Function, insn: InsnId, new_type: Type| -> bool {
- if this.type_of(insn).bit_equal(new_type) {
- return false;
- }
- this.insn_types[insn.0] = new_type;
- true
- };
+ // Macro instead of closure so the borrow checker sees individual field
+ // accesses rather than an `&mut self` borrow that conflicts with
+ // `&self.insns` held by an outer match.
+ macro_rules! set_type {
+ ($insn:expr, $new_type:expr) => {{
+ let insn = $insn;
+ let new_type = $new_type;
+ let old_type = self.insn_types[self.union_find.borrow_mut().find(insn).0];
+ if old_type.bit_equal(new_type) {
+ false
+ } else {
+ self.insn_types[insn.0] = new_type;
+ true
+ }
+ }};
+ }
let mut reachable = BlockSet::with_capacity(self.blocks.len());
reachable.insert(self.entries_block);
@@ -3035,29 +3057,25 @@ impl Function {
// Instructions without output, including branch instructions, can't be targets
// of make_equal_to, so we don't need find() here.
let insn_type = match &self.insns[insn_id.0] {
- &Insn::IfTrue { val, target: BranchEdge { target, ref args } } => {
- assert!(!self.type_of(val).bit_equal(types::Empty));
- if self.type_of(val).could_be(Type::from_cbool(true)) {
- reachable.insert(target);
+ Insn::CondBranch { val, if_true, if_false } => {
+ assert!(!self.type_of(*val).bit_equal(types::Empty));
+ if self.type_of(*val).could_be(Type::from_cbool(true)) {
+ reachable.insert(if_true.target);
// Snapshot arg types before any param updates so phi-style
// updates happen in parallel (the args of a self-loop may name
// params of `target` itself).
- let arg_types: Vec<Type> = args.iter().map(|a| self.type_of(*a)).collect();
+ let arg_types: Vec<Type> = if_true.args.iter().map(|a| self.type_of(*a)).collect();
for (idx, arg_type) in arg_types.into_iter().enumerate() {
- let param = self.blocks[target.0].params[idx];
- changed |= set_type(self, param, self.type_of(param).union(arg_type));
+ let param = self.blocks[if_true.target.0].params[idx];
+ changed |= set_type!(param, self.type_of(param).union(arg_type));
}
}
- continue;
- }
- &Insn::IfFalse { val, target: BranchEdge { target, ref args } } => {
- assert!(!self.type_of(val).bit_equal(types::Empty));
- if self.type_of(val).could_be(Type::from_cbool(false)) {
- reachable.insert(target);
- let arg_types: Vec<Type> = args.iter().map(|a| self.type_of(*a)).collect();
+ if self.type_of(*val).could_be(Type::from_cbool(false)) {
+ reachable.insert(if_false.target);
+ let arg_types: Vec<Type> = if_false.args.iter().map(|a| self.type_of(*a)).collect();
for (idx, arg_type) in arg_types.into_iter().enumerate() {
- let param = self.blocks[target.0].params[idx];
- changed |= set_type(self, param, self.type_of(param).union(arg_type));
+ let param = self.blocks[if_false.target.0].params[idx];
+ changed |= set_type!(param, self.type_of(param).union(arg_type));
}
}
continue;
@@ -3067,7 +3085,7 @@ impl Function {
let arg_types: Vec<Type> = args.iter().map(|a| self.type_of(*a)).collect();
for (idx, arg_type) in arg_types.into_iter().enumerate() {
let param = self.blocks[target.0].params[idx];
- changed |= set_type(self, param, self.type_of(param).union(arg_type));
+ changed |= set_type!(param, self.type_of(param).union(arg_type));
}
continue;
}
@@ -3080,7 +3098,7 @@ impl Function {
insn if insn.has_output() => self.infer_type(insn_id),
_ => continue,
};
- changed |= set_type(self, insn_id, insn_type);
+ changed |= set_type!(insn_id, insn_type);
}
}
if !changed {
@@ -5247,16 +5265,12 @@ impl Function {
Insn::Test { val } if self.type_of(val).is_known_truthy() => {
self.new_insn(Insn::Const { val: Const::CBool(true) })
}
- Insn::IfTrue { val, target } if self.is_a(val, Type::from_cbool(true)) => {
- self.new_insn(Insn::Jump(target))
+ Insn::CondBranch { val, if_true, .. } if self.is_a(val, Type::from_cbool(true)) => {
+ self.new_insn(Insn::Jump(if_true))
}
- Insn::IfFalse { val, target } if self.is_a(val, Type::from_cbool(false)) => {
- self.new_insn(Insn::Jump(target))
+ Insn::CondBranch { val, if_false, .. } if self.is_a(val, Type::from_cbool(false)) => {
+ self.new_insn(Insn::Jump(if_false))
}
- // If we know that the branch condition is never going to cause a branch,
- // completely drop the branch from the block.
- Insn::IfTrue { val, .. } if self.is_a(val, Type::from_cbool(false)) => continue,
- Insn::IfFalse { val, .. } if self.is_a(val, Type::from_cbool(true)) => continue,
_ => insn_id,
};
// If we're adding a new instruction, mark the two equivalent in the union-find and
@@ -5341,22 +5355,8 @@ impl Function {
// * blocks pointed to by blocks that get absorbed retain the same number of in-edges
let mut num_in_edges = vec![0; self.blocks.len()];
for block in self.rpo() {
- for &insn in &self.blocks[block.0].insns {
- // Instructions without output, including branch instructions, can't be targets of
- // make_equal_to, so we don't need find() here.
- match &self.insns[insn.0] {
- Insn::IfTrue { target: BranchEdge { target, .. }, .. }
- | Insn::IfFalse { target: BranchEdge { target, .. }, .. }
- | Insn::Jump(BranchEdge { target, .. }) => {
- num_in_edges[target.0] += 1;
- }
- Insn::Entries { targets } => {
- for target in targets {
- num_in_edges[target.0] += 1;
- }
- }
- _ => {}
- }
+ for target in self.successors(block) {
+ num_in_edges[target.0] += 1;
}
}
let mut changed = false;
@@ -5468,22 +5468,8 @@ impl Function {
}
if !seen.insert(block) { continue; }
stack.push((block, Action::VisitSelf));
- for insn_id in &self.blocks[block.0].insns {
- // Instructions without output, including branch instructions, can't be targets of
- // make_equal_to, so we don't need find() here.
- match &self.insns[insn_id.0] {
- Insn::IfTrue { target, .. } | Insn::IfFalse { target, .. } | Insn::Jump(target) => {
- stack.push((target.target, Action::VisitEdges));
- }
- Insn::Entries { targets } => {
- for target in targets {
- stack.push((*target, Action::VisitEdges));
- }
- }
- _ => {
- debug_assert!(!self.find(*insn_id).is_jump(), "Instruction {:?} should not be in union-find; it has no output", insn_id);
- }
- }
+ for target in self.successors(block) {
+ stack.push((target, Action::VisitEdges));
}
}
result
@@ -5731,35 +5717,45 @@ impl Function {
/// 2. Every terminator must be in the last position.
/// 3. Every block must have a terminator.
fn validate_block_terminators_and_jumps(&self) -> Result<(), ValidationError> {
+ let check_edge = |block_id: BlockId, edge: &BranchEdge| -> Result<(), ValidationError> {
+ let target_len = self.blocks[edge.target.0].params.len();
+ let args_len = edge.args.len();
+ if target_len != args_len {
+ return Err(ValidationError::MismatchedBlockArity(block_id, target_len, args_len));
+ }
+ Ok(())
+ };
+
for block_id in self.rpo() {
- let mut block_has_terminator = false;
let insns = &self.blocks[block_id.0].insns;
for (idx, insn_id) in insns.iter().enumerate() {
let insn = self.find(*insn_id);
+ // Validate arity for all branch edges
match &insn {
- Insn::Jump(BranchEdge{target, args})
- | Insn::IfTrue { val: _, target: BranchEdge{target, args} }
- | Insn::IfFalse { val: _, target: BranchEdge{target, args}} => {
- let target_block = &self.blocks[target.0];
- let target_len = target_block.params.len();
- let args_len = args.len();
- if target_len != args_len {
- return Err(ValidationError::MismatchedBlockArity(block_id, target_len, args_len))
- }
+ Insn::Jump(edge) => {
+ check_edge(block_id, edge)?;
+ }
+ Insn::CondBranch { if_true, if_false, .. } => {
+ check_edge(block_id, if_true)?;
+ check_edge(block_id, if_false)?;
}
_ => {}
}
- if !insn.is_terminator() {
- continue;
+
+ if insn.is_terminator() {
+ // Blow up if we have a terminator that isn't at the end
+ // of the block.
+ if idx != insns.len() - 1 {
+ return Err(ValidationError::TerminatorNotAtEnd(block_id, *insn_id, idx))
+ }
}
- block_has_terminator = true;
- if idx != insns.len() - 1 {
- return Err(ValidationError::TerminatorNotAtEnd(block_id, *insn_id, idx));
+ // If the last instruction isn't a terminator, return an error
+ if idx == insns.len() - 1 {
+ if !insn.is_terminator() {
+ return Err(ValidationError::BlockHasNoTerminator(block_id));
+ }
}
}
- if !block_has_terminator {
- return Err(ValidationError::BlockHasNoTerminator(block_id));
- }
}
Ok(())
}
@@ -5792,25 +5788,25 @@ impl Function {
}
for &insn_id in &self.blocks[block.0].insns {
let insn_id = self.union_find.borrow().find_const(insn_id);
- match self.find(insn_id) {
- Insn::Jump(target) | Insn::IfTrue { target, .. } | Insn::IfFalse { target, .. } => {
- let Some(block_in) = assigned_in[target.target.0].as_mut() else {
- return Err(ValidationError::JumpTargetNotInRPO(target.target));
- };
- // jump target's block_in was modified, we need to queue the block for processing.
- if block_in.intersect_with(&assigned) {
- worklist.push_back(target.target);
- }
+ let insn = self.find(insn_id);
+ let mut propagate = |target: BlockId| -> Result<(), ValidationError> {
+ let Some(block_in) = assigned_in[target.0].as_mut() else {
+ return Err(ValidationError::JumpTargetNotInRPO(target));
+ };
+ if block_in.intersect_with(&assigned) {
+ worklist.push_back(target);
+ }
+ Ok(())
+ };
+ match insn {
+ Insn::Jump(edge) => propagate(edge.target)?,
+ Insn::CondBranch { if_true, if_false, .. } => {
+ propagate(if_true.target)?;
+ propagate(if_false.target)?;
}
Insn::Entries { ref targets } => {
for &target in targets {
- let Some(block_in) = assigned_in[target.0].as_mut() else {
- return Err(ValidationError::JumpTargetNotInRPO(target));
- };
- // jump target's block_in was modified, we need to queue the block for processing.
- if block_in.intersect_with(&assigned) {
- worklist.push_back(target);
- }
+ propagate(target)?;
}
}
insn if insn.has_output() => {
@@ -6068,8 +6064,7 @@ impl Function {
}
}
Insn::BoxBool { val }
- | Insn::IfTrue { val, .. }
- | Insn::IfFalse { val, .. } => {
+ | Insn::CondBranch { val, .. } => {
self.assert_subtype(insn_id, val, types::CBool)
}
Insn::BoxFixnum { val, .. } => self.assert_subtype(insn_id, val, types::CInt64),
@@ -7125,10 +7120,16 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
let nil_false = fun.push_insn(block, Insn::RefineType { val, new_type: nil_false_type });
let mut iffalse_state = state.clone();
iffalse_state.replace(val, nil_false);
- let _branch_id = fun.push_insn(block, Insn::IfFalse {
+ let fall_through = fun.new_block(insn_idx);
+
+ fun.push_insn(block, Insn::CondBranch {
val: test_id,
- target: BranchEdge { target, args: iffalse_state.as_args(self_param) }
+ if_true: BranchEdge { target: fall_through, args: vec![] },
+ if_false: BranchEdge { target, args: iffalse_state.as_args(self_param) }
});
+
+ block = fall_through;
+
let not_nil_false_type = types::Truthy;
let not_nil_false = fun.push_insn(block, Insn::RefineType { val, new_type: not_nil_false_type });
state.replace(val, not_nil_false);
@@ -7147,10 +7148,17 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
let not_nil_false = fun.push_insn(block, Insn::RefineType { val, new_type: not_nil_false_type });
let mut iftrue_state = state.clone();
iftrue_state.replace(val, not_nil_false);
- let _branch_id = fun.push_insn(block, Insn::IfTrue {
+
+ let fall_through = fun.new_block(insn_idx);
+
+ fun.push_insn(block, Insn::CondBranch {
val: test_id,
- target: BranchEdge { target, args: iftrue_state.as_args(self_param) }
+ if_true: BranchEdge { target, args: iftrue_state.as_args(self_param) },
+ if_false: BranchEdge { target: fall_through, args: vec![] }
});
+
+ block = fall_through;
+
let nil_false_type = types::Falsy;
let nil_false = fun.push_insn(block, Insn::RefineType { val, new_type: nil_false_type });
state.replace(val, nil_false);
@@ -7168,10 +7176,16 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
let nil = fun.push_insn(block, Insn::Const { val: Const::Value(Qnil) });
let mut iftrue_state = state.clone();
iftrue_state.replace(val, nil);
- let _branch_id = fun.push_insn(block, Insn::IfTrue {
+
+ let fall_through = fun.new_block(insn_idx);
+
+ fun.push_insn(block, Insn::CondBranch {
val: test_id,
- target: BranchEdge { target, args: iftrue_state.as_args(self_param) }
+ if_true: BranchEdge { target, args: iftrue_state.as_args(self_param) },
+ if_false: BranchEdge { target: fall_through, args: vec![] }
});
+
+ block = fall_through;
let new_type = types::NotNil;
let not_nil = fun.push_insn(block, Insn::RefineType { val, new_type });
state.replace(val, not_nil);
@@ -7197,10 +7211,13 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
// Skip CheckInterrupts since the #new call will do it very soon anyway.
let target_idx = insn_idx_at_offset(insn_idx, dst);
let target = insn_idx_to_block[&target_idx];
- let _branch_id = fun.push_insn(block, Insn::IfFalse {
+ let fall_through = fun.new_block(insn_idx);
+ fun.push_insn(block, Insn::CondBranch {
val: test_id,
- target: BranchEdge { target, args: state.as_args(self_param) }
+ if_true: BranchEdge { target: fall_through, args: vec![] },
+ if_false: BranchEdge { target, args: state.as_args(self_param) }
});
+ block = fall_through;
queue.push_back((state.clone(), target, target_idx, local_inval));
// Move on to the fast path
@@ -7358,8 +7375,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
let flags = fun.load_ep_flags(block, ep);
let is_modified = fun.push_insn(block, Insn::IsBlockParamModified { flags });
- fun.push_insn(block, Insn::IfTrue { val: is_modified, target: BranchEdge { target: modified_block, args: vec![] }});
- fun.push_insn(block, Insn::Jump(BranchEdge { target: unmodified_block, args: vec![] }));
+ fun.push_insn(block, Insn::CondBranch {
+ val: is_modified,
+ if_true: BranchEdge { target: modified_block, args: vec![] },
+ if_false: BranchEdge { target: unmodified_block, args: vec![] }
+ });
// Push modified block: load the block local via EP.
let modified_val = fun.get_local_from_ep(modified_block, ep, ep_offset, level, types::BasicObject);
@@ -7452,20 +7472,29 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
.map(|&kind| (kind, fun.new_block(branch_insn_idx)))
.collect::<Vec<_>>();
+ let mut current_block = unmodified_block;
+
for &(kind, profiled_block) in &profiled_blocks {
match kind {
ProfiledBlockHandlerFamily::Nil => {
- let none_handler = fun.push_insn(unmodified_block, Insn::Const {
+ let none_handler = fun.push_insn(current_block, Insn::Const {
val: Const::CInt64(VM_BLOCK_HANDLER_NONE.into()),
});
- let is_none = fun.push_insn(unmodified_block, Insn::IsBitEqual {
+ let is_none = fun.push_insn(current_block, Insn::IsBitEqual {
left: block_handler,
right: none_handler,
});
- fun.push_insn(unmodified_block, Insn::IfTrue {
+
+ let next_block = fun.new_block(branch_insn_idx);
+
+ fun.push_insn(current_block, Insn::CondBranch {
val: is_none,
- target: BranchEdge { target: profiled_block, args: vec![] },
+ if_true: BranchEdge { target: profiled_block, args: vec![] },
+ if_false: BranchEdge { target: next_block, args: vec![] },
});
+
+ current_block = next_block;
+
let val = fun.push_insn(profiled_block, Insn::Const { val: Const::Value(Qnil) });
let mut args = vec![val];
if let Some(local) = original_local { args.push(local); }
@@ -7478,19 +7507,23 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
// VM_BH_ISEQ_BLOCK_P(): block_handler & 0x03 == 0x01
// VM_BH_IFUNC_P(): block_handler & 0x03 == 0x03
// So to check for either of those cases we can use: val & 0x1 == 0x1
- let tag_mask = fun.push_insn(unmodified_block, Insn::Const { val: Const::CInt64(0x1) });
- let tag_bits = fun.push_insn(unmodified_block, Insn::IntAnd {
+ let tag_mask = fun.push_insn(current_block, Insn::Const { val: Const::CInt64(0x1) });
+ let tag_bits = fun.push_insn(current_block, Insn::IntAnd {
left: block_handler,
right: tag_mask,
});
- let is_iseq_or_ifunc = fun.push_insn(unmodified_block, Insn::IsBitEqual {
+ let is_iseq_or_ifunc = fun.push_insn(current_block, Insn::IsBitEqual {
left: tag_bits,
right: tag_mask,
});
- fun.push_insn(unmodified_block, Insn::IfTrue {
+ let next_block = fun.new_block(branch_insn_idx);
+ fun.push_insn(current_block, Insn::CondBranch {
val: is_iseq_or_ifunc,
- target: BranchEdge { target: profiled_block, args: vec![] },
+ if_true: BranchEdge { target: profiled_block, args: vec![] },
+ if_false: BranchEdge { target: next_block, args: vec![] },
});
+ current_block = next_block;
+
// TODO(Shopify/ruby#753): GC root, so we should be able to avoid unnecessary GC tracing
let val = fun.push_insn(profiled_block, Insn::Const { val: Const::Value(unsafe { rb_block_param_proxy }) });
let mut args = vec![val];
@@ -7500,7 +7533,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
}
}
- fun.push_insn(unmodified_block, Insn::SideExit { state: exit_id, reason: SideExitReason::BlockParamProxyProfileNotCovered, recompile: None });
+ fun.push_insn(current_block, Insn::SideExit { state: exit_id, reason: SideExitReason::BlockParamProxyProfileNotCovered, recompile: None });
}
}
@@ -7528,14 +7561,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
let flags = fun.load_ep_flags(block, ep);
let is_modified = fun.push_insn(block, Insn::IsBlockParamModified { flags });
- fun.push_insn(block, Insn::IfTrue {
+ fun.push_insn(block, Insn::CondBranch {
val: is_modified,
- target: BranchEdge { target: modified_block, args: vec![] },
+ if_true: BranchEdge { target: modified_block, args: vec![] },
+ if_false: BranchEdge { target: unmodified_block, args: vec![] }
});
- fun.push_insn(block, Insn::Jump(BranchEdge {
- target: unmodified_block,
- args: vec![],
- }));
// Push modified block: read Proc from EP.
let modified_val = fun.get_local_from_ep(modified_block, ep, ep_offset, level, types::BasicObject);
@@ -7792,7 +7822,13 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
let iftrue_block =
new_branch_block(&mut fun, cd, argc as usize, opcode, expected, branch_insn_idx, &exit_state, locals_count, stack_count, join_block);
let target = BranchEdge { target: iftrue_block, args: entry_args.clone() };
- fun.push_insn(block, Insn::IfTrue { val: has_type, target });
+ let fall_through = fun.new_block(insn_idx);
+ fun.push_insn(block, Insn::CondBranch {
+ val: has_type,
+ if_true: target,
+ if_false: BranchEdge { target: fall_through, args: vec![] }
+ });
+ block = fall_through;
}
// Continue compilation from the join block at the next instruction.
// Make a copy of the current state without the args (pop the receiver
@@ -8059,7 +8095,16 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
let join_block = fun.new_block(insn_idx);
let join_param = fun.push_insn(join_block, Insn::Param);
let ifunc_block = fun.new_block(insn_idx);
- fun.push_insn(block, Insn::IfTrue { val: is_ifunc_match, target: BranchEdge { target: ifunc_block, args: vec![] } });
+ let fall_through = fun.new_block(insn_idx);
+
+ fun.push_insn(block, Insn::CondBranch {
+ val: is_ifunc_match,
+ if_true: BranchEdge { target: ifunc_block, args: vec![] },
+ if_false: BranchEdge { target: fall_through, args: vec![] },
+ });
+
+ block = fall_through;
+
let ifunc_result = fun.push_insn(ifunc_block, Insn::InvokeBlockIfunc {
cd,
block_handler,
@@ -8135,7 +8180,14 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
let has_shape_and_type = fun.push_insn(block, Insn::IsBitEqual { left: masked, right: expected_rbasic_flags });
let iftrue_block = fun.new_block(insn_idx);
let target = BranchEdge { target: iftrue_block, args: vec![] };
- fun.push_insn(block, Insn::IfTrue { val: has_shape_and_type, target });
+ let fall_through = fun.new_block(insn_idx);
+
+ fun.push_insn(block, Insn::CondBranch { val: has_shape_and_type,
+ if_true: target,
+ if_false: BranchEdge { target: fall_through, args: vec![] }
+ });
+
+ block = fall_through;
let result = fun.load_ivar(iftrue_block, self_param, profiled_type, id, exit_id);
fun.push_insn(iftrue_block, Insn::Jump(BranchEdge { target: join_block, args: vec![result] }));
}
@@ -8356,13 +8408,14 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
/// Compile an entry_block for the interpreter
fn compile_entry_block(fun: &mut Function, jit_entry_insns: &[u32], insn_idx_to_block: &HashMap<u32, BlockId>) {
- let entry_block = fun.entry_block;
+ let mut entry_block = fun.entry_block;
let (self_param, entry_state) = compile_entry_state(fun);
let mut pc: Option<InsnId> = None;
let &all_opts_passed_insn_idx = jit_entry_insns.last().unwrap();
// Check-and-jump for each missing optional PC
- for &jit_entry_insn in jit_entry_insns.iter() {
+ let mut iter = jit_entry_insns.iter().peekable();
+ while let Some(&jit_entry_insn) = iter.next() {
if jit_entry_insn == all_opts_passed_insn_idx {
continue;
}
@@ -8376,10 +8429,16 @@ fn compile_entry_block(fun: &mut Function, jit_entry_insns: &[u32], insn_idx_to_
val: Const::CPtr(unsafe { rb_iseq_pc_at_idx(fun.iseq, jit_entry_insn) } as *const u8),
});
let test_id = fun.push_insn(entry_block, Insn::IsBitEqual { left: pc, right: expected_pc });
- fun.push_insn(entry_block, Insn::IfTrue {
+
+ let next_insn_idx = **iter.peek().expect("last entry is skipped so there is always a next");
+ let fall_through = fun.new_block(next_insn_idx);
+
+ fun.push_insn(entry_block, Insn::CondBranch {
val: test_id,
- target: BranchEdge { target: target_block, args: entry_state.as_args(self_param) },
+ if_true: BranchEdge { target: target_block, args: entry_state.as_args(self_param) },
+ if_false: BranchEdge { target: fall_through, args: vec![] }
});
+ entry_block = fall_through;
}
// Terminate the block with a jump to the block with all optionals passed
@@ -8632,34 +8691,10 @@ impl<'a> ControlFlowInfo<'a> {
pub fn new(function: &'a Function) -> Self {
let mut successor_map: HashMap<BlockId, Vec<BlockId>> = HashMap::new();
let mut predecessor_map: HashMap<BlockId, Vec<BlockId>> = HashMap::new();
- let uf = function.union_find.borrow();
for block_id in function.rpo() {
- let block = &function.blocks[block_id.0];
-
- // Since ZJIT uses extended basic blocks, one must check all instructions
- // for their ability to jump to another basic block, rather than just
- // the instructions at the end of a given basic block.
- //
- // Use BTreeSet to avoid duplicates and maintain an ordering. Also
- // `BTreeSet<BlockId>` provides conversion trivially back to an `Vec<BlockId>`.
- // Ordering is important so that the expect tests that serialize the predecessors
- // and successors don't fail intermittently.
- // todo(aidenfoxivey): Use `BlockSet` in lieu of `BTreeSet<BlockId>`
- let mut successors: BTreeSet<BlockId> = BTreeSet::new();
- for &insn_id in &block.insns {
- let insn_id = uf.find_const(insn_id);
- match &function.insns[insn_id.0] {
- Insn::Entries { targets } => {
- successors.extend(targets);
- }
- insn => {
- if let Some(target) = Self::extract_jump_target(insn) {
- successors.insert(target);
- }
- }
- }
- }
+ let mut successors = function.successors(block_id);
+ successors.dedup();
// Update predecessors for successor blocks.
for &succ_id in &successors {
@@ -8670,8 +8705,7 @@ impl<'a> ControlFlowInfo<'a> {
}
// Store successors for this block.
- // Convert successors from a `BTreeSet<BlockId>` to a `Vec<BlockId>`.
- successor_map.insert(block_id, successors.iter().copied().collect());
+ successor_map.insert(block_id, successors);
}
Self {
@@ -8696,16 +8730,6 @@ impl<'a> ControlFlowInfo<'a> {
pub fn successors(&self, block: BlockId) -> impl Iterator<Item = BlockId> {
self.successor_map.get(&block).into_iter().flatten().copied()
}
-
- /// Helper function to extract the target of a jump instruction.
- fn extract_jump_target(insn: &Insn) -> Option<BlockId> {
- match insn {
- Insn::Jump(target)
- | Insn::IfTrue { target, .. }
- | Insn::IfFalse { target, .. } => Some(target.target),
- _ => None,
- }
- }
}
pub struct LoopInfo<'a> {
@@ -8856,8 +8880,8 @@ mod rpo_tests {
let entry = function.entry_block;
let exit = function.new_block(0);
function.push_insn(entry, Insn::Jump(BranchEdge { target: exit, args: vec![] }));
- let val = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) });
- function.push_insn(entry, Insn::Return { val });
+ let val = function.push_insn(exit, Insn::Const { val: Const::Value(Qnil) });
+ function.push_insn(exit, Insn::Return { val });
function.seal_entries();
assert_eq!(function.rpo(), vec![entries, entry, exit]);
}
@@ -8871,10 +8895,13 @@ mod rpo_tests {
let exit = function.new_block(0);
function.push_insn(side, Insn::Jump(BranchEdge { target: exit, args: vec![] }));
let val = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) });
- function.push_insn(entry, Insn::IfTrue { val, target: BranchEdge { target: side, args: vec![] } });
- function.push_insn(entry, Insn::Jump(BranchEdge { target: exit, args: vec![] }));
- let val = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) });
- function.push_insn(entry, Insn::Return { val });
+ function.push_insn(entry, Insn::CondBranch {
+ val,
+ if_true: BranchEdge { target: side, args: vec![] },
+ if_false: BranchEdge { target: exit, args: vec![] }
+ });
+ let val = function.push_insn(exit, Insn::Const { val: Const::Value(Qnil) });
+ function.push_insn(exit, Insn::Return { val });
function.seal_entries();
assert_eq!(function.rpo(), vec![entries, entry, side, exit]);
}
@@ -8888,10 +8915,13 @@ mod rpo_tests {
let exit = function.new_block(0);
function.push_insn(side, Insn::Jump(BranchEdge { target: exit, args: vec![] }));
let val = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) });
- function.push_insn(entry, Insn::IfFalse { val, target: BranchEdge { target: side, args: vec![] } });
- function.push_insn(entry, Insn::Jump(BranchEdge { target: exit, args: vec![] }));
- let val = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) });
- function.push_insn(entry, Insn::Return { val });
+ function.push_insn(entry, Insn::CondBranch {
+ val,
+ if_true: BranchEdge { target: exit, args: vec![] },
+ if_false: BranchEdge { target: side, args: vec![] },
+ });
+ let val = function.push_insn(exit, Insn::Const { val: Const::Value(Qnil) });
+ function.push_insn(exit, Insn::Return { val });
function.seal_entries();
assert_eq!(function.rpo(), vec![entries, entry, side, exit]);
}
@@ -8937,6 +8967,7 @@ mod validation_tests {
let val = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) });
let insn_id = function.push_insn(entry, Insn::Return { val });
function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) });
+ function.push_insn(entry, Insn::Unreachable);
function.seal_entries();
assert_matches_err(function.validate(), ValidationError::TerminatorNotAtEnd(entry, insn_id, 1));
}
@@ -8947,7 +8978,14 @@ mod validation_tests {
let entry = function.entry_block;
let side = function.new_block(0);
let val = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) });
- function.push_insn(entry, Insn::IfTrue { val, target: BranchEdge { target: side, args: vec![val, val, val] } });
+ let fall_through = function.new_block(1);
+ function.push_insn(fall_through, Insn::Unreachable);
+ function.push_insn(side, Insn::Unreachable);
+ function.push_insn(entry, Insn::CondBranch {
+ val,
+ if_true: BranchEdge { target: side, args: vec![val, val, val] },
+ if_false: BranchEdge { target: fall_through, args: vec![] }
+ });
function.seal_entries();
assert_matches_err(function.validate(), ValidationError::MismatchedBlockArity(entry, 0, 3));
}
@@ -8958,7 +8996,14 @@ mod validation_tests {
let entry = function.entry_block;
let side = function.new_block(0);
let val = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) });
- function.push_insn(entry, Insn::IfFalse { val, target: BranchEdge { target: side, args: vec![val, val, val] } });
+ let fall_through = function.new_block(1);
+ function.push_insn(fall_through, Insn::Unreachable);
+ function.push_insn(side, Insn::Unreachable);
+ function.push_insn(entry, Insn::CondBranch {
+ val,
+ if_true: BranchEdge { target: fall_through, args: vec![] },
+ if_false: BranchEdge { target: side, args: vec![val, val, val] },
+ });
function.seal_entries();
assert_matches_err(function.validate(), ValidationError::MismatchedBlockArity(entry, 0, 3));
}
@@ -8970,6 +9015,7 @@ mod validation_tests {
let side = function.new_block(0);
let val = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) });
function.push_insn(entry, Insn::Jump ( BranchEdge { target: side, args: vec![val, val, val] } ));
+ function.push_insn(side, Insn::Unreachable);
function.seal_entries();
assert_matches_err(function.validate(), ValidationError::MismatchedBlockArity(entry, 0, 3));
}
@@ -8981,6 +9027,7 @@ mod validation_tests {
// Create an instruction without making it belong to anything.
let dangling = function.new_insn(Insn::Const{val: Const::CBool(true)});
let val = function.push_insn(function.entry_block, Insn::ArrayDup { val: dangling, state: InsnId(0usize) });
+ function.push_insn(function.entry_block, Insn::Unreachable);
function.seal_entries();
assert_matches_err(function.validate_definite_assignment(), ValidationError::OperandNotDefined(entry, val, dangling));
}
@@ -8993,6 +9040,7 @@ mod validation_tests {
// Ret is a non-output instruction.
let ret = function.push_insn(function.entry_block, Insn::Return { val: const_ });
let val = function.push_insn(function.entry_block, Insn::ArrayDup { val: ret, state: InsnId(0usize) });
+ function.push_insn(function.entry_block, Insn::Unreachable);
function.seal_entries();
assert_matches_err(function.validate_definite_assignment(), ValidationError::OperandNotDefined(entry, val, ret));
}
@@ -9007,9 +9055,15 @@ mod validation_tests {
let v0 = function.push_insn(side, Insn::Const { val: Const::Value(VALUE::fixnum_from_usize(3)) });
function.push_insn(side, Insn::Jump(BranchEdge { target: exit, args: vec![] }));
let val1 = function.push_insn(entry, Insn::Const { val: Const::CBool(false) });
- function.push_insn(entry, Insn::IfFalse { val: val1, target: BranchEdge { target: side, args: vec![] } });
- function.push_insn(entry, Insn::Jump(BranchEdge { target: exit, args: vec![] }));
+ function.push_insn(entry, Insn::CondBranch {
+ val: val1,
+ if_true: BranchEdge { target: exit, args: vec![] },
+ if_false: BranchEdge { target: side, args: vec![] },
+ });
let val2 = function.push_insn(exit, Insn::ArrayDup { val: v0, state: v0 });
+ let const_ = function.push_insn(exit, Insn::Const{val: Const::CBool(true)});
+ function.push_insn(exit, Insn::Return { val: const_ });
+
function.seal_entries();
crate::cruby::with_rubyvm(|| {
function.infer_types();
@@ -9027,9 +9081,14 @@ mod validation_tests {
let v0 = function.push_insn(entry, Insn::Const { val: Const::Value(VALUE::fixnum_from_usize(3)) });
function.push_insn(side, Insn::Jump(BranchEdge { target: exit, args: vec![] }));
let val = function.push_insn(entry, Insn::Const { val: Const::CBool(false) });
- function.push_insn(entry, Insn::IfFalse { val, target: BranchEdge { target: side, args: vec![] } });
- function.push_insn(entry, Insn::Jump(BranchEdge { target: exit, args: vec![] }));
+ function.push_insn(entry, Insn::CondBranch {
+ val,
+ if_true: BranchEdge { target: exit, args: vec![] },
+ if_false: BranchEdge { target: side, args: vec![] }
+ });
let _val = function.push_insn(exit, Insn::ArrayDup { val: v0, state: v0 });
+ let const_ = function.push_insn(exit, Insn::Const{val: Const::CBool(true)});
+ function.push_insn(exit, Insn::Return { val: const_ });
function.seal_entries();
crate::cruby::with_rubyvm(|| {
function.infer_types();
@@ -9096,6 +9155,7 @@ mod infer_tests {
fn test_const() {
let mut function = Function::new(std::ptr::null());
let val = function.push_insn(function.entry_block, Insn::Const { val: Const::Value(Qnil) });
+ function.push_insn(function.entry_block, Insn::Unreachable);
assert_bit_equal(function.infer_type(val), types::NilClass);
}
@@ -9105,6 +9165,7 @@ mod infer_tests {
let mut function = Function::new(std::ptr::null());
let nil = function.push_insn(function.entry_block, Insn::Const { val: Const::Value(Qnil) });
let val = function.push_insn(function.entry_block, Insn::Test { val: nil });
+ function.push_insn(function.entry_block, Insn::Unreachable);
function.seal_entries();
function.infer_types();
assert_bit_equal(function.type_of(val), Type::from_cbool(false));
@@ -9117,6 +9178,7 @@ mod infer_tests {
let mut function = Function::new(std::ptr::null());
let false_ = function.push_insn(function.entry_block, Insn::Const { val: Const::Value(Qfalse) });
let val = function.push_insn(function.entry_block, Insn::Test { val: false_ });
+ function.push_insn(function.entry_block, Insn::Unreachable);
function.seal_entries();
function.infer_types();
assert_bit_equal(function.type_of(val), Type::from_cbool(false));
@@ -9129,6 +9191,7 @@ mod infer_tests {
let mut function = Function::new(std::ptr::null());
let true_ = function.push_insn(function.entry_block, Insn::Const { val: Const::Value(Qtrue) });
let val = function.push_insn(function.entry_block, Insn::Test { val: true_ });
+ function.push_insn(function.entry_block, Insn::Unreachable);
function.seal_entries();
function.infer_types();
assert_bit_equal(function.type_of(val), Type::from_cbool(true));
@@ -9161,15 +9224,19 @@ mod infer_tests {
let v0 = function.push_insn(side, Insn::Const { val: Const::Value(VALUE::fixnum_from_usize(3)) });
function.push_insn(side, Insn::Jump(BranchEdge { target: exit, args: vec![v0] }));
let val = function.push_insn(entry, Insn::Const { val: Const::CBool(false) });
- function.push_insn(entry, Insn::IfFalse { val, target: BranchEdge { target: side, args: vec![] } });
let v1 = function.push_insn(entry, Insn::Const { val: Const::Value(VALUE::fixnum_from_usize(4)) });
- function.push_insn(entry, Insn::Jump(BranchEdge { target: exit, args: vec![v1] }));
+ function.push_insn(entry, Insn::CondBranch {
+ val,
+ if_true: BranchEdge { target: exit, args: vec![v1] },
+ if_false: BranchEdge { target: side, args: vec![] },
+ });
let param = function.push_insn(exit, Insn::Param);
+ function.push_insn(exit, Insn::Unreachable);
function.seal_entries();
crate::cruby::with_rubyvm(|| {
function.infer_types();
});
- assert_bit_equal(function.type_of(param), types::Fixnum);
+ assert_bit_equal(function.type_of(param), Type::fixnum(3));
}
#[test]
@@ -9228,14 +9295,18 @@ mod infer_tests {
let v0 = function.push_insn(side, Insn::Const { val: Const::Value(Qtrue) });
function.push_insn(side, Insn::Jump(BranchEdge { target: exit, args: vec![v0] }));
let val = function.push_insn(entry, Insn::Const { val: Const::CBool(false) });
- function.push_insn(entry, Insn::IfFalse { val, target: BranchEdge { target: side, args: vec![] } });
let v1 = function.push_insn(entry, Insn::Const { val: Const::Value(Qfalse) });
- function.push_insn(entry, Insn::Jump(BranchEdge { target: exit, args: vec![v1] }));
+ function.push_insn(entry, Insn::CondBranch {
+ val,
+ if_true: BranchEdge { target: exit, args: vec![v1] },
+ if_false: BranchEdge { target: side, args: vec![] },
+ });
let param = function.push_insn(exit, Insn::Param);
+ function.push_insn(exit, Insn::Unreachable);
function.seal_entries();
crate::cruby::with_rubyvm(|| {
function.infer_types();
- assert_bit_equal(function.type_of(param), types::TrueClass.union(types::FalseClass));
+ assert_bit_equal(function.type_of(param), types::TrueClass);
});
}
}
diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs
index 61b85e3a76..0976ae659e 100644
--- a/zjit/src/hir/opt_tests.rs
+++ b/zjit/src/hir/opt_tests.rs
@@ -4617,9 +4617,9 @@ mod hir_opt_tests {
v15:Fixnum[1] = Const Value(1)
PatchPoint MethodRedefined(Array@0x1008, new@0x1009, cme:0x1010)
PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010)
- v56:BasicObject = CCallVariadic v46, :Array.new@0x1040, v15
+ v52:BasicObject = CCallVariadic v46, :Array.new@0x1040, v15
CheckInterrupts
- Return v56
+ Return v52
");
}
@@ -4889,14 +4889,15 @@ mod hir_opt_tests {
v17:CPtr = GetEP 0
v18:CUInt64 = LoadField v17, :_ep_flags@0x1001
v19:CBool = IsBlockParamModified v18
- IfTrue v19, bb4()
- v24:CInt64 = LoadField v17, :_env_data_index_specval@0x1002
- v25:CInt64 = GuardAnyBitSet v24, CUInt64(1)
- v26:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb6(v26, v10)
+ CondBranch v19, bb4(), bb5()
bb4():
- v22:BasicObject = LoadField v17, :block@0x1010
- Jump bb6(v22, v22)
+ v21:BasicObject = LoadField v17, :block@0x1002
+ Jump bb6(v21, v21)
+ bb5():
+ v23:CInt64 = LoadField v17, :_env_data_index_specval@0x1003
+ v24:CInt64 = GuardAnyBitSet v23, CUInt64(1)
+ v25:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb6(v25, v10)
bb6(v15:BasicObject, v16:BasicObject):
SideExit NoProfileSend recompile
");
@@ -4930,25 +4931,27 @@ mod hir_opt_tests {
v18:CPtr = GetEP 0
v19:CUInt64 = LoadField v18, :_ep_flags@0x1001
v20:CBool = IsBlockParamModified v19
- IfTrue v20, bb4()
- v25:BasicObject = GetBlockParam :block, l0, EP@4
- Jump bb6(v25)
+ CondBranch v20, bb4(), bb5()
bb4():
- v23:BasicObject = LoadField v18, :block@0x1002
- Jump bb6(v23)
+ v22:BasicObject = LoadField v18, :block@0x1002
+ Jump bb6(v22)
+ bb5():
+ v24:BasicObject = GetBlockParam :block, l0, EP@4
+ Jump bb6(v24)
bb6(v17:BasicObject):
- v33:CPtr = GetEP 0
- v34:CUInt64 = LoadField v33, :_ep_flags@0x1001
- v35:CBool = IsBlockParamModified v34
- IfTrue v35, bb7()
- v40:CInt64 = LoadField v33, :_env_data_index_specval@0x1003
- v41:CInt64 = GuardAnyBitSet v40, CUInt64(1)
- v42:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb9(v42, v17)
+ v32:CPtr = GetEP 0
+ v33:CUInt64 = LoadField v32, :_ep_flags@0x1001
+ v34:CBool = IsBlockParamModified v33
+ CondBranch v34, bb7(), bb8()
bb7():
- v38:BasicObject = LoadField v33, :block@0x1002
- Jump bb9(v38, v38)
- bb9(v31:BasicObject, v32:BasicObject):
+ v36:BasicObject = LoadField v32, :block@0x1002
+ Jump bb9(v36, v36)
+ bb8():
+ v38:CInt64 = LoadField v32, :_env_data_index_specval@0x1003
+ v39:CInt64 = GuardAnyBitSet v38, CUInt64(1)
+ v40:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb9(v40, v17)
+ bb9(v30:BasicObject, v31:BasicObject):
SideExit NoProfileSend recompile
");
}
@@ -4979,25 +4982,27 @@ mod hir_opt_tests {
v14:CPtr = GetEP 1
v15:CUInt64 = LoadField v14, :_ep_flags@0x1000
v16:CBool = IsBlockParamModified v15
- IfTrue v16, bb4()
- v21:BasicObject = GetBlockParam :block, l1, EP@3
- Jump bb6(v21)
+ CondBranch v16, bb4(), bb5()
bb4():
- v19:BasicObject = LoadField v14, :block@0x1001
- Jump bb6(v19)
+ v18:BasicObject = LoadField v14, :block@0x1001
+ Jump bb6(v18)
+ bb5():
+ v20:BasicObject = GetBlockParam :block, l1, EP@3
+ Jump bb6(v20)
bb6(v13:BasicObject):
- v28:CPtr = GetEP 1
- v29:CUInt64 = LoadField v28, :_ep_flags@0x1000
- v30:CBool = IsBlockParamModified v29
- IfTrue v30, bb7()
- v35:CInt64 = LoadField v28, :_env_data_index_specval@0x1002
- v36:CInt64 = GuardAnyBitSet v35, CUInt64(1)
- v37:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb9(v37)
+ v27:CPtr = GetEP 1
+ v28:CUInt64 = LoadField v27, :_ep_flags@0x1000
+ v29:CBool = IsBlockParamModified v28
+ CondBranch v29, bb7(), bb8()
bb7():
- v33:BasicObject = LoadField v28, :block@0x1001
- Jump bb9(v33)
- bb9(v27:BasicObject):
+ v31:BasicObject = LoadField v27, :block@0x1001
+ Jump bb9(v31)
+ bb8():
+ v33:CInt64 = LoadField v27, :_env_data_index_specval@0x1002
+ v34:CInt64 = GuardAnyBitSet v33, CUInt64(1)
+ v35:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb9(v35)
+ bb9(v26:BasicObject):
SideExit NoProfileSend recompile
");
}
@@ -5032,29 +5037,32 @@ mod hir_opt_tests {
v18:CPtr = GetEP 0
v19:CUInt64 = LoadField v18, :_ep_flags@0x1001
v20:CBool = IsBlockParamModified v19
- IfTrue v20, bb4()
- v25:CInt64 = LoadField v18, :_env_data_index_specval@0x1002
- v26:CInt64[1] = Const CInt64(1)
- v27:CInt64 = IntAnd v25, v26
- v28:CBool = IsBitEqual v27, v26
- IfTrue v28, bb7()
- v32:CInt64[0] = Const CInt64(0)
- v33:CBool = IsBitEqual v25, v32
- IfTrue v33, bb8()
- SideExit BlockParamProxyProfileNotCovered
+ CondBranch v20, bb4(), bb5()
bb4():
- v23:BasicObject = LoadField v18, :block@0x1003
- Jump bb6(v23, v23)
+ v22:BasicObject = LoadField v18, :block@0x1002
+ Jump bb6(v22, v22)
+ bb5():
+ v24:CInt64 = LoadField v18, :_env_data_index_specval@0x1003
+ v25:CInt64[1] = Const CInt64(1)
+ v26:CInt64 = IntAnd v24, v25
+ v27:CBool = IsBitEqual v26, v25
+ CondBranch v27, bb7(), bb9()
bb7():
- v30:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb6(v30, v10)
+ v29:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb6(v29, v10)
+ bb9():
+ v31:CInt64[0] = Const CInt64(0)
+ v32:CBool = IsBitEqual v24, v31
+ CondBranch v32, bb8(), bb10()
bb8():
- v35:NilClass = Const Value(nil)
- Jump bb6(v35, v10)
+ v34:NilClass = Const Value(nil)
+ Jump bb6(v34, v10)
bb6(v16:BasicObject, v17:BasicObject):
- v39:BasicObject = Send v14, &block, :then, v16 # SendFallbackReason: Complex argument passing
+ v38:BasicObject = Send v14, &block, :then, v16 # SendFallbackReason: Complex argument passing
CheckInterrupts
- Return v39
+ Return v38
+ bb10():
+ SideExit BlockParamProxyProfileNotCovered
");
}
@@ -5080,12 +5088,13 @@ mod hir_opt_tests {
v15:CPtr = GetEP 0
v16:CUInt64 = LoadField v15, :_ep_flags@0x1001
v17:CBool = IsBlockParamModified v16
- IfTrue v17, bb4()
- v22:BasicObject = GetBlockParam :block, l0, EP@3
- Jump bb6(v22)
+ CondBranch v17, bb4(), bb5()
bb4():
- v20:BasicObject = LoadField v15, :block@0x1002
- Jump bb6(v20)
+ v19:BasicObject = LoadField v15, :block@0x1002
+ Jump bb6(v19)
+ bb5():
+ v21:BasicObject = GetBlockParam :block, l0, EP@3
+ Jump bb6(v21)
bb6(v14:BasicObject):
CheckInterrupts
Return v14
@@ -5115,12 +5124,13 @@ mod hir_opt_tests {
v11:CPtr = GetEP 1
v12:CUInt64 = LoadField v11, :_ep_flags@0x1000
v13:CBool = IsBlockParamModified v12
- IfTrue v13, bb4()
- v18:BasicObject = GetBlockParam :block, l1, EP@3
- Jump bb6(v18)
+ CondBranch v13, bb4(), bb5()
bb4():
- v16:BasicObject = LoadField v11, :block@0x1001
- Jump bb6(v16)
+ v15:BasicObject = LoadField v11, :block@0x1001
+ Jump bb6(v15)
+ bb5():
+ v17:BasicObject = GetBlockParam :block, l1, EP@3
+ Jump bb6(v17)
bb6(v10:BasicObject):
CheckInterrupts
Return v10
@@ -7259,7 +7269,8 @@ mod hir_opt_tests {
CheckInterrupts
v16:CBool = Test v10
v17:Falsy = RefineType v10, Falsy
- IfFalse v16, bb4(v9, v17)
+ CondBranch v16, bb6(), bb4(v9, v17)
+ bb6():
v19:Truthy = RefineType v10, Truthy
v21:FalseClass = Const Value(false)
CheckInterrupts
@@ -7590,21 +7601,23 @@ mod hir_opt_tests {
v16 = RefineType v15, CUInt64
v17:CInt64 = IntAnd v12, v14
v18:CBool = IsBitEqual v17, v16
- IfTrue v18, bb5()
+ CondBranch v18, bb5(), bb6()
+ bb5():
+ v20:BasicObject = LoadField v11, :@foo@0x1002
+ Jump bb4(v20)
+ bb6():
v22:CUInt64[0xffffffff0000001f] = Const CUInt64(0xffffffff0000001f)
- v23:CPtr[CPtr(0x1002)] = Const CPtr(0x1002)
+ v23:CPtr[CPtr(0x1003)] = Const CPtr(0x1003)
v24 = RefineType v23, CUInt64
v25:CInt64 = IntAnd v12, v22
v26:CBool = IsBitEqual v25, v24
- IfTrue v26, bb6()
- v30:BasicObject = GetIvar v11, :@foo
- Jump bb4(v30)
- bb5():
- v20:BasicObject = LoadField v11, :@foo@0x1003
- Jump bb4(v20)
- bb6():
+ CondBranch v26, bb7(), bb8()
+ bb7():
v28:BasicObject = LoadField v11, :@foo@0x1004
Jump bb4(v28)
+ bb8():
+ v30:BasicObject = GetIvar v11, :@foo
+ Jump bb4(v30)
bb4(v13:BasicObject):
CheckInterrupts
Return v13
@@ -7947,22 +7960,24 @@ mod hir_opt_tests {
v16 = RefineType v15, CUInt64
v17:CInt64 = IntAnd v12, v14
v18:CBool = IsBitEqual v17, v16
- IfTrue v18, bb5()
+ CondBranch v18, bb5(), bb6()
+ bb5():
+ v20:BasicObject = LoadField v11, :@foo@0x1002
+ Jump bb4(v20)
+ bb6():
v22:CUInt64[0xffffffff0000001f] = Const CUInt64(0xffffffff0000001f)
- v23:CPtr[CPtr(0x1002)] = Const CPtr(0x1002)
+ v23:CPtr[CPtr(0x1003)] = Const CPtr(0x1003)
v24 = RefineType v23, CUInt64
v25:CInt64 = IntAnd v12, v22
v26:CBool = IsBitEqual v25, v24
- IfTrue v26, bb6()
- v31:BasicObject = GetIvar v11, :@foo
- Jump bb4(v31)
- bb5():
- v20:BasicObject = LoadField v11, :@foo@0x1003
- Jump bb4(v20)
- bb6():
+ CondBranch v26, bb7(), bb8()
+ bb7():
v28:CPtr = LoadField v11, :_as_heap@0x1004
v29:BasicObject = LoadField v28, :@foo@0x1000
Jump bb4(v29)
+ bb8():
+ v31:BasicObject = GetIvar v11, :@foo
+ Jump bb4(v31)
bb4(v13:BasicObject):
v34:Fixnum[1] = Const Value(1)
PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018)
@@ -8024,25 +8039,27 @@ mod hir_opt_tests {
v16 = RefineType v15, CUInt64
v17:CInt64 = IntAnd v12, v14
v18:CBool = IsBitEqual v17, v16
- IfTrue v18, bb5()
+ CondBranch v18, bb5(), bb6()
+ bb5():
+ v20:CPtr = LoadField v11, :_as_heap@0x1002
+ v21:BasicObject = LoadField v20, :@foo@0x1000
+ Jump bb4(v21)
+ bb6():
v23:CUInt64[0xffffffff0000001f] = Const CUInt64(0xffffffff0000001f)
- v24:CPtr[CPtr(0x1002)] = Const CPtr(0x1002)
+ v24:CPtr[CPtr(0x1003)] = Const CPtr(0x1003)
v25 = RefineType v24, CUInt64
v26:CInt64 = IntAnd v12, v23
v27:CBool = IsBitEqual v26, v25
- IfTrue v27, bb6()
- v44:CShape = LoadField v11, :_shape_id@0x1003
- v45:CShape[0x1004] = GuardBitEquals v44, CShape(0x1004)
- v46:CPtr = LoadField v11, :_as_heap@0x1005
+ CondBranch v27, bb7(), bb8()
+ bb7():
+ v29:BasicObject = LoadField v11, :@foo@0x1004
+ Jump bb4(v29)
+ bb8():
+ v44:CShape = LoadField v11, :_shape_id@0x1005
+ v45:CShape[0x1006] = GuardBitEquals v44, CShape(0x1006)
+ v46:CPtr = LoadField v11, :_as_heap@0x1002
v47:BasicObject = LoadField v46, :@foo@0x1000
Jump bb4(v47)
- bb5():
- v20:CPtr = LoadField v11, :_as_heap@0x1005
- v21:BasicObject = LoadField v20, :@foo@0x1000
- Jump bb4(v21)
- bb6():
- v29:BasicObject = LoadField v11, :@foo@0x1006
- Jump bb4(v29)
bb4(v13:BasicObject):
v34:Fixnum[1] = Const Value(1)
PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018)
@@ -8096,21 +8113,23 @@ mod hir_opt_tests {
v16 = RefineType v15, CUInt64
v17:CInt64 = IntAnd v12, v14
v18:CBool = IsBitEqual v17, v16
- IfTrue v18, bb5()
+ CondBranch v18, bb5(), bb6()
+ bb5():
+ v20:BasicObject = LoadField v11, :@foo@0x1002
+ Jump bb4(v20)
+ bb6():
v22:CUInt64[0xffffffff0000001f] = Const CUInt64(0xffffffff0000001f)
- v23:CPtr[CPtr(0x1002)] = Const CPtr(0x1002)
+ v23:CPtr[CPtr(0x1003)] = Const CPtr(0x1003)
v24 = RefineType v23, CUInt64
v25:CInt64 = IntAnd v12, v22
v26:CBool = IsBitEqual v25, v24
- IfTrue v26, bb6()
+ CondBranch v26, bb7(), bb8()
+ bb7():
+ v28:BasicObject = LoadField v11, :@foo@0x1002
+ Jump bb4(v28)
+ bb8():
v30:BasicObject = GetIvar v11, :@foo
Jump bb4(v30)
- bb5():
- v20:BasicObject = LoadField v11, :@foo@0x1003
- Jump bb4(v20)
- bb6():
- v28:BasicObject = LoadField v11, :@foo@0x1003
- Jump bb4(v28)
bb4(v13:BasicObject):
v33:Fixnum[1] = Const Value(1)
PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018)
@@ -8162,24 +8181,26 @@ mod hir_opt_tests {
v16 = RefineType v15, CUInt64
v17:CInt64 = IntAnd v12, v14
v18:CBool = IsBitEqual v17, v16
- IfTrue v18, bb5()
+ CondBranch v18, bb5(), bb6()
+ bb5():
+ v20:RubyValue = LoadField v11, :_fields_obj@0x1002
+ v21:BasicObject = LoadField v20, :@a@0x1002
+ Jump bb4(v21)
+ bb6():
v23:CUInt64[0xffffffff0000001f] = Const CUInt64(0xffffffff0000001f)
- v24:CPtr[CPtr(0x1002)] = Const CPtr(0x1002)
+ v24:CPtr[CPtr(0x1003)] = Const CPtr(0x1003)
v25 = RefineType v24, CUInt64
v26:CInt64 = IntAnd v12, v23
v27:CBool = IsBitEqual v26, v25
- IfTrue v27, bb6()
- v33:BasicObject = GetIvar v11, :@a
- Jump bb4(v33)
- bb5():
- v20:RubyValue = LoadField v11, :_fields_obj@0x1003
- v21:BasicObject = LoadField v20, :@a@0x1003
- Jump bb4(v21)
- bb6():
+ CondBranch v27, bb7(), bb8()
+ bb7():
PatchPoint RootBoxOnly
v30:RubyValue = LoadField v11, :_fields_obj@0x1004
- v31:BasicObject = LoadField v30, :@a@0x1003
+ v31:BasicObject = LoadField v30, :@a@0x1002
Jump bb4(v31)
+ bb8():
+ v33:BasicObject = GetIvar v11, :@a
+ Jump bb4(v33)
bb4(v13:BasicObject):
CheckInterrupts
Return v13
@@ -8227,15 +8248,16 @@ mod hir_opt_tests {
Jump bb3(v6, v7)
bb3(v9:BasicObject, v10:BasicObject):
v15:CBool = HasType v10, ObjectSubclass[class_exact:C]
- IfTrue v15, bb5(v9, v10, v10)
- v24:BasicObject = Send v10, :foo # SendFallbackReason: SendWithoutBlock: polymorphic fallback
- Jump bb4(v9, v10, v24)
+ CondBranch v15, bb5(v9, v10, v10), bb6()
bb5(v16:BasicObject, v17:BasicObject, v18:BasicObject):
v20:ObjectSubclass[class_exact:C] = RefineType v18, ObjectSubclass[class_exact:C]
PatchPoint NoSingletonClass(C@0x1008)
PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018)
v37:BasicObject = GetIvar v20, :@foo
Jump bb4(v16, v17, v37)
+ bb6():
+ v24:BasicObject = Send v10, :foo # SendFallbackReason: SendWithoutBlock: polymorphic fallback
+ Jump bb4(v9, v10, v24)
bb4(v26:BasicObject, v27:BasicObject, v28:BasicObject):
CheckInterrupts
Return v28
@@ -8376,18 +8398,19 @@ mod hir_opt_tests {
v18:CPtr = GetEP 0
v19:CUInt64 = LoadField v18, :_ep_flags@0x1001
v20:CBool = IsBlockParamModified v19
- IfTrue v20, bb4()
- v25:CInt64 = LoadField v18, :_env_data_index_specval@0x1002
- v26:CInt64 = GuardAnyBitSet v25, CUInt64(1)
- v27:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb6(v27, v10)
+ CondBranch v20, bb4(), bb5()
bb4():
- v23:BasicObject = LoadField v18, :block@0x1010
- Jump bb6(v23, v23)
+ v22:BasicObject = LoadField v18, :block@0x1002
+ Jump bb6(v22, v22)
+ bb5():
+ v24:CInt64 = LoadField v18, :_env_data_index_specval@0x1003
+ v25:CInt64 = GuardAnyBitSet v24, CUInt64(1)
+ v26:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb6(v26, v10)
bb6(v16:BasicObject, v17:BasicObject):
- v30:BasicObject = Send v14, &block, :map, v16 # SendFallbackReason: Complex argument passing
+ v29:BasicObject = Send v14, &block, :map, v16 # SendFallbackReason: Complex argument passing
CheckInterrupts
- Return v30
+ Return v29
");
}
@@ -8415,18 +8438,19 @@ mod hir_opt_tests {
v18:CPtr = GetEP 0
v19:CUInt64 = LoadField v18, :_ep_flags@0x1001
v20:CBool = IsBlockParamModified v19
- IfTrue v20, bb4()
- v25:CInt64 = LoadField v18, :_env_data_index_specval@0x1002
- v26:CInt64[0] = GuardBitEquals v25, CInt64(0)
- v27:NilClass = Const Value(nil)
- Jump bb6(v27, v10)
+ CondBranch v20, bb4(), bb5()
bb4():
- v23:BasicObject = LoadField v18, :block@0x1003
- Jump bb6(v23, v23)
+ v22:BasicObject = LoadField v18, :block@0x1002
+ Jump bb6(v22, v22)
+ bb5():
+ v24:CInt64 = LoadField v18, :_env_data_index_specval@0x1003
+ v25:CInt64[0] = GuardBitEquals v24, CInt64(0)
+ v26:NilClass = Const Value(nil)
+ Jump bb6(v26, v10)
bb6(v16:BasicObject, v17:BasicObject):
- v30:BasicObject = Send v14, &block, :map, v16 # SendFallbackReason: Complex argument passing
+ v29:BasicObject = Send v14, &block, :map, v16 # SendFallbackReason: Complex argument passing
CheckInterrupts
- Return v30
+ Return v29
");
}
@@ -8455,18 +8479,19 @@ mod hir_opt_tests {
v13:CPtr = GetEP 1
v14:CUInt64 = LoadField v13, :_ep_flags@0x1000
v15:CBool = IsBlockParamModified v14
- IfTrue v15, bb4()
- v20:CInt64 = LoadField v13, :_env_data_index_specval@0x1001
- v21:CInt64 = GuardAnyBitSet v20, CUInt64(1)
- v22:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb6(v22)
+ CondBranch v15, bb4(), bb5()
bb4():
- v18:BasicObject = LoadField v13, :block@0x1010
- Jump bb6(v18)
+ v17:BasicObject = LoadField v13, :block@0x1001
+ Jump bb6(v17)
+ bb5():
+ v19:CInt64 = LoadField v13, :_env_data_index_specval@0x1002
+ v20:CInt64 = GuardAnyBitSet v19, CUInt64(1)
+ v21:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb6(v21)
bb6(v12:BasicObject):
- v25:BasicObject = Send v10, &block, :map, v12 # SendFallbackReason: Complex argument passing
+ v24:BasicObject = Send v10, &block, :map, v12 # SendFallbackReason: Complex argument passing
CheckInterrupts
- Return v25
+ Return v24
");
}
@@ -11950,18 +11975,19 @@ mod hir_opt_tests {
v18:CPtr = GetEP 0
v19:CUInt64 = LoadField v18, :_ep_flags@0x1001
v20:CBool = IsBlockParamModified v19
- IfTrue v20, bb4()
- v25:CInt64 = LoadField v18, :_env_data_index_specval@0x1002
- v26:CInt64 = GuardAnyBitSet v25, CUInt64(1)
- v27:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb6(v27, v10)
+ CondBranch v20, bb4(), bb5()
bb4():
- v23:BasicObject = LoadField v18, :block@0x1010
- Jump bb6(v23, v23)
+ v22:BasicObject = LoadField v18, :block@0x1002
+ Jump bb6(v22, v22)
+ bb5():
+ v24:CInt64 = LoadField v18, :_env_data_index_specval@0x1003
+ v25:CInt64 = GuardAnyBitSet v24, CUInt64(1)
+ v26:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb6(v26, v10)
bb6(v16:BasicObject, v17:BasicObject):
- v30:BasicObject = Send v14, &block, :map, v16 # SendFallbackReason: Complex argument passing
+ v29:BasicObject = Send v14, &block, :map, v16 # SendFallbackReason: Complex argument passing
CheckInterrupts
- Return v30
+ Return v29
");
}
@@ -14565,7 +14591,8 @@ mod hir_opt_tests {
v5:CPtr = LoadPC
v6:CPtr[CPtr(0x1002)] = Const CPtr(0x1002)
v7:CBool = IsBitEqual v5, v6
- IfTrue v7, bb3(v1, v3, v4)
+ CondBranch v7, bb3(v1, v3, v4), bb6()
+ bb6():
Jump bb5(v1, v3, v4)
bb2():
EntryPoint JIT(0)
@@ -14723,7 +14750,8 @@ mod hir_opt_tests {
v4:CPtr = LoadPC
v5:CPtr[CPtr(0x1001)] = Const CPtr(0x1001)
v6:CBool = IsBitEqual v4, v5
- IfTrue v6, bb3(v1, v3)
+ CondBranch v6, bb3(v1, v3), bb6()
+ bb6():
Jump bb5(v1, v3)
bb2():
EntryPoint JIT(0)
@@ -14782,7 +14810,8 @@ mod hir_opt_tests {
CheckInterrupts
v16:CBool = Test v10
v17:Falsy = RefineType v10, Falsy
- IfFalse v16, bb6(v9, v17)
+ CondBranch v16, bb7(), bb6(v9, v17)
+ bb7():
v19:Truthy = RefineType v10, Truthy
CheckInterrupts
v38:Fixnum[3] = Const Value(3)
@@ -14827,21 +14856,23 @@ mod hir_opt_tests {
Jump bb3(v6, v7)
bb3(v9:BasicObject, v10:BasicObject):
v15:CBool = HasType v10, ObjectSubclass[class_exact:C]
- IfTrue v15, bb5(v9, v10, v10)
- v24:CBool = HasType v10, ObjectSubclass[class_exact:D]
- IfTrue v24, bb6(v9, v10, v10)
- v33:BasicObject = Send v10, :foo # SendFallbackReason: SendWithoutBlock: polymorphic fallback
- Jump bb4(v9, v10, v33)
+ CondBranch v15, bb5(v9, v10, v10), bb6()
bb5(v16:BasicObject, v17:BasicObject, v18:BasicObject):
PatchPoint NoSingletonClass(C@0x1008)
PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018)
v55:Fixnum[3] = Const Value(3)
Jump bb4(v16, v17, v55)
- bb6(v25:BasicObject, v26:BasicObject, v27:BasicObject):
+ bb6():
+ v24:CBool = HasType v10, ObjectSubclass[class_exact:D]
+ CondBranch v24, bb7(v9, v10, v10), bb8()
+ bb7(v25:BasicObject, v26:BasicObject, v27:BasicObject):
PatchPoint NoSingletonClass(D@0x1040)
PatchPoint MethodRedefined(D@0x1040, foo@0x1010, cme:0x1048)
v56:Fixnum[4] = Const Value(4)
Jump bb4(v25, v26, v56)
+ bb8():
+ v33:BasicObject = Send v10, :foo # SendFallbackReason: SendWithoutBlock: polymorphic fallback
+ Jump bb4(v9, v10, v33)
bb4(v35:BasicObject, v36:BasicObject, v37:BasicObject):
v40:Fixnum[2] = Const Value(2)
PatchPoint MethodRedefined(Integer@0x1070, +@0x1078, cme:0x1080)
@@ -14879,20 +14910,22 @@ mod hir_opt_tests {
Jump bb3(v6, v7)
bb3(v9:BasicObject, v10:BasicObject):
v15:CBool = HasType v10, ObjectSubclass[class_exact:C]
- IfTrue v15, bb5(v9, v10, v10)
- v24:CBool = HasType v10, Fixnum
- IfTrue v24, bb6(v9, v10, v10)
- v33:BasicObject = Send v10, :itself # SendFallbackReason: SendWithoutBlock: polymorphic fallback
- Jump bb4(v9, v10, v33)
+ CondBranch v15, bb5(v9, v10, v10), bb6()
bb5(v16:BasicObject, v17:BasicObject, v18:BasicObject):
v20:ObjectSubclass[class_exact:C] = RefineType v18, ObjectSubclass[class_exact:C]
PatchPoint NoSingletonClass(C@0x1008)
PatchPoint MethodRedefined(C@0x1008, itself@0x1010, cme:0x1018)
Jump bb4(v16, v17, v20)
- bb6(v25:BasicObject, v26:BasicObject, v27:BasicObject):
+ bb6():
+ v24:CBool = HasType v10, Fixnum
+ CondBranch v24, bb7(v9, v10, v10), bb8()
+ bb7(v25:BasicObject, v26:BasicObject, v27:BasicObject):
v29:Fixnum = RefineType v27, Fixnum
PatchPoint MethodRedefined(Integer@0x1040, itself@0x1010, cme:0x1018)
Jump bb4(v25, v26, v29)
+ bb8():
+ v33:BasicObject = Send v10, :itself # SendFallbackReason: SendWithoutBlock: polymorphic fallback
+ Jump bb4(v9, v10, v33)
bb4(v35:BasicObject, v36:BasicObject, v37:BasicObject):
CheckInterrupts
Return v37
@@ -14932,21 +14965,23 @@ mod hir_opt_tests {
Jump bb3(v6, v7)
bb3(v9:BasicObject, v10:BasicObject):
v15:CBool = HasType v10, Fixnum
- IfTrue v15, bb5(v9, v10, v10)
- v24:CBool = HasType v10, Bignum
- IfTrue v24, bb6(v9, v10, v10)
- v33:BasicObject = Send v10, :to_s # SendFallbackReason: SendWithoutBlock: polymorphic fallback
- Jump bb4(v9, v10, v33)
+ CondBranch v15, bb5(v9, v10, v10), bb6()
bb5(v16:BasicObject, v17:BasicObject, v18:BasicObject):
v20:Fixnum = RefineType v18, Fixnum
PatchPoint MethodRedefined(Integer@0x1008, to_s@0x1010, cme:0x1018)
v46:StringExact = CCallVariadic v20, :Integer#to_s@0x1040
Jump bb4(v16, v17, v46)
- bb6(v25:BasicObject, v26:BasicObject, v27:BasicObject):
+ bb6():
+ v24:CBool = HasType v10, Bignum
+ CondBranch v24, bb7(v9, v10, v10), bb8()
+ bb7(v25:BasicObject, v26:BasicObject, v27:BasicObject):
v29:Bignum = RefineType v27, Bignum
PatchPoint MethodRedefined(Integer@0x1008, to_s@0x1010, cme:0x1018)
v49:StringExact = CCallVariadic v29, :Integer#to_s@0x1040
Jump bb4(v25, v26, v49)
+ bb8():
+ v33:BasicObject = Send v10, :to_s # SendFallbackReason: SendWithoutBlock: polymorphic fallback
+ Jump bb4(v9, v10, v33)
bb4(v35:BasicObject, v36:BasicObject, v37:BasicObject):
CheckInterrupts
Return v37
@@ -14983,21 +15018,23 @@ mod hir_opt_tests {
Jump bb3(v6, v7)
bb3(v9:BasicObject, v10:BasicObject):
v15:CBool = HasType v10, Flonum
- IfTrue v15, bb5(v9, v10, v10)
- v24:CBool = HasType v10, HeapFloat
- IfTrue v24, bb6(v9, v10, v10)
- v33:BasicObject = Send v10, :to_s # SendFallbackReason: SendWithoutBlock: polymorphic fallback
- Jump bb4(v9, v10, v33)
+ CondBranch v15, bb5(v9, v10, v10), bb6()
bb5(v16:BasicObject, v17:BasicObject, v18:BasicObject):
v20:Flonum = RefineType v18, Flonum
PatchPoint MethodRedefined(Float@0x1008, to_s@0x1010, cme:0x1018)
v46:BasicObject = CCallWithFrame v20, :Float#to_s@0x1040
Jump bb4(v16, v17, v46)
- bb6(v25:BasicObject, v26:BasicObject, v27:BasicObject):
+ bb6():
+ v24:CBool = HasType v10, HeapFloat
+ CondBranch v24, bb7(v9, v10, v10), bb8()
+ bb7(v25:BasicObject, v26:BasicObject, v27:BasicObject):
v29:HeapFloat = RefineType v27, HeapFloat
PatchPoint MethodRedefined(Float@0x1008, to_s@0x1010, cme:0x1018)
v49:BasicObject = CCallWithFrame v29, :Float#to_s@0x1040
Jump bb4(v25, v26, v49)
+ bb8():
+ v33:BasicObject = Send v10, :to_s # SendFallbackReason: SendWithoutBlock: polymorphic fallback
+ Jump bb4(v9, v10, v33)
bb4(v35:BasicObject, v36:BasicObject, v37:BasicObject):
CheckInterrupts
Return v37
@@ -15034,21 +15071,23 @@ mod hir_opt_tests {
Jump bb3(v6, v7)
bb3(v9:BasicObject, v10:BasicObject):
v15:CBool = HasType v10, StaticSymbol
- IfTrue v15, bb5(v9, v10, v10)
- v24:CBool = HasType v10, DynamicSymbol
- IfTrue v24, bb6(v9, v10, v10)
- v33:BasicObject = Send v10, :to_s # SendFallbackReason: SendWithoutBlock: polymorphic fallback
- Jump bb4(v9, v10, v33)
+ CondBranch v15, bb5(v9, v10, v10), bb6()
bb5(v16:BasicObject, v17:BasicObject, v18:BasicObject):
v20:StaticSymbol = RefineType v18, StaticSymbol
PatchPoint MethodRedefined(Symbol@0x1008, to_s@0x1010, cme:0x1018)
v48:StringExact = InvokeBuiltin leaf <inline_expr>, v20
Jump bb4(v16, v17, v48)
- bb6(v25:BasicObject, v26:BasicObject, v27:BasicObject):
+ bb6():
+ v24:CBool = HasType v10, DynamicSymbol
+ CondBranch v24, bb7(v9, v10, v10), bb8()
+ bb7(v25:BasicObject, v26:BasicObject, v27:BasicObject):
v29:DynamicSymbol = RefineType v27, DynamicSymbol
PatchPoint MethodRedefined(Symbol@0x1008, to_s@0x1010, cme:0x1018)
v49:StringExact = InvokeBuiltin leaf <inline_expr>, v29
Jump bb4(v25, v26, v49)
+ bb8():
+ v33:BasicObject = Send v10, :to_s # SendFallbackReason: SendWithoutBlock: polymorphic fallback
+ Jump bb4(v9, v10, v33)
bb4(v35:BasicObject, v36:BasicObject, v37:BasicObject):
CheckInterrupts
Return v37
@@ -15089,14 +15128,15 @@ mod hir_opt_tests {
Jump bb3(v6, v7)
bb3(v9:BasicObject, v10:BasicObject):
v15:CBool = HasType v10, ObjectSubclass[class_exact:C]
- IfTrue v15, bb5(v9, v10, v10)
- v24:BasicObject = Send v10, :foo # SendFallbackReason: SendWithoutBlock: polymorphic fallback
- Jump bb4(v9, v10, v24)
+ CondBranch v15, bb5(v9, v10, v10), bb6()
bb5(v16:BasicObject, v17:BasicObject, v18:BasicObject):
PatchPoint NoSingletonClass(C@0x1008)
PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018)
v38:Fixnum[3] = Const Value(3)
Jump bb4(v16, v17, v38)
+ bb6():
+ v24:BasicObject = Send v10, :foo # SendFallbackReason: SendWithoutBlock: polymorphic fallback
+ Jump bb4(v9, v10, v24)
bb4(v26:BasicObject, v27:BasicObject, v28:BasicObject):
CheckInterrupts
Return v28
@@ -15206,10 +15246,12 @@ mod hir_opt_tests {
v7:CPtr = LoadPC
v8:CPtr[CPtr(0x1003)] = Const CPtr(0x1003)
v9:CBool = IsBitEqual v7, v8
- IfTrue v9, bb3(v1, v3, v4, v5, v6)
+ CondBranch v9, bb3(v1, v3, v4, v5, v6), bb9()
+ bb9():
v11:CPtr[CPtr(0x1004)] = Const CPtr(0x1004)
v12:CBool = IsBitEqual v7, v11
- IfTrue v12, bb5(v1, v3, v4, v5, v6)
+ CondBranch v12, bb5(v1, v3, v4, v5, v6), bb10()
+ bb10():
Jump bb7(v1, v3, v4, v5, v6)
bb2():
EntryPoint JIT(0)
@@ -15247,7 +15289,8 @@ mod hir_opt_tests {
CheckInterrupts
v68:CBool = Test v60
v69:Truthy = RefineType v60, Truthy
- IfTrue v68, bb8(v58, v59, v69, v61, v62)
+ CondBranch v68, bb8(v58, v59, v69, v61, v62), bb11()
+ bb11():
v71:Falsy = RefineType v60, Falsy
PatchPoint MethodRedefined(Object@0x1010, lambda@0x1018, cme:0x1020)
v118:ObjectSubclass[class_exact*:Object@VALUE(0x1010)] = GuardType v58, ObjectSubclass[class_exact*:Object@VALUE(0x1010)]
@@ -15297,20 +15340,18 @@ mod hir_opt_tests {
v13:NilClass = Const Value(nil)
v15:TrueClass|NilClass = Defined yield, v13
v17:CBool = Test v15
- IfFalse v17, bb4(v8, v9)
+ CondBranch v17, bb9(), bb4(v8, v9)
+ bb9():
v35:Fixnum[0] = Const Value(0)
Jump bb8(v8, v35)
- bb4(v23:BasicObject, v24:NilClass):
- v28:BasicObject = InvokeBuiltin <inline_expr>, v23
- CheckInterrupts
- Return v28
bb8(v48:BasicObject, v49:Fixnum):
v84:Array = RefineType v48, Array
v85:CInt64 = ArrayLength v84
v86:Fixnum = BoxFixnum v85
v87:BoolExact = FixnumGe v49, v86
v54:CBool = Test v87
- IfFalse v54, bb7(v48, v49)
+ CondBranch v54, bb10(), bb7(v48, v49)
+ bb10():
CheckInterrupts
Return v48
bb7(v67:BasicObject, v68:Fixnum):
@@ -15322,6 +15363,10 @@ mod hir_opt_tests {
v92:Fixnum = FixnumAdd v68, v91
PatchPoint NoEPEscape(each)
Jump bb8(v67, v92)
+ bb4(v23:BasicObject, v24:NilClass):
+ v28:BasicObject = InvokeBuiltin <inline_expr>, v23
+ CheckInterrupts
+ Return v28
");
}
@@ -15556,7 +15601,8 @@ mod hir_opt_tests {
CheckInterrupts
v16:CBool = Test v10
v17:Falsy = RefineType v10, Falsy
- IfFalse v16, bb4(v9, v17)
+ CondBranch v16, bb5(), bb4(v9, v17)
+ bb5():
v19:Truthy = RefineType v10, Truthy
v23:Fixnum[42] = Const Value(42)
PatchPoint MethodRedefined(Object@0x1008, greet_recompile@0x1010, cme:0x1018)
@@ -15614,7 +15660,8 @@ mod hir_opt_tests {
CheckInterrupts
v16:CBool = Test v10
v17:Falsy = RefineType v10, Falsy
- IfFalse v16, bb4(v9, v17)
+ CondBranch v16, bb5(), bb4(v9, v17)
+ bb5():
v19:Truthy = RefineType v10, Truthy
v23:Fixnum[42] = Const Value(42)
v25:BasicObject = Send v9, :greet_final, v23 # SendFallbackReason: SendWithoutBlock: no profile data available
@@ -15658,12 +15705,13 @@ mod hir_opt_tests {
v15:CInt64 = IntAnd v13, v14
v16:CInt64[3] = Const CInt64(3)
v17:CBool = IsBitEqual v15, v16
- IfTrue v17, bb5()
- v22:BasicObject = InvokeBlock, v10 # SendFallbackReason: InvokeBlock: not yet specialized
- Jump bb4(v22)
+ CondBranch v17, bb5(), bb6()
bb5():
v20:BasicObject = InvokeBlockIfunc v13, v10
Jump bb4(v20)
+ bb6():
+ v22:BasicObject = InvokeBlock, v10 # SendFallbackReason: InvokeBlock: not yet specialized
+ Jump bb4(v22)
bb4(v18:BasicObject):
v27:Fixnum[2] = Const Value(2)
v29:CPtr = GetEP 0
@@ -15672,13 +15720,14 @@ mod hir_opt_tests {
v32:CInt64 = IntAnd v30, v31
v33:CInt64[3] = Const CInt64(3)
v34:CBool = IsBitEqual v32, v33
- IfTrue v34, bb7()
- v39:BasicObject = InvokeBlock, v27 # SendFallbackReason: InvokeBlock: not yet specialized
- Jump bb6(v39)
- bb7():
+ CondBranch v34, bb8(), bb9()
+ bb8():
v37:BasicObject = InvokeBlockIfunc v30, v27
- Jump bb6(v37)
- bb6(v35:BasicObject):
+ Jump bb7(v37)
+ bb9():
+ v39:BasicObject = InvokeBlock, v27 # SendFallbackReason: InvokeBlock: not yet specialized
+ Jump bb7(v39)
+ bb7(v35:BasicObject):
CheckInterrupts
Return v35
");
@@ -15825,10 +15874,7 @@ mod hir_opt_tests {
v110:BoolExact = FixnumLt v20, v24
CheckInterrupts
v30:CBool = Test v110
- IfTrue v30, bb4(v19, v20)
- v35:NilClass = Const Value(nil)
- CheckInterrupts
- Return v35
+ CondBranch v30, bb4(v19, v20), bb7()
bb4(v40:BasicObject, v41:Fixnum):
PatchPoint SingleRactorMode
v46:HeapBasicObject = GuardType v40, HeapBasicObject
@@ -15838,35 +15884,38 @@ mod hir_opt_tests {
v51 = RefineType v50, CUInt64
v52:CInt64 = IntAnd v47, v49
v53:CBool = IsBitEqual v52, v51
- IfTrue v53, bb8()
+ CondBranch v53, bb9(), bb10()
+ bb9():
+ v55:BasicObject = LoadField v46, :@levar@0x103a
+ Jump bb8(v55)
+ bb10():
v57:CUInt64[0xffffffff0000001f] = Const CUInt64(0xffffffff0000001f)
- v58:CPtr[CPtr(0x103a)] = Const CPtr(0x103a)
+ v58:CPtr[CPtr(0x103b)] = Const CPtr(0x103b)
v59 = RefineType v58, CUInt64
v60:CInt64 = IntAnd v47, v57
v61:CBool = IsBitEqual v60, v59
- IfTrue v61, bb9()
- v97:CShape = LoadField v46, :_shape_id@0x103b
- v98:CShape[0x103c] = GuardBitEquals v97, CShape(0x103c)
- v99:BasicObject = LoadField v46, :@levar@0x103d
- Jump bb7(v99)
- bb8():
- v55:BasicObject = LoadField v46, :@levar@0x103d
- Jump bb7(v55)
- bb9():
+ CondBranch v61, bb11(), bb12()
+ bb11():
v63:NilClass = Const Value(nil)
- Jump bb7(v63)
- bb7(v48:BasicObject):
+ Jump bb8(v63)
+ bb12():
+ v97:CShape = LoadField v46, :_shape_id@0x103c
+ v98:CShape[0x103d] = GuardBitEquals v97, CShape(0x103d)
+ v99:BasicObject = LoadField v46, :@levar@0x103a
+ Jump bb8(v99)
+ bb8(v48:BasicObject):
CheckInterrupts
v69:CBool = Test v48
- IfTrue v69, bb5(v46, v41)
+ CondBranch v69, bb5(v46, v41), bb13()
+ bb13():
PatchPoint NoEPEscape(set_value_loop)
PatchPoint SingleRactorMode
- v101:CShape = LoadField v46, :_shape_id@0x103b
+ v101:CShape = LoadField v46, :_shape_id@0x103c
v102:CShape[0x103e] = GuardBitEquals v101, CShape(0x103e)
- StoreField v46, :@levar@0x103d, v41
+ StoreField v46, :@levar@0x103a, v41
WriteBarrier v46, v41
- v105:CShape[0x103c] = Const CShape(0x103c)
- StoreField v46, :_shape_id@0x103b, v105
+ v105:CShape[0x103d] = Const CShape(0x103d)
+ StoreField v46, :_shape_id@0x103c, v105
v79:HeapBasicObject = RefineType v46, HeapBasicObject
Jump bb5(v79, v41)
bb5(v81:HeapBasicObject, v82:Fixnum):
@@ -15875,6 +15924,10 @@ mod hir_opt_tests {
PatchPoint MethodRedefined(Integer@0x1000, +@0x103f, cme:0x1040)
v114:Fixnum = FixnumAdd v82, v89
Jump bb6(v81, v114)
+ bb7():
+ v35:NilClass = Const Value(nil)
+ CheckInterrupts
+ Return v35
");
}
diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs
index 1a836a561f..838a9b8808 100644
--- a/zjit/src/hir/tests.rs
+++ b/zjit/src/hir/tests.rs
@@ -296,7 +296,8 @@ pub(crate) mod hir_build_tests {
v4:CPtr = LoadPC
v5:CPtr[CPtr(0x1001)] = Const CPtr(0x1001)
v6:CBool = IsBitEqual v4, v5
- IfTrue v6, bb3(v1, v3)
+ CondBranch v6, bb3(v1, v3), bb6()
+ bb6():
Jump bb5(v1, v3)
bb2():
EntryPoint JIT(0)
@@ -373,15 +374,16 @@ pub(crate) mod hir_build_tests {
CheckInterrupts
v23:CBool = Test v20
v24:Truthy = RefineType v20, Truthy
- IfTrue v23, bb4(v9, v10, v14, v10)
- v26:Falsy = RefineType v20, Falsy
- v31:Fixnum[2] = Const Value(2)
- CheckInterrupts
- Return v31
+ CondBranch v23, bb4(v9, v10, v14, v10), bb5()
bb4(v36:BasicObject, v37:BasicObject, v38:NilClass, v39:BasicObject):
v44:Fixnum[1] = Const Value(1)
CheckInterrupts
Return v44
+ bb5():
+ v26:Falsy = RefineType v20, Falsy
+ v31:Fixnum[2] = Const Value(2)
+ CheckInterrupts
+ Return v31
");
}
@@ -419,15 +421,16 @@ pub(crate) mod hir_build_tests {
CheckInterrupts
v22:CBool = Test v19
v23:Truthy = RefineType v19, Truthy
- IfTrue v22, bb4(v9, v10, v10)
- v25:Falsy = RefineType v19, Falsy
- v29:Fixnum[2] = Const Value(2)
- CheckInterrupts
- Return v29
+ CondBranch v22, bb4(v9, v10, v10), bb5()
bb4(v34:BasicObject, v35:BasicObject, v36:BasicObject):
v41:Fixnum[1] = Const Value(1)
CheckInterrupts
Return v41
+ bb5():
+ v25:Falsy = RefineType v19, Falsy
+ v29:Fixnum[2] = Const Value(2)
+ CheckInterrupts
+ Return v29
");
}
@@ -463,15 +466,16 @@ pub(crate) mod hir_build_tests {
CheckInterrupts
v18:CBool = Test v15
v19:Truthy = RefineType v15, Truthy
- IfTrue v18, bb4(v6)
- v21:Falsy = RefineType v15, Falsy
- v24:Fixnum[2] = Const Value(2)
- CheckInterrupts
- Return v24
+ CondBranch v18, bb4(v6), bb5()
bb4(v29:BasicObject):
v33:Fixnum[1] = Const Value(1)
CheckInterrupts
Return v33
+ bb5():
+ v21:Falsy = RefineType v15, Falsy
+ v24:Fixnum[2] = Const Value(2)
+ CheckInterrupts
+ Return v24
");
}
@@ -1161,7 +1165,8 @@ pub(crate) mod hir_build_tests {
v5:CPtr = LoadPC
v6:CPtr[CPtr(0x1001)] = Const CPtr(0x1001)
v7:CBool = IsBitEqual v5, v6
- IfTrue v7, bb3(v1, v3, v4)
+ CondBranch v7, bb3(v1, v3, v4), bb6()
+ bb6():
Jump bb5(v1, v3, v4)
bb2():
EntryPoint JIT(0)
@@ -1203,7 +1208,8 @@ pub(crate) mod hir_build_tests {
v5:CPtr = LoadPC
v6:CPtr[CPtr(0x1001)] = Const CPtr(0x1001)
v7:CBool = IsBitEqual v5, v6
- IfTrue v7, bb3(v1, v3, v4)
+ CondBranch v7, bb3(v1, v3, v4), bb6()
+ bb6():
Jump bb5(v1, v3, v4)
bb2():
EntryPoint JIT(0)
@@ -1241,7 +1247,8 @@ pub(crate) mod hir_build_tests {
v4:CPtr = LoadPC
v5:CPtr[CPtr(0x1001)] = Const CPtr(0x1001)
v6:CBool = IsBitEqual v4, v5
- IfTrue v6, bb3(v1, v3)
+ CondBranch v6, bb3(v1, v3), bb6()
+ bb6():
Jump bb5(v1, v3)
bb2():
EntryPoint JIT(0)
@@ -1340,7 +1347,8 @@ pub(crate) mod hir_build_tests {
CheckInterrupts
v13:CBool = Test v10
v14:NilClass = RefineType v10, Falsy
- IfFalse v13, bb4(v6)
+ CondBranch v13, bb5(), bb4(v6)
+ bb5():
v16:TrueClass = RefineType v10, Truthy
v19:Fixnum[3] = Const Value(3)
CheckInterrupts
@@ -1457,7 +1465,8 @@ pub(crate) mod hir_build_tests {
CheckInterrupts
v16:CBool = Test v10
v17:Falsy = RefineType v10, Falsy
- IfFalse v16, bb4(v9, v17)
+ CondBranch v16, bb5(), bb4(v9, v17)
+ bb5():
v19:Truthy = RefineType v10, Truthy
v22:Fixnum[3] = Const Value(3)
CheckInterrupts
@@ -1500,7 +1509,8 @@ pub(crate) mod hir_build_tests {
CheckInterrupts
v19:CBool = Test v12
v20:Falsy = RefineType v12, Falsy
- IfFalse v19, bb4(v11, v20, v13)
+ CondBranch v19, bb6(), bb4(v11, v20, v13)
+ bb6():
v22:Truthy = RefineType v12, Truthy
v25:Fixnum[3] = Const Value(3)
CheckInterrupts
@@ -1843,17 +1853,18 @@ pub(crate) mod hir_build_tests {
CheckInterrupts
v38:CBool = Test v35
v39:Truthy = RefineType v35, Truthy
- IfTrue v38, bb4(v26, v27, v28)
- v41:Falsy = RefineType v35, Falsy
- v43:NilClass = Const Value(nil)
- CheckInterrupts
- Return v27
+ CondBranch v38, bb4(v26, v27, v28), bb6()
bb4(v51:BasicObject, v52:BasicObject, v53:BasicObject):
v58:Fixnum[1] = Const Value(1)
v61:BasicObject = Send v52, :+, v58 # SendFallbackReason: Uncategorized(opt_plus)
v66:Fixnum[1] = Const Value(1)
v69:BasicObject = Send v53, :-, v66 # SendFallbackReason: Uncategorized(opt_minus)
Jump bb5(v51, v61, v69)
+ bb6():
+ v41:Falsy = RefineType v35, Falsy
+ v43:NilClass = Const Value(nil)
+ CheckInterrupts
+ Return v27
");
}
@@ -1915,7 +1926,8 @@ pub(crate) mod hir_build_tests {
CheckInterrupts
v19:CBool[true] = Test v13
v20 = RefineType v13, Falsy
- IfFalse v19, bb4(v8, v20)
+ CondBranch v19, bb5(), bb4(v8, v20)
+ bb5():
v22:TrueClass = RefineType v13, Truthy
v25:Fixnum[3] = Const Value(3)
CheckInterrupts
@@ -2473,16 +2485,15 @@ pub(crate) mod hir_build_tests {
v36:CPtr = GetEP 0
v37:CUInt64 = LoadField v36, :_ep_flags@0x1004
v38:CBool = IsBlockParamModified v37
- IfTrue v38, bb4()
- Jump bb5()
+ CondBranch v38, bb4(), bb5()
bb4():
- v41:BasicObject = LoadField v36, :&@0x1005
- Jump bb6(v41, v41)
+ v40:BasicObject = LoadField v36, :&@0x1005
+ Jump bb6(v40, v40)
bb5():
- v43:CInt64 = LoadField v36, :_env_data_index_specval@0x1006
- v44:CInt64 = GuardAnyBitSet v43, CUInt64(1)
- v45:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb6(v45, v21)
+ v42:CInt64 = LoadField v36, :_env_data_index_specval@0x1006
+ v43:CInt64 = GuardAnyBitSet v42, CUInt64(1)
+ v44:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb6(v44, v21)
bb6(v34:BasicObject, v35:BasicObject):
SideExit SplatKwNotProfiled
");
@@ -2509,7 +2520,8 @@ pub(crate) mod hir_build_tests {
v10:BasicObject = GetConstantPath 0x1000
v12:NilClass = Const Value(nil)
v15:CBool = IsMethodCFunc v10, :new
- IfFalse v15, bb4(v6, v12, v10)
+ CondBranch v15, bb6(), bb4(v6, v12, v10)
+ bb6():
v17:HeapBasicObject = ObjectAlloc v10
v19:BasicObject = Send v17, :initialize # SendFallbackReason: Uncategorized(opt_send_without_block)
CheckInterrupts
@@ -3393,14 +3405,13 @@ pub(crate) mod hir_build_tests {
v15:CPtr = GetEP 0
v16:CUInt64 = LoadField v15, :_ep_flags@0x1001
v17:CBool = IsBlockParamModified v16
- IfTrue v17, bb4()
- Jump bb5()
+ CondBranch v17, bb4(), bb5()
bb4():
- v20:BasicObject = LoadField v15, :block@0x1002
- Jump bb6(v20)
+ v19:BasicObject = LoadField v15, :block@0x1002
+ Jump bb6(v19)
bb5():
- v22:BasicObject = GetBlockParam :block, l0, EP@3
- Jump bb6(v22)
+ v21:BasicObject = GetBlockParam :block, l0, EP@3
+ Jump bb6(v21)
bb6(v14:BasicObject):
CheckInterrupts
Return v14
@@ -3430,20 +3441,19 @@ pub(crate) mod hir_build_tests {
v17:CPtr = GetEP 0
v18:CUInt64 = LoadField v17, :_ep_flags@0x1001
v19:CBool = IsBlockParamModified v18
- IfTrue v19, bb4()
- Jump bb5()
+ CondBranch v19, bb4(), bb5()
bb4():
- v22:BasicObject = LoadField v17, :block@0x1002
- Jump bb6(v22, v22)
+ v21:BasicObject = LoadField v17, :block@0x1002
+ Jump bb6(v21, v21)
bb5():
- v24:CInt64 = LoadField v17, :_env_data_index_specval@0x1003
- v25:CInt64 = GuardAnyBitSet v24, CUInt64(1)
- v26:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb6(v26, v10)
+ v23:CInt64 = LoadField v17, :_env_data_index_specval@0x1003
+ v24:CInt64 = GuardAnyBitSet v23, CUInt64(1)
+ v25:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb6(v25, v10)
bb6(v15:BasicObject, v16:BasicObject):
- v29:BasicObject = Send v9, &block, :tap, v15 # SendFallbackReason: Uncategorized(send)
+ v28:BasicObject = Send v9, &block, :tap, v15 # SendFallbackReason: Uncategorized(send)
CheckInterrupts
- Return v29
+ Return v28
");
}
@@ -3475,32 +3485,30 @@ pub(crate) mod hir_build_tests {
v18:CPtr = GetEP 0
v19:CUInt64 = LoadField v18, :_ep_flags@0x1001
v20:CBool = IsBlockParamModified v19
- IfTrue v20, bb4()
- Jump bb5()
+ CondBranch v20, bb4(), bb5()
bb4():
- v23:BasicObject = LoadField v18, :block@0x1002
- Jump bb6(v23)
+ v22:BasicObject = LoadField v18, :block@0x1002
+ Jump bb6(v22)
bb5():
- v25:BasicObject = GetBlockParam :block, l0, EP@4
- Jump bb6(v25)
+ v24:BasicObject = GetBlockParam :block, l0, EP@4
+ Jump bb6(v24)
bb6(v17:BasicObject):
- v33:CPtr = GetEP 0
- v34:CUInt64 = LoadField v33, :_ep_flags@0x1001
- v35:CBool = IsBlockParamModified v34
- IfTrue v35, bb7()
- Jump bb8()
+ v32:CPtr = GetEP 0
+ v33:CUInt64 = LoadField v32, :_ep_flags@0x1001
+ v34:CBool = IsBlockParamModified v33
+ CondBranch v34, bb7(), bb8()
bb7():
- v38:BasicObject = LoadField v33, :block@0x1002
- Jump bb9(v38, v38)
+ v36:BasicObject = LoadField v32, :block@0x1002
+ Jump bb9(v36, v36)
bb8():
- v40:CInt64 = LoadField v33, :_env_data_index_specval@0x1003
- v41:CInt64 = GuardAnyBitSet v40, CUInt64(1)
- v42:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb9(v42, v17)
- bb9(v31:BasicObject, v32:BasicObject):
- v45:BasicObject = Send v11, &block, :tap, v31 # SendFallbackReason: Uncategorized(send)
+ v38:CInt64 = LoadField v32, :_env_data_index_specval@0x1003
+ v39:CInt64 = GuardAnyBitSet v38, CUInt64(1)
+ v40:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb9(v40, v17)
+ bb9(v30:BasicObject, v31:BasicObject):
+ v43:BasicObject = Send v11, &block, :tap, v30 # SendFallbackReason: Uncategorized(send)
CheckInterrupts
- Return v45
+ Return v43
");
}
@@ -3530,32 +3538,30 @@ pub(crate) mod hir_build_tests {
v14:CPtr = GetEP 1
v15:CUInt64 = LoadField v14, :_ep_flags@0x1000
v16:CBool = IsBlockParamModified v15
- IfTrue v16, bb4()
- Jump bb5()
+ CondBranch v16, bb4(), bb5()
bb4():
- v19:BasicObject = LoadField v14, :block@0x1001
- Jump bb6(v19)
+ v18:BasicObject = LoadField v14, :block@0x1001
+ Jump bb6(v18)
bb5():
- v21:BasicObject = GetBlockParam :block, l1, EP@3
- Jump bb6(v21)
+ v20:BasicObject = GetBlockParam :block, l1, EP@3
+ Jump bb6(v20)
bb6(v13:BasicObject):
- v28:CPtr = GetEP 1
- v29:CUInt64 = LoadField v28, :_ep_flags@0x1000
- v30:CBool = IsBlockParamModified v29
- IfTrue v30, bb7()
- Jump bb8()
+ v27:CPtr = GetEP 1
+ v28:CUInt64 = LoadField v27, :_ep_flags@0x1000
+ v29:CBool = IsBlockParamModified v28
+ CondBranch v29, bb7(), bb8()
bb7():
- v33:BasicObject = LoadField v28, :block@0x1001
- Jump bb9(v33)
+ v31:BasicObject = LoadField v27, :block@0x1001
+ Jump bb9(v31)
bb8():
- v35:CInt64 = LoadField v28, :_env_data_index_specval@0x1002
- v36:CInt64 = GuardAnyBitSet v35, CUInt64(1)
- v37:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb9(v37)
- bb9(v27:BasicObject):
- v40:BasicObject = Send v8, &block, :tap, v27 # SendFallbackReason: Uncategorized(send)
+ v33:CInt64 = LoadField v27, :_env_data_index_specval@0x1002
+ v34:CInt64 = GuardAnyBitSet v33, CUInt64(1)
+ v35:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb9(v35)
+ bb9(v26:BasicObject):
+ v38:BasicObject = Send v8, &block, :tap, v26 # SendFallbackReason: Uncategorized(send)
CheckInterrupts
- Return v40
+ Return v38
");
}
@@ -3589,31 +3595,32 @@ pub(crate) mod hir_build_tests {
v18:CPtr = GetEP 0
v19:CUInt64 = LoadField v18, :_ep_flags@0x1001
v20:CBool = IsBlockParamModified v19
- IfTrue v20, bb4()
- Jump bb5()
+ CondBranch v20, bb4(), bb5()
bb4():
- v23:BasicObject = LoadField v18, :block@0x1002
- Jump bb6(v23, v23)
+ v22:BasicObject = LoadField v18, :block@0x1002
+ Jump bb6(v22, v22)
bb5():
- v25:CInt64 = LoadField v18, :_env_data_index_specval@0x1003
- v26:CInt64[1] = Const CInt64(1)
- v27:CInt64 = IntAnd v25, v26
- v28:CBool = IsBitEqual v27, v26
- IfTrue v28, bb7()
- v32:CInt64[0] = Const CInt64(0)
- v33:CBool = IsBitEqual v25, v32
- IfTrue v33, bb8()
- SideExit BlockParamProxyProfileNotCovered
+ v24:CInt64 = LoadField v18, :_env_data_index_specval@0x1003
+ v25:CInt64[1] = Const CInt64(1)
+ v26:CInt64 = IntAnd v24, v25
+ v27:CBool = IsBitEqual v26, v25
+ CondBranch v27, bb7(), bb9()
bb7():
- v30:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb6(v30, v10)
+ v29:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb6(v29, v10)
+ bb9():
+ v31:CInt64[0] = Const CInt64(0)
+ v32:CBool = IsBitEqual v24, v31
+ CondBranch v32, bb8(), bb10()
bb8():
- v35:NilClass = Const Value(nil)
- Jump bb6(v35, v10)
+ v34:NilClass = Const Value(nil)
+ Jump bb6(v34, v10)
bb6(v16:BasicObject, v17:BasicObject):
- v39:BasicObject = Send v14, &block, :then, v16 # SendFallbackReason: Uncategorized(send)
+ v38:BasicObject = Send v14, &block, :then, v16 # SendFallbackReason: Uncategorized(send)
CheckInterrupts
- Return v39
+ Return v38
+ bb10():
+ SideExit BlockParamProxyProfileNotCovered
");
}
@@ -3640,14 +3647,13 @@ pub(crate) mod hir_build_tests {
v11:CPtr = GetEP 1
v12:CUInt64 = LoadField v11, :_ep_flags@0x1000
v13:CBool = IsBlockParamModified v12
- IfTrue v13, bb4()
- Jump bb5()
+ CondBranch v13, bb4(), bb5()
bb4():
- v16:BasicObject = LoadField v11, :block@0x1001
- Jump bb6(v16)
+ v15:BasicObject = LoadField v11, :block@0x1001
+ Jump bb6(v15)
bb5():
- v18:BasicObject = GetBlockParam :block, l1, EP@3
- Jump bb6(v18)
+ v17:BasicObject = GetBlockParam :block, l1, EP@3
+ Jump bb6(v17)
bb6(v10:BasicObject):
CheckInterrupts
Return v10
@@ -3746,16 +3752,15 @@ pub(crate) mod hir_build_tests {
v21:CPtr = GetEP 0
v22:CUInt64 = LoadField v21, :_ep_flags@0x1002
v23:CBool = IsBlockParamModified v22
- IfTrue v23, bb4()
- Jump bb5()
+ CondBranch v23, bb4(), bb5()
bb4():
- v26:BasicObject = LoadField v21, :b@0x1003
- Jump bb6(v26, v26)
+ v25:BasicObject = LoadField v21, :b@0x1003
+ Jump bb6(v25, v25)
bb5():
- v28:CInt64 = LoadField v21, :_env_data_index_specval@0x1004
- v29:CInt64 = GuardAnyBitSet v28, CUInt64(1)
- v30:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb6(v30, v13)
+ v27:CInt64 = LoadField v21, :_env_data_index_specval@0x1004
+ v28:CInt64 = GuardAnyBitSet v27, CUInt64(1)
+ v29:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb6(v29, v13)
bb6(v19:BasicObject, v20:BasicObject):
SideExit SplatKwNotProfiled
");
@@ -3796,21 +3801,20 @@ pub(crate) mod hir_build_tests {
v36:CPtr = GetEP 0
v37:CUInt64 = LoadField v36, :_ep_flags@0x1004
v38:CBool = IsBlockParamModified v37
- IfTrue v38, bb4()
- Jump bb5()
+ CondBranch v38, bb4(), bb5()
bb4():
- v41:BasicObject = LoadField v36, :&@0x1005
- Jump bb6(v41, v41)
+ v40:BasicObject = LoadField v36, :&@0x1005
+ Jump bb6(v40, v40)
bb5():
- v43:CInt64 = LoadField v36, :_env_data_index_specval@0x1006
- v44:CInt64[0] = GuardBitEquals v43, CInt64(0)
- v45:NilClass = Const Value(nil)
- Jump bb6(v45, v21)
+ v42:CInt64 = LoadField v36, :_env_data_index_specval@0x1006
+ v43:CInt64[0] = GuardBitEquals v42, CInt64(0)
+ v44:NilClass = Const Value(nil)
+ Jump bb6(v44, v21)
bb6(v34:BasicObject, v35:BasicObject):
- v48:NilClass = GuardType v20, NilClass
- v50:BasicObject = Send v17, &block, :foo, v18, v29, v48, v34 # SendFallbackReason: Uncategorized(send)
+ v47:NilClass = GuardType v20, NilClass
+ v49:BasicObject = Send v17, &block, :foo, v18, v29, v47, v34 # SendFallbackReason: Uncategorized(send)
CheckInterrupts
- Return v50
+ Return v49
");
}
@@ -3841,21 +3845,20 @@ pub(crate) mod hir_build_tests {
v21:CPtr = GetEP 0
v22:CUInt64 = LoadField v21, :_ep_flags@0x1002
v23:CBool = IsBlockParamModified v22
- IfTrue v23, bb4()
- Jump bb5()
+ CondBranch v23, bb4(), bb5()
bb4():
- v26:BasicObject = LoadField v21, :b@0x1003
- Jump bb6(v26, v26)
+ v25:BasicObject = LoadField v21, :b@0x1003
+ Jump bb6(v25, v25)
bb5():
- v28:CInt64 = LoadField v21, :_env_data_index_specval@0x1004
- v29:CInt64 = GuardAnyBitSet v28, CUInt64(1)
- v30:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb6(v30, v13)
+ v27:CInt64 = LoadField v21, :_env_data_index_specval@0x1004
+ v28:CInt64 = GuardAnyBitSet v27, CUInt64(1)
+ v29:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb6(v29, v13)
bb6(v19:BasicObject, v20:BasicObject):
- v33:HashExact = GuardType v12, HashExact
- v35:BasicObject = Send v11, &block, :foo, v33, v19 # SendFallbackReason: Uncategorized(send)
+ v32:HashExact = GuardType v12, HashExact
+ v34:BasicObject = Send v11, &block, :foo, v32, v19 # SendFallbackReason: Uncategorized(send)
CheckInterrupts
- Return v35
+ Return v34
");
}
@@ -3886,21 +3889,20 @@ pub(crate) mod hir_build_tests {
v21:CPtr = GetEP 0
v22:CUInt64 = LoadField v21, :_ep_flags@0x1002
v23:CBool = IsBlockParamModified v22
- IfTrue v23, bb4()
- Jump bb5()
+ CondBranch v23, bb4(), bb5()
bb4():
- v26:BasicObject = LoadField v21, :b@0x1003
- Jump bb6(v26, v26)
+ v25:BasicObject = LoadField v21, :b@0x1003
+ Jump bb6(v25, v25)
bb5():
- v28:CInt64 = LoadField v21, :_env_data_index_specval@0x1004
- v29:CInt64 = GuardAnyBitSet v28, CUInt64(1)
- v30:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb6(v30, v13)
+ v27:CInt64 = LoadField v21, :_env_data_index_specval@0x1004
+ v28:CInt64 = GuardAnyBitSet v27, CUInt64(1)
+ v29:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb6(v29, v13)
bb6(v19:BasicObject, v20:BasicObject):
- v33:HashExact = GuardType v12, HashExact
- v35:BasicObject = Send v11, &block, :foo, v33, v19 # SendFallbackReason: Uncategorized(send)
+ v32:HashExact = GuardType v12, HashExact
+ v34:BasicObject = Send v11, &block, :foo, v32, v19 # SendFallbackReason: Uncategorized(send)
CheckInterrupts
- Return v35
+ Return v34
");
}
@@ -3941,16 +3943,15 @@ pub(crate) mod hir_build_tests {
v36:CPtr = GetEP 0
v37:CUInt64 = LoadField v36, :_ep_flags@0x1004
v38:CBool = IsBlockParamModified v37
- IfTrue v38, bb4()
- Jump bb5()
+ CondBranch v38, bb4(), bb5()
bb4():
- v41:BasicObject = LoadField v36, :&@0x1005
- Jump bb6(v41, v41)
+ v40:BasicObject = LoadField v36, :&@0x1005
+ Jump bb6(v40, v40)
bb5():
- v43:CInt64 = LoadField v36, :_env_data_index_specval@0x1006
- v44:CInt64[0] = GuardBitEquals v43, CInt64(0)
- v45:NilClass = Const Value(nil)
- Jump bb6(v45, v21)
+ v42:CInt64 = LoadField v36, :_env_data_index_specval@0x1006
+ v43:CInt64[0] = GuardBitEquals v42, CInt64(0)
+ v44:NilClass = Const Value(nil)
+ Jump bb6(v44, v21)
bb6(v34:BasicObject, v35:BasicObject):
SideExit SplatKwPolymorphic
");
@@ -3985,16 +3986,15 @@ pub(crate) mod hir_build_tests {
v21:CPtr = GetEP 0
v22:CUInt64 = LoadField v21, :_ep_flags@0x1002
v23:CBool = IsBlockParamModified v22
- IfTrue v23, bb4()
- Jump bb5()
+ CondBranch v23, bb4(), bb5()
bb4():
- v26:BasicObject = LoadField v21, :block@0x1003
- Jump bb6(v26, v26)
+ v25:BasicObject = LoadField v21, :block@0x1003
+ Jump bb6(v25, v25)
bb5():
- v28:CInt64 = LoadField v21, :_env_data_index_specval@0x1004
- v29:CInt64 = GuardAnyBitSet v28, CUInt64(1)
- v30:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb6(v30, v13)
+ v27:CInt64 = LoadField v21, :_env_data_index_specval@0x1004
+ v28:CInt64 = GuardAnyBitSet v27, CUInt64(1)
+ v29:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb6(v29, v13)
bb6(v19:BasicObject, v20:BasicObject):
SideExit SplatKwNotNilOrHash
");
@@ -4469,7 +4469,8 @@ pub(crate) mod hir_build_tests {
CheckInterrupts
v17:CBool = IsNil v10
v18:NilClass = Const Value(nil)
- IfTrue v17, bb4(v9, v18, v18)
+ CondBranch v17, bb4(v9, v18, v18), bb5()
+ bb5():
v20:NotNil = RefineType v10, NotNil
v22:BasicObject = Send v20, :itself # SendFallbackReason: Uncategorized(opt_send_without_block)
Jump bb4(v9, v20, v22)
@@ -4509,12 +4510,14 @@ pub(crate) mod hir_build_tests {
CheckInterrupts
v16:CBool = Test v10
v17:Falsy = RefineType v10, Falsy
- IfFalse v16, bb4(v9, v17)
+ CondBranch v16, bb6(), bb4(v9, v17)
+ bb6():
v19:Truthy = RefineType v10, Truthy
CheckInterrupts
v25:CBool[false] = IsNil v19
v26:NilClass = Const Value(nil)
- IfTrue v25, bb5(v9, v26, v26)
+ CondBranch v25, bb5(v9, v26, v26), bb7()
+ bb7():
v28:Truthy = RefineType v19, NotNil
v30:BasicObject = Send v28, :itself # SendFallbackReason: Uncategorized(opt_send_without_block)
CheckInterrupts
@@ -4564,33 +4567,36 @@ pub(crate) mod hir_build_tests {
CheckInterrupts
v16:CBool = Test v10
v17:Falsy = RefineType v10, Falsy
- IfFalse v16, bb6(v9, v17)
+ CondBranch v16, bb7(), bb6(v9, v17)
+ bb7():
v19:Truthy = RefineType v10, Truthy
CheckInterrupts
v24:CBool[true] = Test v19
v25 = RefineType v19, Falsy
- IfFalse v24, bb5(v9, v25)
+ CondBranch v24, bb8(), bb5(v9, v25)
+ bb8():
v27:Truthy = RefineType v19, Truthy
CheckInterrupts
v32:CBool[true] = Test v27
v33 = RefineType v27, Falsy
- IfFalse v32, bb4(v9, v33)
+ CondBranch v32, bb9(), bb4(v9, v33)
+ bb9():
v35:Truthy = RefineType v27, Truthy
v38:Fixnum[3] = Const Value(3)
CheckInterrupts
Return v38
- bb6(v43:BasicObject, v44:Falsy):
- v48:Fixnum[6] = Const Value(6)
+ bb4(v63, v64):
+ v68 = Const Value(4)
CheckInterrupts
- Return v48
+ Return v68
bb5(v53, v54):
v58 = Const Value(5)
CheckInterrupts
Return v58
- bb4(v63, v64):
- v68 = Const Value(4)
+ bb6(v43:BasicObject, v44:Falsy):
+ v48:Fixnum[6] = Const Value(6)
CheckInterrupts
- Return v68
+ Return v48
");
}
@@ -4680,29 +4686,29 @@ pub(crate) mod hir_build_tests {
v35:CPtr = GetEP 0
v36:CUInt64 = LoadField v35, :_ep_flags@0x1004
v37:CBool = IsBlockParamModified v36
- IfTrue v37, bb5()
- Jump bb6()
+ CondBranch v37, bb5(), bb6()
bb5():
- v40:BasicObject = LoadField v35, :block@0x1005
- Jump bb7(v40, v40)
+ v39:BasicObject = LoadField v35, :block@0x1005
+ Jump bb7(v39, v39)
bb6():
- v42:CInt64 = LoadField v35, :_env_data_index_specval@0x1006
- v43:CInt64 = GuardAnyBitSet v42, CUInt64(1)
- v44:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
- Jump bb7(v44, v22)
+ v41:CInt64 = LoadField v35, :_env_data_index_specval@0x1006
+ v42:CInt64 = GuardAnyBitSet v41, CUInt64(1)
+ v43:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1008))
+ Jump bb7(v43, v22)
bb7(v33:BasicObject, v34:BasicObject):
CheckInterrupts
- v48:CBool = Test v33
- v49:Falsy = RefineType v33, Falsy
- IfFalse v48, bb4(v18, v19, v20, v21, v34, v27)
- v51:Truthy = RefineType v33, Truthy
- v55:BasicObject = InvokeBlock, v27 # SendFallbackReason: InvokeBlock: not yet specialized
- v58:BasicObject = InvokeBuiltin dir_s_close, v18, v27
+ v47:CBool = Test v33
+ v48:Falsy = RefineType v33, Falsy
+ CondBranch v47, bb8(), bb4(v18, v19, v20, v21, v34, v27)
+ bb8():
+ v50:Truthy = RefineType v33, Truthy
+ v54:BasicObject = InvokeBlock, v27 # SendFallbackReason: InvokeBlock: not yet specialized
+ v57:BasicObject = InvokeBuiltin dir_s_close, v18, v27
CheckInterrupts
- Return v55
- bb4(v64:BasicObject, v65:BasicObject, v66:BasicObject, v67:BasicObject, v68:BasicObject, v69:BasicObject):
+ Return v54
+ bb4(v63:BasicObject, v64:BasicObject, v65:BasicObject, v66:BasicObject, v67:BasicObject, v68:BasicObject):
CheckInterrupts
- Return v69
+ Return v68
");
}
@@ -4836,15 +4842,16 @@ pub(crate) mod hir_build_tests {
CheckInterrupts
v26:CBool = Test v22
v27:Truthy = RefineType v22, Truthy
- IfTrue v26, bb4(v9, v10, v14, v10, v17, v19, v27)
+ CondBranch v26, bb4(v9, v10, v14, v10, v17, v19, v27), bb5()
+ bb4(v41:BasicObject, v42:BasicObject, v43:NilClass, v44:BasicObject, v45:Fixnum[0], v46:Fixnum[1], v47:Truthy):
+ CheckInterrupts
+ Return v47
+ bb5():
v29:Falsy = RefineType v22, Falsy
v32:Fixnum[2] = Const Value(2)
v35:BasicObject = Send v10, :[]=, v17, v19, v32 # SendFallbackReason: Uncategorized(opt_send_without_block)
CheckInterrupts
Return v32
- bb4(v41:BasicObject, v42:BasicObject, v43:NilClass, v44:BasicObject, v45:Fixnum[0], v46:Fixnum[1], v47:Truthy):
- CheckInterrupts
- Return v47
");
}
@@ -5214,7 +5221,8 @@ pub(crate) mod hir_build_tests {
CheckInterrupts
v20:CBool = Test v17
v21:TrueClass = RefineType v17, Truthy
- IfTrue v20, bb4(v12, v13, v14)
+ CondBranch v20, bb4(v12, v13, v14), bb5()
+ bb5():
v23:FalseClass = RefineType v17, Falsy
v25:Fixnum[1] = Const Value(1)
v27:Fixnum[1] = Const Value(1)
@@ -5342,15 +5350,10 @@ pub(crate) mod hir_build_tests {
v15:TrueClass|NilClass = Defined yield, v13
v17:CBool = Test v15
v18:NilClass = RefineType v15, Falsy
- IfFalse v17, bb4(v8, v9)
+ CondBranch v17, bb9(), bb4(v8, v9)
+ bb9():
v20:TrueClass = RefineType v15, Truthy
Jump bb6(v8, v9)
- bb4(v23:BasicObject, v24:NilClass):
- v28:BasicObject = InvokeBuiltin <inline_expr>, v23
- Jump bb5(v23, v24, v28)
- bb5(v40:BasicObject, v41:NilClass, v42:BasicObject):
- CheckInterrupts
- Return v42
bb6(v30:BasicObject, v31:NilClass):
v35:Fixnum[0] = Const Value(0)
Jump bb8(v30, v35)
@@ -5358,7 +5361,8 @@ pub(crate) mod hir_build_tests {
v52:BoolExact = InvokeBuiltin rb_jit_ary_at_end, v48, v49
v54:CBool = Test v52
v55:FalseClass = RefineType v52, Falsy
- IfFalse v54, bb7(v48, v49)
+ CondBranch v54, bb10(), bb7(v48, v49)
+ bb10():
v57:TrueClass = RefineType v52, Truthy
v59:NilClass = Const Value(nil)
CheckInterrupts
@@ -5369,6 +5373,12 @@ pub(crate) mod hir_build_tests {
v78:Fixnum = InvokeBuiltin rb_jit_fixnum_inc, v67, v68
PatchPoint NoEPEscape(each)
Jump bb8(v67, v78)
+ bb4(v23:BasicObject, v24:NilClass):
+ v28:BasicObject = InvokeBuiltin <inline_expr>, v23
+ Jump bb5(v23, v24, v28)
+ bb5(v40:BasicObject, v41:NilClass, v42:BasicObject):
+ CheckInterrupts
+ Return v42
");
}
@@ -5560,8 +5570,7 @@ pub(crate) mod hir_build_tests {
let bb3 = function.new_block(0);
let v1 = function.push_insn(bb0, Insn::Const { val: Const::Value(Qfalse) });
- let _ = function.push_insn(bb0, Insn::IfTrue { val: v1, target: edge(bb2)});
- function.push_insn(bb0, Insn::Jump(edge(bb1)));
+ let _ = function.push_insn(bb0, Insn::CondBranch { val: v1, if_true: edge(bb2), if_false: edge(bb1) });
function.push_insn(bb1, Insn::Jump(edge(bb3)));
function.push_insn(bb2, Insn::Jump(edge(bb3)));
@@ -5587,8 +5596,7 @@ pub(crate) mod hir_build_tests {
// Construct two separate jump instructions.
let v1 = function.push_insn(bb0, Insn::Const { val: Const::Value(Qfalse) });
- let _ = function.push_insn(bb0, Insn::IfTrue { val: v1, target: edge(bb1)});
- function.push_insn(bb0, Insn::Jump(edge(bb1)));
+ let _ = function.push_insn(bb0, Insn::CondBranch { val: v1, if_true: edge(bb1), if_false: edge(bb1)});
let retval = function.push_insn(bb1, Insn::Const { val: Const::CBool(true) });
function.push_insn(bb1, Insn::Return { val: retval });
@@ -5668,8 +5676,7 @@ pub(crate) mod hir_build_tests {
let bb3 = function.new_block(0);
let val = function.push_insn(bb0, Insn::Const { val: Const::Value(Qfalse) });
- let _ = function.push_insn(bb0, Insn::IfTrue { val, target: edge(bb1)});
- function.push_insn(bb0, Insn::Jump(edge(bb2)));
+ let _ = function.push_insn(bb0, Insn::CondBranch { val, if_true: edge(bb1), if_false: edge(bb2) });
function.push_insn(bb2, Insn::Jump(edge(bb3)));
function.push_insn(bb1, Insn::Jump(edge(bb3)));
@@ -5682,15 +5689,14 @@ pub(crate) mod hir_build_tests {
fn <manual>:
bb1():
v0:Any = Const Value(false)
- IfTrue v0, bb2()
- Jump bb3()
+ CondBranch v0, bb2(), bb3()
bb2():
Jump bb4()
bb3():
Jump bb4()
bb4():
- v5:Any = Const CBool(true)
- Return v5
+ v4:Any = Const CBool(true)
+ Return v4
");
let dominators = Dominators::new(&function);
@@ -5718,14 +5724,12 @@ pub(crate) mod hir_build_tests {
function.push_insn(bb0, Insn::Jump(edge(bb1)));
let v0 = function.push_insn(bb1, Insn::Const { val: Const::Value(Qfalse) });
- let _ = function.push_insn(bb1, Insn::IfTrue { val: v0, target: edge(bb2)});
- function.push_insn(bb1, Insn::Jump(edge(bb4)));
+ let _ = function.push_insn(bb1, Insn::CondBranch { val: v0, if_true: edge(bb2), if_false: edge(bb4) });
function.push_insn(bb2, Insn::Jump(edge(bb3)));
let v1 = function.push_insn(bb3, Insn::Const { val: Const::Value(Qfalse) });
- let _ = function.push_insn(bb3, Insn::IfTrue { val: v1, target: edge(bb5)});
- function.push_insn(bb3, Insn::Jump(edge(bb7)));
+ let _ = function.push_insn(bb3, Insn::CondBranch { val: v1, if_true: edge(bb5), if_false: edge(bb7) });
function.push_insn(bb4, Insn::Jump(edge(bb5)));
@@ -5743,14 +5747,12 @@ pub(crate) mod hir_build_tests {
Jump bb2()
bb2():
v1:Any = Const Value(false)
- IfTrue v1, bb3()
- Jump bb5()
+ CondBranch v1, bb3(), bb5()
bb3():
Jump bb4()
bb4():
- v5:Any = Const Value(false)
- IfTrue v5, bb6()
- Jump bb8()
+ v4:Any = Const Value(false)
+ CondBranch v4, bb6(), bb8()
bb5():
Jump bb6()
bb6():
@@ -5758,8 +5760,8 @@ pub(crate) mod hir_build_tests {
bb7():
Jump bb8()
bb8():
- v11:Any = Const CBool(true)
- Return v11
+ v9:Any = Const CBool(true)
+ Return v9
");
let dominators = Dominators::new(&function);
@@ -5787,20 +5789,17 @@ pub(crate) mod hir_build_tests {
let bb5 = function.new_block(0);
let v0 = function.push_insn(bb0, Insn::Const { val: Const::Value(Qfalse) });
- let _ = function.push_insn(bb0, Insn::IfTrue { val: v0, target: edge(bb1)});
- function.push_insn(bb0, Insn::Jump(edge(bb4)));
+ let _ = function.push_insn(bb0, Insn::CondBranch { val: v0, if_true: edge(bb1), if_false: edge(bb4) });
let v1 = function.push_insn(bb1, Insn::Const { val: Const::Value(Qfalse) });
- let _ = function.push_insn(bb1, Insn::IfTrue { val: v1, target: edge(bb2)});
- function.push_insn(bb1, Insn::Jump(edge(bb3)));
+ let _ = function.push_insn(bb1, Insn::CondBranch { val: v1, if_true: edge(bb2), if_false: edge(bb3) });
function.push_insn(bb2, Insn::Jump(edge(bb3)));
function.push_insn(bb4, Insn::Jump(edge(bb5)));
let v2 = function.push_insn(bb5, Insn::Const { val: Const::Value(Qfalse) });
- let _ = function.push_insn(bb5, Insn::IfTrue { val: v2, target: edge(bb3)});
- function.push_insn(bb5, Insn::Jump(edge(bb4)));
+ let _ = function.push_insn(bb5, Insn::CondBranch { val: v2, if_true: edge(bb3), if_false: edge(bb4) });
let retval = function.push_insn(bb3, Insn::Const { val: Const::CBool(true) });
function.push_insn(bb3, Insn::Return { val: retval });
@@ -5810,23 +5809,20 @@ pub(crate) mod hir_build_tests {
fn <manual>:
bb1():
v0:Any = Const Value(false)
- IfTrue v0, bb2()
- Jump bb5()
+ CondBranch v0, bb2(), bb5()
bb2():
- v3:Any = Const Value(false)
- IfTrue v3, bb3()
- Jump bb4()
+ v2:Any = Const Value(false)
+ CondBranch v2, bb3(), bb4()
bb3():
Jump bb4()
bb5():
Jump bb6()
bb6():
- v8:Any = Const Value(false)
- IfTrue v8, bb4()
- Jump bb5()
+ v6:Any = Const Value(false)
+ CondBranch v6, bb4(), bb5()
bb4():
- v11:Any = Const CBool(true)
- Return v11
+ v8:Any = Const CBool(true)
+ Return v8
");
let dominators = Dominators::new(&function);
@@ -5891,26 +5887,31 @@ mod loop_info_tests {
#[test]
fn test_loop_depth() {
- // ┌─────┐
- // │ bb0 │
- // └──┬──┘
- // │
- // ┌──▼──┐ ┌─────┐
- // │ bb2 ◄──────┼ bb1 ◄─┐
- // └──┬──┘ └─────┘ │
- // └─────────────────┘
+ // ┌─────┐
+ // │ bb0 │
+ // └──┬──┘
+ // │
+ // ┌──▼──┐ ┌─────┐
+ // ┌►│ bb2 ├─────►│ bb1 │
+ // │ └──┬──┘ T └──┬──┘
+ // │ F│ │
+ // │ ┌──▼──┐ │
+ // │ │ bb3 │ │
+ // │ └─────┘ │
+ // └─────────────────┘
let mut function = Function::new(std::ptr::null());
let bb0 = function.entry_block;
let bb1 = function.new_block(0);
let bb2 = function.new_block(0);
+ let bb3 = function.new_block(0);
function.push_insn(bb0, Insn::Jump(edge(bb2)));
- let val = function.push_insn(bb0, Insn::Const { val: Const::Value(Qfalse) });
- let _ = function.push_insn(bb2, Insn::IfTrue { val, target: edge(bb1)});
- let retval = function.push_insn(bb2, Insn::Const { val: Const::CBool(true) });
- let _ = function.push_insn(bb2, Insn::Return { val: retval });
+ let val = function.push_insn(bb2, Insn::Const { val: Const::Value(Qfalse) });
+ let _ = function.push_insn(bb2, Insn::CondBranch { val, if_true: edge(bb1), if_false: edge(bb3) });
+ let retval = function.push_insn(bb3, Insn::Const { val: Const::CBool(true) });
+ let _ = function.push_insn(bb3, Insn::Return { val: retval });
function.push_insn(bb1, Insn::Jump(edge(bb2)));
@@ -5923,13 +5924,14 @@ mod loop_info_tests {
fn <manual>:
bb1():
Jump bb3()
- v1:Any = Const Value(false)
bb3():
- IfTrue v1, bb2()
- v3:Any = Const CBool(true)
- Return v3
+ v1:Any = Const Value(false)
+ CondBranch v1, bb2(), bb4()
bb2():
Jump bb3()
+ bb4():
+ v3:Any = Const CBool(true)
+ Return v3
");
assert!(loop_info.is_loop_header(bb2));
@@ -5971,12 +5973,10 @@ mod loop_info_tests {
function.push_insn(bb1, Insn::Jump(edge(bb2)));
let cond = function.push_insn(bb2, Insn::Const { val: Const::Value(Qfalse) });
- let _ = function.push_insn(bb2, Insn::IfTrue { val: cond, target: edge(bb1) });
- function.push_insn(bb2, Insn::Jump(edge(bb3)));
+ let _ = function.push_insn(bb2, Insn::CondBranch { val: cond, if_true: edge(bb1), if_false: edge(bb3) });
let cond = function.push_insn(bb3, Insn::Const { val: Const::Value(Qtrue) });
- let _ = function.push_insn(bb3, Insn::IfTrue { val: cond, target: edge(bb0) });
- function.push_insn(bb3, Insn::Jump(edge(bb4)));
+ let _ = function.push_insn(bb3, Insn::CondBranch { val: cond, if_true: edge(bb0), if_false: edge(bb4) });
let retval = function.push_insn(bb4, Insn::Const { val: Const::CBool(true) });
let _ = function.push_insn(bb4, Insn::Return { val: retval });
@@ -5994,15 +5994,13 @@ mod loop_info_tests {
Jump bb3()
bb3():
v2:Any = Const Value(false)
- IfTrue v2, bb2()
- Jump bb4()
+ CondBranch v2, bb2(), bb4()
bb4():
- v5:Any = Const Value(true)
- IfTrue v5, bb1()
- Jump bb5()
+ v4:Any = Const Value(true)
+ CondBranch v4, bb1(), bb5()
bb5():
- v8:Any = Const CBool(true)
- Return v8
+ v6:Any = Const CBool(true)
+ Return v6
");
assert!(loop_info.is_loop_header(bb0));
@@ -6050,21 +6048,17 @@ mod loop_info_tests {
let bb6 = function.new_block(0);
let cond = function.push_insn(bb0, Insn::Const { val: Const::Value(Qfalse) });
- let _ = function.push_insn(bb0, Insn::IfTrue { val: cond, target: edge(bb1) });
- function.push_insn(bb0, Insn::Jump(edge(bb3)));
+ let _ = function.push_insn(bb0, Insn::CondBranch { val: cond, if_true: edge(bb1), if_false: edge(bb3) });
function.push_insn(bb1, Insn::Jump(edge(bb2)));
- let _ = function.push_insn(bb2, Insn::IfTrue { val: cond, target: edge(bb1) });
- function.push_insn(bb2, Insn::Jump(edge(bb5)));
+ let _ = function.push_insn(bb2, Insn::CondBranch { val: cond, if_true: edge(bb1), if_false: edge(bb5) });
function.push_insn(bb3, Insn::Jump(edge(bb4)));
- let _ = function.push_insn(bb4, Insn::IfTrue { val: cond, target: edge(bb3) });
- function.push_insn(bb4, Insn::Jump(edge(bb5)));
+ let _ = function.push_insn(bb4, Insn::CondBranch { val: cond, if_true: edge(bb3), if_false: edge(bb5) });
- let _ = function.push_insn(bb5, Insn::IfTrue { val: cond, target: edge(bb0) });
- function.push_insn(bb5, Insn::Jump(edge(bb6)));
+ let _ = function.push_insn(bb5, Insn::CondBranch { val: cond, if_true: edge(bb0), if_false: edge(bb6) });
let retval = function.push_insn(bb6, Insn::Const { val: Const::CBool(true) });
let _ = function.push_insn(bb6, Insn::Return { val: retval });
@@ -6078,24 +6072,20 @@ mod loop_info_tests {
fn <manual>:
bb1():
v0:Any = Const Value(false)
- IfTrue v0, bb2()
- Jump bb4()
+ CondBranch v0, bb2(), bb4()
bb2():
Jump bb3()
bb3():
- IfTrue v0, bb2()
- Jump bb6()
+ CondBranch v0, bb2(), bb6()
bb4():
Jump bb5()
bb5():
- IfTrue v0, bb4()
- Jump bb6()
+ CondBranch v0, bb4(), bb6()
bb6():
- IfTrue v0, bb1()
- Jump bb7()
+ CondBranch v0, bb1(), bb7()
bb7():
- v11:Any = Const CBool(true)
- Return v11
+ v7:Any = Const CBool(true)
+ Return v7
");
assert!(loop_info.is_loop_header(bb0));
@@ -6206,16 +6196,16 @@ mod loop_info_tests {
let bb3 = function.new_block(0);
let bb4 = function.new_block(0);
let bb5 = function.new_block(0);
+ let bb6 = function.new_block(0);
let cond = function.push_insn(bb0, Insn::Const { val: Const::Value(Qfalse) });
let _ = function.push_insn(bb0, Insn::Jump(edge(bb1)));
let _ = function.push_insn(bb1, Insn::Jump(edge(bb2)));
let _ = function.push_insn(bb2, Insn::Jump(edge(bb3)));
- let _ = function.push_insn(bb3, Insn::Jump(edge(bb4)));
- let _ = function.push_insn(bb3, Insn::IfTrue {val: cond, target: edge(bb2)});
- let _ = function.push_insn(bb4, Insn::Jump(edge(bb5)));
- let _ = function.push_insn(bb4, Insn::IfTrue {val: cond, target: edge(bb1)});
- let _ = function.push_insn(bb5, Insn::IfTrue {val: cond, target: edge(bb0)});
+ let _ = function.push_insn(bb3, Insn::CondBranch {val: cond, if_true: edge(bb2), if_false: edge(bb4) });
+ let _ = function.push_insn(bb4, Insn::CondBranch {val: cond, if_true: edge(bb1), if_false: edge(bb5) });
+ let _ = function.push_insn(bb5, Insn::CondBranch {val: cond, if_true: edge(bb0), if_false: edge(bb6) });
+ function.push_insn(bb6, Insn::Unreachable);
function.seal_entries();
assert_snapshot!(format!("{}", FunctionPrinter::without_snapshot(&function)), @"
@@ -6228,13 +6218,13 @@ mod loop_info_tests {
bb3():
Jump bb4()
bb4():
- Jump bb5()
- IfTrue v0, bb3()
+ CondBranch v0, bb3(), bb5()
bb5():
- Jump bb6()
- IfTrue v0, bb2()
+ CondBranch v0, bb2(), bb6()
bb6():
- IfTrue v0, bb1()
+ CondBranch v0, bb1(), bb7()
+ bb7():
+ Unreachable
");
let cfi = ControlFlowInfo::new(&function);
@@ -6281,9 +6271,10 @@ mod iongraph_tests {
let retval = function.push_insn(bb0, Insn::Const { val: Const::CBool(true) });
function.push_insn(bb0, Insn::Return { val: retval });
+ function.seal_entries();
let json = function.to_iongraph_pass("simple");
- assert_snapshot!(json.to_string(), @r#"{"name":"simple", "mir":{"blocks":[{"ptr":4096, "id":0, "loopDepth":0, "attributes":[], "predecessors":[], "successors":[], "instructions":[]}]}, "lir":{"blocks":[]}}"#);
+ assert_snapshot!(json.to_string(), @r#"{"name":"simple", "mir":{"blocks":[{"ptr":4096, "id":0, "loopDepth":0, "attributes":[], "predecessors":[], "successors":[1], "instructions":[{"ptr":4098, "id":2, "opcode":"Entries bb1", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":""}]}, {"ptr":4097, "id":1, "loopDepth":0, "attributes":[], "predecessors":[0], "successors":[], "instructions":[{"ptr":4096, "id":0, "opcode":"Const CBool(true)", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":"Any"}, {"ptr":4097, "id":1, "opcode":"Return v0", "attributes":[], "inputs":[0], "uses":[], "memInputs":[], "type":""}]}]}, "lir":{"blocks":[]}}"#);
}
#[test]
@@ -6297,8 +6288,9 @@ mod iongraph_tests {
let retval = function.push_insn(bb1, Insn::Const { val: Const::CBool(false) });
function.push_insn(bb1, Insn::Return { val: retval });
+ function.seal_entries();
let json = function.to_iongraph_pass("two_blocks");
- assert_snapshot!(json.to_string(), @r#"{"name":"two_blocks", "mir":{"blocks":[{"ptr":4096, "id":0, "loopDepth":0, "attributes":[], "predecessors":[], "successors":[], "instructions":[]}]}, "lir":{"blocks":[]}}"#);
+ assert_snapshot!(json.to_string(), @r#"{"name":"two_blocks", "mir":{"blocks":[{"ptr":4096, "id":0, "loopDepth":0, "attributes":[], "predecessors":[], "successors":[1], "instructions":[{"ptr":4099, "id":3, "opcode":"Entries bb1", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":""}]}, {"ptr":4097, "id":1, "loopDepth":0, "attributes":[], "predecessors":[0], "successors":[2], "instructions":[{"ptr":4096, "id":0, "opcode":"Jump bb2()", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":""}]}, {"ptr":4098, "id":2, "loopDepth":0, "attributes":[], "predecessors":[1], "successors":[], "instructions":[{"ptr":4097, "id":1, "opcode":"Const CBool(false)", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":"Any"}, {"ptr":4098, "id":2, "opcode":"Return v1", "attributes":[], "inputs":[1], "uses":[], "memInputs":[], "type":""}]}]}, "lir":{"blocks":[]}}"#);
}
#[test]
@@ -6309,8 +6301,9 @@ mod iongraph_tests {
let val1 = function.push_insn(bb0, Insn::Const { val: Const::CBool(true) });
function.push_insn(bb0, Insn::Return { val: val1 });
+ function.seal_entries();
let json = function.to_iongraph_pass("multiple_instructions");
- assert_snapshot!(json.to_string(), @r#"{"name":"multiple_instructions", "mir":{"blocks":[{"ptr":4096, "id":0, "loopDepth":0, "attributes":[], "predecessors":[], "successors":[], "instructions":[]}]}, "lir":{"blocks":[]}}"#);
+ assert_snapshot!(json.to_string(), @r#"{"name":"multiple_instructions", "mir":{"blocks":[{"ptr":4096, "id":0, "loopDepth":0, "attributes":[], "predecessors":[], "successors":[1], "instructions":[{"ptr":4098, "id":2, "opcode":"Entries bb1", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":""}]}, {"ptr":4097, "id":1, "loopDepth":0, "attributes":[], "predecessors":[0], "successors":[], "instructions":[{"ptr":4096, "id":0, "opcode":"Const CBool(true)", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":"Any"}, {"ptr":4097, "id":1, "opcode":"Return v0", "attributes":[], "inputs":[0], "uses":[], "memInputs":[], "type":""}]}]}, "lir":{"blocks":[]}}"#);
}
#[test]
@@ -6318,18 +6311,20 @@ mod iongraph_tests {
let mut function = Function::new(std::ptr::null());
let bb0 = function.entry_block;
let bb1 = function.new_block(0);
+ let bb2 = function.new_block(0);
let cond = function.push_insn(bb0, Insn::Const { val: Const::CBool(true) });
- function.push_insn(bb0, Insn::IfTrue { val: cond, target: edge(bb1) });
+ function.push_insn(bb0, Insn::CondBranch { val: cond, if_true: edge(bb1), if_false: edge(bb2) });
- let retval1 = function.push_insn(bb0, Insn::Const { val: Const::CBool(false) });
- function.push_insn(bb0, Insn::Return { val: retval1 });
+ let retval1 = function.push_insn(bb2, Insn::Const { val: Const::CBool(false) });
+ function.push_insn(bb2, Insn::Return { val: retval1 });
let retval2 = function.push_insn(bb1, Insn::Const { val: Const::CBool(true) });
function.push_insn(bb1, Insn::Return { val: retval2 });
+ function.seal_entries();
let json = function.to_iongraph_pass("conditional_branch");
- assert_snapshot!(json.to_string(), @r#"{"name":"conditional_branch", "mir":{"blocks":[{"ptr":4096, "id":0, "loopDepth":0, "attributes":[], "predecessors":[], "successors":[], "instructions":[]}]}, "lir":{"blocks":[]}}"#);
+ assert_snapshot!(json.to_string(), @r#"{"name":"conditional_branch", "mir":{"blocks":[{"ptr":4096, "id":0, "loopDepth":0, "attributes":[], "predecessors":[], "successors":[1], "instructions":[{"ptr":4102, "id":6, "opcode":"Entries bb1", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":""}]}, {"ptr":4097, "id":1, "loopDepth":0, "attributes":[], "predecessors":[0], "successors":[2, 3], "instructions":[{"ptr":4096, "id":0, "opcode":"Const CBool(true)", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":"Any"}, {"ptr":4097, "id":1, "opcode":"CondBranch v0, bb2(), bb3()", "attributes":[], "inputs":[0], "uses":[], "memInputs":[], "type":""}]}, {"ptr":4098, "id":2, "loopDepth":0, "attributes":[], "predecessors":[1], "successors":[], "instructions":[{"ptr":4100, "id":4, "opcode":"Const CBool(true)", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":"Any"}, {"ptr":4101, "id":5, "opcode":"Return v4", "attributes":[], "inputs":[4], "uses":[], "memInputs":[], "type":""}]}, {"ptr":4099, "id":3, "loopDepth":0, "attributes":[], "predecessors":[1], "successors":[], "instructions":[{"ptr":4098, "id":2, "opcode":"Const CBool(false)", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":"Any"}, {"ptr":4099, "id":3, "opcode":"Return v2", "attributes":[], "inputs":[2], "uses":[], "memInputs":[], "type":""}]}]}, "lir":{"blocks":[]}}"#);
}
#[test]
@@ -6339,18 +6334,20 @@ mod iongraph_tests {
let bb0 = function.entry_block;
let bb1 = function.new_block(0);
let bb2 = function.new_block(0);
+ let bb3 = function.new_block(0);
function.push_insn(bb0, Insn::Jump(edge(bb2)));
- let val = function.push_insn(bb0, Insn::Const { val: Const::Value(Qfalse) });
- let _ = function.push_insn(bb2, Insn::IfTrue { val, target: edge(bb1)});
- let retval = function.push_insn(bb2, Insn::Const { val: Const::CBool(true) });
- let _ = function.push_insn(bb2, Insn::Return { val: retval });
+ let val = function.push_insn(bb2, Insn::Const { val: Const::Value(Qfalse) });
+ let _ = function.push_insn(bb2, Insn::CondBranch { val, if_true: edge(bb1), if_false: edge(bb3) });
+ let retval = function.push_insn(bb3, Insn::Const { val: Const::CBool(true) });
+ let _ = function.push_insn(bb3, Insn::Return { val: retval });
function.push_insn(bb1, Insn::Jump(edge(bb2)));
+ function.seal_entries();
let json = function.to_iongraph_pass("loop_structure");
- assert_snapshot!(json.to_string(), @r#"{"name":"loop_structure", "mir":{"blocks":[{"ptr":4096, "id":0, "loopDepth":0, "attributes":[], "predecessors":[], "successors":[], "instructions":[]}]}, "lir":{"blocks":[]}}"#);
+ assert_snapshot!(json.to_string(), @r#"{"name":"loop_structure", "mir":{"blocks":[{"ptr":4096, "id":0, "loopDepth":0, "attributes":[], "predecessors":[], "successors":[1], "instructions":[{"ptr":4102, "id":6, "opcode":"Entries bb1", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":""}]}, {"ptr":4097, "id":1, "loopDepth":0, "attributes":[], "predecessors":[0], "successors":[3], "instructions":[{"ptr":4096, "id":0, "opcode":"Jump bb3()", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":""}]}, {"ptr":4099, "id":3, "loopDepth":1, "attributes":["loopheader"], "predecessors":[1, 2], "successors":[2, 4], "instructions":[{"ptr":4097, "id":1, "opcode":"Const Value(false)", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":"Any"}, {"ptr":4098, "id":2, "opcode":"CondBranch v1, bb2(), bb4()", "attributes":[], "inputs":[1], "uses":[], "memInputs":[], "type":""}]}, {"ptr":4098, "id":2, "loopDepth":1, "attributes":["backedge"], "predecessors":[3], "successors":[3], "instructions":[{"ptr":4101, "id":5, "opcode":"Jump bb3()", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":""}]}, {"ptr":4100, "id":4, "loopDepth":0, "attributes":[], "predecessors":[3], "successors":[], "instructions":[{"ptr":4099, "id":3, "opcode":"Const CBool(true)", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":"Any"}, {"ptr":4100, "id":4, "opcode":"Return v3", "attributes":[], "inputs":[3], "uses":[], "memInputs":[], "type":""}]}]}, "lir":{"blocks":[]}}"#);
}
#[test]
@@ -6361,8 +6358,7 @@ mod iongraph_tests {
let bb2 = function.new_block(0);
let cond = function.push_insn(bb0, Insn::Const { val: Const::CBool(true) });
- function.push_insn(bb0, Insn::IfTrue { val: cond, target: edge(bb1) });
- function.push_insn(bb0, Insn::Jump(edge(bb2)));
+ function.push_insn(bb0, Insn::CondBranch { val: cond, if_true: edge(bb1), if_false: edge(bb2) });
let retval1 = function.push_insn(bb1, Insn::Const { val: Const::CBool(true) });
function.push_insn(bb1, Insn::Return { val: retval1 });
@@ -6370,7 +6366,8 @@ mod iongraph_tests {
let retval2 = function.push_insn(bb2, Insn::Const { val: Const::CBool(false) });
function.push_insn(bb2, Insn::Return { val: retval2 });
+ function.seal_entries();
let json = function.to_iongraph_pass("multiple_successors");
- assert_snapshot!(json.to_string(), @r#"{"name":"multiple_successors", "mir":{"blocks":[{"ptr":4096, "id":0, "loopDepth":0, "attributes":[], "predecessors":[], "successors":[], "instructions":[]}]}, "lir":{"blocks":[]}}"#);
+ assert_snapshot!(json.to_string(), @r#"{"name":"multiple_successors", "mir":{"blocks":[{"ptr":4096, "id":0, "loopDepth":0, "attributes":[], "predecessors":[], "successors":[1], "instructions":[{"ptr":4102, "id":6, "opcode":"Entries bb1", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":""}]}, {"ptr":4097, "id":1, "loopDepth":0, "attributes":[], "predecessors":[0], "successors":[2, 3], "instructions":[{"ptr":4096, "id":0, "opcode":"Const CBool(true)", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":"Any"}, {"ptr":4097, "id":1, "opcode":"CondBranch v0, bb2(), bb3()", "attributes":[], "inputs":[0], "uses":[], "memInputs":[], "type":""}]}, {"ptr":4098, "id":2, "loopDepth":0, "attributes":[], "predecessors":[1], "successors":[], "instructions":[{"ptr":4098, "id":2, "opcode":"Const CBool(true)", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":"Any"}, {"ptr":4099, "id":3, "opcode":"Return v2", "attributes":[], "inputs":[2], "uses":[], "memInputs":[], "type":""}]}, {"ptr":4099, "id":3, "loopDepth":0, "attributes":[], "predecessors":[1], "successors":[], "instructions":[{"ptr":4100, "id":4, "opcode":"Const CBool(false)", "attributes":[], "inputs":[], "uses":[], "memInputs":[], "type":"Any"}, {"ptr":4101, "id":5, "opcode":"Return v4", "attributes":[], "inputs":[4], "uses":[], "memInputs":[], "type":""}]}]}, "lir":{"blocks":[]}}"#);
}
}