use crate::cursor::{Cursor, EncCursor};
use crate::dbg::DisplayList;
use crate::dominator_tree::{DominatorTree, DominatorTreePreorder};
use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
use crate::fx::FxHashMap;
use crate::ir::{self, InstBuilder, ProgramOrder};
use crate::ir::{Block, ExpandedProgramPoint, Function, Inst, Value};
use crate::isa::{EncInfo, TargetIsa};
use crate::regalloc::affinity::Affinity;
use crate::regalloc::liveness::Liveness;
use crate::regalloc::virtregs::{VirtReg, VirtRegs};
use crate::timing;
use alloc::vec::Vec;
use core::cmp;
use core::fmt;
use core::iter;
use core::slice;
pub struct Coalescing {
preorder: DominatorTreePreorder,
forest: DomForest,
vcopies: VirtualCopies,
values: Vec<Value>,
predecessors: Vec<Inst>,
backedges: Vec<Inst>,
}
struct Context<'a> {
isa: &'a dyn TargetIsa,
encinfo: EncInfo,
func: &'a mut Function,
cfg: &'a ControlFlowGraph,
domtree: &'a DominatorTree,
preorder: &'a DominatorTreePreorder,
liveness: &'a mut Liveness,
virtregs: &'a mut VirtRegs,
forest: &'a mut DomForest,
vcopies: &'a mut VirtualCopies,
values: &'a mut Vec<Value>,
predecessors: &'a mut Vec<Inst>,
backedges: &'a mut Vec<Inst>,
}
impl Coalescing {
pub fn new() -> Self {
Self {
forest: DomForest::new(),
preorder: DominatorTreePreorder::new(),
vcopies: VirtualCopies::new(),
values: Vec::new(),
predecessors: Vec::new(),
backedges: Vec::new(),
}
}
pub fn clear(&mut self) {
self.forest.clear();
self.vcopies.clear();
self.values.clear();
self.predecessors.clear();
self.backedges.clear();
}
pub fn conventional_ssa(
&mut self,
isa: &dyn TargetIsa,
func: &mut Function,
cfg: &ControlFlowGraph,
domtree: &DominatorTree,
liveness: &mut Liveness,
virtregs: &mut VirtRegs,
) {
let _tt = timing::ra_cssa();
log::trace!("Coalescing for:\n{}", func.display(isa));
self.preorder.compute(domtree, &func.layout);
let mut context = Context {
isa,
encinfo: isa.encoding_info(),
func,
cfg,
domtree,
preorder: &self.preorder,
liveness,
virtregs,
forest: &mut self.forest,
vcopies: &mut self.vcopies,
values: &mut self.values,
predecessors: &mut self.predecessors,
backedges: &mut self.backedges,
};
for &block in domtree.cfg_postorder() {
context.union_find_block(block);
}
context.finish_union_find();
context.process_vregs();
}
}
impl<'a> Context<'a> {
pub fn union_find_block(&mut self, block: Block) {
let num_params = self.func.dfg.num_block_params(block);
if num_params == 0 {
return;
}
self.isolate_conflicting_params(block, num_params);
for i in 0..num_params {
self.union_pred_args(block, i);
}
}
fn isolate_conflicting_params(&mut self, block: Block, num_params: usize) {
debug_assert_eq!(num_params, self.func.dfg.num_block_params(block));
for BlockPredecessor {
block: pred_block,
inst: pred_inst,
} in self.cfg.pred_iter(block)
{
if !self.preorder.dominates(block, pred_block) {
continue;
}
log::trace!(
" - checking {} params at back-edge {}: {}",
num_params,
pred_block,
self.func.dfg.display_inst(pred_inst, self.isa)
);
for i in 0..num_params {
let param = self.func.dfg.block_params(block)[i];
if self.liveness[param].reaches_use(pred_inst, pred_block, &self.func.layout) {
self.isolate_param(block, param);
}
}
}
}
fn union_pred_args(&mut self, block: Block, argnum: usize) {
let param = self.func.dfg.block_params(block)[argnum];
for BlockPredecessor {
block: pred_block,
inst: pred_inst,
} in self.cfg.pred_iter(block)
{
let arg = self.func.dfg.inst_variable_args(pred_inst)[argnum];
if let ir::ValueDef::Param(def_block, def_num) = self.func.dfg.value_def(arg) {
if Some(def_block) == self.func.layout.entry_block()
&& self.func.signature.params[def_num].location.is_stack()
{
log::trace!("-> isolating function stack parameter {}", arg);
let new_arg = self.isolate_arg(pred_block, pred_inst, argnum, arg);
self.virtregs.union(param, new_arg);
continue;
}
}
let interference = {
let lr = &self.liveness[arg];
debug_assert!(
lr.def() != block.into(),
"{} parameter {} was missed by isolate_conflicting_params()",
block,
arg
);
lr.is_livein(block, &self.func.layout)
};
if interference {
let new_arg = self.isolate_arg(pred_block, pred_inst, argnum, arg);
self.virtregs.union(param, new_arg);
} else {
self.virtregs.union(param, arg);
}
}
}
fn isolate_param(&mut self, block: Block, param: Value) -> Value {
debug_assert_eq!(
self.func.dfg.value_def(param).pp(),
ExpandedProgramPoint::Block(block)
);
let ty = self.func.dfg.value_type(param);
let new_val = self.func.dfg.replace_block_param(param, ty);
let mut pos = EncCursor::new(self.func, self.isa).at_first_inst(block);
if let Some(inst) = pos.current_inst() {
pos.use_srcloc(inst);
}
pos.ins().with_result(param).copy(new_val);
let inst = pos.built_inst();
self.liveness.move_def_locally(param, inst);
log::trace!(
"-> inserted {}, following {}({}: {})",
pos.display_inst(inst),
block,
new_val,
ty
);
let affinity = Affinity::new(
&self
.encinfo
.operand_constraints(pos.func.encodings[inst])
.expect("Bad copy encoding")
.outs[0],
);
self.liveness.create_dead(new_val, block, affinity);
self.liveness
.extend_locally(new_val, block, inst, &pos.func.layout);
new_val
}
fn isolate_arg(
&mut self,
pred_block: Block,
pred_inst: Inst,
argnum: usize,
pred_val: Value,
) -> Value {
let mut pos = EncCursor::new(self.func, self.isa).at_inst(pred_inst);
pos.use_srcloc(pred_inst);
let copy = pos.ins().copy(pred_val);
let inst = pos.built_inst();
let affinity = Affinity::new(
&self
.encinfo
.operand_constraints(pos.func.encodings[inst])
.expect("Bad copy encoding")
.outs[0],
);
self.liveness.create_dead(copy, inst, affinity);
self.liveness
.extend_locally(copy, pred_block, pred_inst, &pos.func.layout);
pos.func.dfg.inst_variable_args_mut(pred_inst)[argnum] = copy;
log::trace!(
"-> inserted {}, before {}: {}",
pos.display_inst(inst),
pred_block,
pos.display_inst(pred_inst)
);
copy
}
fn finish_union_find(&mut self) {
self.virtregs.finish_union_find(None);
log::trace!("After union-find phase:{}", self.virtregs);
}
}
impl<'a> Context<'a> {
pub fn process_vregs(&mut self) {
for vreg in self.virtregs.all_virtregs() {
self.process_vreg(vreg);
}
}
fn process_vreg(&mut self, vreg: VirtReg) {
if !self.check_vreg(vreg) {
self.synthesize_vreg(vreg);
}
}
fn check_vreg(&mut self, vreg: VirtReg) -> bool {
let values = self.virtregs.sort_values(vreg, self.func, self.preorder);
log::trace!("Checking {} = {}", vreg, DisplayList(values));
self.forest.clear();
for &value in values {
let node = Node::value(value, 0, self.func);
let parent = match self
.forest
.push_node(node, self.func, self.domtree, self.preorder)
{
None => continue,
Some(n) => n,
};
if self.liveness[parent.value].overlaps_def(node.def, node.block, &self.func.layout) {
log::trace!("-> interference: {} overlaps def of {}", parent, value);
return false;
}
}
true
}
fn synthesize_vreg(&mut self, vreg: VirtReg) {
self.vcopies.initialize(
self.virtregs.values(vreg),
self.func,
self.cfg,
self.preorder,
);
log::trace!(
"Synthesizing {} from {} branches and params {}",
vreg,
self.vcopies.branches.len(),
DisplayList(&self.vcopies.params)
);
self.virtregs.remove(vreg);
while let Some(param) = self.vcopies.next_param() {
self.merge_param(param);
self.vcopies.merged_param(param, self.func);
}
}
fn merge_param(&mut self, param: Value) {
let (block, argnum) = match self.func.dfg.value_def(param) {
ir::ValueDef::Param(e, n) => (e, n),
ir::ValueDef::Result(_, _) => panic!("Expected parameter"),
};
debug_assert!(self.predecessors.is_empty());
debug_assert!(self.backedges.is_empty());
for BlockPredecessor {
block: pred_block,
inst: pred_inst,
} in self.cfg.pred_iter(block)
{
if self.preorder.dominates(block, pred_block) {
self.backedges.push(pred_inst);
} else {
self.predecessors.push(pred_inst);
}
}
{
let l = &self.func.layout;
self.backedges.sort_unstable_by(|&a, &b| l.cmp(b, a));
self.predecessors.sort_unstable_by(|&a, &b| l.cmp(a, b));
self.predecessors.extend_from_slice(&self.backedges);
self.backedges.clear();
}
while let Some(pred_inst) = self.predecessors.pop() {
let arg = self.func.dfg.inst_variable_args(pred_inst)[argnum];
if self.try_merge_vregs(param, arg) {
continue;
}
let pred_block = self.func.layout.pp_block(pred_inst);
let new_arg = self.isolate_arg(pred_block, pred_inst, argnum, arg);
self.virtregs
.insert_single(param, new_arg, self.func, self.preorder);
}
}
fn try_merge_vregs(&mut self, param: Value, arg: Value) -> bool {
if self.virtregs.same_class(param, arg) {
return true;
}
if !self.can_merge_vregs(param, arg) {
return false;
}
let _vreg = self.virtregs.unify(self.values);
log::trace!("-> merged into {} = {}", _vreg, DisplayList(self.values));
true
}
fn can_merge_vregs(&mut self, param: Value, arg: Value) -> bool {
let func = &*self.func;
let domtree = self.domtree;
let preorder = self.preorder;
self.vcopies
.set_filter([param, arg], func, self.virtregs, preorder);
let v0 = self.virtregs.congruence_class(¶m);
let v1 = self.virtregs.congruence_class(&arg);
log::trace!(
" - set 0: {}\n - set 1: {}",
DisplayList(v0),
DisplayList(v1)
);
let nodes = MergeNodes::new(
func,
preorder,
MergeNodes::new(
func,
preorder,
v0.iter().map(|&value| Node::value(value, 0, func)),
v1.iter().map(|&value| Node::value(value, 1, func)),
),
self.vcopies.iter(func),
);
self.forest.clear();
self.values.clear();
for node in nodes {
if node.is_value() {
self.values.push(node.value);
}
let parent = match self.forest.push_node(node, func, domtree, preorder) {
None => {
if node.is_vcopy {
self.forest.pop_last();
}
continue;
}
Some(n) => n,
};
if node.is_vcopy {
if parent.is_vcopy || node.value == parent.value {
self.forest.pop_last();
continue;
}
let inst = node.def.unwrap_inst();
if node.set_id != parent.set_id
&& self.liveness[parent.value].reaches_use(inst, node.block, &self.func.layout)
{
log::trace!(
" - interference: {} overlaps vcopy at {}:{}",
parent,
node.block,
self.func.dfg.display_inst(inst, self.isa)
);
return false;
}
continue;
}
if parent.is_vcopy {
continue;
}
debug_assert!(node.is_value() && parent.is_value());
if node.set_id != parent.set_id
&& self.liveness[parent.value].overlaps_def(node.def, node.block, &self.func.layout)
{
log::trace!(" - interference: {} overlaps def of {}", parent, node.value);
return false;
}
}
debug_assert_eq!(v0.len() + v1.len(), self.values.len());
true
}
}
#[allow(dead_code)]
struct DomForest {
stack: Vec<Node>,
}
#[derive(Clone, Copy, Debug)]
#[allow(dead_code)]
struct Node {
def: ExpandedProgramPoint,
block: Block,
is_vcopy: bool,
set_id: u8,
value: Value,
}
impl Node {
pub fn value(value: Value, set_id: u8, func: &Function) -> Self {
let def = func.dfg.value_def(value).pp();
let block = func.layout.pp_block(def);
Self {
def,
block,
is_vcopy: false,
set_id,
value,
}
}
pub fn vcopy(branch: Inst, value: Value, set_id: u8, func: &Function) -> Self {
let def = branch.into();
let block = func.layout.pp_block(def);
Self {
def,
block,
is_vcopy: true,
set_id,
value,
}
}
pub fn is_value(&self) -> bool {
!self.is_vcopy
}
}
impl fmt::Display for Node {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_vcopy {
write!(f, "{}:vcopy({})@{}", self.set_id, self.value, self.block)
} else {
write!(f, "{}:{}@{}", self.set_id, self.value, self.block)
}
}
}
impl DomForest {
pub fn new() -> Self {
Self { stack: Vec::new() }
}
pub fn clear(&mut self) {
self.stack.clear();
}
fn push_node(
&mut self,
node: Node,
func: &Function,
domtree: &DominatorTree,
preorder: &DominatorTreePreorder,
) -> Option<Node> {
while let Some(top) = self.stack.pop() {
if preorder.dominates(top.block, node.block) {
self.stack.push(top);
self.stack.push(node);
let mut last_dom = node.def;
for &n in self.stack.iter().rev().skip(1) {
let def_inst = match n.def {
ExpandedProgramPoint::Block(_) => return Some(n),
ExpandedProgramPoint::Inst(i) => i,
};
last_dom = match domtree.last_dominator(n.block, last_dom, &func.layout) {
None => n.block.into(),
Some(inst) => {
if func.layout.cmp(def_inst, inst) != cmp::Ordering::Greater {
return Some(n);
}
inst.into()
}
};
}
return None;
}
}
self.stack.push(node);
None
}
pub fn pop_last(&mut self) {
self.stack.pop().expect("Stack is empty");
}
}
struct VirtualCopies {
params: Vec<Value>,
branches: Vec<(Inst, Block)>,
filter: FxHashMap<Block, (u8, usize)>,
}
impl VirtualCopies {
pub fn new() -> Self {
Self {
params: Vec::new(),
branches: Vec::new(),
filter: FxHashMap(),
}
}
pub fn clear(&mut self) {
self.params.clear();
self.branches.clear();
self.filter.clear();
}
pub fn initialize(
&mut self,
values: &[Value],
func: &Function,
cfg: &ControlFlowGraph,
preorder: &DominatorTreePreorder,
) {
self.clear();
let mut last_block = None;
for &val in values {
if let ir::ValueDef::Param(block, _) = func.dfg.value_def(val) {
self.params.push(val);
if let Some(last) = last_block {
match preorder.pre_cmp_block(last, block) {
cmp::Ordering::Less => {}
cmp::Ordering::Equal => continue,
cmp::Ordering::Greater => panic!("values in wrong order"),
}
}
for BlockPredecessor {
inst: pred_inst, ..
} in cfg.pred_iter(block)
{
self.branches.push((pred_inst, block));
}
last_block = Some(block);
}
}
self.branches
.sort_unstable_by(|&(a, _), &(b, _)| preorder.pre_cmp(a, b, &func.layout));
}
pub fn next_param(&self) -> Option<Value> {
self.params.last().cloned()
}
pub fn merged_param(&mut self, param: Value, func: &Function) {
let popped = self.params.pop();
debug_assert_eq!(popped, Some(param));
let last = match self.params.last() {
None => return,
Some(x) => *x,
};
let block = func.dfg.value_def(param).unwrap_block();
if func.dfg.value_def(last).unwrap_block() == block {
return;
}
self.branches.retain(|&(_, dest)| dest != block);
}
pub fn set_filter(
&mut self,
reprs: [Value; 2],
func: &Function,
virtregs: &VirtRegs,
preorder: &DominatorTreePreorder,
) {
self.filter.clear();
let last_param = *self.params.last().expect("No more parameters");
let limit = func.dfg.value_def(last_param).unwrap_block();
for (set_id, repr) in reprs.iter().enumerate() {
let set_id = set_id as u8;
for &value in virtregs.congruence_class(repr) {
if let ir::ValueDef::Param(block, num) = func.dfg.value_def(value) {
if preorder.pre_cmp_block(block, limit) == cmp::Ordering::Greater {
break;
}
self.filter.insert(block, (set_id, num));
}
}
}
}
fn lookup(&self, block: Block) -> Option<(u8, usize)> {
self.filter.get(&block).cloned()
}
pub fn iter<'a>(&'a self, func: &'a Function) -> VCopyIter {
VCopyIter {
func,
vcopies: self,
branches: self.branches.iter(),
}
}
}
struct VCopyIter<'a> {
func: &'a Function,
vcopies: &'a VirtualCopies,
branches: slice::Iter<'a, (Inst, Block)>,
}
impl<'a> Iterator for VCopyIter<'a> {
type Item = Node;
fn next(&mut self) -> Option<Node> {
while let Some(&(branch, dest)) = self.branches.next() {
if let Some((set_id, argnum)) = self.vcopies.lookup(dest) {
let arg = self.func.dfg.inst_variable_args(branch)[argnum];
return Some(Node::vcopy(branch, arg, set_id, self.func));
}
}
None
}
}
struct MergeNodes<'a, IA, IB>
where
IA: Iterator<Item = Node>,
IB: Iterator<Item = Node>,
{
a: iter::Peekable<IA>,
b: iter::Peekable<IB>,
layout: &'a ir::Layout,
preorder: &'a DominatorTreePreorder,
}
impl<'a, IA, IB> MergeNodes<'a, IA, IB>
where
IA: Iterator<Item = Node>,
IB: Iterator<Item = Node>,
{
pub fn new(func: &'a Function, preorder: &'a DominatorTreePreorder, a: IA, b: IB) -> Self {
MergeNodes {
a: a.peekable(),
b: b.peekable(),
layout: &func.layout,
preorder,
}
}
}
impl<'a, IA, IB> Iterator for MergeNodes<'a, IA, IB>
where
IA: Iterator<Item = Node>,
IB: Iterator<Item = Node>,
{
type Item = Node;
fn next(&mut self) -> Option<Node> {
let ord = match (self.a.peek(), self.b.peek()) {
(Some(a), Some(b)) => {
let layout = self.layout;
self.preorder
.pre_cmp_block(a.block, b.block)
.then_with(|| layout.cmp(a.def, b.def))
}
(Some(_), None) => cmp::Ordering::Less,
(None, Some(_)) => cmp::Ordering::Greater,
(None, None) => return None,
};
if ord != cmp::Ordering::Greater {
self.a.next()
} else {
self.b.next()
}
}
}