Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign up[WIP][MIR] Generic lattice-based DF framework #33628
Conversation
nagisa
added some commits
May 14, 2016
rust-highfive
assigned
Aatch
May 14, 2016
nagisa
unassigned
Aatch
May 14, 2016
This comment has been minimized.
This comment has been minimized.
|
r? @Aatch (rust_highfive has picked a reviewer for you, use r? to override) |
This comment has been minimized.
This comment has been minimized.
|
|
gereeter
reviewed
May 14, 2016
| StatementChange::None => StatementChange::Statement(ns), | ||
| x => x | ||
| }, | ||
| StatementChange::Statements(nss) => { |
This comment has been minimized.
This comment has been minimized.
gereeter
May 14, 2016
Contributor
I think that, in the case of multiple statements, just transforming each statement doesn't work - the fact needs to be transferred across each statement. For example, an (admittedly silly) transformation that takes x = y to newtmp = y; x = newtmp should not mess up constant propogation - if y is constant, it should infer that newtmp is the same constant and fill in the value for x.
In Hoopl, this is solved by having rewrite functions be more like lists of rewrite functions - thenFwdRw doesn't actually run one rewrite after another, instead doing the first rewrite and then scheduling the second rewrite to occur next. Thus, the final transfer and rewrite engine is responsible for running rewrites in sequence, and can transfer facts across newly transformed statements.
This comment has been minimized.
This comment has been minimized.
nagisa
May 14, 2016
Author
Contributor
I think that, in the case of multiple statements, just transforming each statement doesn't work - the fact needs to be transferred across each statement.
That’s right, I was thinking about how I would approach it and that’s exactly why I didn’t include a way to replace a statement with a (sub-)graph but replacement with a list of statements fell through the cracks.
gereeter
reviewed
May 14, 2016
| self.1 = true; | ||
| *lvalue = nlval.clone(); | ||
| } | ||
| _ => {} |
This comment has been minimized.
This comment has been minimized.
gereeter
reviewed
May 14, 2016
| impl<'a, 'tcx> MutVisitor<'tcx> for RewriteConstVisitor<'a, 'tcx> { | ||
| fn visit_operand(&mut self, op: &mut Operand<'tcx>) { | ||
| let repl = if let Operand::Consume(ref lval) = *op { | ||
| if let Some(&Either::Const(ref c)) = self.0.get(lval) { |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
gereeter
May 16, 2016
Contributor
Er... Scratch that. This doesn't work. I don't know what I was thinking.
gereeter
reviewed
May 14, 2016
| //! For all of them we will be using a lattice of Hashmap from Lvalue to | ||
| //! WTop<Either<Lvalue, Constant>> | ||
| //! | ||
| //! My personal believ is that it should be possible to make a way to compose two hashmap lattices |
This comment has been minimized.
This comment has been minimized.
gereeter
reviewed
May 14, 2016
| @@ -939,7 +939,8 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, | |||
| passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks); | |||
| passes.push_pass(box mir::transform::qualify_consts::QualifyAndPromoteConstants); | |||
| passes.push_pass(box mir::transform::type_check::TypeckMir); | |||
| passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg); | |||
| passes.push_pass(box mir::transform::acs_propagate::ACSPropagate); | |||
| // passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg); | |||
This comment has been minimized.
This comment has been minimized.
gereeter
reviewed
May 14, 2016
| } | ||
|
|
||
| /// Analyse and rewrite using dataflow in the forward direction | ||
| pub fn ar_forward<'tcx, T, P>(cfg: &CFG<'tcx>, fs: Facts<P::Lattice>, mut queue: BitVector) |
This comment has been minimized.
This comment has been minimized.
gereeter
May 14, 2016
Contributor
Could ar_forward just generate queue itself instead of taking the queue as an argument?
gereeter
reviewed
May 14, 2016
|
|
||
| impl<F: Lattice> ::std::ops::IndexMut<BasicBlock> for Facts<F> { | ||
| fn index_mut(&mut self, index: BasicBlock) -> &mut F { | ||
| if let None = self.0.get_mut(index.index()) { |
This comment has been minimized.
This comment has been minimized.
arielb1
reviewed
May 14, 2016
| //! WTop<Either<Lvalue, Constant>> | ||
| //! | ||
| //! My personal believ is that it should be possible to make a way to compose two hashmap lattices | ||
| //! into one, but I can’t seem to get it just right yet, so we do the composing and decomposing |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
nagisa
May 15, 2016
•
Author
Contributor
I do not think this a problem for this pass as it is currently implemented. Most notably this pass does not propagate through Rvalue::Ref, only through Rvalue::Use nor does it replace the destination lvalues. These both being considered, the pass should not cause any problems related to aliased memory that do not already exist in the original MIR AFAICT.
Is there any particular examples you have in mind such pass should watch out for?
gereeter
reviewed
May 15, 2016
| use mir::transform::lattice::Lattice; | ||
|
|
||
|
|
||
| pub trait DataflowPass<'tcx> { |
This comment has been minimized.
This comment has been minimized.
gereeter
May 15, 2016
Contributor
I'm not sure that DataflowPass is pulling its weight. ar_forward already takes two parameters, one for the analysis and one for the pass. If DataflowPass was removed and ar_forward took two parameters, one for analysis, and one for rewriting, that would require less boilerplate while being just as ergonomic. Moreover, I don't think it really makes sense to tie the transfer function to the rewrite - as an example, no rewrite cares how alias results are generated, but there are many different alias analyses out there, so rewrites should not be tied to whatever alias analysis is used.
This would require that Transfer have its Lattice as an associated type instead of a parameter, but I can't see an example where it makes sense to implement Transfer for different lattices.
This comment has been minimized.
This comment has been minimized.
gereeter
reviewed
May 15, 2016
| /// that is, given some fact `fact` true before both the statement and relacement graph, and | ||
| /// a fact `fact2` which is true after the statement, the same `fact2` must be true after the | ||
| /// replacement graph too. | ||
| fn stmt(&mir::Statement<'tcx>, &L, &mut CFG<'tcx>) -> StatementChange<'tcx>; |
This comment has been minimized.
This comment has been minimized.
gereeter
May 15, 2016
Contributor
It might be nicer for these methods to take &self. This would allow runtime customization of what passes do. Additionally, it might make specifying compound rewrites more ergonomic by creating an and_then method on rewrite creating a RewriteAndThen. Thus,
impl<'tcx> DataflowPass<'tcx> for ACSPropagate {
type Lattice = ACSLattice<'tcx>;
type Rewrite = RewriteAndThen<'tcx, AliasRewrite,
RewriteAndThen<'tcx, ConstRewrite, SimplifyRewrite>>;
type Transfer = ACSPropagateTransfer;
}
ar_forward::<ACSPropagateTransfer, ACSPropagate>(&mut mir.cfg, Facts::new(), q);would turn into
ar_forward(
&mut mir.cfg,
Facts::new(), q,
ACSPropogateTransfer,
AliasRewrite.and_then(ConstRewrite).and_then(SimplifyRewrite)
);This would require that ar_forward take its rewrite function and analysis by value instead of by type.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Note for everybody looking at this: my exam session begins this week and lasts through the 2nd part of june. I’m pretty sure this PR won’t progress at all before the session ends. All the reviews are appreciated and I’ll look at and try to respond to them in as timely manner as possible. |
This comment has been minimized.
This comment has been minimized.
|
Cc me |
This comment has been minimized.
This comment has been minimized.
|
Note to self: do not forget about nagisa#1 |
This comment has been minimized.
This comment has been minimized.
|
cc @rust-lang/compiler (I for one didn't see this due to lack of notification :) |
This comment has been minimized.
This comment has been minimized.
|
@nagisa good luck on exams :) |
dotdash
reviewed
May 19, 2016
| @@ -118,12 +83,11 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg { | |||
| while changed { | |||
| pretty::dump_mir(tcx, "simplify_cfg", &counter, src, mir, None); | |||
| counter += 1; | |||
| changed = self.simplify_branches(mir); | |||
| changed |= self.remove_goto_chains(mir); | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
nagisa
May 19, 2016
Author
Contributor
Yes that's why I commented the pass out.
On May 19, 2016 3:39 PM, "Björn Steinbrink" notifications@github.com
wrote:
In src/librustc_mir/transform/simplify_cfg.rs
#33628 (comment):@@ -118,12 +83,11 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg {
while changed {
pretty::dump_mir(tcx, "simplify_cfg", &counter, src, mir, None);
counter += 1;
changed = self.simplify_branches(mir); changed |= self.remove_goto_chains(mir);This is an endless loop now, as changed is never set to false.
—
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
https://github.com/rust-lang/rust/pull/33628/files/ee4a7c39e84237756fd453356eaec0921356873f#r63869926
nagisa commentedMay 14, 2016
This PR at its current state has only the implementation of forward dataflow analysis and a accompanying alias-constant-simplify propagation pass.
The lattice-based dataflow framework is greatly inspired by the Hoopl and its paper. The framework besides just being yet another dataflow engine allows for interleaving transformations and analysis + composition of the passes. These are something the gen-kill-set-based dataflow engines aren’t quite suited for. Those features are showcased by the accompanying ACS pass and without them the ACS pass wouldn‘t be otherwise possible (at least not within 200 lines).
Some notes: this is not yet complete: I expect to expand the framework to gain some more useful combinators (or at least those described in the paper) as well as some more other niceties to simplify production of dataflow-reliant passes and backwards analysis capabilities and so on and on.
Some results:
For following pieces of code this MIR (with dead variables removed by yours truly) gets produced: