2019-11-11 19:48:17 +00:00
|
|
|
//! A solver for dataflow problems.
|
|
|
|
|
2022-08-19 09:21:14 +00:00
|
|
|
use crate::errors::{
|
|
|
|
DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
|
|
|
|
};
|
2022-02-09 13:47:48 +00:00
|
|
|
use crate::framework::BitSetExt;
|
|
|
|
|
2023-02-20 22:28:03 +00:00
|
|
|
use std::borrow::Borrow;
|
2019-11-11 19:48:17 +00:00
|
|
|
use std::ffi::OsString;
|
2023-02-20 22:28:03 +00:00
|
|
|
use std::marker::PhantomData;
|
2019-11-11 19:48:17 +00:00
|
|
|
use std::path::PathBuf;
|
|
|
|
|
2020-04-27 17:56:11 +00:00
|
|
|
use rustc_ast as ast;
|
2019-11-11 19:48:17 +00:00
|
|
|
use rustc_data_structures::work_queue::WorkQueue;
|
2020-06-02 17:19:49 +00:00
|
|
|
use rustc_graphviz as dot;
|
2019-11-11 19:48:17 +00:00
|
|
|
use rustc_hir::def_id::DefId;
|
2023-04-19 10:57:17 +00:00
|
|
|
use rustc_index::{Idx, IndexVec};
|
2020-03-22 19:09:40 +00:00
|
|
|
use rustc_middle::mir::{self, traversal, BasicBlock};
|
2021-01-05 18:53:07 +00:00
|
|
|
use rustc_middle::mir::{create_dump_file, dump_enabled};
|
2023-04-05 02:06:37 +00:00
|
|
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
2020-10-05 17:12:40 +00:00
|
|
|
use rustc_middle::ty::TyCtxt;
|
2019-11-11 19:48:17 +00:00
|
|
|
use rustc_span::symbol::{sym, Symbol};
|
|
|
|
|
2020-08-28 06:02:46 +00:00
|
|
|
use super::fmt::DebugWithContext;
|
2019-11-11 19:48:17 +00:00
|
|
|
use super::graphviz;
|
2020-03-22 19:09:40 +00:00
|
|
|
use super::{
|
2023-02-20 22:28:03 +00:00
|
|
|
visit_results, Analysis, AnalysisDomain, CloneAnalysis, Direction, GenKill, GenKillAnalysis,
|
|
|
|
GenKillSet, JoinSemiLattice, ResultsClonedCursor, ResultsCursor, ResultsRefCursor,
|
|
|
|
ResultsVisitor,
|
2020-03-22 19:09:40 +00:00
|
|
|
};
|
2019-11-11 19:48:17 +00:00
|
|
|
|
2023-02-20 22:28:03 +00:00
|
|
|
pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as AnalysisDomain<'tcx>>::Domain>;
|
|
|
|
|
2020-03-22 19:09:40 +00:00
|
|
|
/// A dataflow analysis that has converged to fixpoint.
|
2023-02-20 22:28:03 +00:00
|
|
|
pub struct Results<'tcx, A, E = EntrySets<'tcx, A>>
|
2020-03-22 19:09:40 +00:00
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
|
|
|
pub analysis: A,
|
2023-02-20 22:28:03 +00:00
|
|
|
pub(super) entry_sets: E,
|
|
|
|
pub(super) _marker: PhantomData<&'tcx ()>,
|
2020-03-22 19:09:40 +00:00
|
|
|
}
|
|
|
|
|
2023-02-20 22:28:03 +00:00
|
|
|
/// `Results` type with a cloned `Analysis` and borrowed entry sets.
|
|
|
|
pub type ResultsCloned<'res, 'tcx, A> = Results<'tcx, A, &'res EntrySets<'tcx, A>>;
|
|
|
|
|
|
|
|
impl<'tcx, A, E> Results<'tcx, A, E>
|
2020-03-22 19:09:40 +00:00
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
2023-02-20 22:28:03 +00:00
|
|
|
E: Borrow<EntrySets<'tcx, A>>,
|
2020-03-22 19:09:40 +00:00
|
|
|
{
|
|
|
|
/// Creates a `ResultsCursor` that can inspect these `Results`.
|
2021-12-14 17:02:45 +00:00
|
|
|
pub fn into_results_cursor<'mir>(
|
|
|
|
self,
|
|
|
|
body: &'mir mir::Body<'tcx>,
|
2023-02-20 22:28:03 +00:00
|
|
|
) -> ResultsCursor<'mir, 'tcx, A, Self> {
|
2020-03-22 19:09:40 +00:00
|
|
|
ResultsCursor::new(body, self)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the dataflow state for the given block.
|
2020-08-28 06:02:46 +00:00
|
|
|
pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
|
2023-02-20 22:28:03 +00:00
|
|
|
&self.entry_sets.borrow()[block]
|
2020-03-22 19:09:40 +00:00
|
|
|
}
|
|
|
|
|
2021-12-14 17:02:45 +00:00
|
|
|
pub fn visit_with<'mir>(
|
2023-02-20 22:28:03 +00:00
|
|
|
&mut self,
|
2020-03-22 19:09:40 +00:00
|
|
|
body: &'mir mir::Body<'tcx>,
|
|
|
|
blocks: impl IntoIterator<Item = BasicBlock>,
|
2023-02-20 22:28:03 +00:00
|
|
|
vis: &mut impl ResultsVisitor<'mir, 'tcx, Self, FlowState = A::Domain>,
|
2020-03-22 19:09:40 +00:00
|
|
|
) {
|
|
|
|
visit_results(body, blocks, self, vis)
|
|
|
|
}
|
|
|
|
|
2021-12-14 17:02:45 +00:00
|
|
|
pub fn visit_reachable_with<'mir>(
|
2023-02-20 22:28:03 +00:00
|
|
|
&mut self,
|
2020-07-08 16:47:14 +00:00
|
|
|
body: &'mir mir::Body<'tcx>,
|
2023-02-20 22:28:03 +00:00
|
|
|
vis: &mut impl ResultsVisitor<'mir, 'tcx, Self, FlowState = A::Domain>,
|
2020-07-08 16:47:14 +00:00
|
|
|
) {
|
|
|
|
let blocks = mir::traversal::reachable(body);
|
|
|
|
visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
|
|
|
|
}
|
2020-03-22 19:09:40 +00:00
|
|
|
}
|
2023-02-20 22:28:03 +00:00
|
|
|
impl<'tcx, A> Results<'tcx, A>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
|
|
|
/// Creates a `ResultsCursor` that can inspect these `Results`.
|
|
|
|
pub fn as_results_cursor<'a, 'mir>(
|
|
|
|
&'a mut self,
|
|
|
|
body: &'mir mir::Body<'tcx>,
|
|
|
|
) -> ResultsRefCursor<'a, 'mir, 'tcx, A> {
|
|
|
|
ResultsCursor::new(body, self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<'tcx, A> Results<'tcx, A>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx> + CloneAnalysis,
|
|
|
|
{
|
|
|
|
/// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets.
|
|
|
|
pub fn clone_analysis(&self) -> ResultsCloned<'_, 'tcx, A> {
|
|
|
|
Results {
|
|
|
|
analysis: self.analysis.clone_analysis(),
|
|
|
|
entry_sets: &self.entry_sets,
|
|
|
|
_marker: PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a `ResultsCursor` that can inspect these `Results`.
|
|
|
|
pub fn cloned_results_cursor<'mir>(
|
|
|
|
&self,
|
|
|
|
body: &'mir mir::Body<'tcx>,
|
|
|
|
) -> ResultsClonedCursor<'_, 'mir, 'tcx, A> {
|
|
|
|
self.clone_analysis().into_results_cursor(body)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<'res, 'tcx, A> Results<'tcx, A, &'res EntrySets<'tcx, A>>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx> + CloneAnalysis,
|
|
|
|
{
|
|
|
|
/// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets.
|
|
|
|
pub fn reclone_analysis(&self) -> Self {
|
|
|
|
Results {
|
|
|
|
analysis: self.analysis.clone_analysis(),
|
|
|
|
entry_sets: self.entry_sets,
|
|
|
|
_marker: PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-22 19:09:40 +00:00
|
|
|
|
2019-11-11 19:48:17 +00:00
|
|
|
/// A solver for dataflow problems.
|
|
|
|
pub struct Engine<'a, 'tcx, A>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
body: &'a mir::Body<'tcx>,
|
2020-08-28 06:02:46 +00:00
|
|
|
entry_sets: IndexVec<BasicBlock, A::Domain>,
|
2020-09-14 23:54:53 +00:00
|
|
|
pass_name: Option<&'static str>,
|
2019-11-11 19:48:17 +00:00
|
|
|
analysis: A,
|
|
|
|
|
|
|
|
/// Cached, cumulative transfer functions for each block.
|
2020-08-28 21:20:14 +00:00
|
|
|
//
|
|
|
|
// FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for
|
|
|
|
// gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade
|
|
|
|
// performance in practice. I've tried a few ways to avoid this, but they have downsides. See
|
|
|
|
// the message for the commit that added this FIXME for more information.
|
2020-08-28 06:02:46 +00:00
|
|
|
apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
|
2019-11-11 19:48:17 +00:00
|
|
|
}
|
|
|
|
|
2021-12-14 17:02:45 +00:00
|
|
|
impl<'a, 'tcx, A, D, T> Engine<'a, 'tcx, A>
|
2019-11-11 19:48:17 +00:00
|
|
|
where
|
2020-08-28 06:02:46 +00:00
|
|
|
A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
|
2022-02-09 13:47:48 +00:00
|
|
|
D: Clone + JoinSemiLattice + GenKill<T> + BitSetExt<T>,
|
2020-08-28 06:02:46 +00:00
|
|
|
T: Idx,
|
2019-11-11 19:48:17 +00:00
|
|
|
{
|
|
|
|
/// Creates a new `Engine` to solve a gen-kill dataflow problem.
|
2023-02-20 22:28:03 +00:00
|
|
|
pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, mut analysis: A) -> Self {
|
2020-01-20 23:15:37 +00:00
|
|
|
// If there are no back-edges in the control-flow graph, we only ever need to apply the
|
|
|
|
// transfer function for each block exactly once (assuming that we process blocks in RPO).
|
|
|
|
//
|
|
|
|
// In this case, there's no need to compute the block transfer functions ahead of time.
|
2022-07-05 00:00:00 +00:00
|
|
|
if !body.basic_blocks.is_cfg_cyclic() {
|
2020-10-04 22:22:23 +00:00
|
|
|
return Self::new(tcx, body, analysis, None);
|
2020-01-20 23:15:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, compute and store the cumulative transfer function for each block.
|
|
|
|
|
2022-02-09 13:47:48 +00:00
|
|
|
let identity = GenKillSet::identity(analysis.bottom_value(body).domain_size());
|
2022-07-05 00:00:00 +00:00
|
|
|
let mut trans_for_block = IndexVec::from_elem(identity, &body.basic_blocks);
|
2019-11-11 19:48:17 +00:00
|
|
|
|
2022-07-05 00:00:00 +00:00
|
|
|
for (block, block_data) in body.basic_blocks.iter_enumerated() {
|
2019-11-11 19:48:17 +00:00
|
|
|
let trans = &mut trans_for_block[block];
|
2023-02-20 22:28:03 +00:00
|
|
|
A::Direction::gen_kill_effects_in_block(&mut analysis, trans, block, block_data);
|
2019-11-11 19:48:17 +00:00
|
|
|
}
|
|
|
|
|
2020-08-28 06:02:46 +00:00
|
|
|
let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
|
2022-02-09 13:47:48 +00:00
|
|
|
trans_for_block[bb].apply(state);
|
2020-08-28 06:02:46 +00:00
|
|
|
});
|
|
|
|
|
2020-10-04 22:22:23 +00:00
|
|
|
Self::new(tcx, body, analysis, Some(apply_trans as Box<_>))
|
2019-11-11 19:48:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-14 17:02:45 +00:00
|
|
|
impl<'a, 'tcx, A, D> Engine<'a, 'tcx, A>
|
2019-11-11 19:48:17 +00:00
|
|
|
where
|
2020-08-28 06:02:46 +00:00
|
|
|
A: Analysis<'tcx, Domain = D>,
|
|
|
|
D: Clone + JoinSemiLattice,
|
2019-11-11 19:48:17 +00:00
|
|
|
{
|
|
|
|
/// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
|
|
|
|
/// function.
|
|
|
|
///
|
|
|
|
/// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
|
|
|
|
/// better performance.
|
2020-10-04 22:22:23 +00:00
|
|
|
pub fn new_generic(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self {
|
|
|
|
Self::new(tcx, body, analysis, None)
|
2019-11-11 19:48:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn new(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
body: &'a mir::Body<'tcx>,
|
|
|
|
analysis: A,
|
2020-08-28 06:02:46 +00:00
|
|
|
apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
|
2019-11-11 19:48:17 +00:00
|
|
|
) -> Self {
|
2020-08-28 06:02:46 +00:00
|
|
|
let bottom_value = analysis.bottom_value(body);
|
2022-07-05 00:00:00 +00:00
|
|
|
let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), &body.basic_blocks);
|
2019-11-11 19:48:17 +00:00
|
|
|
analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
|
|
|
|
|
2022-06-05 00:00:00 +00:00
|
|
|
if A::Direction::IS_BACKWARD && entry_sets[mir::START_BLOCK] != bottom_value {
|
2020-03-22 19:09:40 +00:00
|
|
|
bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
|
|
|
|
}
|
|
|
|
|
Remove dead unwinds before drop elaboration
As a part of drop elaboration, we identify dead unwinds, i.e., unwind
edges on a drop terminators which are known to be unreachable, because
there is no need to drop anything.
Previously, the data flow framework was informed about the dead unwinds,
and it assumed those edges are absent from MIR. Unfortunately, the data
flow framework wasn't consistent in maintaining this assumption.
In particular, if a block was reachable only through a dead unwind edge,
its state was propagated to other blocks still. This became an issue in
the context of change removes DropAndReplace terminator, since it
introduces initialization into cleanup blocks.
To avoid this issue, remove unreachable unwind edges before the drop
elaboration, and elaborate only blocks that remain reachable.
2023-01-05 09:15:33 +00:00
|
|
|
Engine { analysis, tcx, body, pass_name: None, entry_sets, apply_trans_for_block }
|
2019-11-11 19:48:17 +00:00
|
|
|
}
|
|
|
|
|
2020-09-14 23:54:53 +00:00
|
|
|
/// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
|
|
|
|
///
|
|
|
|
/// Some analyses are run multiple times in the compilation pipeline. Give them a `pass_name`
|
|
|
|
/// to differentiate them. Otherwise, only the results for the latest run will be saved.
|
|
|
|
pub fn pass_name(mut self, name: &'static str) -> Self {
|
|
|
|
self.pass_name = Some(name);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-01-20 02:47:29 +00:00
|
|
|
/// Computes the fixpoint for this dataflow problem and returns it.
|
2020-08-28 06:02:46 +00:00
|
|
|
pub fn iterate_to_fixpoint(self) -> Results<'tcx, A>
|
|
|
|
where
|
|
|
|
A::Domain: DebugWithContext<A>,
|
|
|
|
{
|
2020-03-22 19:09:40 +00:00
|
|
|
let Engine {
|
2023-02-20 22:28:03 +00:00
|
|
|
mut analysis,
|
|
|
|
body,
|
|
|
|
mut entry_sets,
|
|
|
|
tcx,
|
|
|
|
apply_trans_for_block,
|
|
|
|
pass_name,
|
|
|
|
..
|
2020-03-22 19:09:40 +00:00
|
|
|
} = self;
|
2019-11-11 19:48:17 +00:00
|
|
|
|
2022-07-05 00:00:00 +00:00
|
|
|
let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_none(body.basic_blocks.len());
|
2019-11-11 19:48:17 +00:00
|
|
|
|
2022-06-05 00:00:00 +00:00
|
|
|
if A::Direction::IS_FORWARD {
|
2020-03-22 19:09:40 +00:00
|
|
|
for (bb, _) in traversal::reverse_postorder(body) {
|
|
|
|
dirty_queue.insert(bb);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Reverse post-order on the reverse CFG may generate a better iteration order for
|
|
|
|
// backward dataflow analyses, but probably not enough to matter.
|
|
|
|
for (bb, _) in traversal::postorder(body) {
|
|
|
|
dirty_queue.insert(bb);
|
|
|
|
}
|
2019-11-11 19:48:17 +00:00
|
|
|
}
|
|
|
|
|
2020-11-08 21:52:15 +00:00
|
|
|
// `state` is not actually used between iterations;
|
|
|
|
// this is just an optimization to avoid reallocating
|
|
|
|
// every iteration.
|
2020-08-28 06:02:46 +00:00
|
|
|
let mut state = analysis.bottom_value(body);
|
2019-11-11 19:48:17 +00:00
|
|
|
while let Some(bb) = dirty_queue.pop() {
|
2020-03-22 19:09:40 +00:00
|
|
|
let bb_data = &body[bb];
|
2019-11-11 19:48:17 +00:00
|
|
|
|
2020-11-08 21:52:15 +00:00
|
|
|
// Set the state to the entry state of the block.
|
|
|
|
// This is equivalent to `state = entry_sets[bb].clone()`,
|
|
|
|
// but it saves an allocation, thus improving compile times.
|
2020-08-28 06:02:46 +00:00
|
|
|
state.clone_from(&entry_sets[bb]);
|
2020-11-08 21:52:15 +00:00
|
|
|
|
|
|
|
// Apply the block transfer function, using the cached one if it exists.
|
2020-08-28 06:02:46 +00:00
|
|
|
match &apply_trans_for_block {
|
|
|
|
Some(apply) => apply(bb, &mut state),
|
2023-02-20 22:28:03 +00:00
|
|
|
None => {
|
|
|
|
A::Direction::apply_effects_in_block(&mut analysis, &mut state, bb, bb_data)
|
|
|
|
}
|
2020-03-22 19:09:40 +00:00
|
|
|
}
|
2019-11-11 19:48:17 +00:00
|
|
|
|
2020-03-22 19:09:40 +00:00
|
|
|
A::Direction::join_state_into_successors_of(
|
2023-02-20 22:28:03 +00:00
|
|
|
&mut analysis,
|
2020-03-22 19:09:40 +00:00
|
|
|
tcx,
|
|
|
|
body,
|
|
|
|
&mut state,
|
2019-11-11 19:48:17 +00:00
|
|
|
(bb, bb_data),
|
2020-08-28 06:02:46 +00:00
|
|
|
|target: BasicBlock, state: &A::Domain| {
|
|
|
|
let set_changed = entry_sets[target].join(state);
|
2020-03-22 19:09:40 +00:00
|
|
|
if set_changed {
|
|
|
|
dirty_queue.insert(target);
|
|
|
|
}
|
|
|
|
},
|
2019-11-11 19:48:17 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-02-20 22:28:03 +00:00
|
|
|
let mut results = Results { analysis, entry_sets, _marker: PhantomData };
|
2019-11-11 19:48:17 +00:00
|
|
|
|
2023-06-14 12:14:19 +00:00
|
|
|
if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
|
|
|
|
let res = write_graphviz_results(tcx, &body, &mut results, pass_name);
|
|
|
|
if let Err(e) = res {
|
|
|
|
error!("Failed to write graphviz dataflow results: {}", e);
|
|
|
|
}
|
2019-11-11 19:48:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
results
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Graphviz
|
|
|
|
|
|
|
|
/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
|
2023-06-14 12:14:19 +00:00
|
|
|
/// `rustc_mir` attributes and `-Z dump-mir-dataflow`.
|
2021-12-14 17:02:45 +00:00
|
|
|
fn write_graphviz_results<'tcx, A>(
|
2019-11-11 19:48:17 +00:00
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
body: &mir::Body<'tcx>,
|
2023-02-20 22:28:03 +00:00
|
|
|
results: &mut Results<'tcx, A>,
|
2020-09-14 23:54:53 +00:00
|
|
|
pass_name: Option<&'static str>,
|
2019-11-11 19:48:17 +00:00
|
|
|
) -> std::io::Result<()>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
2020-08-28 06:02:46 +00:00
|
|
|
A::Domain: DebugWithContext<A>,
|
2019-11-11 19:48:17 +00:00
|
|
|
{
|
2020-10-05 17:12:40 +00:00
|
|
|
use std::fs;
|
|
|
|
use std::io::{self, Write};
|
|
|
|
|
2020-10-04 22:22:23 +00:00
|
|
|
let def_id = body.source.def_id();
|
2022-02-18 23:48:49 +00:00
|
|
|
let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else {
|
2020-03-28 21:09:12 +00:00
|
|
|
// Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
|
2022-02-18 23:48:49 +00:00
|
|
|
return Ok(());
|
2019-11-11 19:48:17 +00:00
|
|
|
};
|
|
|
|
|
2020-10-05 17:12:40 +00:00
|
|
|
let mut file = match attrs.output_path(A::NAME) {
|
|
|
|
Some(path) => {
|
|
|
|
debug!("printing dataflow results for {:?} to {}", def_id, path.display());
|
|
|
|
if let Some(parent) = path.parent() {
|
|
|
|
fs::create_dir_all(parent)?;
|
|
|
|
}
|
|
|
|
io::BufWriter::new(fs::File::create(&path)?)
|
|
|
|
}
|
2020-03-28 21:09:12 +00:00
|
|
|
|
2023-06-14 12:14:19 +00:00
|
|
|
None if dump_enabled(tcx, A::NAME, def_id) => {
|
2022-12-01 08:49:51 +00:00
|
|
|
create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
|
2020-03-28 21:09:12 +00:00
|
|
|
}
|
|
|
|
|
2020-10-05 17:12:40 +00:00
|
|
|
_ => return Ok(()),
|
2019-11-11 19:48:17 +00:00
|
|
|
};
|
|
|
|
|
2020-08-28 06:02:46 +00:00
|
|
|
let style = match attrs.formatter {
|
|
|
|
Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter,
|
|
|
|
_ => graphviz::OutputStyle::AfterOnly,
|
2019-11-11 19:48:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut buf = Vec::new();
|
|
|
|
|
2020-10-04 22:22:23 +00:00
|
|
|
let graphviz = graphviz::Formatter::new(body, results, style);
|
2020-09-16 15:10:06 +00:00
|
|
|
let mut render_opts =
|
2022-07-06 12:44:47 +00:00
|
|
|
vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())];
|
|
|
|
if tcx.sess.opts.unstable_opts.graphviz_dark_mode {
|
2020-09-08 23:08:35 +00:00
|
|
|
render_opts.push(dot::RenderOption::DarkTheme);
|
|
|
|
}
|
2023-04-05 02:06:37 +00:00
|
|
|
with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts)?);
|
2020-03-28 21:09:34 +00:00
|
|
|
|
2020-10-05 17:12:40 +00:00
|
|
|
file.write_all(&buf)?;
|
2020-03-28 21:09:34 +00:00
|
|
|
|
2019-11-11 19:48:17 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
struct RustcMirAttrs {
|
|
|
|
basename_and_suffix: Option<PathBuf>,
|
|
|
|
formatter: Option<Symbol>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RustcMirAttrs {
|
2021-12-14 17:02:45 +00:00
|
|
|
fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result<Self, ()> {
|
2019-11-11 19:48:17 +00:00
|
|
|
let mut result = Ok(());
|
|
|
|
let mut ret = RustcMirAttrs::default();
|
|
|
|
|
2022-05-02 07:31:56 +00:00
|
|
|
let rustc_mir_attrs = tcx
|
|
|
|
.get_attrs(def_id, sym::rustc_mir)
|
2019-11-11 19:48:17 +00:00
|
|
|
.flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
|
|
|
|
|
|
|
|
for attr in rustc_mir_attrs {
|
2020-08-02 10:17:20 +00:00
|
|
|
let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) {
|
2019-11-11 19:48:17 +00:00
|
|
|
Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
|
|
|
|
let path = PathBuf::from(s.to_string());
|
|
|
|
match path.file_name() {
|
|
|
|
Some(_) => Ok(path),
|
|
|
|
None => {
|
2022-08-19 09:21:14 +00:00
|
|
|
tcx.sess.emit_err(PathMustEndInFilename { span: attr.span() });
|
2019-11-11 19:48:17 +00:00
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2020-08-02 10:17:20 +00:00
|
|
|
} else if attr.has_name(sym::borrowck_graphviz_format) {
|
2019-11-11 19:48:17 +00:00
|
|
|
Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
|
|
|
|
sym::gen_kill | sym::two_phase => Ok(s),
|
|
|
|
_ => {
|
2022-08-19 09:21:14 +00:00
|
|
|
tcx.sess.emit_err(UnknownFormatter { span: attr.span() });
|
2019-11-11 19:48:17 +00:00
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
};
|
|
|
|
|
|
|
|
result = result.and(attr_result);
|
|
|
|
}
|
|
|
|
|
|
|
|
result.map(|()| ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_field<T>(
|
|
|
|
field: &mut Option<T>,
|
2021-12-14 17:02:45 +00:00
|
|
|
tcx: TyCtxt<'_>,
|
2019-11-11 19:48:17 +00:00
|
|
|
attr: &ast::NestedMetaItem,
|
|
|
|
mapper: impl FnOnce(Symbol) -> Result<T, ()>,
|
|
|
|
) -> Result<(), ()> {
|
|
|
|
if field.is_some() {
|
2022-08-19 09:21:14 +00:00
|
|
|
tcx.sess.emit_err(DuplicateValuesFor { span: attr.span(), name: attr.name_or_empty() });
|
2019-11-11 19:48:17 +00:00
|
|
|
|
|
|
|
return Err(());
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(s) = attr.value_str() {
|
|
|
|
*field = Some(mapper(s)?);
|
|
|
|
Ok(())
|
|
|
|
} else {
|
2022-08-19 09:21:14 +00:00
|
|
|
tcx.sess.emit_err(RequiresAnArgument { span: attr.span(), name: attr.name_or_empty() });
|
2019-11-11 19:48:17 +00:00
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the path where dataflow results should be written, or `None`
|
|
|
|
/// `borrowck_graphviz_postflow` was not specified.
|
|
|
|
///
|
|
|
|
/// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
|
|
|
|
///
|
|
|
|
/// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
|
|
|
|
fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
|
|
|
|
let mut ret = self.basename_and_suffix.as_ref().cloned()?;
|
|
|
|
let suffix = ret.file_name().unwrap(); // Checked when parsing attrs
|
|
|
|
|
|
|
|
let mut file_name: OsString = analysis_name.into();
|
|
|
|
file_name.push("_");
|
|
|
|
file_name.push(suffix);
|
|
|
|
ret.set_file_name(file_name);
|
|
|
|
|
|
|
|
Some(ret)
|
|
|
|
}
|
|
|
|
}
|