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 10, 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.10.13")]
90
91pub mod alias;
92pub mod analysis;
93pub mod canon;
94pub mod cfg;
95pub(crate) mod copy;
96pub mod dce;
97pub mod discharge;
98pub mod dom;
99pub mod extents;
100pub mod fold;
101pub mod frequency;
102pub mod frontier;
103pub mod fuel;
104pub mod gate;
105pub mod header_copy;
106pub mod heap;
107pub mod hoist;
108pub mod ivopts;
109pub mod licm;
110pub mod live;
111pub mod loops;
112pub mod machine;
113pub mod memssa;
114pub mod narrow;
115pub mod nests;
116pub mod nofree;
117pub mod optinfo;
118pub mod params;
119pub mod pass;
120pub mod phiopt;
121pub mod pipeline;
122pub mod predict;
123pub mod pressure;
124pub mod profile;
125pub mod prune;
126pub mod purity;
127pub mod range;
128pub mod rules;
129pub mod scev;
130pub mod short_circuit;
131pub mod simplify;
132pub mod simplify_cfg;
133pub mod speculate;
134pub mod split;
135pub mod stats;
136pub mod switch_conv;
137#[cfg(test)]
138mod testing;
139pub mod thread;
140pub(crate) mod trip;
141pub mod unroll;
142pub mod uses;
143
144// `alias::Options` is deliberately not re-exported: [`pipeline::Options`] already has that name
145// here and two of them at the top of the crate would be one import mistake away from a flag going
146// to the wrong place.
147pub use alias::{Access, Alias, Answer, Counts, Escapes, Origin, Reason};
148pub use analysis::{Analyses, Analysis, Preserved};
149pub use cfg::Cfg;
150pub use dom::{Dominators, PostDominators};
151pub use frequency::Frequencies;
152pub use frontier::{ControlDependence, Frontiers};
153pub use fuel::Fuel;
154pub use gate::Gates;
155pub use live::{LiveHere, Liveness};
156pub use loops::{Exit, LoopId, Loops};
157pub use machine::Machine;
158// `memssa::Counts` is deliberately not re-exported either, for the same reason: [`alias::Counts`]
159// has that name here, the two count different things, and a pass reporting one under the other's
160// name would be read as a much worse number than it is. `memssa::build` stays behind its module
161// because a bare `build` at the top of an optimizer says nothing about what it builds.
162pub use memssa::{Clobber, Step, Walk};
163pub use optinfo::Wants;
164pub use pass::{PASSES, Pass};
165pub use pipeline::{Dump, Dumps, Options, Remark, Report, run};
166// `nofree::Summaries` stays behind its module as well, because a bare `Summaries` at the top of an
167// optimizer says nothing about what is being summarised and section 7.5 asks for three more fields
168// that are not the same summary.
169pub use predict::{Callees, Predictions, Predictor};
170pub use pressure::Pressure;
171pub use profile::{Frequency, Hotness, Probability, Quality};
172// `purity::Callee` and `purity::Facts` stay behind their module. `Callee` is one letter away from
173// [`predict::Callees`], which is a different thing about the same instructions, and `Facts` at the
174// top of an optimizer says nothing about which facts. [`purity::Purity`] is the answer everything
175// asks for and is worth having here.
176pub use purity::Purity;
177// `range::query::Options` and `range::query::Counts` stay behind their module for the two reasons
178// already given above, which is that both names are taken at the top of this crate and neither of
179// the things holding them is the thing a caller would mean.
180pub use range::query::Ranges;
181pub use range::{Bits, Range};
182pub use scev::{
183 Assumption, Bound, Chrec, Count, Estimate, Evolution, Invariant, Plain, Reading, Scev,
184};
185pub use stats::Stats;
186
187/// The milestone in `spec/17-milestones.md` that fills this crate in.
188pub const MILESTONE: &str = "M4";
189
190#[cfg(test)]
191mod tests {
192 #[test]
193 fn milestone_is_recorded() {
194 assert!(super::MILESTONE.starts_with('M'));
195 }
196}