Use an FxHashMap in RegDiversions.

Because it's hot and the number of entries can reach the 1000s, so
linear insertion and search is bad.

This reduces runtime for `sqlite` and `UE4Game-HTML5-Shipping` by 3-4%,
and a couple of other benchmarks (`sqlite`, `godot`, `clang`) by smaller
amounts.

It also increases runtime for `mono` and `tanks` by about 1%; this seems
to be due to incidental changes in which functions are inlined more than
algorithmic changes.
This commit is contained in:
Nicholas Nethercote
2018-12-14 09:34:23 +11:00
committed by Dan Gohman
parent c9666381f6
commit 46d9a3cd1a
4 changed files with 40 additions and 36 deletions

View File

@@ -654,10 +654,10 @@ impl<'a> Context<'a> {
where
Pred: FnMut(&LiveRange, LiveRangeContext<Layout>) -> bool,
{
for rdiv in self.divert.all() {
for (&value, rdiv) in self.divert.iter() {
let lr = self
.liveness
.get(rdiv.value)
.get(value)
.expect("Missing live range for diverted register");
if pred(lr, self.liveness.context(&self.cur.func.layout)) {
if let Affinity::Reg(rci) = lr.affinity {
@@ -665,7 +665,7 @@ impl<'a> Context<'a> {
// Stack diversions should not be possible here. The only live transiently
// during `shuffle_inputs()`.
self.solver.reassign_in(
rdiv.value,
value,
rc,
rdiv.to.unwrap_reg(),
rdiv.from.unwrap_reg(),
@@ -673,7 +673,7 @@ impl<'a> Context<'a> {
} else {
panic!(
"Diverted register {} with {} affinity",
rdiv.value,
value,
lr.affinity.display(&self.reginfo)
);
}

View File

@@ -7,11 +7,12 @@
//! These register diversions are local to an EBB. No values can be diverted when entering a new
//! EBB.
use fx::FxHashMap;
use ir::{InstructionData, Opcode};
use ir::{StackSlot, Value, ValueLoc, ValueLocations};
use isa::{RegInfo, RegUnit};
use std::collections::hash_map::{Entry, Iter};
use std::fmt;
use std::vec::Vec;
/// A diversion of a value from its original location to a new register or stack location.
///
@@ -22,8 +23,6 @@ use std::vec::Vec;
/// the current one.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Diversion {
/// The value that is diverted.
pub value: Value,
/// The original value location.
pub from: ValueLoc,
/// The current value location.
@@ -32,22 +31,22 @@ pub struct Diversion {
impl Diversion {
/// Make a new diversion.
pub fn new(value: Value, from: ValueLoc, to: ValueLoc) -> Self {
pub fn new(from: ValueLoc, to: ValueLoc) -> Self {
debug_assert!(from.is_assigned() && to.is_assigned());
Self { value, from, to }
Self { from, to }
}
}
/// Keep track of diversions in an EBB.
pub struct RegDiversions {
current: Vec<Diversion>,
current: FxHashMap<Value, Diversion>,
}
impl RegDiversions {
/// Create a new empty diversion tracker.
pub fn new() -> Self {
Self {
current: Vec::new(),
current: FxHashMap::default(),
}
}
@@ -63,12 +62,12 @@ impl RegDiversions {
/// Get the current diversion of `value`, if any.
pub fn diversion(&self, value: Value) -> Option<&Diversion> {
self.current.iter().find(|d| d.value == value)
self.current.get(&value)
}
/// Get all current diversions.
pub fn all(&self) -> &[Diversion] {
self.current.as_slice()
pub fn iter(&self) -> Iter<'_, Value, Diversion> {
self.current.iter()
}
/// Get the current location for `value`. Fall back to the assignment map for non-diverted
@@ -95,15 +94,22 @@ impl RegDiversions {
/// The `from` location must match an existing `to` location, if any.
pub fn divert(&mut self, value: Value, from: ValueLoc, to: ValueLoc) {
debug_assert!(from.is_assigned() && to.is_assigned());
if let Some(i) = self.current.iter().position(|d| d.value == value) {
debug_assert_eq!(self.current[i].to, from, "Bad regmove chain for {}", value);
if self.current[i].from != to {
self.current[i].to = to;
} else {
self.current.swap_remove(i);
match self.current.entry(value) {
Entry::Occupied(mut e) => {
// TODO: non-lexical lifetimes should allow removal of the scope and early return.
{
let d = e.get_mut();
debug_assert_eq!(d.to, from, "Bad regmove chain for {}", value);
if d.from != to {
d.to = to;
return;
}
}
e.remove();
}
Entry::Vacant(e) => {
e.insert(Diversion::new(from, to));
}
} else {
self.current.push(Diversion::new(value, from, to));
}
}
@@ -154,10 +160,7 @@ impl RegDiversions {
///
/// Returns the `to` location of the removed diversion.
pub fn remove(&mut self, value: Value) -> Option<ValueLoc> {
self.current
.iter()
.position(|d| d.value == value)
.map(|i| self.current.swap_remove(i).to)
self.current.remove(&value).map(|d| d.to)
}
/// Return an object that can display the diversions.
@@ -172,11 +175,11 @@ pub struct DisplayDiversions<'a>(&'a RegDiversions, Option<&'a RegInfo>);
impl<'a> fmt::Display for DisplayDiversions<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{{")?;
for div in self.0.all() {
for (value, div) in self.0.iter() {
write!(
f,
" {}: {} -> {}",
div.value,
value,
div.from.display(self.1),
div.to.display(self.1)
)?
@@ -201,7 +204,6 @@ mod tests {
assert_eq!(
divs.diversion(v1),
Some(&Diversion {
value: v1,
from: ValueLoc::Reg(10),
to: ValueLoc::Reg(12),
})

View File

@@ -318,14 +318,14 @@ impl<'a> LocationVerifier<'a> {
dfg.display_inst(inst, self.isa)
),
SingleDest(ebb, _) => {
for d in divert.all() {
let lr = &liveness[d.value];
for (&value, d) in divert.iter() {
let lr = &liveness[value];
if lr.is_livein(ebb, liveness.context(&self.func.layout)) {
return fatal!(
errors,
inst,
"{} is diverted to {} and live in to {}",
d.value,
value,
d.to.display(&self.reginfo),
ebb
);
@@ -333,15 +333,15 @@ impl<'a> LocationVerifier<'a> {
}
}
Table(jt, ebb) => {
for d in divert.all() {
let lr = &liveness[d.value];
for (&value, d) in divert.iter() {
let lr = &liveness[value];
if let Some(ebb) = ebb {
if lr.is_livein(ebb, liveness.context(&self.func.layout)) {
return fatal!(
errors,
inst,
"{} is diverted to {} and live in to {}",
d.value,
value,
d.to.display(&self.reginfo),
ebb
);
@@ -353,7 +353,7 @@ impl<'a> LocationVerifier<'a> {
errors,
inst,
"{} is diverted to {} and live in to {}",
d.value,
value,
d.to.display(&self.reginfo),
ebb
);

View File

@@ -97,6 +97,7 @@ where
}
/// Resize the map to have `n` entries by adding default entries as needed.
#[inline]
pub fn resize(&mut self, n: usize) {
self.elems.resize(n, self.default.clone());
}
@@ -125,6 +126,7 @@ where
K: EntityRef,
V: Clone,
{
#[inline]
fn index_mut(&mut self, k: K) -> &mut V {
let i = k.index();
if i >= self.elems.len() {