Skip to content

Commit a8f269a

Browse files
authored
ZJIT: Deduplicate successor and predecessor sets (ruby#15263)
Fixes #877 I didn't consider the ability to have the successor or predecessor sets having duplicates when originally crafting the Iongraph support PR, but have added this to prevent that happening in the future. I don't think it interferes with the underlying Iongraph implementation, but it doesn't really make sense. I think this kind of behaviour happens when there are multiple jump instructions that go to the same basic block within a given block.
1 parent 4802725 commit a8f269a

2 files changed

Lines changed: 31 additions & 3 deletions

File tree

zjit/src/hir.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, payload::{get_or_create_iseq_payload, IseqPayload}, options::{debug, get_option, DumpHIR}, state::ZJITState, json::Json
1010
};
1111
use std::{
12-
cell::RefCell, collections::{HashMap, HashSet, VecDeque}, ffi::{c_void, c_uint, c_int, CStr}, fmt::Display, mem::{align_of, size_of}, ptr, slice::Iter
12+
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
1313
};
1414
use crate::hir_type::{Type, types};
1515
use crate::bitset::BitSet;
@@ -5849,7 +5849,13 @@ impl<'a> ControlFlowInfo<'a> {
58495849
// Since ZJIT uses extended basic blocks, one must check all instructions
58505850
// for their ability to jump to another basic block, rather than just
58515851
// the instructions at the end of a given basic block.
5852-
let successors: Vec<BlockId> = block
5852+
//
5853+
// Use BTreeSet to avoid duplicates and maintain an ordering. Also
5854+
// `BTreeSet<BlockId>` provides conversion trivially back to an `Vec<BlockId>`.
5855+
// Ordering is important so that the expect tests that serialize the predecessors
5856+
// and successors don't fail intermittently.
5857+
// todo(aidenfoxivey): Use `BlockSet` in lieu of `BTreeSet<BlockId>`
5858+
let successors: BTreeSet<BlockId> = block
58535859
.insns
58545860
.iter()
58555861
.map(|&insn_id| uf.find_const(insn_id))
@@ -5867,7 +5873,8 @@ impl<'a> ControlFlowInfo<'a> {
58675873
}
58685874

58695875
// Store successors for this block.
5870-
successor_map.insert(block_id, successors);
5876+
// Convert successors from a `BTreeSet<BlockId>` to a `Vec<BlockId>`.
5877+
successor_map.insert(block_id, successors.iter().copied().collect());
58715878
}
58725879

58735880
Self {

zjit/src/hir/tests.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3484,6 +3484,27 @@ pub mod hir_build_tests {
34843484
assert!(cfi.is_succeeded_by(bb1, bb0));
34853485
assert!(cfi.is_succeeded_by(bb3, bb1));
34863486
}
3487+
3488+
#[test]
3489+
fn test_cfi_deduplicated_successors_and_predecessors() {
3490+
let mut function = Function::new(std::ptr::null());
3491+
3492+
let bb0 = function.entry_block;
3493+
let bb1 = function.new_block(0);
3494+
3495+
// Construct two separate jump instructions.
3496+
let v1 = function.push_insn(bb0, Insn::Const { val: Const::Value(Qfalse) });
3497+
let _ = function.push_insn(bb0, Insn::IfTrue { val: v1, target: edge(bb1)});
3498+
function.push_insn(bb0, Insn::Jump(edge(bb1)));
3499+
3500+
let retval = function.push_insn(bb1, Insn::Const { val: Const::CBool(true) });
3501+
function.push_insn(bb1, Insn::Return { val: retval });
3502+
3503+
let cfi = ControlFlowInfo::new(&function);
3504+
3505+
assert_eq!(cfi.predecessors(bb1).collect::<Vec<_>>().len(), 1);
3506+
assert_eq!(cfi.successors(bb0).collect::<Vec<_>>().len(), 1);
3507+
}
34873508
}
34883509

34893510
/// Test dominator set computations.

0 commit comments

Comments
 (0)