Skip to main content

rucc_opt/
lib.rs

1//! The pass manager, the acyclic e-graph, the rewrite rules and the analyses.
2//!
3//! Design: `spec/09-optimizer.md`. Layer rank 9, see `spec/18-package-layout.md`.
4//!
5//! # What is here
6//!
7//! The pass manager and five passes. [`pipeline`] holds the six pipelines, one per optimization
8//! level, written out rather than assembled from flags, along with the fuel, the dumps and the
9//! verification that section 9.10 asks of every pass. [`gate`] is the other half of the
10//! bisection interface, which is `-fdisable-<pass>` and `-fenable-<pass>` over a list of
11//! functions, so that which pass and which function are two searches rather than one. [`fold`] is the first pass through it,
12//! [`simplify`] is the peephole the e-graph will eventually absorb, [`narrow`] takes the width
13//! back off arithmetic that C promoted, [`simplify_cfg`] turns a branch whose condition is known
14//! into a jump and removes the blocks that leaves stranded, and [`dce`] is what clears up after
15//! all four of them. [`uses`] is the one thing two of them share, which is a count of who reads
16//! what.
17//!
18//! [`stats`] is what a pass has to return, and [`optinfo`] is that printed. A pass reports what
19//! it did and what it gave up on, and there is no other way for it to tell the manager it changed
20//! anything, so the instrumentation cannot be the thing nobody got round to. Section 42.2 of
21//! `spec/optimizer/42-measurement.md` counted what happens otherwise.
22//!
23//! [`mod@cfg`], [`dom`], [`loops`], [`scev`], [`alias`], [`memssa`] and [`range`] are the analyses
24//! so far, and everything in `spec/optimizer/07` through `spec/optimizer/11` is built on them.
25//! [`mod@cfg`] is the shape of a function with the instructions taken out, [`dom`] answers what
26//! every path has to go through, forwards and backwards, [`loops`] says what loops there are, how
27//! they nest, and which cycles are not loops at all, [`scev`] says how a value changes across the
28//! iterations of one and how many iterations there are, [`alias`] answers the one question every
29//! memory optimization is gated on, which is whether two references can touch the same byte,
30//! [`memssa`] puts memory on a chain so a load can walk back to the store it sees, and [`range`]
31//! says what values an integer can hold at the place it is asked about, which is not the same
32//! question as what it can hold where it was defined.
33//!
34//! [`profile`] is how likely an edge is taken and how often a block runs, along with the field
35//! that says how much either is worth believing. The types come first because section 11.5 of
36//! `spec/optimizer/11-profile-and-frequency.md` says what M4 owes the profile work that arrives
37//! after it, which is the shape rather than the data: a quality on every number, arithmetic that
38//! degrades it, and no way to build one without saying where it came from. Retrofitting that into
39//! thirty passes once there is real profile data is the failure mode, and it is GCC's, whose
40//! profile maintenance bugs are mostly in passes written before the quality field existed.
41//!
42//! [`predict`] is where the first of those numbers comes from, which is a guess: ten predictors
43//! from section 11.2, first match, each one a syntactic situation somebody measured in the 1990s
44//! and a rate it turned out right at. Nothing in here is a measurement and every probability out
45//! of it says so.
46//!
47//! [`frequency`] turns those guesses into the number the consumers actually want, which is how
48//! often a block runs compared with the function entry. Section 11.3's method: solve each loop
49//! from the inside out, take the chance of going round again, and the header runs one over one
50//! minus that many times, which is the sum of the series. A loop nothing predicted an exit for
51//! gets a cap rather than a division by zero, an irreducible region gets an answer that is marked
52//! as not meaning anything, and the check section 11.5 asks for, which is that what arrives at a
53//! block adds up to the block, is in [`frequency::Frequencies::problems`].
54//!
55//! [`purity`] is the other question asked about a call, which is what it is allowed to do. Five
56//! answers rather than a boolean, because whether a call reads memory and whether it comes back are
57//! separate questions and GCC needs both, and the default is the one that permits everything, so a
58//! call nobody has taught it about costs a missed optimization rather than a wrong program. The
59//! declaration the user wrote and the answer an analysis works out are kept in separate fields and
60//! combined where they are read, which is what makes it possible to check one against the other.
61//!
62//! [`analysis`] is where a pass gets one from. It computes on demand, caches per function, and
63//! throws out what a pass broke, working from what the pass said it preserved rather than from a
64//! list kept somewhere else. A pass that claims to preserve an analysis it broke is caught under
65//! `--verify`, by recomputing the analysis and comparing.
66//!
67//! The e-graph and the rewrite rule set are still M4 work and are not here yet.
68//!
69//! # Stability
70//!
71//! Every crate in the workspace is published, and publishing implies a promise. This one is
72//! tier 3: its Rust API is explicitly unstable and will change without a major version bump.
73//! Depend on the `rucc` binary's behaviour, not on this.
74
75#![doc(html_root_url = "https://docs.rs/rucc-opt/0.5.1")]
76
77pub mod alias;
78pub mod analysis;
79pub mod cfg;
80pub mod dce;
81pub mod dom;
82pub mod fold;
83pub mod frequency;
84pub mod frontier;
85pub mod fuel;
86pub mod gate;
87pub mod loops;
88pub mod memssa;
89pub mod narrow;
90pub mod optinfo;
91pub mod pass;
92pub mod pipeline;
93pub mod predict;
94pub mod profile;
95pub mod purity;
96pub mod range;
97pub mod scev;
98pub mod simplify;
99pub mod simplify_cfg;
100pub mod stats;
101#[cfg(test)]
102mod testing;
103pub mod uses;
104
105// `alias::Options` is deliberately not re-exported: [`pipeline::Options`] already has that name
106// here and two of them at the top of the crate would be one import mistake away from a flag going
107// to the wrong place.
108pub use alias::{Access, Alias, Answer, Counts, Escapes, Origin, Reason};
109pub use analysis::{Analyses, Analysis, Preserved};
110pub use cfg::Cfg;
111pub use dom::{Dominators, PostDominators};
112pub use frequency::Frequencies;
113pub use frontier::{ControlDependence, Frontiers};
114pub use fuel::Fuel;
115pub use gate::Gates;
116pub use loops::{Exit, LoopId, Loops};
117// `memssa::Counts` is deliberately not re-exported either, for the same reason: [`alias::Counts`]
118// has that name here, the two count different things, and a pass reporting one under the other's
119// name would be read as a much worse number than it is. `memssa::build` stays behind its module
120// because a bare `build` at the top of an optimizer says nothing about what it builds.
121pub use memssa::{Clobber, Step, Walk};
122pub use optinfo::Wants;
123pub use pass::{PASSES, Pass};
124pub use pipeline::{Dump, Dumps, Options, Remark, Report, run};
125pub use predict::{Callees, Predictions, Predictor};
126pub use profile::{Frequency, Hotness, Probability, Quality};
127// `purity::Callee` and `purity::Facts` stay behind their module. `Callee` is one letter away from
128// [`predict::Callees`], which is a different thing about the same instructions, and `Facts` at the
129// top of an optimizer says nothing about which facts. [`purity::Purity`] is the answer everything
130// asks for and is worth having here.
131pub use purity::Purity;
132// `range::query::Options` and `range::query::Counts` stay behind their module for the two reasons
133// already given above, which is that both names are taken at the top of this crate and neither of
134// the things holding them is the thing a caller would mean.
135pub use range::query::Ranges;
136pub use range::{Bits, Range};
137pub use scev::{Assumption, Bound, Chrec, Count, Estimate, Evolution, Invariant, Scev};
138pub use stats::Stats;
139
140/// The milestone in `spec/17-milestones.md` that fills this crate in.
141pub const MILESTONE: &str = "M4";
142
143#[cfg(test)]
144mod tests {
145    #[test]
146    fn milestone_is_recorded() {
147        assert!(super::MILESTONE.starts_with('M'));
148    }
149}