Basic-block: Insert regmove instructions in new blocks dedicated to hold the diversions.
This commit is contained in:
committed by
GitHub
parent
0f4101a509
commit
7e398af999
@@ -212,7 +212,7 @@ impl Context {
|
||||
/// Run the locations verifier on the function.
|
||||
pub fn verify_locations(&self, isa: &dyn TargetIsa) -> VerifierResult<()> {
|
||||
let mut errors = VerifierErrors::default();
|
||||
let _ = verify_locations(isa, &self.func, None, &mut errors);
|
||||
let _ = verify_locations(isa, &self.func, &self.cfg, None, &mut errors);
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
|
||||
@@ -44,18 +44,19 @@
|
||||
|
||||
use crate::cursor::{Cursor, EncCursor};
|
||||
use crate::dominator_tree::DominatorTree;
|
||||
use crate::flowgraph::ControlFlowGraph;
|
||||
use crate::ir::{ArgumentLoc, InstBuilder, ValueDef};
|
||||
use crate::ir::{Ebb, Function, Inst, InstructionData, Layout, Opcode, SigRef, Value, ValueLoc};
|
||||
use crate::isa::{regs_overlap, RegClass, RegInfo, RegUnit};
|
||||
use crate::isa::{ConstraintKind, EncInfo, OperandConstraint, RecipeConstraints, TargetIsa};
|
||||
use crate::packed_option::PackedOption;
|
||||
use crate::regalloc::affinity::Affinity;
|
||||
use crate::regalloc::diversion::RegDiversions;
|
||||
use crate::regalloc::live_value_tracker::{LiveValue, LiveValueTracker};
|
||||
use crate::regalloc::liveness::Liveness;
|
||||
use crate::regalloc::liverange::{LiveRange, LiveRangeContext};
|
||||
use crate::regalloc::register_set::RegisterSet;
|
||||
use crate::regalloc::solver::{Solver, SolverError};
|
||||
use crate::regalloc::RegDiversions;
|
||||
use crate::timing;
|
||||
use core::mem;
|
||||
use log::debug;
|
||||
@@ -92,6 +93,7 @@ struct Context<'a> {
|
||||
encinfo: EncInfo,
|
||||
|
||||
// References to contextual data structures we need.
|
||||
cfg: &'a ControlFlowGraph,
|
||||
domtree: &'a DominatorTree,
|
||||
liveness: &'a mut Liveness,
|
||||
|
||||
@@ -126,6 +128,7 @@ impl Coloring {
|
||||
&mut self,
|
||||
isa: &dyn TargetIsa,
|
||||
func: &mut Function,
|
||||
cfg: &ControlFlowGraph,
|
||||
domtree: &DominatorTree,
|
||||
liveness: &mut Liveness,
|
||||
tracker: &mut LiveValueTracker,
|
||||
@@ -137,6 +140,7 @@ impl Coloring {
|
||||
cur: EncCursor::new(func, isa),
|
||||
reginfo: isa.register_info(),
|
||||
encinfo: isa.encoding_info(),
|
||||
cfg,
|
||||
domtree,
|
||||
liveness,
|
||||
divert: &mut self.divert,
|
||||
@@ -166,13 +170,13 @@ impl<'a> Context<'a> {
|
||||
debug!("Coloring {}:", ebb);
|
||||
let mut regs = self.visit_ebb_header(ebb, tracker);
|
||||
tracker.drop_dead_params();
|
||||
self.divert.clear();
|
||||
|
||||
// Now go through the instructions in `ebb` and color the values they define.
|
||||
self.cur.goto_top(ebb);
|
||||
while let Some(inst) = self.cur.next_inst() {
|
||||
self.cur.use_srcloc(inst);
|
||||
if !self.cur.func.dfg[inst].opcode().is_ghost() {
|
||||
let opcode = self.cur.func.dfg[inst].opcode();
|
||||
if !opcode.is_ghost() {
|
||||
// This is an instruction which either has an encoding or carries ABI-related
|
||||
// register allocation constraints.
|
||||
let enc = self.cur.func.encodings[inst];
|
||||
@@ -189,6 +193,54 @@ impl<'a> Context<'a> {
|
||||
self.process_ghost_kills(kills, &mut regs);
|
||||
}
|
||||
tracker.drop_dead(inst);
|
||||
|
||||
// We are not able to insert any regmove for diversion or un-diversion after the first
|
||||
// branch. Instead, we record the diversion to be restored at the entry of the next EBB,
|
||||
// which should have a single predecessor.
|
||||
if opcode.is_branch() && cfg!(feature = "basic-blocks") {
|
||||
// The next instruction is necessarily an unconditional branch.
|
||||
if let Some(branch) = self.cur.next_inst() {
|
||||
debug!(
|
||||
"Skip coloring {}\n from {}\n with diversions {}",
|
||||
self.cur.display_inst(branch),
|
||||
regs.input.display(&self.reginfo),
|
||||
self.divert.display(&self.reginfo)
|
||||
);
|
||||
use crate::ir::instructions::BranchInfo::*;
|
||||
let target = match self.cur.func.dfg.analyze_branch(branch) {
|
||||
NotABranch | Table(_, _) => panic!(
|
||||
"unexpected instruction {} after a conditional branch",
|
||||
self.cur.display_inst(branch)
|
||||
),
|
||||
SingleDest(ebb, _) => ebb,
|
||||
};
|
||||
|
||||
// We have a single branch with a single target, and an EBB with a single
|
||||
// predecessor. Thus we can forward the diversion set to the next EBB.
|
||||
if self.cfg.pred_iter(target).count() == 1 {
|
||||
// Transfer the diversion to the next EBB.
|
||||
self.divert
|
||||
.save_for_ebb(&mut self.cur.func.entry_diversions, target);
|
||||
debug!(
|
||||
"Set entry-diversion for {} to\n {}",
|
||||
target,
|
||||
self.divert.display(&self.reginfo)
|
||||
);
|
||||
} else {
|
||||
debug_assert!(
|
||||
self.divert.is_empty(),
|
||||
"Divert set is non-empty after the terminator."
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
self.cur.next_inst(),
|
||||
None,
|
||||
"Unexpected instruction after a branch group."
|
||||
);
|
||||
} else {
|
||||
assert!(opcode.is_terminator());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +257,15 @@ impl<'a> Context<'a> {
|
||||
self.domtree,
|
||||
);
|
||||
|
||||
// Copy the content of the registered diversions to be reused at the
|
||||
// entry of this basic block.
|
||||
self.divert.at_ebb(&self.cur.func.entry_diversions, ebb);
|
||||
debug!(
|
||||
"Start {} with entry-diversion set to\n {}",
|
||||
ebb,
|
||||
self.divert.display(&self.reginfo)
|
||||
);
|
||||
|
||||
if self.cur.func.layout.entry_block() == Some(ebb) {
|
||||
// Parameters on the entry block have ABI constraints.
|
||||
self.color_entry_params(tracker.live())
|
||||
@@ -231,17 +291,35 @@ impl<'a> Context<'a> {
|
||||
"Live-in: {}:{} in {}",
|
||||
lv.value,
|
||||
lv.affinity.display(&self.reginfo),
|
||||
self.cur.func.locations[lv.value].display(&self.reginfo)
|
||||
self.divert
|
||||
.get(lv.value, &self.cur.func.locations)
|
||||
.display(&self.reginfo)
|
||||
);
|
||||
if let Affinity::Reg(rci) = lv.affinity {
|
||||
let rc = self.reginfo.rc(rci);
|
||||
let loc = self.cur.func.locations[lv.value];
|
||||
match loc {
|
||||
ValueLoc::Reg(reg) => regs.take(rc, reg, lv.is_local),
|
||||
let reg = match loc {
|
||||
ValueLoc::Reg(reg) => reg,
|
||||
ValueLoc::Unassigned => panic!("Live-in {} wasn't assigned", lv.value),
|
||||
ValueLoc::Stack(ss) => {
|
||||
panic!("Live-in {} is in {}, should be register", lv.value, ss)
|
||||
}
|
||||
};
|
||||
if lv.is_local {
|
||||
regs.take(rc, reg, lv.is_local);
|
||||
} else {
|
||||
let loc = self.divert.get(lv.value, &self.cur.func.locations);
|
||||
let reg_divert = match loc {
|
||||
ValueLoc::Reg(reg) => reg,
|
||||
ValueLoc::Unassigned => {
|
||||
panic!("Diversion: Live-in {} wasn't assigned", lv.value)
|
||||
}
|
||||
ValueLoc::Stack(ss) => panic!(
|
||||
"Diversion: Live-in {} is in {}, should be register",
|
||||
lv.value, ss
|
||||
),
|
||||
};
|
||||
regs.take_divert(rc, reg, reg_divert);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1155,4 +1233,10 @@ impl AvailableRegs {
|
||||
self.global.take(rc, reg);
|
||||
}
|
||||
}
|
||||
|
||||
/// Take a diverted register from both sets for a non-local allocation.
|
||||
pub fn take_divert(&mut self, rc: RegClass, reg: RegUnit, reg_divert: RegUnit) {
|
||||
self.input.take(rc, reg_divert);
|
||||
self.global.take(rc, reg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,8 +198,14 @@ impl Context {
|
||||
}
|
||||
|
||||
// Pass: Coloring.
|
||||
self.coloring
|
||||
.run(isa, func, domtree, &mut self.liveness, &mut self.tracker);
|
||||
self.coloring.run(
|
||||
isa,
|
||||
func,
|
||||
cfg,
|
||||
domtree,
|
||||
&mut self.liveness,
|
||||
&mut self.tracker,
|
||||
);
|
||||
|
||||
// This function runs after register allocation has taken
|
||||
// place, meaning values have locations assigned already.
|
||||
@@ -218,7 +224,7 @@ impl Context {
|
||||
if isa.flags().enable_verifier() {
|
||||
let ok = verify_context(func, cfg, domtree, isa, &mut errors).is_ok()
|
||||
&& verify_liveness(isa, func, cfg, &self.liveness, &mut errors).is_ok()
|
||||
&& verify_locations(isa, func, Some(&self.liveness), &mut errors).is_ok()
|
||||
&& verify_locations(isa, func, cfg, Some(&self.liveness), &mut errors).is_ok()
|
||||
&& verify_cssa(
|
||||
func,
|
||||
cfg,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Verify value locations.
|
||||
|
||||
use crate::flowgraph::ControlFlowGraph;
|
||||
use crate::ir;
|
||||
use crate::isa;
|
||||
use crate::regalloc::liveness::Liveness;
|
||||
@@ -21,6 +22,7 @@ use crate::verifier::{VerifierErrors, VerifierStepResult};
|
||||
pub fn verify_locations(
|
||||
isa: &dyn isa::TargetIsa,
|
||||
func: &ir::Function,
|
||||
cfg: &ControlFlowGraph,
|
||||
liveness: Option<&Liveness>,
|
||||
errors: &mut VerifierErrors,
|
||||
) -> VerifierStepResult<()> {
|
||||
@@ -30,6 +32,7 @@ pub fn verify_locations(
|
||||
func,
|
||||
reginfo: isa.register_info(),
|
||||
encinfo: isa.encoding_info(),
|
||||
cfg,
|
||||
liveness,
|
||||
};
|
||||
verifier.check_constraints(errors)?;
|
||||
@@ -41,6 +44,7 @@ struct LocationVerifier<'a> {
|
||||
func: &'a ir::Function,
|
||||
reginfo: isa::RegInfo,
|
||||
encinfo: isa::EncInfo,
|
||||
cfg: &'a ControlFlowGraph,
|
||||
liveness: Option<&'a Liveness>,
|
||||
}
|
||||
|
||||
@@ -53,6 +57,7 @@ impl<'a> LocationVerifier<'a> {
|
||||
for ebb in self.func.layout.ebbs() {
|
||||
divert.at_ebb(&self.func.entry_diversions, ebb);
|
||||
|
||||
let mut is_after_branch = false;
|
||||
for inst in self.func.layout.ebb_insts(ebb) {
|
||||
let enc = self.func.encodings[inst];
|
||||
|
||||
@@ -70,10 +75,11 @@ impl<'a> LocationVerifier<'a> {
|
||||
if opcode.is_return() {
|
||||
self.check_return_abi(inst, &divert, errors)?;
|
||||
} else if opcode.is_branch() && !divert.is_empty() {
|
||||
self.check_cfg_edges(inst, &divert, errors)?;
|
||||
self.check_cfg_edges(inst, &mut divert, is_after_branch, errors)?;
|
||||
}
|
||||
|
||||
self.update_diversions(inst, &mut divert, errors)?;
|
||||
is_after_branch = opcode.is_branch();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,8 +290,9 @@ impl<'a> LocationVerifier<'a> {
|
||||
return fatal!(
|
||||
errors,
|
||||
inst,
|
||||
"inconsistent with global location {}",
|
||||
self.func.locations[arg].display(&self.reginfo)
|
||||
"inconsistent with global location {} ({})",
|
||||
self.func.locations[arg].display(&self.reginfo),
|
||||
self.func.dfg.display_inst(inst, None)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -299,37 +306,52 @@ impl<'a> LocationVerifier<'a> {
|
||||
fn check_cfg_edges(
|
||||
&self,
|
||||
inst: ir::Inst,
|
||||
divert: &RegDiversions,
|
||||
divert: &mut RegDiversions,
|
||||
is_after_branch: bool,
|
||||
errors: &mut VerifierErrors,
|
||||
) -> VerifierStepResult<()> {
|
||||
use crate::ir::instructions::BranchInfo::*;
|
||||
let dfg = &self.func.dfg;
|
||||
let branch_kind = dfg.analyze_branch(inst);
|
||||
|
||||
// We can only check CFG edges if we have a liveness analysis.
|
||||
let liveness = match self.liveness {
|
||||
Some(l) => l,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let dfg = &self.func.dfg;
|
||||
|
||||
match dfg.analyze_branch(inst) {
|
||||
match branch_kind {
|
||||
NotABranch => panic!(
|
||||
"No branch information for {}",
|
||||
dfg.display_inst(inst, self.isa)
|
||||
),
|
||||
SingleDest(ebb, _) => {
|
||||
let unique_predecessor = self.cfg.pred_iter(ebb).count() == 1;
|
||||
let mut val_to_remove = vec![];
|
||||
for (&value, d) in divert.iter() {
|
||||
let lr = &liveness[value];
|
||||
if lr.is_livein(ebb, liveness.context(&self.func.layout)) {
|
||||
if is_after_branch && unique_predecessor {
|
||||
// Forward diversions based on the targeted branch.
|
||||
if !lr.is_livein(ebb, liveness.context(&self.func.layout)) {
|
||||
val_to_remove.push(value)
|
||||
}
|
||||
} else if lr.is_livein(ebb, liveness.context(&self.func.layout)) {
|
||||
return fatal!(
|
||||
errors,
|
||||
inst,
|
||||
"{} is diverted to {} and live in to {}",
|
||||
"SingleDest: {} is diverted to {} and live in to {}",
|
||||
value,
|
||||
d.to.display(&self.reginfo),
|
||||
ebb
|
||||
);
|
||||
}
|
||||
}
|
||||
if is_after_branch && unique_predecessor {
|
||||
for val in val_to_remove.into_iter() {
|
||||
divert.remove(val);
|
||||
}
|
||||
debug_assert!(divert.check_ebb_entry(&self.func.entry_diversions, ebb));
|
||||
}
|
||||
}
|
||||
Table(jt, ebb) => {
|
||||
for (&value, d) in divert.iter() {
|
||||
@@ -339,7 +361,7 @@ impl<'a> LocationVerifier<'a> {
|
||||
return fatal!(
|
||||
errors,
|
||||
inst,
|
||||
"{} is diverted to {} and live in to {}",
|
||||
"Table.default: {} is diverted to {} and live in to {}",
|
||||
value,
|
||||
d.to.display(&self.reginfo),
|
||||
ebb
|
||||
@@ -351,7 +373,7 @@ impl<'a> LocationVerifier<'a> {
|
||||
return fatal!(
|
||||
errors,
|
||||
inst,
|
||||
"{} is diverted to {} and live in to {}",
|
||||
"Table.case: {} is diverted to {} and live in to {}",
|
||||
value,
|
||||
d.to.display(&self.reginfo),
|
||||
ebb
|
||||
|
||||
Reference in New Issue
Block a user