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//! [`live`] is what is live where, and [`pressure`] is that counted per register class, which is
56//! section 40.6's one function with four consumers. In SSA the number of values live at a point is
57//! the number of registers the program needs there rather than an estimate of it, which is what
58//! makes it worth computing exactly: loop invariant motion, the scheduler, the spill phase and if
59//! conversion all ask about the same quantity, and four passes each working out their own would be
60//! four chances for two of them to make opposite decisions off different counts of one thing. How
61//! many registers there are is the target's and is not here, so the answer is a count and the
62//! caller brings the register file.
63//!
64//! [`purity`] is the other question asked about a call, which is what it is allowed to do. Five
65//! answers rather than a boolean, because whether a call reads memory and whether it comes back are
66//! separate questions and GCC needs both, and the default is the one that permits everything, so a
67//! call nobody has taught it about costs a missed optimization rather than a wrong program. The
68//! declaration the user wrote and the answer an analysis works out are kept in separate fields and
69//! combined where they are read, which is what makes it possible to check one against the other.
70//!
71//! [`analysis`] is where a pass gets one from. It computes on demand, caches per function, and
72//! throws out what a pass broke, working from what the pass said it preserved rather than from a
73//! list kept somewhere else. A pass that claims to preserve an analysis it broke is caught under
74//! `--verify`, by recomputing the analysis and comparing.
75//!
76//! [`rules`] is the rewrite rule set. Tier one of `spec/optimizer/13-rewrite-rules.md` is
77//! written, proved and matched, and the tiers above it are still M4 work. The e-graph that is
78//! meant to apply them all at once is not here yet, so [`simplify`] applies them one at a time
79//! and in the order they are found, which is why it runs twice in every pipeline above `-O0`.
80//! The second run is after [`narrow`], because C promotes before it operates and nothing else
81//! produces a term at a width below `int` for the narrow half of the table to match.
82//!
83//! # Stability
84//!
85//! Every crate in the workspace is published, and publishing implies a promise. This one is
86//! tier 3: its Rust API is explicitly unstable and will change without a major version bump.
87//! Depend on the `rucc` binary's behaviour, not on this.
88
89#![doc(html_root_url = "https://docs.rs/rucc-opt/0.7.7")]
90
91pub mod alias;
92pub mod analysis;
93pub mod cfg;
94pub mod dce;
95pub mod dom;
96pub mod fold;
97pub mod frequency;
98pub mod frontier;
99pub mod fuel;
100pub mod gate;
101pub mod live;
102pub mod loops;
103pub mod memssa;
104pub mod narrow;
105pub mod optinfo;
106pub mod pass;
107pub mod phiopt;
108pub mod pipeline;
109pub mod predict;
110pub mod pressure;
111pub mod profile;
112pub mod purity;
113pub mod range;
114pub mod rules;
115pub mod scev;
116pub mod short_circuit;
117pub mod simplify;
118pub mod simplify_cfg;
119pub mod stats;
120#[cfg(test)]
121mod testing;
122pub mod thread;
123pub mod uses;
124
125// `alias::Options` is deliberately not re-exported: [`pipeline::Options`] already has that name
126// here and two of them at the top of the crate would be one import mistake away from a flag going
127// to the wrong place.
128pub use alias::{Access, Alias, Answer, Counts, Escapes, Origin, Reason};
129pub use analysis::{Analyses, Analysis, Preserved};
130pub use cfg::Cfg;
131pub use dom::{Dominators, PostDominators};
132pub use frequency::Frequencies;
133pub use frontier::{ControlDependence, Frontiers};
134pub use fuel::Fuel;
135pub use gate::Gates;
136pub use live::{LiveHere, Liveness};
137pub use loops::{Exit, LoopId, Loops};
138// `memssa::Counts` is deliberately not re-exported either, for the same reason: [`alias::Counts`]
139// has that name here, the two count different things, and a pass reporting one under the other's
140// name would be read as a much worse number than it is. `memssa::build` stays behind its module
141// because a bare `build` at the top of an optimizer says nothing about what it builds.
142pub use memssa::{Clobber, Step, Walk};
143pub use optinfo::Wants;
144pub use pass::{PASSES, Pass};
145pub use pipeline::{Dump, Dumps, Options, Remark, Report, run};
146pub use predict::{Callees, Predictions, Predictor};
147pub use pressure::Pressure;
148pub use profile::{Frequency, Hotness, Probability, Quality};
149// `purity::Callee` and `purity::Facts` stay behind their module. `Callee` is one letter away from
150// [`predict::Callees`], which is a different thing about the same instructions, and `Facts` at the
151// top of an optimizer says nothing about which facts. [`purity::Purity`] is the answer everything
152// asks for and is worth having here.
153pub use purity::Purity;
154// `range::query::Options` and `range::query::Counts` stay behind their module for the two reasons
155// already given above, which is that both names are taken at the top of this crate and neither of
156// the things holding them is the thing a caller would mean.
157pub use range::query::Ranges;
158pub use range::{Bits, Range};
159pub use scev::{Assumption, Bound, Chrec, Count, Estimate, Evolution, Invariant, Scev};
160pub use stats::Stats;
161
162/// The milestone in `spec/17-milestones.md` that fills this crate in.
163pub const MILESTONE: &str = "M4";
164
165#[cfg(test)]
166mod tests {
167 #[test]
168 fn milestone_is_recorded() {
169 assert!(super::MILESTONE.starts_with('M'));
170 }
171}