Skip to content

Commit d3a4ee9

Browse files
authored
Prefer registers for block-param moves (#137)
Prefer registers for block-param moves, and use a HashMap to store block-param move sources.
1 parent b8434a5 commit d3a4ee9

File tree

1 file changed

+36
-78
lines changed

1 file changed

+36
-78
lines changed

src/ion/moves.rs

Lines changed: 36 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use super::{
1717
RedundantMoveEliminator, VRegIndex, SLOT_NONE,
1818
};
1919
use crate::ion::data_structures::{
20-
u128_key, u64_key, BlockparamIn, BlockparamOut, CodeRange, FixedRegFixupLevel, LiveRangeKey,
20+
u64_key, BlockparamIn, BlockparamOut, CodeRange, FixedRegFixupLevel, LiveRangeKey,
2121
LiveRangeListEntry, PosWithPrio,
2222
};
2323
use crate::ion::reg_traversal::RegTraversalIter;
@@ -230,25 +230,16 @@ impl<'a, F: Function> Env<'a, F> {
230230
let mut inter_block_sources: FxHashMap<Block, Allocation> = FxHashMap::default();
231231
let mut inter_block_dests = Vec::with_capacity(self.func.num_blocks());
232232

233-
// This is the same data as BlockparamOut, but the fields are in a different order to
234-
// prefer sorting by destination vreg first. This allows us to advance two pointers
235-
// simultaneously into block_param_sources and block_param_dests when generating moves.
236-
struct BlockparamSource {
237-
from_vreg: VRegIndex,
238-
from_block: Block,
239-
to_block: Block,
240-
to_vreg: VRegIndex,
241-
alloc: Allocation,
233+
#[derive(Hash, Eq, PartialEq)]
234+
struct BlockparamSourceKey {
235+
bits: u64,
242236
}
243237

244-
impl BlockparamSource {
245-
fn key(&self) -> u128 {
246-
u128_key(
247-
self.to_vreg.raw_u32(),
248-
self.to_block.raw_u32(),
249-
self.from_block.raw_u32(),
250-
self.from_vreg.raw_u32(),
251-
)
238+
impl BlockparamSourceKey {
239+
fn new(from_block: Block, to_vreg: VRegIndex) -> Self {
240+
BlockparamSourceKey {
241+
bits: u64_key(from_block.raw_u32(), to_vreg.raw_u32()),
242+
}
252243
}
253244
}
254245

@@ -263,9 +254,17 @@ impl<'a, F: Function> Env<'a, F> {
263254
fn key(&self) -> u64 {
264255
u64_key(self.to_block.raw_u32(), self.from_block.raw_u32())
265256
}
257+
258+
fn source(&self) -> BlockparamSourceKey {
259+
BlockparamSourceKey::new(self.from_block, self.to_vreg)
260+
}
266261
}
267262

268-
let mut block_param_sources = Vec::with_capacity(3 * self.func.num_insts());
263+
let mut block_param_sources =
264+
FxHashMap::<BlockparamSourceKey, Allocation>::with_capacity_and_hasher(
265+
3 * self.func.num_insts(),
266+
Default::default(),
267+
);
269268
let mut block_param_dests = Vec::with_capacity(3 * self.func.num_insts());
270269

271270
let debug_labels = self.func.debug_value_labels();
@@ -287,7 +286,6 @@ impl<'a, F: Function> Env<'a, F> {
287286
// `blockparam_outs`, which are sorted by (block, vreg),
288287
// to fill in allocations.
289288
let mut prev = PrevBuffer::new(blockparam_in_idx);
290-
let dests_start = block_param_dests.len();
291289
for range_idx in 0..self.vregs[vreg.index()].ranges.len() {
292290
let entry = self.vregs[vreg.index()].ranges[range_idx];
293291
let alloc = self.get_alloc_for_range(entry.index);
@@ -426,13 +424,19 @@ impl<'a, F: Function> Env<'a, F> {
426424
to_vreg.index()
427425
);
428426

429-
block_param_sources.push(BlockparamSource {
430-
from_block,
431-
to_block,
432-
to_vreg,
433-
from_vreg: vreg,
434-
alloc,
435-
});
427+
let key = BlockparamSourceKey::new(from_block, to_vreg);
428+
match block_param_sources.entry(key) {
429+
// As with inter-block moves, if the entry is already present we'll
430+
// try to prefer a register allocation.
431+
Entry::Occupied(mut entry) => {
432+
if !entry.get().is_reg() {
433+
entry.insert(alloc);
434+
}
435+
}
436+
Entry::Vacant(entry) => {
437+
entry.insert(alloc);
438+
}
439+
}
436440

437441
if self.annotations_enabled {
438442
self.annotate(
@@ -615,11 +619,6 @@ impl<'a, F: Function> Env<'a, F> {
615619
}
616620
}
617621

618-
// Sort the newly added block_param destinations. The key function ignores the vreg,
619-
// which is why we sort here: we preserve the vreg order automatically, and sort
620-
// smaller slices of the dests according to their source/destination blocks.
621-
block_param_dests[dests_start..].sort_unstable_by_key(BlockparamDest::key);
622-
623622
if !inter_block_dests.is_empty() {
624623
self.stats.halfmoves_count += inter_block_dests.len() * 2;
625624

@@ -650,53 +649,12 @@ impl<'a, F: Function> Env<'a, F> {
650649
self.stats.halfmoves_count += block_param_sources.len();
651650
self.stats.halfmoves_count += block_param_dests.len();
652651

653-
// Sort the sources to mirror the destinations now, ordering by dest vreg/dest
654-
// block/source block.
655-
block_param_sources.sort_unstable_by_key(BlockparamSource::key);
656-
657-
// We traverse the `block_param_sources` and `block_param_dests` vectors in parallel,
658-
// advancing a pointer into the sources vector as we disocver dests that don't match
659-
// it. There are two places that we ensure that the order of those two vectors enables
660-
// this traversal: above when we sort `block_param_sources` according to its key; at
661-
// the end of the main vreg loop above when we sort a slice of `block_param_dests` by
662-
// its key.
663-
//
664-
// In both cases, the key function will order entries lexicographically according to
665-
// (destination vreg, destination block, source block). One implication of this is that
666-
// if there are multiple sources available for a destination, we'll always pick the one
667-
// with the lowest source vreg. We could potentially improve this by selecting from the
668-
// range of possible sources based on some heuristic (prefer registers, for example).
669-
670652
trace!("processing block-param moves");
671-
let mut block_param_sources = block_param_sources.into_iter().peekable();
672-
'outer: for dest in block_param_dests {
673-
while let Some(src) = block_param_sources.peek() {
674-
if src.to_vreg == dest.to_vreg
675-
&& src.from_block == dest.from_block
676-
&& src.to_block == dest.to_block
677-
{
678-
trace!(" -> moving from {} to {}", src.alloc, dest.alloc);
679-
680-
let (pos, prio) =
681-
choose_move_location(self, dest.from_block, dest.to_block);
682-
inserted_moves.push(
683-
pos,
684-
prio,
685-
src.alloc,
686-
dest.alloc,
687-
self.vreg(dest.to_vreg),
688-
);
689-
690-
// We don't advance the block_param_sources iterator here because there
691-
// could be additional destinations that would take from that source. Thus,
692-
// we continue the outer loop to keep the iterator unchanged.
693-
continue 'outer;
694-
}
695-
696-
block_param_sources.next();
697-
}
698-
699-
panic!("Ran out of block-param sources");
653+
for dest in block_param_dests {
654+
let src = dest.source();
655+
let src_alloc = block_param_sources.get(&src).unwrap();
656+
let (pos, prio) = choose_move_location(self, dest.from_block, dest.to_block);
657+
inserted_moves.push(pos, prio, *src_alloc, dest.alloc, self.vreg(dest.to_vreg));
700658
}
701659
}
702660

0 commit comments

Comments
 (0)