Skip to main content

implicit_predicates_gen/emit/
mod.rs

1//! Rust code for the stages of a predicate.
2//!
3//! For each predicate the generated module holds its constants, the
4//! floating-point filter ([`filter`]), three exact stages ([`exact`]) and the
5//! chain that runs them ([`chain`]); a second module holds the checks that
6//! its tests use ([`checks`]). The output is built as text, in a fixed
7//! order, so it is the same on every run; it is not formatted.
8
9mod chain;
10mod check_fns;
11mod checks;
12mod edges;
13mod exact;
14mod filter;
15mod filter_axis;
16mod generic;
17mod grid;
18mod point_checks;
19mod point_eval;
20mod point_exact;
21mod point_rules;
22mod point_types;
23mod points;
24mod program;
25mod spans;
26mod stages;
27mod statements;
28mod text;
29
30#[cfg(test)]
31mod tests;
32
33use std::fmt::Write;
34
35use implicit_predicates_core::eft::{PRODUCT_OPERAND_MAX, PRODUCT_OPERAND_MIN};
36
37use crate::bound::{FilterForm, MAX_DEGREE, filter_form};
38use crate::differences::difference_graph;
39use crate::error::Error;
40use crate::parse::{Predicate, parse};
41use crate::polynomial::check_same_polynomial;
42use crate::range::{
43    Leaves, MAX_COORD_COMPONENTS, exact_plan_differences, exact_plan_raw,
44    exact_plan_spans_translated, exact_plan_spans_untranslated,
45};
46use chain::ChainStage;
47use exact::{write_in_range, write_product};
48use filter::FilterKind;
49pub use points::generate_points;
50use program::{Program, Step};
51use spans::write_span_in_range;
52use stages::{Forms, fits_on_stack, write_constants, write_exact_stages};
53pub(crate) use text::{comment, literal, power_of_two};
54
55/// The code generated for one predicate.
56#[derive(Clone, Debug, PartialEq, Eq)]
57pub struct Generated {
58    /// The name of the predicate, and of the files to write.
59    pub name: String,
60    /// The module of its stages, unformatted.
61    pub stages: String,
62    /// The module of its checks, for its tests, unformatted: `exact`, the
63    /// exact sign of the predicate, `bound_edge_cases`, inputs on either
64    /// side of the error bound of the filter, and for a per-axis filter
65    /// `uncertainty_edge_cases`, the same with an uncertainty. Empty when
66    /// there are none, in which case no file is written.
67    pub checks: String,
68}
69
70/// Generates the code of every predicate defined in `source`.
71///
72/// `file` labels errors and is named in the header of the generated code,
73/// for instance `predicates/orient3d.pred`. `regenerate` tells how to
74/// regenerate the code, for instance the command that produced it; it is
75/// written in the header of every generated file after "To regenerate,",
76/// one comment line per line of text. The command line tool passes the
77/// command it was run with.
78///
79/// For each predicate the module of its stages holds the constants of its
80/// filter and the ranges of its exact stages, and the functions `filter`
81/// (with one size and one uncertainty per axis when every term of the main
82/// form has the same degree in each axis, see [`crate::filter_form`]),
83/// `exact_differences`, `expansions_translated`, `expansions_untranslated`
84/// (only when the predicate has an `#[exact]` form) on stack buffers, the
85/// same two expansion forms over arena spans, `expansions_translated_in` and
86/// `expansions_untranslated_in`, which take the exact coordinates of any
87/// point type, and `staged`, which runs the filter and then the exact stages
88/// in the order: differences that are exact, the translated expansion form,
89/// then the `#[exact]` form if one is given. The order is the same for every
90/// predicate for now. A stage on the stack is emitted only when its
91/// temporaries take at most 4,096 `f64`; when one does not fit, `staged`
92/// takes an arena and runs the stage over spans of the same form in its
93/// place, on the exact coordinates of the explicit points. For points of
94/// any type that implements `Point`, `staged_in` runs `filter_points`, the
95/// filter with the uncertainty of the errors of the points, then
96/// `exact_stages`, the exact stages of `staged`, when every point is
97/// explicit, and otherwise `exact_stages_in`, the stages over spans on the
98/// exact coordinates of the points, the translated form first. The module of
99/// its checks holds `exact`, the main form evaluated with the exact numbers
100/// of `implicit-predicates-oracle`, `bound_edge_cases`, pairs of inputs at
101/// the edge of the error bound of the filter for explicit points, and, for a
102/// per-axis filter, `uncertainty_edge_cases`, the same with an uncertainty.
103///
104/// # Errors
105///
106/// Returns the first problem found in the definitions: a syntax error, a
107/// main form that is not homogeneous in differences of coordinates, a
108/// degree above the 1022 that the filter accepts, a definition too large to
109/// derive the filter bound for or to expand, an `#[exact]` form that is a
110/// different polynomial, a main form with more than 256 distinct
111/// subexpressions, or a predicate that the exact stages cannot support.
112///
113/// # Work
114///
115/// The checks run from the cheapest to the most expensive, and each step
116/// whose work can grow faster than the size of the definition has a budget,
117/// checked before the work it limits:
118///
119/// - [`parse`] nests brackets at most 32 deep, and the difference form and
120///   the degree of the `#[exact]` form take one pass each;
121/// - [`crate::filter_bound`] keeps its exact coefficients within `2^26`
122///   bits in all;
123/// - [`crate::canonical`] keeps every expansion within `2^16` terms, `2^16`
124///   bits per coefficient and `2^20` units of work;
125/// - the exact plans reject temporaries of `2^32` components or more, or of
126///   more than `2^51` operations on the stack and `2^50` over spans, and take
127///   a few passes over the graph for each power of two they try; the plans
128///   over spans stop each pass at the first node whose bound overflows;
129/// - the search for the edge cases of the checks, the most expensive step
130///   per node, runs only on main forms of at most 256 distinct
131///   subexpressions, and makes at most a fixed number of exact evaluations
132///   of the form.
133///
134/// So the work of `generate` is bounded for every definition, and the
135/// slowest definitions accepted take a few seconds in a debug build, most
136/// of it in the search for the edge cases; a definition beyond a budget is
137/// rejected with an error that names the limit.
138pub fn generate(source: &str, file: &str, regenerate: &str) -> Result<Vec<Generated>, Error> {
139    let notice = notice(file, regenerate);
140    parse(source, file)?
141        .iter()
142        .map(|predicate| generate_one(predicate, file, &notice))
143        .collect()
144}
145
146/// Generates the code of `predicate`, whose files start with `notice`.
147fn generate_one(predicate: &Predicate, file: &str, notice: &str) -> Result<Generated, Error> {
148    // The cheap checks come first: the difference form gives the degree of
149    // the main form, one pass bounds the degree of the exact form, and the
150    // filter bound checks its own size, all before any polynomial is
151    // expanded.
152    let position = predicate.position;
153    let graph = difference_graph(&predicate.main, file, position)?;
154    if let Some(exact) = &predicate.exact {
155        let degree = exact.degree_bound();
156        if degree > u64::from(MAX_DEGREE) {
157            return Err(Error::at(
158                file,
159                predicate.exact_position.unwrap_or(position),
160                format!(
161                    "the exact form of `{}` has a degree of up to {degree}, above {MAX_DEGREE}, \
162                     the highest degree the filter accepts",
163                    predicate.name
164                ),
165            ));
166        }
167    }
168    let filter = match filter_form(&graph, file, position)? {
169        FilterForm::PerAxis(bound) => FilterKind::PerAxis(bound.constants(), bound.certain_zero()),
170        FilterForm::Global(bound) => FilterKind::Global(bound.constants()),
171    };
172    check_same_polynomial(predicate, file)?;
173    if graph.nodes.len() > edges::MAX_NODES {
174        return Err(Error::at(
175            file,
176            position,
177            format!(
178                "the main form of `{}` has {} distinct subexpressions, more than the {} \
179                 for which the checks of its tests are generated: their edge cases take \
180                 thousands of exact evaluations of the whole form",
181                predicate.name,
182                graph.nodes.len(),
183                edges::MAX_NODES
184            ),
185        ));
186    }
187    let (min, max) = (PRODUCT_OPERAND_MIN, PRODUCT_OPERAND_MAX);
188    // A stage on the stack is emitted only if its temporaries fit.
189    let stack = |leaves| {
190        exact_plan_differences(&graph, leaves, file, position, min, max).map(|plan| {
191            fits_on_stack(plan.total).then(|| {
192                (
193                    Program::differences(&graph, leaves, &plan.lengths),
194                    plan.range,
195                )
196            })
197        })
198    };
199    let differences = stack(Leaves::One)?;
200    let translated = stack(Leaves::Two)?;
201    let spans =
202        exact_plan_spans_translated(&graph, MAX_COORD_COMPONENTS, file, position, min, max)?;
203    let (untranslated, spans_untranslated) = match &predicate.exact {
204        Some(exact) => {
205            let position = predicate.exact_position.unwrap_or(position);
206            let plan = exact_plan_raw(exact, file, position, min, max)?;
207            let program = |lengths: &[usize]| Program::raw(exact, lengths);
208            let stack = fits_on_stack(plan.total).then(|| (program(&plan.lengths), plan.range));
209            let spans = exact_plan_spans_untranslated(
210                exact,
211                MAX_COORD_COMPONENTS,
212                file,
213                position,
214                min,
215                max,
216            )?;
217            (stack, Some((exact, spans.range)))
218        }
219        None => (None, None),
220    };
221    let forms = Forms {
222        filter,
223        differences,
224        translated,
225        untranslated,
226        spans_translated: (&graph, spans.range),
227        spans_untranslated,
228    };
229    let points = predicate.params.len();
230    let mut out = String::new();
231    write_header(&mut out, predicate, notice, &forms);
232    write_constants(&mut out, &forms);
233    let (uses_max, explicit) = match &forms.filter {
234        FilterKind::Global(c) => (
235            filter::write_filter(&mut out, &graph, points, c.degree),
236            "0.0",
237        ),
238        FilterKind::PerAxis(c, zero) => (
239            filter_axis::write_filter(&mut out, &graph, points, c, *zero),
240            "[0.0; 3]",
241        ),
242    };
243    write_exact_stages(&mut out, points, &forms);
244    let explicit_stages = chain_stages(&forms);
245    chain::write_chain(&mut out, points, &explicit_stages, explicit);
246    let spans: &[&str] = if forms.spans_untranslated.is_some() {
247        &["expansions_translated_in", "expansions_untranslated_in"]
248    } else {
249        &["expansions_translated_in"]
250    };
251    generic::write_generic_chain(
252        &mut out,
253        &generic::GenericChain {
254            points,
255            per_axis: matches!(forms.filter, FilterKind::PerAxis(..)),
256            explicit: &explicit_stages,
257            spans,
258        },
259    );
260    if uses_max {
261        filter::write_max(&mut out);
262    }
263    write_in_range(&mut out);
264    write_span_in_range(&mut out);
265    if forms
266        .stack_stages()
267        .flat_map(|(_, program)| &program.steps)
268        .any(|step| matches!(step, Step::Product { .. }))
269    {
270        write_product(&mut out);
271    }
272    let mut checks = String::new();
273    checks::write_checks(&mut checks, predicate, &graph, &forms.filter, notice);
274    Ok(Generated {
275        name: predicate.name.clone(),
276        stages: out,
277        checks,
278    })
279}
280
281/// The notice that starts every generated file: where it comes from, that
282/// it must not be edited, and how to regenerate it, followed by a blank
283/// line.
284pub(crate) fn notice(file: &str, regenerate: &str) -> String {
285    let mut out = format!("// Generated by implicit-predicates-gen from {file}. Do not edit.\n");
286    for (i, line) in regenerate.lines().enumerate() {
287        let line = if i == 0 {
288            format!("To regenerate, {line}")
289        } else {
290            line.to_string()
291        };
292        writeln!(out, "// {line}").unwrap();
293    }
294    out.push('\n');
295    out
296}
297
298fn write_header(out: &mut String, predicate: &Predicate, notice: &str, forms: &Forms) {
299    out.push_str(notice);
300    comment(
301        out,
302        "//!",
303        0,
304        &format!(
305            "The stages of the predicate `{}`.\n\n\
306             Every stage takes the points {} of the definition as `p`, in that order, \
307             and returns the sign of the predicate, or `None` when it cannot decide. \
308             [`staged`] runs them in order on explicit points, and [`staged_in`] on points \
309             of any type.",
310            predicate.name,
311            list(&predicate.params)
312        ),
313    );
314    // The names the module uses, in the order rustfmt sorts them.
315    let mut names = vec![
316        "ExactCoords",
317        "ExactError",
318        "Point",
319        "Scratch",
320        "ScratchExhausted",
321        "Sign",
322        "Span",
323        "Weight",
324    ];
325    if forms.differences.is_some() {
326        names.push("eft");
327    }
328    if forms.stack_stages().next().is_some() {
329        names.push("expansion");
330    }
331    writeln!(
332        out,
333        "\nuse implicit_predicates_core::{{{}}};\n",
334        names.join(", ")
335    )
336    .unwrap();
337}
338
339/// The exact stages of the chain for explicit points, in order: each stage
340/// on the stack, or the stage over spans in its place when it is not
341/// emitted. The stage of exact differences has no counterpart over spans.
342fn chain_stages(forms: &Forms) -> Vec<ChainStage> {
343    let mut stages = Vec::new();
344    if forms.differences.is_some() {
345        stages.push(ChainStage::Stack("exact_differences"));
346    }
347    stages.push(match forms.translated {
348        Some(_) => ChainStage::Stack("expansions_translated"),
349        None => ChainStage::Spans("expansions_translated_in"),
350    });
351    if forms.spans_untranslated.is_some() {
352        stages.push(match forms.untranslated {
353            Some(_) => ChainStage::Stack("expansions_untranslated"),
354            None => ChainStage::Spans("expansions_untranslated_in"),
355        });
356    }
357    stages
358}
359
360/// `a`, `b` and `c`.
361pub(crate) fn list(names: &[String]) -> String {
362    let quoted: Vec<String> = names.iter().map(|n| format!("`{n}`")).collect();
363    match quoted.split_last() {
364        Some((last, rest)) if !rest.is_empty() => format!("{} and {last}", rest.join(", ")),
365        _ => quoted.concat(),
366    }
367}