Skip to content

Commit f2b9533

Browse files
authored
Adding new API that accepts resusable context. (#196)
Sadly due to how the code was structured, I needed to change the `Env' fields so basically everything that was used was changed as well. I did not benchmark anything yet (work in progress). Context -> https://bytecodealliance.zulipchat.com/#narrow/stream/217117-cranelift/topic/Using.20context.20for.20.60TargetIsa.3A.3Acompile_function.60
1 parent 0130fee commit f2b9533

File tree

22 files changed

+801
-564
lines changed

22 files changed

+801
-564
lines changed

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ repository = "https://github.com/bytecodealliance/regalloc2"
1717
log = { version = "0.4.8", default-features = false }
1818
smallvec = { version = "1.6.1", features = ["union"] }
1919
rustc-hash = { version = "2.0.0", default-features = false }
20-
slice-group-by = { version = "0.3.0", default-features = false }
21-
hashbrown = { version = "0.14", features = ["ahash"], default-features = false }
20+
hashbrown = { version = "0.14", default-features = false, features = [] }
2221

2322
# Optional serde support, enabled by feature below.
2423
serde = { version = "1.0.136", features = [
@@ -28,6 +27,8 @@ serde = { version = "1.0.136", features = [
2827

2928
# The below are only needed for fuzzing.
3029
libfuzzer-sys = { version = "0.4.2", optional = true }
30+
bumpalo = { version = "3.16.0", features = ["allocator-api2"] }
31+
allocator-api2 = { version = "0.2.18", default-features = false, features = ["alloc"] }
3132

3233
# When testing regalloc2 by itself, enable debug assertions and overflow checks
3334
[profile.release]

fuzz/fuzz_targets/domtree.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,21 @@ impl Arbitrary<'_> for TestCase {
116116
}
117117

118118
fuzz_target!(|testcase: TestCase| {
119-
let postord = postorder::calculate(testcase.cfg.num_blocks, Block::new(0), |block| {
120-
&testcase.cfg.succs[block.index()]
121-
});
122-
let idom = domtree::calculate(
119+
let mut postorder = vec![];
120+
postorder::calculate(
121+
testcase.cfg.num_blocks,
122+
Block::new(0),
123+
&mut vec![],
124+
&mut postorder,
125+
|block| &testcase.cfg.succs[block.index()],
126+
);
127+
let mut idom = vec![];
128+
domtree::calculate(
123129
testcase.cfg.num_blocks,
124130
|block| &testcase.cfg.preds[block.index()],
125-
&postord[..],
131+
&postorder[..],
132+
&mut vec![],
133+
&mut idom,
126134
Block::new(0),
127135
);
128136
check_idom_violations(&idom[..], &testcase.path);

fuzz/fuzz_targets/ion.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ fuzz_target!(|func: Func| {
1111
let _ = env_logger::try_init();
1212
log::trace!("func:\n{:?}", func);
1313
let env = regalloc2::fuzzing::func::machine_env();
14-
let _out =
15-
regalloc2::fuzzing::ion::run(&func, &env, false, false).expect("regalloc did not succeed");
14+
15+
thread_local! {
16+
// We test that ctx is cleared properly between runs.
17+
static CTX: std::cell::RefCell<regalloc2::fuzzing::ion::Ctx> = std::cell::RefCell::default();
18+
}
19+
20+
CTX.with(|ctx| {
21+
let _out = regalloc2::fuzzing::ion::run(&func, &env, &mut *ctx.borrow_mut(), false, false)
22+
.expect("regalloc did not succeed");
23+
});
1624
});

fuzz/fuzz_targets/ion_checker.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,18 @@ fuzz_target!(|testcase: TestCase| {
3636
let _ = env_logger::try_init();
3737
log::trace!("func:\n{:?}", func);
3838
let env = regalloc2::fuzzing::func::machine_env();
39-
let out =
40-
regalloc2::fuzzing::ion::run(&func, &env, true, false).expect("regalloc did not succeed");
4139

42-
let mut checker = Checker::new(&func, &env);
43-
checker.prepare(&out);
44-
checker.run().expect("checker failed");
40+
thread_local! {
41+
// We test that ctx is cleared properly between runs.
42+
static CTX: std::cell::RefCell<regalloc2::fuzzing::ion::Ctx> = std::cell::RefCell::default();
43+
}
44+
45+
CTX.with(|ctx| {
46+
regalloc2::fuzzing::ion::run(&func, &env, &mut *ctx.borrow_mut(), true, false)
47+
.expect("regalloc did not succeed");
48+
49+
let mut checker = Checker::new(&func, &env);
50+
checker.prepare(&ctx.borrow().output);
51+
checker.run().expect("checker failed");
52+
});
4553
});

fuzz/fuzz_targets/ssagen.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
#![no_main]
77
use regalloc2::fuzzing::arbitrary::{Arbitrary, Result, Unstructured};
8-
use regalloc2::fuzzing::cfg::CFGInfo;
8+
use regalloc2::fuzzing::cfg::{CFGInfo, CFGInfoCtx};
99
use regalloc2::fuzzing::func::{Func, Options};
1010
use regalloc2::fuzzing::fuzz_target;
1111
use regalloc2::ssa::validate_ssa;
@@ -33,6 +33,13 @@ impl Arbitrary<'_> for TestCase {
3333
}
3434

3535
fuzz_target!(|t: TestCase| {
36-
let cfginfo = CFGInfo::new(&t.f).expect("could not create CFG info");
37-
validate_ssa(&t.f, &cfginfo).expect("invalid SSA");
36+
thread_local! {
37+
// We test that ctx is cleared properly between runs.
38+
static CFG_INFO: std::cell::RefCell<(CFGInfo, CFGInfoCtx)> = std::cell::RefCell::default();
39+
}
40+
41+
CFG_INFO.with_borrow_mut(|(cfginfo, ctx)| {
42+
cfginfo.init(&t.f, ctx).expect("could not create CFG info");
43+
validate_ssa(&t.f, &cfginfo).expect("invalid SSA");
44+
});
3845
});

regalloc2-tool/src/main.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ fn main() {
7171
}
7272

7373
fn print_output(func: &SerializableFunction, output: &Output) {
74-
print!("Register allocation result: {{\n");
74+
println!("Register allocation result: {{");
7575
for i in 0..func.num_blocks() {
7676
let block = Block::new(i);
7777
let succs = func
@@ -84,7 +84,7 @@ fn print_output(func: &SerializableFunction, output: &Output) {
8484
.iter()
8585
.map(|b| b.index())
8686
.collect::<Vec<_>>();
87-
print!(" block{}: # succs:{:?} preds:{:?}\n", i, succs, preds);
87+
println!(" block{}: # succs:{:?} preds:{:?}", i, succs, preds);
8888
for inst_or_edit in output.block_insts_and_edits(func, block) {
8989
match inst_or_edit {
9090
InstOrEdit::Inst(inst) => {
@@ -102,13 +102,13 @@ fn print_output(func: &SerializableFunction, output: &Output) {
102102
.map(|(op, alloc)| format!("{op} => {alloc}"))
103103
.collect();
104104
let ops = ops.join(", ");
105-
print!(" inst{}: {op} {ops}\n", inst.index(),);
105+
println!(" inst{}: {op} {ops}", inst.index(),);
106106
}
107107
InstOrEdit::Edit(Edit::Move { from, to }) => {
108-
print!(" edit: move {to} <- {from}\n");
108+
println!(" edit: move {to} <- {from}");
109109
}
110110
}
111111
}
112112
}
113-
print!("}}\n");
113+
println!("}}");
114114
}

src/cfg.rs

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,19 @@
55

66
//! Lightweight CFG analyses.
77
8-
use crate::{domtree, postorder, Block, Function, Inst, ProgPoint, RegAllocError};
9-
use alloc::vec;
10-
use alloc::vec::Vec;
8+
use crate::alloc::vec::Vec;
9+
10+
use crate::{domtree, postorder, Block, Function, Inst, ProgPoint, RegAllocError, VecExt};
1111
use smallvec::{smallvec, SmallVec};
1212

13-
#[derive(Clone, Debug)]
13+
#[derive(Debug, Default)]
14+
pub struct CFGInfoCtx {
15+
visited: Vec<bool>,
16+
block_to_rpo: Vec<Option<u32>>,
17+
backedge: Vec<u32>,
18+
}
19+
20+
#[derive(Debug, Default)]
1421
pub struct CFGInfo {
1522
/// Postorder traversal of blocks.
1623
pub postorder: Vec<Block>,
@@ -33,21 +40,41 @@ pub struct CFGInfo {
3340
}
3441

3542
impl CFGInfo {
36-
pub fn new<F: Function>(f: &F) -> Result<CFGInfo, RegAllocError> {
37-
let postorder = postorder::calculate(f.num_blocks(), f.entry_block(), |block| {
38-
f.block_succs(block)
39-
});
40-
let domtree = domtree::calculate(
41-
f.num_blocks(),
43+
pub fn new<F: Function>(f: &F) -> Result<Self, RegAllocError> {
44+
let mut ctx = CFGInfoCtx::default();
45+
let mut this = Self::default();
46+
this.init(f, &mut ctx)?;
47+
Ok(this)
48+
}
49+
50+
pub fn init<F: Function>(&mut self, f: &F, ctx: &mut CFGInfoCtx) -> Result<(), RegAllocError> {
51+
let nb = f.num_blocks();
52+
53+
postorder::calculate(
54+
nb,
55+
f.entry_block(),
56+
&mut ctx.visited,
57+
&mut self.postorder,
58+
|block| f.block_succs(block),
59+
);
60+
61+
domtree::calculate(
62+
nb,
4263
|block| f.block_preds(block),
43-
&postorder[..],
64+
&self.postorder,
65+
&mut ctx.block_to_rpo,
66+
&mut self.domtree,
4467
f.entry_block(),
4568
);
46-
let mut insn_block = vec![Block::invalid(); f.num_insts()];
47-
let mut block_entry = vec![ProgPoint::before(Inst::invalid()); f.num_blocks()];
48-
let mut block_exit = vec![ProgPoint::before(Inst::invalid()); f.num_blocks()];
49-
let mut backedge_in = vec![0; f.num_blocks()];
50-
let mut backedge_out = vec![0; f.num_blocks()];
69+
70+
let insn_block = self.insn_block.repopulate(f.num_insts(), Block::invalid());
71+
let block_entry = self
72+
.block_entry
73+
.repopulate(nb, ProgPoint::before(Inst::invalid()));
74+
let block_exit = self
75+
.block_exit
76+
.repopulate(nb, ProgPoint::before(Inst::invalid()));
77+
let (backedge_in, backedge_out) = ctx.backedge.repopulate(nb * 2, 0).split_at_mut(nb);
5178

5279
for block in 0..f.num_blocks() {
5380
let block = Block::new(block);
@@ -98,10 +125,10 @@ impl CFGInfo {
98125
}
99126
}
100127

101-
let mut approx_loop_depth = vec![];
102-
let mut backedge_stack: SmallVec<[usize; 4]> = smallvec![];
128+
let approx_loop_depth = self.approx_loop_depth.cleared();
129+
let mut backedge_stack: SmallVec<[u32; 4]> = smallvec![];
103130
let mut cur_depth = 0;
104-
for block in 0..f.num_blocks() {
131+
for block in 0..nb {
105132
if backedge_in[block] > 0 {
106133
cur_depth += 1;
107134
backedge_stack.push(backedge_in[block]);
@@ -119,14 +146,7 @@ impl CFGInfo {
119146
}
120147
}
121148

122-
Ok(CFGInfo {
123-
postorder,
124-
domtree,
125-
insn_block,
126-
block_entry,
127-
block_exit,
128-
approx_loop_depth,
129-
})
149+
Ok(())
130150
}
131151

132152
pub fn dominates(&self, a: Block, b: Block) -> bool {

src/domtree.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
// TR-06-33870
1313
// https://www.cs.rice.edu/~keith/EMBED/dom.pdf
1414

15-
use alloc::vec;
15+
use core::u32;
16+
1617
use alloc::vec::Vec;
1718

18-
use crate::Block;
19+
use crate::{Block, VecExt};
1920

2021
// Helper
2122
fn merge_sets(
@@ -44,19 +45,18 @@ pub fn calculate<'a, PredFn: Fn(Block) -> &'a [Block]>(
4445
num_blocks: usize,
4546
preds: PredFn,
4647
post_ord: &[Block],
48+
block_to_rpo_scratch: &mut Vec<Option<u32>>,
49+
out: &mut Vec<Block>,
4750
start: Block,
48-
) -> Vec<Block> {
51+
) {
4952
// We have post_ord, which is the postorder sequence.
50-
5153
// Compute maps from RPO to block number and vice-versa.
52-
let mut block_to_rpo = vec![None; num_blocks];
53-
block_to_rpo.resize(num_blocks, None);
54+
let block_to_rpo = block_to_rpo_scratch.repopulate(num_blocks, None);
5455
for (i, rpo_block) in post_ord.iter().rev().enumerate() {
5556
block_to_rpo[rpo_block.index()] = Some(i as u32);
5657
}
5758

58-
let mut idom = vec![Block::invalid(); num_blocks];
59-
59+
let idom = out.repopulate(num_blocks, Block::invalid());
6060
// The start node must have itself as a parent.
6161
idom[start.index()] = start;
6262

@@ -70,11 +70,11 @@ pub fn calculate<'a, PredFn: Fn(Block) -> &'a [Block]>(
7070
let mut parent = Block::invalid();
7171
for &pred in preds(node).iter() {
7272
let pred_rpo = match block_to_rpo[pred.index()] {
73-
Some(r) => r,
7473
None => {
7574
// Skip unreachable preds.
7675
continue;
7776
}
77+
Some(r) => r,
7878
};
7979
if pred_rpo < rponum {
8080
parent = pred;
@@ -104,8 +104,6 @@ pub fn calculate<'a, PredFn: Fn(Block) -> &'a [Block]>(
104104
// Now set the start node's dominator-tree parent to "invalid";
105105
// this allows the loop in `dominates` to terminate.
106106
idom[start.index()] = Block::invalid();
107-
108-
idom
109107
}
110108

111109
pub fn dominates(idom: &[Block], a: Block, mut b: Block) -> bool {

src/fastalloc/lru.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
use crate::{PReg, PRegSet, RegClass};
1+
use crate::{FxHashSet, PReg, PRegSet, RegClass};
22
use alloc::vec;
33
use alloc::vec::Vec;
44
use core::{
55
fmt,
66
ops::{Index, IndexMut},
77
};
8-
use hashbrown::HashSet;
98

109
/// A least-recently-used cache organized as a linked list based on a vector.
1110
pub struct Lru {
@@ -193,7 +192,7 @@ impl Lru {
193192
);
194193
if self.head != u8::MAX {
195194
let mut node = self.data[self.head as usize].next;
196-
let mut seen = HashSet::new();
195+
let mut seen = FxHashSet::default();
197196
while node != self.head {
198197
if seen.contains(&node) {
199198
panic!(
@@ -245,7 +244,7 @@ impl fmt::Debug for Lru {
245244
} else {
246245
let mut data_str = format!("p{}", self.head);
247246
let mut node = self.data[self.head as usize].next;
248-
let mut seen = HashSet::new();
247+
let mut seen = FxHashSet::default();
249248
while node != self.head {
250249
if seen.contains(&node) {
251250
panic!(

src/fastalloc/mod.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use crate::moves::{MoveAndScratchResolver, ParallelMoves};
22
use crate::{cfg::CFGInfo, ion::Stats, Allocation, RegAllocError};
33
use crate::{ssa::validate_ssa, Edit, Function, MachineEnv, Output, ProgPoint};
44
use crate::{
5-
AllocationKind, Block, Inst, InstPosition, Operand, OperandConstraint, OperandKind, OperandPos,
6-
PReg, PRegSet, RegClass, SpillSlot, VReg,
5+
AllocationKind, Block, FxHashMap, Inst, InstPosition, Operand, OperandConstraint, OperandKind,
6+
OperandPos, PReg, PRegSet, RegClass, SpillSlot, VReg,
77
};
88
use alloc::vec::Vec;
99
use core::convert::TryInto;
@@ -1150,17 +1150,16 @@ impl<'a, F: Function> Env<'a, F> {
11501150

11511151
fn log_post_reload_at_begin_state(&self, block: Block) {
11521152
use alloc::format;
1153-
use hashbrown::HashMap;
11541153
trace!("");
11551154
trace!("State after instruction reload_at_begin of {:?}", block);
1156-
let mut map = HashMap::new();
1155+
let mut map = FxHashMap::default();
11571156
for (vreg_idx, alloc) in self.vreg_allocs.iter().enumerate() {
11581157
if *alloc != Allocation::none() {
11591158
map.insert(format!("vreg{vreg_idx}"), alloc);
11601159
}
11611160
}
11621161
trace!("vreg_allocs: {:?}", map);
1163-
let mut map = HashMap::new();
1162+
let mut map = FxHashMap::default();
11641163
for i in 0..self.vreg_in_preg.len() {
11651164
if self.vreg_in_preg[i] != VReg::invalid() {
11661165
map.insert(PReg::from_index(i), self.vreg_in_preg[i]);
@@ -1174,10 +1173,9 @@ impl<'a, F: Function> Env<'a, F> {
11741173

11751174
fn log_post_inst_processing_state(&self, inst: Inst) {
11761175
use alloc::format;
1177-
use hashbrown::HashMap;
11781176
trace!("");
11791177
trace!("State after instruction {:?}", inst);
1180-
let mut map = HashMap::new();
1178+
let mut map = FxHashMap::default();
11811179
for (vreg_idx, alloc) in self.vreg_allocs.iter().enumerate() {
11821180
if *alloc != Allocation::none() {
11831181
map.insert(format!("vreg{vreg_idx}"), alloc);
@@ -1289,8 +1287,7 @@ pub fn run<F: Function>(
12891287
enable_ssa_checker: bool,
12901288
) -> Result<Output, RegAllocError> {
12911289
if enable_ssa_checker {
1292-
let cfginfo = CFGInfo::new(func)?;
1293-
validate_ssa(func, &cfginfo)?;
1290+
validate_ssa(func, &CFGInfo::new(func)?)?;
12941291
}
12951292

12961293
if trace_enabled!() || verbose_log {

0 commit comments

Comments
 (0)