Skip to main content

rucc_driver/
compile.rs

1//! Running the front end over one file, from the bytes on disk to the typed tree.
2//!
3//! Design: `spec/04-driver-and-cli.md` section 4.3, and the `M2` exit criterion in
4//! `spec/17-milestones.md` that says `--emit=tast` works.
5//!
6//! [`preprocess`](mod@crate::preprocess) stops after phase 4 because `-E` stops there. This
7//! carries on: phase 7, the parse, and the checking. It is one function rather than four composed
8//! ones because of what the four share. The tokens hold interned symbols, the untyped tree holds
9//! tokens, the typed tree holds the untyped tree's spans, and none of them owns the table it is
10//! reading, so one [`Session`] has to outlive all of them and there has to be one place that
11//! holds it.
12
13use std::collections::HashMap;
14use std::path::Path;
15
16use rucc_base::{Interner, Symbol};
17use rucc_codegen::coverage::Fired;
18use rucc_codegen::elsewhere::{Elsewhere, Slot};
19use rucc_codegen::lowering::Lowerings;
20use rucc_codegen::pipeline::{self, Machine, Recording};
21use rucc_codegen::pressure::Pressure;
22use rucc_codegen::usage::StackUsage;
23use rucc_cost::Goal;
24use rucc_diag::{Diagnostic, Severity, SourceMap, Span};
25use rucc_ir::{FpContract, Pic as IrPic, Visibility as IrVisibility};
26use rucc_lex::{Convert, Keywords, PpToken, convert};
27use rucc_lower::Protector as LowerProtector;
28use rucc_sema::{Checker, Context as CheckContext};
29use rucc_session::{
30    Contract, EmitKind, FileSystem, Options, Padding, Pic, Protector, Session, Visibility,
31};
32use rucc_target::TargetInfo;
33use rucc_tuple::{Arch, ObjectFormat};
34
35use crate::preprocess::render;
36
37/// What a compilation produced, which is text for most of the kinds and bytes for one of them.
38///
39/// Two variants rather than a string, because an object file is not text and a `Vec<u8>` holding
40/// UTF-8 for six kinds and a file format for the seventh would leave every reader guessing which
41/// it had. [`Artifact::Nothing`] is what a compilation that stopped early gives back, and it is
42/// not the same as an empty file: nothing is written for it at all.
43#[derive(Debug, Clone, PartialEq, Eq, Default)]
44pub enum Artifact {
45    /// The compilation stopped before it produced anything, or the kind asked for produces
46    /// nothing yet.
47    #[default]
48    Nothing,
49    /// Text, which is every kind up to and including assembly.
50    Text(String),
51    /// An object file, which is `-c`, and the names a linker can find in it.
52    ///
53    /// The names travel with the bytes rather than beside them because what wants them is the
54    /// archive step, and an index entry that does not match the member is worse than no archive:
55    /// the linker searches the index, pulls the member out, and still reports the name undefined.
56    /// One value holding both is one value the two cannot disagree in.
57    Object {
58        /// The file.
59        bytes: Vec<u8>,
60        /// Every name another object can reach, as the object writer wrote them. Empty is a real
61        /// answer: a translation unit of nothing but `static` functions is a member an archive
62        /// carries and nothing ever pulls out.
63        defines: Vec<String>,
64    },
65}
66
67impl Artifact {
68    /// The bytes to write, which is nothing at all for [`Artifact::Nothing`].
69    #[must_use]
70    pub fn bytes(&self) -> &[u8] {
71        match self {
72            Artifact::Nothing => &[],
73            Artifact::Text(text) => text.as_bytes(),
74            Artifact::Object { bytes, .. } => bytes,
75        }
76    }
77}
78
79/// What compiling one file produced.
80#[derive(Debug, Clone, PartialEq, Eq)]
81pub struct Compiled {
82    /// What to write, which is nothing when the compilation failed or produced nothing.
83    pub artifact: Artifact,
84    /// The diagnostics, already rendered, one per element, in the order they were reported.
85    pub messages: Vec<String>,
86    /// How many of them were errors.
87    pub errors: u32,
88    /// Which lowering rules this file fired, for `-Zrule-coverage`.
89    ///
90    /// Empty for a compilation that stopped before the back end, which every kind up to and
91    /// including `--emit=ir` does. That is not the same as a rule set nothing reaches and the
92    /// caller unions these rather than reading one, so a file that fired nothing adds nothing.
93    pub fired: Fired,
94    /// What the register allocator had to put on the stack, for `-Zregister-pressure`.
95    ///
96    /// Empty for the same compilations `fired` is empty for and for the same reason, since both
97    /// are written by the back end and neither is a fact a file that stopped before it has.
98    pub pressure: Pressure,
99    /// What the pre-selection lowering group did, for `-Zlowering`.
100    ///
101    /// Empty for the same compilations `fired` is empty for and for the same reason, since the
102    /// group runs in the back end and a file that stopped before it lowered nothing.
103    pub lowerings: Lowerings,
104    /// What `-fdump-ir=` asked to see, in the order the passes ran.
105    ///
106    /// The optimizer does not write files, because nothing below the driver in
107    /// `spec/18-package-layout.md` knows what a file is, so the text comes back here and the
108    /// caller decides where it goes.
109    pub dumps: Vec<rucc_opt::Dump>,
110    /// What `-fopt-info` asked to hear, already rendered, one remark per line.
111    ///
112    /// Empty when the flag was not given, and also empty when it was given and no pass had
113    /// anything of the kinds asked for to say. Those two are the same text and different facts,
114    /// which is why a misspelled keyword is an error rather than a quiet nothing.
115    pub remarks: String,
116    /// Every file an `#include` found, for the `-M` family.
117    ///
118    /// The same list `Preprocessed` carries and for the same reason. A `-MD` writes it beside
119    /// the object, so the compiling path needs it as much as the preprocessing one does.
120    pub deps: Vec<rucc_pp::Dependency>,
121    /// What `-save-temps` asked to be kept, which is nothing at all unless it was given.
122    ///
123    /// It comes back from here rather than being produced by a second run of the compiler under
124    /// different flags, because a second run is a second answer: the file a person reads has to
125    /// be the file that was compiled, and two runs of anything with a `__TIME__` in it are not
126    /// the same text.
127    pub temps: Temps,
128    /// Where the time went, phase by phase and pass by pass, for `-frucc-trace`.
129    pub timing: crate::trace::Timing,
130    /// The `.su` file `-fstack-usage` asked for, already rendered, one line per function.
131    ///
132    /// Rendered here rather than handed back as rows, because a row points at the source through
133    /// a span and the map that turns a span into a file, a line and a column is this compilation's
134    /// and is gone once it returns. Empty when the flag was not given and for every compilation
135    /// that stopped before the back end.
136    pub stack_usage: String,
137}
138
139/// The intermediate text a compilation went through, kept when `-save-temps` asked for it.
140///
141/// Both are `None` on a compilation that was not asked to keep anything, and the assembly is
142/// `None` on one that stopped before there was any. Holding the text rather than writing it is
143/// what keeps this function free of the file system, which is what lets it be tested against a
144/// map from path to bytes.
145#[derive(Debug, Clone, PartialEq, Eq, Default)]
146pub struct Temps {
147    /// Phase 4's output, the same text `-E` would have printed.
148    pub preprocessed: Option<String>,
149    /// The assembly the back end produced on the way to the object file.
150    pub assembly: Option<String>,
151}
152
153impl Compiled {
154    /// Whether anything went wrong badly enough that the output should not be used.
155    #[must_use]
156    pub fn failed(&self) -> bool {
157        self.errors > 0
158    }
159
160    /// The text that was produced, and the empty string for anything that is not text.
161    ///
162    /// A caller that asked for one of the text kinds knows which it asked for, so this saves it
163    /// matching on a variant it has already ruled out.
164    #[must_use]
165    pub fn text(&self) -> &str {
166        match &self.artifact {
167            Artifact::Text(text) => text,
168            _ => "",
169        }
170    }
171}
172
173/// Compiles one file as far as `opts.emit` asks for and renders the result.
174///
175/// `name` is the path as the user wrote it, which is the name every diagnostic about the file
176/// uses. Every kind but the executable produces something today, and that one runs the same front
177/// end and gives back nothing, so that a file with a mistake in it is reported the same way
178/// whichever kind was asked for, rather than compiling silently until the part that is written
179/// notices.
180///
181/// The checking is skipped when the parse reported an error. The two poisoning rules mean a
182/// diagnosed expression produces no further complaints, but a declaration the parser had to skip
183/// past leaves no declaration behind at all, and every later use of that name would be reported
184/// as undeclared. One mistake is worth one message.
185#[must_use]
186pub fn compile(opts: &Options, name: &str, fs: &dyn FileSystem) -> Compiled {
187    let mut clock = crate::trace::Clock::start();
188    let mut sess = Session::new(opts.clone());
189    // Before anything else interns a name. The keyword symbols have to be one unbroken run for
190    // a lookup to be a subtraction, and the preprocessor interns every identifier it reads, so
191    // building this after the expansion would mean building it after `char` had been seen.
192    let mut keywords = Keywords::new(&mut sess.interner, opts.std, opts.gnu_extensions);
193    if sess.target.tuple.os() == rucc_tuple::Os::Windows {
194        keywords = if sess.target.tuple.env() == rucc_tuple::Env::Msvc {
195            keywords.msvc(&mut sess.interner)
196        } else {
197            keywords.windows()
198        };
199    }
200    let mut diagnostics: Vec<Diagnostic> = Vec::new();
201    // Filled in by the back end when there is one, and empty for every kind that stops before it.
202    let mut fired = Fired::new();
203    // The same, and the other thing the back end is asked to record about itself.
204    let mut pressure = Pressure::new();
205    let mut lowerings = Lowerings::asked(opts.lowering_dump.is_some());
206    // And the frames it laid out, for `-fstack-usage`. Recorded whether or not the flag was given,
207    // since a row per function is nothing next to compiling the function, and written only if it
208    // was.
209    let mut stack = StackUsage::new();
210    // Filled in by the optimizer, and only when `-fdump-ir=` asked for something.
211    let mut dumps = Vec::new();
212    let mut remarks = String::new();
213    // How long each optimizer pass took, for `-frucc-trace`.
214    let mut passes = Vec::new();
215    // Filled in as the compilation goes past each of them, and only under `-save-temps`.
216    let mut temps = Temps::default();
217
218    let bytes = match fs.read(Path::new(name)) {
219        Ok(bytes) => bytes,
220        Err(e) => return failure(format!("{name}: {e}")),
221    };
222    let Ok(file) = sess.sources.add_shared(crate::phase::source_name(name), bytes, None) else {
223        return failure(format!("{name}: the source map has no room left for this file"));
224    };
225    clock.lap("read");
226
227    // Phases 1 to 4. The expanded stream is turned into pp-tokens straight away, because the
228    // include context borrows the source map that rendering a diagnostic reads and the borrow
229    // has to end before anything is rendered.
230    let mut pp = rucc_pp::Preprocessor::with_prefix_map(opts.prefix_map.macros.clone());
231    let predef = rucc_pp::Predef::for_options(opts);
232    let expanded: Vec<PpToken> = {
233        let mut tokens = Vec::new();
234        // The inner block is the borrow. The printer under `-save-temps` reads the source map
235        // that the include context is holding, so the context has to be gone before it runs, and
236        // nothing happens in between, which is what makes the text it prints the text that is
237        // compiled below rather than a second answer to the same question.
238        {
239            let mut cx =
240                rucc_pp::Context::new(&mut sess.interner, &mut sess.sources, fs, &opts.search);
241            cx.lex = rucc_lex::Options::for_dialect(opts.std, opts.gnu_extensions);
242            cx.pedantic = opts.pedantic;
243            if pp.predefine(&sess.target, &predef, &mut cx).is_err() {
244                return failure(format!(
245                    "{name}: the source map has no room for the built in macros"
246                ));
247            }
248            if pp.preinclude(&opts.preincludes, &mut tokens, &mut cx).is_err() {
249                return failure(format!("{name}: the source map has no room for the command line"));
250            }
251            tokens.append(&mut pp.run(file, &mut cx));
252        }
253        if opts.save_temps.wanted() {
254            temps.preprocessed = Some(rucc_pp::print(
255                file,
256                &tokens,
257                pp.line_directives(),
258                &sess.sources,
259                &sess.interner,
260                rucc_pp::PrintOptions { line_markers: opts.line_markers },
261            ));
262        }
263        tokens.iter().map(|token| token.to_pp()).collect()
264    };
265    diagnostics.extend(pp.take_diagnostics());
266    // Taken here rather than at the end, because the preprocessor is done with and everything
267    // after this is about the tree it produced.
268    let deps = pp.dependencies().to_vec();
269    clock.lap("preprocess");
270
271    // Phase 7, which is where a spelling becomes a keyword and a preprocessing number becomes
272    // a constant of a type.
273    let cx = Convert {
274        keywords: &keywords,
275        interner: &sess.interner,
276        target: &sess.target,
277        std: opts.std,
278        gnu: opts.gnu_extensions,
279        pedantic: opts.pedantic,
280    };
281    let (tokens, complaints) = convert(&expanded, &cx);
282    diagnostics.extend(complaints);
283    clock.lap("convert");
284
285    // Only the ones the file wrote, since a name nothing interned is one nothing can use.
286    let type_names: Vec<Symbol> =
287        sess.target.type_names().iter().filter_map(|&(name, _)| sess.interner.find(name)).collect();
288    let parsed = rucc_parse::parse(
289        &tokens,
290        rucc_parse::Context {
291            interner: &sess.interner,
292            std: opts.std,
293            gnu: opts.gnu_extensions,
294            pedantic: opts.pedantic,
295            error_limit: opts.error_limit as usize,
296            type_names: &type_names,
297        },
298    );
299    clock.lap("parse");
300    let parse_failed = parsed.diagnostics.iter().any(|d| d.severity.is_fatal());
301    diagnostics.extend(parsed.diagnostics);
302    let comments = parsed.comments;
303
304    let mut artifact = Artifact::Nothing;
305    // Zero when nothing instruments, which is the truthful summary of a file built without
306    // `-fsafety`: no checks went in, so none is standing, and every call it makes is unmodelled.
307    let mut instrumented = Instrumented::default();
308    if !parse_failed {
309        let mut checker = Checker::new(
310            &parsed.ast,
311            CheckContext {
312                names: &sess.interner,
313                target: &sess.target,
314                std: opts.std,
315                gnu: opts.gnu_extensions,
316                pedantic: opts.pedantic,
317                permissive: opts.permissive,
318                gnu89_inline: opts.gnu89_inline,
319                error_limit: opts.error_limit as usize,
320                // A freestanding program has no C library, so a name that is the library's
321                // everywhere else is the program's own here and means whatever it defined.
322                builtins: opts.builtins && opts.hosted,
323                no_builtin: &opts.no_builtin,
324                short_enums: opts.short_enums,
325                ms_extensions: sess.ms_extensions(),
326                trapping_math: opts.trapping_math,
327                isa: opts.isa,
328            },
329        );
330        checker.check_unit();
331        let checked = checker.finish();
332        clock.lap("check");
333        if !checked.failed() {
334            match opts.emit {
335                EmitKind::Tast => {
336                    artifact = Artifact::Text(rucc_sema::print(
337                        &checked.tast,
338                        &checked.types,
339                        &sess.interner,
340                    ));
341                }
342                // Nothing past the checker, because a granule is a fact about a layout and a
343                // layout is settled the moment the closing brace is seen. Lowering the
344                // function bodies would take minutes on an amalgamation and answer nothing.
345                EmitKind::TypeGranules => {
346                    artifact = Artifact::Text(rucc_types::granule_report(
347                        &checked.types,
348                        &sess.interner,
349                        &sess.target,
350                    ));
351                }
352                EmitKind::Ir
353                | EmitKind::MirFinal
354                | EmitKind::Asm
355                | EmitKind::Object
356                | EmitKind::Archive
357                | EmitKind::Executable
358                | EmitKind::SafetySummary => {
359                    // What a `.incbin` in an `asm` at file scope names is read through the same
360                    // file system the sources came through, and from where the compiler was run
361                    // rather than from beside the source, because that is where an assembler
362                    // looks for it.
363                    let mut read = |named: &str| {
364                        fs.read(Path::new(named))
365                            .map(|bytes| bytes.as_slice().to_vec())
366                            .map_err(|why| why.to_string())
367                    };
368                    // What the debug information will say about types and signatures, taken
369                    // here because this is the last place the checker's types are readable
370                    // without the back end's borrow of the interner in the way. Nothing at all
371                    // when the build asked for no debug information, since a translation unit
372                    // the size of an amalgamation has tens of thousands of types in it.
373                    let meaning = if opts.debug_info {
374                        crate::shapes::collect(
375                            &checked.tast,
376                            &checked.types,
377                            &sess.target,
378                            &sess.interner,
379                            &sess.sources,
380                        )
381                    } else {
382                        crate::shapes::Meaning::default()
383                    };
384                    let common = sess.common();
385                    let mut lowered = rucc_lower::lower(
386                        crate::phase::source_name(name),
387                        rucc_lower::Context {
388                            tast: &checked.tast,
389                            types: &checked.types,
390                            target: &sess.target,
391                            names: &mut sess.interner,
392                            visibility: match opts.visibility {
393                                Visibility::Default => IrVisibility::Default,
394                                Visibility::Hidden => IrVisibility::Hidden,
395                                Visibility::Protected => IrVisibility::Protected,
396                            },
397                            protector: match opts.protector {
398                                Protector::None => LowerProtector::None,
399                                Protector::Buffers => LowerProtector::Buffers,
400                                Protector::Strong => LowerProtector::Strong,
401                                Protector::All => LowerProtector::All,
402                            },
403                            wrapping: rucc_lower::Wrapping {
404                                signed: opts.wrapping.signed,
405                                pointer: opts.wrapping.pointer,
406                                trap: opts.wrapping.trap,
407                            },
408                            aliasing: opts.strict_aliasing,
409                            padding: opts.padding == Padding::Ignored,
410                            contract: match opts.fp_contract {
411                                Contract::Off => FpContract::Off,
412                                Contract::On => FpContract::On,
413                                Contract::Fast => FpContract::Fast,
414                            },
415                            align: opts.align_functions,
416                            instrument: opts.instrument_functions,
417                            exceptions: opts.exceptions,
418                            common,
419                            read: &mut read,
420                        },
421                    );
422                    // The walk reports what it cannot build, and what it did build is printed
423                    // anyway: a file with one construct missing from it is more use to read
424                    // than nothing at all, and the errors are what stop it being compiled.
425                    clock.lap("lower");
426                    for option in linker_options(&comments, &sess.target) {
427                        lowered.module.add_linker_option(option);
428                    }
429                    let failed = lowered.diagnostics.iter().any(|d| d.severity.is_fatal());
430                    if !failed {
431                        // The verifier runs on everything the walk builds, always. It is the
432                        // one check that a bug in the walk cannot talk its way past, and a
433                        // wrong instruction found here costs a message rather than an hour
434                        // in front of a debugger over the assembly it turned into.
435                        if let Err(errors) = clock
436                            .time("verify", || rucc_ir::verify(&lowered.module, &sess.interner))
437                        {
438                            for error in errors {
439                                diagnostics.push(internal(&format!("invalid IR, {error}")));
440                            }
441                        } else if let Err(complaints) = clock
442                            .time("instrument", || {
443                                instrument(&mut lowered.module, &mut sess.interner, opts)
444                            })
445                            .map(|done| instrumented = done)
446                        {
447                            diagnostics.extend(complaints);
448                        } else if let Err(complaints) = clock
449                            .time("optimize", || {
450                                optimize(
451                                    &mut lowered.module,
452                                    &mut sess.interner,
453                                    &sess.target,
454                                    opts,
455                                    name,
456                                    &mut dumps,
457                                    &mut remarks,
458                                )
459                            })
460                            .map(|times| passes = times)
461                        {
462                            diagnostics.extend(complaints);
463                        } else if opts.emit == EmitKind::SafetySummary {
464                            // After the optimizer, because the number that matters is how many
465                            // checks are still standing and there is no way to know that before it
466                            // has run. Before the back end, because the back end turns a check into
467                            // a call and a summary of calls is not a summary of checks.
468                            artifact = Artifact::Text(
469                                rucc_safety::summarize(
470                                    &lowered.module,
471                                    &sess.interner,
472                                    name,
473                                    opts.safety.as_str(),
474                                    instrumented.checks,
475                                    instrumented.interposed,
476                                    instrumented.crossings,
477                                )
478                                .render(),
479                            );
480                        } else if opts.emit == EmitKind::Ir {
481                            // After the optimizer rather than before it, so that `--emit=ir -O2`
482                            // is the IR the back end will be given rather than the IR it would
483                            // have been given at `-O0`. There is no other way to see what a pass
484                            // did without reading the assembly it turned into.
485                            artifact =
486                                Artifact::Text(rucc_ir::print(&lowered.module, &sess.interner));
487                        } else {
488                            // The back end, which is every pass after the IR and which is
489                            // where a construct nothing has a rule for is finally noticed.
490                            let made = clock.time("generate", || {
491                                generate(
492                                    &mut lowered.module,
493                                    &mut sess.interner,
494                                    &sess.target,
495                                    opts,
496                                    &mut Recording {
497                                        fired: &mut fired,
498                                        pressure: &mut pressure,
499                                        lowerings: &mut lowerings,
500                                        stack: &mut stack,
501                                    },
502                                    &mut temps.assembly,
503                                    Origin { map: &sess.sources, name, meaning: &meaning },
504                                )
505                            });
506                            match made {
507                                Ok(made) => artifact = made,
508                                Err(complaints) => diagnostics.extend(complaints),
509                            }
510                        }
511                    }
512                    diagnostics.extend(lowered.diagnostics);
513                }
514                // The checker has said everything it has to say, and that is all that was asked.
515                EmitKind::SyntaxOnly => {}
516                _ => {}
517            }
518        }
519        diagnostics.extend(checked.diagnostics);
520    }
521    // The back end's remarks after the optimizer's, which is the order the work happened in. Only
522    // the `switch` lowering says anything yet, and what it says is a rewrite.
523    let mut wants = rucc_opt::Wants::none();
524    for spec in &opts.opt_info {
525        // Checked when the arguments were parsed, and again by the optimizer.
526        let _ = wants.add(spec);
527    }
528    if wants.wants(rucc_opt::stats::Kind::Optimized) {
529        remarks.push_str(&lowerings.remarks(name));
530    }
531
532    let mut messages = Vec::with_capacity(diagnostics.len());
533    let mut errors = 0;
534    for diag in &diagnostics {
535        // `-w` drops the warning here rather than at the several hundred places one is raised,
536        // and it drops it before the count, so `-w -Werror` compiles. A warning that was never
537        // raised is not a warning there is anything to promote. A warning about something in a
538        // header that came with the machine goes the same way for the same reason, unless
539        // `-Wsystem-headers` asked for it.
540        if rucc_diag::dropped(diag, &sess.sources, opts.warnings, opts.system_header_warnings) {
541            continue;
542        }
543        if diag.severity.is_fatal()
544            || (diag.severity == Severity::Warning && opts.warnings_are_errors)
545        {
546            errors += 1;
547        }
548        messages.push(render(diag, &sess.sources, opts.warnings_are_errors));
549    }
550    if errors > 0 {
551        // A tree built from a file that did not compile is not a tree anything should read.
552        artifact = Artifact::Nothing;
553    }
554    // Before the session goes, since the map that says where each function is goes with it. A
555    // file that did not compile gets an empty report, which is what gcc leaves for one.
556    let stack_usage = if opts.stack_usage && errors == 0 {
557        su_file(&stack, &sess.sources, crate::phase::source_name(name))
558    } else {
559        String::new()
560    };
561    // Kept even when the compilation failed, because a rule that fired did fire and a report about
562    // which rules a corpus reaches should not lose the ones a file with a mistake in it reached.
563    clock.passes(passes);
564    let timing = clock.finish();
565    Compiled {
566        artifact,
567        messages,
568        errors,
569        fired,
570        pressure,
571        lowerings,
572        dumps,
573        remarks,
574        deps,
575        temps,
576        timing,
577        stack_usage,
578    }
579}
580
581/// Reads one file of IR, checks it, and prints it back.
582///
583/// This is the compiler's own textual IR arriving as an input rather than leaving as an output,
584/// which is what makes the round trip in the M2 exit criterion something to run rather than
585/// something to believe: what the printer wrote is read back, verified, and written again, and
586/// the two files are either the same bytes or they are not.
587///
588/// The verifier runs here for the reason it runs after the walk. A module that was printed by
589/// this compiler has been through it once already, and one that a person edited has not.
590#[must_use]
591pub fn compile_ir(opts: &Options, name: &str, fs: &dyn FileSystem) -> Compiled {
592    let mut sess = Session::new(opts.clone());
593    if opts.emit != EmitKind::Ir {
594        return failure(format!(
595            "{name}: an input of IR can only be emitted as IR, and `--emit={}` asks for what \
596             the C in front of it became",
597            opts.emit.as_str()
598        ));
599    }
600    let bytes = match fs.read(Path::new(name)) {
601        Ok(bytes) => bytes,
602        Err(e) => return failure(format!("{name}: {e}")),
603    };
604    let Ok(text) = std::str::from_utf8(bytes.as_slice()) else {
605        return failure(format!("{name}: this is not text, so it is not IR"));
606    };
607
608    let module = match rucc_ir::parse(text, &mut sess.interner) {
609        Ok(module) => module,
610        Err(error) => {
611            return failure(format!("{name}:{}: {}", error.line, error.message));
612        }
613    };
614    let mut diagnostics: Vec<Diagnostic> = Vec::new();
615    if let Err(errors) = rucc_ir::verify(&module, &sess.interner) {
616        for error in errors {
617            diagnostics.push(invalid(&format!("invalid IR, {error}")));
618        }
619    }
620    let mut messages = Vec::with_capacity(diagnostics.len());
621    for diag in &diagnostics {
622        messages.push(render(diag, &sess.sources, opts.warnings_are_errors));
623    }
624    let errors = u32::try_from(messages.len()).unwrap_or(u32::MAX);
625    let artifact = if errors > 0 {
626        Artifact::Nothing
627    } else {
628        Artifact::Text(rucc_ir::print(&module, &sess.interner))
629    };
630    // Nothing here reaches the back end, so no rule fired and there is nothing to record.
631    Compiled {
632        artifact,
633        messages,
634        errors,
635        fired: Fired::new(),
636        pressure: Pressure::new(),
637        lowerings: Lowerings::new(),
638        dumps: Vec::new(),
639        remarks: String::new(),
640        deps: Vec::new(),
641        temps: Temps::default(),
642        timing: crate::trace::Timing::default(),
643        stack_usage: String::new(),
644    }
645}
646
647/// Puts the memory safety checks in and redirects the calls that cross the boundary, when
648/// `-fsafety=` asked for them.
649///
650/// Between the walk and the optimizer, which is where section 15.3 of
651/// `spec/safe-memory/15-integration.md` puts it and which is the whole design in one line: the
652/// checks go in while the addresses the program computes still exist, and the optimizer then
653/// discharges the ones it can prove. Every sanitizer that came before instruments after the
654/// optimizer so that its checks cannot be deleted, and pays for all of them forever.
655///
656/// The calls to the C library are redirected here too, and in the same window and for a related
657/// reason. `spec/safe-memory/10-boundaries.md` section 10.3 wants a `memcpy` modelled by a wrapper
658/// that performs the judgements, and `rucc_safety::wrap` is why that has to happen before the
659/// optimizer sees the call rather than after.
660///
661/// The verifier runs again afterwards, for the reason it runs after the walk. This pass rewrites
662/// every function in the module, and a pass that produced IR nothing else accepts should say so
663/// here rather than in the assembly it turned into.
664///
665/// # Errors
666///
667/// When the inserted checks left the module in a state the verifier refuses, which is a bug in
668/// this compiler and not in the program being compiled.
669/// What the unit's `#pragma comment` lines ask the linker for, spelled the way clang spells it,
670/// which is the same for mingw-w64 and for MSVC: lld reads `/DEFAULTLIB:` in both modes and looks
671/// for `libws2_32.a` as well as `ws2_32.lib` under mingw-w64. A library with no `.lib` or `.a` on
672/// the end gets `.lib`, and one with a space in it is quoted. Only COFF has a section to put them
673/// in, so everywhere else they are dropped, which is what clang and gcc do too.
674fn linker_options(comments: &[rucc_parse::Comment], target: &TargetInfo) -> Vec<String> {
675    if target.tuple.os().object_format() != Some(ObjectFormat::Coff) {
676        return Vec::new();
677    }
678    comments
679        .iter()
680        .map(|comment| match comment {
681            rucc_parse::Comment::Lib(lib) => {
682                let lower = lib.to_ascii_lowercase();
683                let suffix =
684                    if lower.ends_with(".lib") || lower.ends_with(".a") { "" } else { ".lib" };
685                if lib.contains(' ') {
686                    format!("/DEFAULTLIB:\"{lib}{suffix}\"")
687                } else {
688                    format!("/DEFAULTLIB:{lib}{suffix}")
689                }
690            }
691            rucc_parse::Comment::Linker(option) => option.clone(),
692        })
693        .collect()
694}
695
696fn instrument(
697    module: &mut rucc_ir::Module,
698    names: &mut Interner,
699    opts: &Options,
700) -> Result<Instrumented, Vec<Diagnostic>> {
701    if !opts.safety.instruments() {
702        return Ok(Instrumented::default());
703    }
704    let mut checks = rucc_safety::run(module, opts.subobject, opts.promise, opts.races);
705    // The one check that is about a call rather than about an access, so it is a walk of its own
706    // and it is here rather than in the walk above. `rucc_safety::ending` is why, and the short
707    // version is that deciding it means resolving a name, which takes the interner.
708    //
709    // Before the redirection for the same reason the redirection is before the optimizer: what this
710    // reads is the name the program wrote, and a pass that had already pointed the call somewhere
711    // else would leave it with a name this one has no row for.
712    checks.freed = rucc_safety::ending::checks(module, names);
713    // Before the optimizer rather than beside the check lowering, which is what
714    // `rucc_safety::wrap` argues out: `memcpy` is a name an optimizer knows things about, and a
715    // pass that turns a short copy into a pair of loads and stores would leave behind accesses the
716    // check insertion has already finished walking past.
717    let interposed = rucc_safety::redirect(module, names);
718    // After the redirection, so that a call this build models with a wrapper is not also counted
719    // as a crossing it did not model.
720    let crossings = rucc_safety::witness(module, names);
721    match rucc_ir::verify(module, names) {
722        Ok(()) => Ok(Instrumented { checks, interposed, crossings }),
723        Err(errors) => Err(errors
724            .iter()
725            .map(|e| internal(&format!("invalid IR after check insertion, {e}")))
726            .collect()),
727    }
728}
729
730/// What the instrumentation did, which nothing but the summary reads.
731///
732/// Carried out of [`instrument`] rather than recovered from the module afterwards because neither
733/// number survives the optimizer: a check that was discharged leaves nothing behind saying it was
734/// ever there, and a call that was pointed at a wrapper looks like a call that always named one.
735#[derive(Clone, Copy, Debug, Default)]
736struct Instrumented {
737    /// How many checks of each class went in.
738    checks: rucc_safety::Counts,
739    /// How many calls were pointed at an interposition wrapper.
740    interposed: usize,
741    /// How many places a pointer crosses to or from code this build did not instrument.
742    crossings: rucc_safety::Sites,
743}
744
745/// Runs the optimizer over the module, and collects whatever the dumps asked for.
746///
747/// The level chooses a pipeline, the `-f` flags edit it, and at `-O0` there is nothing in it, so
748/// this is a walk over an empty list rather than a branch on the level. See section 9.1 of
749/// `spec/09-optimizer.md` for why the pipelines are written out rather than assembled.
750///
751/// Gives back how long each pass took, for `-frucc-trace`.
752///
753/// # Errors
754///
755/// When a pass left the module in a state the verifier refuses, which is a bug in the pass and
756/// not in the program being compiled, so it is reported as an internal error the way a bad
757/// lowering is.
758fn optimize(
759    module: &mut rucc_ir::Module,
760    names: &mut Interner,
761    target: &TargetInfo,
762    opts: &Options,
763    file: &str,
764    dumps: &mut Vec<rucc_opt::Dump>,
765    remarks: &mut String,
766) -> Result<Vec<(&'static str, std::time::Duration)>, Vec<Diagnostic>> {
767    let mut settings = rucc_opt::Options::for_level(opts.opt_level);
768    // What the analyses that read a body may believe about it. The same question the back end asks
769    // about addresses, with one thing on top: `-fno-semantic-interposition` is the build promising
770    // that a name it exports is the one that will run, which is what every distribution builds a
771    // library with. It says nothing about how an address is reached, and gcc does not change that
772    // under the flag either, so the back end is not given this value.
773    settings.interposition = match opts.interposition {
774        true => replaceable(target, opts),
775        false => IrPic::Executable,
776    };
777    settings.toggles.clone_from(&opts.passes);
778    // The same pair the front end reads a call to a standard name with, which is section 20.1's
779    // three way split: `-ffreestanding` says the library is not there, `-fno-builtin` says it is
780    // there and is not to be assumed to do what the standard says, and a fold that leaves behind a
781    // call to `puts` needs both of those to be off.
782    settings.builtins = opts.builtins && opts.hosted;
783    settings.no_builtin.clone_from(&opts.no_builtin);
784    // What a function with no `target` attribute is built for, which the inliner compares a
785    // callee with one against.
786    settings.isa = opts.isa;
787    settings.fuel = opts.pass_fuel.iter().cloned().collect();
788    settings.global_fuel = opts.pass_fuel_global;
789    settings.verify |= opts.verify_each;
790    for (on, spec) in &opts.pass_gates {
791        // Same argument as the dumps below: every spelling in here was checked while the
792        // arguments were parsed, so a rejection now is this compiler disagreeing with itself.
793        if let Err(why) = settings.gates.add(*on, spec) {
794            return Err(vec![internal(&why)]);
795        }
796    }
797    for spec in &opts.dump_ir {
798        // Every spelling in here was checked while the arguments were parsed, so a rejection
799        // now is this compiler disagreeing with itself rather than the command line being wrong.
800        if let Err(why) = settings.dumps.add(spec) {
801            return Err(vec![internal(&why)]);
802        }
803    }
804    let mut wants = rucc_opt::Wants::none();
805    for spec in &opts.opt_info {
806        // Same argument as the dumps above: every spelling was checked while the arguments were
807        // parsed, so a rejection now is the compiler disagreeing with itself.
808        if let Err(why) = wants.add(spec) {
809            return Err(vec![internal(&why)]);
810        }
811    }
812    let report = rucc_opt::run(module, names, &settings);
813    remarks.push_str(&rucc_opt::optinfo::render(file, &report, names, wants));
814    dumps.extend(report.dumps);
815    match report.broke.is_empty() {
816        true => Ok(report.time),
817        false => Err(report.broke.iter().map(|why| internal(why)).collect()),
818    }
819}
820
821/// Runs the back end over every function in `module` and writes what came out.
822///
823/// One machine function per definition in the module, in the order the module holds them, every
824/// register physical and every frame offset a constant. A declaration has no body and is skipped,
825/// because there is nothing in it to compile.
826///
827/// What the last step is, is the only thing `--emit=mir-final`, `-S` and `-c` disagree about. The
828/// three read the same functions and differ in whether they are printed as machine IR, printed as
829/// assembly, or encoded and put in a file, which is the point of section 11.1 of
830/// `spec/11-asm-objects-debug.md`: a listing that disagrees with the object file beside it is
831/// worse than no listing, and the way to make that impossible is to have one description of an
832/// instruction and two ways of writing it down.
833///
834/// # Errors
835///
836/// One diagnostic per function the back end could not compile, or one about the target when no
837/// back end covers it at all. Every function is attempted rather than stopping at the first, so a
838/// file with three constructs missing from the rule set reports three rather than one at a time.
839///
840/// `assembly` is where `-save-temps` gets its listing from on the path that does not print one,
841/// which is the same functions written the other way rather than a second compilation of the same
842/// file. A listing that disagrees with the object beside it would be worse than none.
843/// Whether a name this file exports is one another object may define or replace.
844///
845/// The link that reads the object decides half of what is in it, and the command line is where that
846/// is said, which is why the flag reaches this far down. See #756.
847///
848/// ELF only, because it is a question about a format rather than about a machine and the other two
849/// answer it differently. Mach-O has a two level namespace, so a name a library defines is bound to
850/// that library and is not replaced by a definition loaded earlier, and it has no copy relocations,
851/// so a variable defined elsewhere needs the table whichever link is coming. COFF decides what
852/// leaves a DLL by an export table the linker is handed. Neither has an object writer here yet, so
853/// what this does is decline to say the ELF answer about them.
854fn replaceable(target: &TargetInfo, opts: &Options) -> IrPic {
855    match (target.tuple.os().object_format(), opts.pic) {
856        (Some(ObjectFormat::Elf), Pic::Library) => IrPic::Library,
857        _ => IrPic::Executable,
858    }
859}
860
861/// Where the file being generated came from, which is what the debug information is about.
862///
863/// The three together rather than separately because none of them is any use on its own here: a
864/// span without the map it points into is a pair of numbers, a name without the spans is a file
865/// nothing in the object refers to, and a signature without the name of the function it belongs to
866/// is an entry with nothing to attach it to.
867#[derive(Clone, Copy)]
868struct Origin<'a> {
869    /// Where every span in the module points.
870    map: &'a SourceMap,
871    /// What the command line called the file, which is what `DW_AT_name` says.
872    name: &'a str,
873    /// The types and the signatures, and empty where the build wanted no debug information.
874    meaning: &'a crate::shapes::Meaning,
875}
876
877fn generate(
878    module: &mut rucc_ir::Module,
879    names: &mut Interner,
880    target: &TargetInfo,
881    opts: &Options,
882    recording: &mut Recording<'_>,
883    assembly: &mut Option<String>,
884    origin: Origin<'_>,
885) -> Result<Artifact, Vec<Diagnostic>> {
886    let Some(machine) = Machine::for_target(target) else {
887        return Err(vec![unsupported(&format!(
888            "there is no back end for {} in this compiler yet, so there is nothing to generate",
889            target.tuple
890        ))]);
891    };
892    // Refused rather than dropped. A command line that asks for a stack protector on a target
893    // that has nowhere to keep the word one is compared against would otherwise get code with no
894    // protection in it and no indication that the flag did nothing, which is the one outcome worse
895    // than the error. Windows is the case: it has a protector and it is a different mechanism.
896    if opts.protector != Protector::None && machine.conv.guard.is_none() {
897        return Err(vec![unsupported(&format!(
898            "{} is not supported for {} yet, because the stack protector on that target is not \
899             the one this compiler writes",
900            opts.protector, target.tuple
901        ))]);
902    }
903    // The same answer for the same reason. What says a file was built to have its control flow
904    // checked is a note, the note is an ELF one, and a target whose objects are not ELF has nowhere
905    // to put it: the landing pads would go in and nothing would ever turn the check on. Windows has
906    // the same hardware and asks for it a different way, which is a bit in the image the linker is
907    // told to set rather than anything a compiler writes into an object.
908    if opts.control.any() && target.tuple.os().object_format() != Some(ObjectFormat::Elf) {
909        return Err(vec![unsupported(&format!(
910            "-fcf-protection={} is not supported for {} yet, because what says a file was built \
911             for it there is not the note this compiler writes",
912            opts.control, target.tuple
913        ))]);
914    }
915    // And once more. A profiled build is one whose functions call a routine the runtime provides,
916    // and a target whose runtime provides no such routine would get a call to a name nothing
917    // defines, which is a link error a long way from the flag that caused it. Windows profiles a
918    // build by calling something else, asked for a different way and taking its argument in a
919    // register, so it is not this hook spelled differently.
920    let profile = match machine.conv.trace {
921        Some(trace) => opts.profile.then(|| opts.hook.early(trace.fentry)),
922        None if opts.profile => {
923            return Err(vec![unsupported(&format!(
924                "-pg is not supported for {} yet, because the profiler's hook on that target is \
925                 not the one this compiler calls",
926                target.tuple
927            ))]);
928        }
929        None => None,
930    };
931    // And once more. The room a patcher was promised is only half the feature: the other half is a
932    // section listing where every function's room is, and both the section's shape and the way it
933    // points at the text it belongs to are ELF's. A format that has no such section would take the
934    // nops and quietly lose the list, which is a build that looks patchable and is not.
935    if opts.patchable.any() && target.tuple.os().object_format() != Some(ObjectFormat::Elf) {
936        return Err(vec![unsupported(&format!(
937            "-fpatchable-function-entry= is not supported for {} yet, because what records where \
938             the room is there is not the section this compiler writes",
939            target.tuple
940        ))]);
941    }
942    let flags = pipeline::Flags {
943        frame_pointer: opts.keeps_frame_pointer(),
944        red_zone: opts.red_zone,
945        stack_clash: opts.stack_clash,
946        landing: opts.control.branch(),
947        profile: match profile {
948            None => pipeline::Profile::No,
949            Some(true) => pipeline::Profile::Early,
950            Some(false) => pipeline::Profile::Late,
951        },
952        patch: pipeline::Room { after: opts.patchable.after(), before: opts.patchable.before },
953        // On at every level above `-O0`, which is where gcc turns `-freorder-blocks` on
954        // (`gcc/opts.cc:604`) and what `spec/optimizer/38-scheduling-and-layout.md` section 38.3
955        // reads off that: it is one of the earliest optimizations there is, it is nearly free,
956        // and it helps every target. `-O0` keeps the order the shape of the graph gives, so that
957        // the blocks come out in the order they were written and a person stepping through the
958        // code walks down the screen.
959        reorder: opts.reorder_blocks.unwrap_or_else(|| opts.opt_level.runs_optimizer()),
960        // On at every level above `-O0`, for the reason the line above is off at it. Sharing one
961        // run of bytes between two locals is a smaller frame and a worse debugger: a variable that
962        // is out of scope reads as whatever took its place, which is what `-O0` exists not to do.
963        // Above it the frame is the win, and `-fstack-reuse=` says either answer at any level.
964        reuse: opts.stack_reuse.unwrap_or_else(|| opts.opt_level.runs_optimizer()),
965        // On from `-O2`, which is where gcc turns `-fschedule-insns2` on and what
966        // `spec/optimizer/38-scheduling-and-layout.md` section 38.6 asks for. Not at `-O1`,
967        // because a schedule is a whole dependence graph per block and `-O1` is the level whose
968        // budget is roughly `-O0`'s. Not at `-O0` for the reason nothing else is.
969        schedule: opts.schedule_insns.unwrap_or_else(|| opts.opt_level.schedules()),
970        // Off unless asked for. gcc pads loops at `-O2` and `-O3`. gcc's padding here cost a third
971        // of a percent of the corpus's text and more than a percent of SQLite's for no speed
972        // anybody could measure, which is tamnd/rucc#1823. The padding this asks for now keeps a
973        // small loop inside one line, which is 18% on AMD EPYC and nothing on an Intel Core, so no
974        // level asks for it on every machine's behalf. See tamnd/rucc#1838.
975        align_loops: opts.align_loops.unwrap_or(false),
976        // Whatever the command line said, and the model's own answer when it said nothing.
977        accurate: opts.cycle_accurate_model,
978        // The same flag that turns the IR verifier on in a release build, since what it says is
979        // that this run should check itself and the back end has checks of its own.
980        verify: opts.verify_each,
981        // The backtracking allocator whenever the optimizer runs, and the single pass one at `-O0`,
982        // which is what section 39.7 keeps it for. `-Zregalloc=` picks either at any level. See
983        // `rucc_regalloc::backtrack` for what the backtracking one does differently.
984        allocator: if opts.backtracking.unwrap_or_else(|| opts.opt_level.runs_optimizer()) {
985            pipeline::Allocator::Backtracking
986        } else {
987            pipeline::Allocator::Single
988        },
989        // What the level asked for. The back end had no way to know until now, which is
990        // tamnd/rucc#741: `-Os` picked a shorter list of middle end passes and then compiled the
991        // result exactly as `-O2` would have. The level is asked whether it optimizes for size
992        // rather than matched against, so a level added later answers this without editing it.
993        goal: Goal::for_size(opts.opt_level.is_size()),
994        // Only when somebody is measuring, and checked when the arguments were parsed.
995        switch: opts.switch_shape.as_deref().and_then(rucc_codegen::switch::Force::named),
996        // On from `-O2` and at `-Os`, which is where gcc turns `-foptimize-sibling-calls` on.
997        sibling: opts.sibling_calls.unwrap_or_else(|| opts.opt_level.sibling_calls()),
998        debug: opts.debug_info,
999    };
1000
1001    // The checks become calls here rather than beside the insertion, because the id each one
1002    // carries is an index into a table and a row for a check the optimizer deleted is a row nothing
1003    // will ever name. Section 6.3.1 of `spec/safe-memory/06-instrumentation.md` is what this
1004    // eventually becomes and `rucc_safety::lower` says why it is not that yet.
1005    //
1006    // It is inside the back end rather than beside the optimizer so that `--emit=ir` still shows
1007    // the checks. The IR a person reads should say what the compiler decided, not how it spelled it
1008    // for the machine.
1009    if opts.safety.instruments() {
1010        // Which calls hand back storage, which the lowering needs and `-O0` has not worked out.
1011        // `rucc_opt::pipeline` runs this only when some pass in the run reads the summaries, since a
1012        // flag nothing reads is noise in a dump, and at `-O0` nothing did. Something does now: the
1013        // capability for a pointer an allocator just returned is the one capability that is exact
1014        // and costs a load, and `rucc_safety::slot` finds those sites by the flag. The safety suite
1015        // runs at `-O0`, so without this the cheap case would be the one case that never happens.
1016        //
1017        // Safe to run twice and safe to run late, because it only ever sets the flag and never
1018        // clears one, so a build that had it already gets the same module back.
1019        rucc_opt::heap::annotate(module, names);
1020        // Which calls hand their capabilities to the callee and which say there are none. Here and
1021        // not beside the insertion, because the rule is what each function still has left to check
1022        // and the optimizer is what makes that small: running before it would give every callee a
1023        // frame for checks that are about to be discharged. `rucc_safety::handover` is the rule and
1024        // the pass both, and the census in `--emit=safety-summary` reads the same rule, so the
1025        // buckets it prints describe the code that was actually built.
1026        rucc_safety::handover::arrange(module);
1027        rucc_safety::lower(module, names);
1028        if let Err(errors) = rucc_ir::verify(module, names) {
1029            return Err(errors
1030                .iter()
1031                .map(|e| internal(&format!("invalid IR after check lowering, {e}")))
1032                .collect());
1033        }
1034    }
1035
1036    // Worked out before the loop and not inside it, because it reads the whole module and the loop
1037    // is holding one function of it. It has to be after the check lowering above, since that adds
1038    // calls to the runtime and so can add a name this file does not define.
1039    //
1040    // The link that reads the object decides half of what is in it, and the command line is where
1041    // that is said, which is why the flag reaches this far down. See #756. The format decides the
1042    // other half, since a table only exists on a format that has one to reach through.
1043    //
1044    // Only x86-64 copies a variable into the executable for a reference from the instruction
1045    // pointer, so on the other machines a variable this file only declares is read from the table.
1046    let copies = target.tuple.arch() == Arch::X86_64;
1047    let elsewhere = Elsewhere::of(module, replaceable(target, opts), target.object_format, copies);
1048
1049    let mut funcs = Vec::new();
1050    let mut complaints = Vec::new();
1051    for id in module.funcs() {
1052        if module[id].is_declaration() {
1053            continue;
1054        }
1055        match pipeline::compile_recording(
1056            &mut module[id],
1057            names,
1058            &machine,
1059            &elsewhere,
1060            flags,
1061            recording,
1062        ) {
1063            Ok(func) => funcs.push(func),
1064            Err(why) => {
1065                let name = names.resolve(module[id].name).to_owned();
1066                // The function knows where the instruction came from, so the message lands on
1067                // the line somebody wrote rather than on the file as a whole.
1068                let span = why.inst().map_or(Span::DUMMY, |inst| module[id].span(inst));
1069                let said = format!("cannot generate code for '{name}': {why}");
1070                complaints.push(unsupported_at(&said, span));
1071            }
1072        }
1073    }
1074    if !complaints.is_empty() {
1075        return Err(complaints);
1076    }
1077    // The variables the file defines, which go through the back end the way the functions did not:
1078    // there is nothing in a variable to select instructions for, so the module is what says what
1079    // one is right up to the point where it is written down.
1080    // The second names go the same way and for the same reason, and they are neither a function
1081    // nor a variable: an alias is an entry in the symbol table and no bytes of anything.
1082    let (globals, aliases) = match opts.emit {
1083        EmitKind::Asm | EmitKind::Object | EmitKind::Archive | EmitKind::Executable => {
1084            let mut globals =
1085                rucc_asm::globals(module, names, target.object_format).map_err(refused)?;
1086            // The pointer each variable this file reads and only declares is reached through on
1087            // COFF, which is a variable of this file's all the same. Asked for after the loop
1088            // rather than before it, because the loop is what optimized the functions, and a read
1089            // the optimizer took out is a pointer nobody would load.
1090            globals.pointers(elsewhere.referred(module).into_iter().map(|name| {
1091                let target = names.resolve(name).to_owned();
1092                (Slot::Referred.name(&target), target)
1093            }));
1094            if target.tuple.env() == rucc_tuple::Env::Msvc {
1095                globals.keep_imported(
1096                    elsewhere
1097                        .imported_variables(module)
1098                        .into_iter()
1099                        .map(|name| names.resolve(name).to_owned()),
1100                );
1101            }
1102            (globals, rucc_asm::aliases(module, names).map_err(refused)?)
1103        }
1104        _ => (rucc_asm::Globals::default(), Vec::new()),
1105    };
1106    // A failure in either of the last two is a bug here rather than a program this compiler is
1107    // behind on, because every instruction in a function that got this far came out of the same
1108    // description both of them read and every register in it has been allocated.
1109    let unwind = opts.unwinds();
1110    match opts.emit {
1111        EmitKind::Asm => {
1112            rucc_asm::print(&funcs, &globals, &aliases, names, target, unwind, output(opts, target))
1113                .map(Artifact::Text)
1114                .map_err(refused)
1115        }
1116        // An executable is an object as far as this gets: one is what each file of a link
1117        // contributes, and the linker is what turns them into the other. An archive is the same
1118        // again, with the archive writer in place of the linker.
1119        EmitKind::Object | EmitKind::Archive | EmitKind::Executable => {
1120            if opts.save_temps.wanted() {
1121                let listing = rucc_asm::print(
1122                    &funcs,
1123                    &globals,
1124                    &aliases,
1125                    names,
1126                    target,
1127                    unwind,
1128                    output(opts, target),
1129                );
1130                *assembly = Some(listing.map_err(refused)?);
1131            }
1132            // A template kept as text has no bytes until an assembler reads it. Most are read on
1133            // their own where they are, but one may jump to a label another statement's text
1134            // defines or switch section halfway through, and a unit with one of those in it is
1135            // assembled the way gcc assembles every unit: written out as a listing and read back.
1136            // A build that asked for debug information gets a label in front of every instruction,
1137            // and where the reader placed those is the row the encoder would have recorded.
1138            //
1139            // Every unit for AArch64 goes this way for now. The listing is already written from
1140            // the encoder's own tables, so reading it back is the encoder run over the same values,
1141            // and it is one path to get right rather than two.
1142            let aarch64 = target.tuple.arch() == Arch::Aarch64;
1143            if aarch64 || globals.kept() || rucc_asm::kept(&funcs, names, target) {
1144                // A unit with a landing pad comes through this too. The listing names the
1145                // personality routine and the call site table with `.cfi_personality` and
1146                // `.cfi_lsda`, writes the table in `.gcc_except_table`, and the reader keeps both.
1147                let print = if opts.debug_info { rucc_asm::print_marked } else { rucc_asm::print };
1148                let listing =
1149                    print(&funcs, &globals, &aliases, names, target, unwind, output(opts, target))
1150                        .map_err(refused)?;
1151                let arch = target.tuple.arch();
1152                let read =
1153                    rucc_asm::read_as(&listing, arch, target.object_format).map_err(|trouble| {
1154                        let what = if aarch64 {
1155                            "a unit for aarch64"
1156                        } else if globals.kept() {
1157                            "an `asm` at file scope"
1158                        } else {
1159                            "an `asm` template kept as text"
1160                        };
1161                        vec![unsupported(&format!(
1162                            "{what}, whose listing the assembler stopped at on line {}: {}",
1163                            trouble.line, trouble.why
1164                        ))]
1165                    })?;
1166                let info = if opts.debug_info {
1167                    let assembled =
1168                        placed(&read, &funcs, names, target).map_err(|why| vec![internal(&why)])?;
1169                    describe(&assembled, &globals.image(), &funcs, origin, opts, target)
1170                        .map_err(|why| vec![internal(&why)])?
1171                } else {
1172                    rucc_object::Info::default()
1173                };
1174                let defines = rucc_object::assembled_defines(&read);
1175                let bytes =
1176                    rucc_object::assembled_described(&read, target, &info).map_err(wrote)?;
1177                return Ok(Artifact::Object { bytes, defines });
1178            }
1179            let assembled = rucc_asm::assemble(&funcs, names, target, unwind, opts.debug_info)
1180                .map_err(refused)?;
1181            let data = globals.image();
1182            // The line table, from the spans the assembler kept beside the bytes. Empty when the
1183            // build asked for no debug information, which is the case the rows above are not even
1184            // recorded in.
1185            let info = if opts.debug_info {
1186                describe(&assembled, &data, &funcs, origin, opts, target)
1187                    .map_err(|why| vec![internal(&why)])?
1188            } else {
1189                rucc_object::Info::default()
1190            };
1191            let text = assembled.text;
1192            // A format with no writer is a target this compiler is behind on and anything else
1193            // the writer refused is a bug here, and the two are not the same news to get.
1194            let bytes =
1195                rucc_object::write(&text, &data, &aliases, target, output(opts, target), &info)
1196                    .map_err(wrote)?;
1197            // Asked of the writer rather than worked out from the same three values here, so that
1198            // what the archive's index says and what is in the member cannot come apart. It is
1199            // wanted only by `--emit=archive` and is cheap enough that the other two kinds are not
1200            // worth a second path.
1201            let defines = rucc_object::defines(&text, &data, &aliases, target).map_err(wrote)?;
1202            Ok(Artifact::Object { bytes, defines })
1203        }
1204        _ => Ok(Artifact::Text(rucc_mir::print(&funcs, names, target.regs))),
1205    }
1206}
1207
1208/// The rows a listing marked by [`rucc_asm::print_marked`] would have had from the encoder, read
1209/// off where the reader placed each label.
1210///
1211/// Each function is where its own symbol is and as long as its `.size` says, and each row is its
1212/// label's distance from the symbol. The row for the front of the function is the one the encoder
1213/// writes from `Func::declared`, and it is written here the same way.
1214///
1215/// # Errors
1216///
1217/// A function or a label the reader did not place, which is a listing this compiler wrote and got
1218/// wrong.
1219fn placed(
1220    read: &rucc_object::Assembled,
1221    funcs: &[rucc_mir::Func],
1222    names: &Interner,
1223    target: &TargetInfo,
1224) -> Result<rucc_asm::Assembled, String> {
1225    let at: HashMap<&str, &rucc_object::Name> =
1226        read.names.iter().map(|name| (name.name.as_str(), name)).collect();
1227    let offset = |name: &str| match at.get(name).map(|name| name.at) {
1228        Some(rucc_object::Held::In { part, offset }) => Some((part, offset)),
1229        _ => None,
1230    };
1231    let mut text = rucc_object::Text::default();
1232    let mut lines = Vec::with_capacity(funcs.len());
1233    // The name the listing gave each function, which on Mach-O has the underscore in front. The
1234    // debug information keeps the C name, and the object writer puts the underscore back on when
1235    // it looks one up.
1236    let symbol = rucc_asm::Directives::of(target.object_format).symbol();
1237    for (which, func) in funcs.iter().enumerate() {
1238        let name = names.resolve(func.name);
1239        let Some((part, start)) = offset(&format!("{symbol}{name}")) else {
1240            return Err(format!("the listing has no label for the function '{name}'"));
1241        };
1242        let mut rows = Vec::with_capacity(func.inst_count() + 1);
1243        if !func.declared.is_dummy() {
1244            rows.push(rucc_asm::Row { at: 0, span: func.declared, inst: None });
1245        }
1246        for block in func.blocks() {
1247            for inst in func.insts(block) {
1248                let label = rucc_asm::mark(target, which, inst);
1249                let Some((held, here)) = offset(&label) else {
1250                    return Err(format!("the listing has no label '{label}'"));
1251                };
1252                if held != part || here < start {
1253                    return Err(format!("the label '{label}' is not inside '{name}'"));
1254                }
1255                let at = usize::try_from(here - start).map_err(|why| why.to_string())?;
1256                rows.push(rucc_asm::Row { at, span: func.span(inst), inst: Some(inst) });
1257            }
1258        }
1259        // What `.size` said, or on a format without it, how far the label after the last
1260        // instruction is from the front.
1261        let size = at.get(format!("{symbol}{name}").as_str()).map_or(0, |name| name.size);
1262        let len = match offset(&rucc_asm::mark_end(target, which)) {
1263            Some((held, end)) if size == 0 && held == part && end >= start => end - start,
1264            _ => size,
1265        };
1266        text.funcs.push(rucc_object::Extent {
1267            name: name.to_owned(),
1268            start: usize::try_from(start).map_err(|why| why.to_string())?,
1269            len: usize::try_from(len).map_err(|why| why.to_string())?,
1270            align: func.align.unwrap_or(rucc_object::FUNC_ALIGN),
1271            binding: rucc_object::Binding::Global,
1272            visibility: rucc_object::Visibility::Default,
1273            patch: None,
1274            landings: Vec::new(),
1275        });
1276        lines.push(rows);
1277    }
1278    Ok(rucc_asm::Assembled { text, lines, frames: None })
1279}
1280
1281/// The debug sections for what was just assembled, as bytes and relocations.
1282///
1283/// This is where a span becomes a file and a line, and it is here rather than anywhere further down
1284/// because the source map is the driver's and because the paths in it are still paths at this point.
1285/// [`rucc_session::PrefixMap::apply`] is run over every one of them, which is the whole of what
1286/// `-fdebug-prefix-map=` and `-ffile-prefix-map=` asked for: a build is only reproducible if all of
1287/// the paths in it are rewritten rather than most, so the file names, the name of the unit and the
1288/// directory it was compiled in all go through it.
1289///
1290/// A row whose span is [`Span::DUMMY`] is dropped rather than written at line zero. Those are the
1291/// instructions a pass invented, a prologue and a spill among them, and a debugger asking what a
1292/// program counter is in the middle of is better told the line before than told a line that is not
1293/// in the file. The row that follows covers those bytes, which is the same answer gcc gives.
1294///
1295/// # Errors
1296///
1297/// Whatever the DWARF writer refused, which is a bug here rather than a program this compiler is
1298/// behind on.
1299fn describe(
1300    assembled: &rucc_asm::Assembled,
1301    data: &rucc_object::Data,
1302    machine: &[rucc_mir::Func],
1303    origin: Origin<'_>,
1304    opts: &Options,
1305    target: &TargetInfo,
1306) -> Result<rucc_object::Info, String> {
1307    let rucc_asm::Assembled { text, lines, frames } = assembled;
1308    let rewrite = |path: &str| opts.prefix_map.debug.apply(path).into_owned();
1309    // The file table, built as the rows are walked rather than up front, because what belongs in it
1310    // is the files the code came from and not the files the preprocessor opened. A header that
1311    // contributed nothing but declarations is not one of them, and one that holds a definition is
1312    // in it twice over: once for the rows and once for the line the definition is declared on.
1313    let mut files: Vec<String> = Vec::new();
1314    let mut funcs = Vec::with_capacity(text.funcs.len());
1315    for ((extent, rows), built) in text.funcs.iter().zip(lines).zip(machine) {
1316        let mut out: Vec<rucc_debug::Row> = Vec::with_capacity(rows.len());
1317        for row in rows {
1318            if row.span.is_dummy() {
1319                continue;
1320            }
1321            let Some(at) = origin.map.presumed(row.span.lo) else {
1322                continue;
1323            };
1324            let which = interned(&mut files, rewrite(at.name));
1325            let place = rucc_debug::Row {
1326                at: row.at as u64,
1327                file: which,
1328                line: at.line,
1329                column: at.column,
1330            };
1331            // Two rows at one address is one row, and the first of the two wins. The only place it
1332            // happens is the front of a function, where the row the assembler writes for the
1333            // declaration and the row for the first instruction land on the same byte, which is
1334            // what a function this compiler built no prologue for looks like: two instructions
1335            // cannot start at one address, so nowhere else has the question. The declaration is the
1336            // better answer there because it is the answer gcc gives, which it gives because gcc
1337            // always builds a frame at -O0 and so always has a byte of prologue for the brace to be
1338            // about. A breakpoint on a function wants the line of the function rather than the line
1339            // of whatever its first statement happened to be.
1340            match out.last() {
1341                Some(last) if last.at == place.at => {}
1342                _ => out.push(place),
1343            }
1344        }
1345        // And the front of the function, for a function whose declaration had no span to give. The
1346        // assembler writes a row there from `Func::declared` and that is the usual way this is
1347        // covered, but a function that came from something other than a C source has no such span,
1348        // and the front of one is the one part of it no row would otherwise cover. A program
1349        // counter in there would get no answer at all rather than a slightly early one, and no
1350        // answer is the worse of the two for anybody reading a backtrace.
1351        if let Some(first) = out.first_mut() {
1352            first.at = 0;
1353        }
1354        // And what the function is, for the one this unit holds a definition of. A function the
1355        // walk above found and this did not is one whose name in the object is not the name the
1356        // declaration had, which `__asm__` on a declaration is the way to arrange, and one whose
1357        // signature could not be described. Both get rows and no entry, which leaves a debugger
1358        // where it is for every function today rather than anywhere worse.
1359        let known = origin.meaning.funcs.get(&extent.name);
1360        let decl = known.map(|known| rucc_debug::Place {
1361            file: interned(&mut files, rewrite(&known.file)),
1362            line: known.line,
1363        });
1364        // And where each of its locals is, for the ones the frame gave a slot. The back end hands
1365        // back the declaration each of them is and how far below the frame base it ended up, and
1366        // this is where a number turns back into a name, a type and a line, because this is the
1367        // last place the checker's declarations are still in hand.
1368        //
1369        // A parameter goes on the entry the signature already wrote for it rather than getting one
1370        // of its own, which is what the parameter numbers on the function are for. Two entries of
1371        // one name in one scope is a debugger's problem rather than a reader's.
1372        let mut sig = known.and_then(|known| known.sig.clone());
1373        let mut placed: Vec<(u32, i32)> = built.locals.clone();
1374        let mut spots = stretches(extent, rows, built, target);
1375        // And a local in the frame that shares its bytes and has no stretch at all, which still
1376        // gets its entry so that a debugger says it is not available rather than that there is no
1377        // such name. That is a function whose instructions were scheduled, where no stretch can be
1378        // given, and the whole of it is then somewhere the local may not be.
1379        for &decl in &built.sharing {
1380            if !spots.iter().any(|(at, _)| *at == decl) {
1381                spots.push((decl, Vec::new()));
1382            }
1383        }
1384        if let (Some(sig), Some(known)) = (sig.as_mut(), known) {
1385            for (param, decl) in sig.params.iter_mut().zip(&known.params) {
1386                let Some(decl) = *decl else { continue };
1387                if let Some(which) = placed.iter().position(|&(at, _)| at == decl) {
1388                    let at = rucc_debug::Held::Frame(i64::from(placed.remove(which).1));
1389                    param.spot = Some(rucc_debug::Spot::Always(at));
1390                    continue;
1391                }
1392                // Or the stretches, for a parameter the front end kept in a value rather than in
1393                // the frame, which is what a scalar parameter whose address is never taken is at
1394                // every optimization level including this one.
1395                let Some(which) = spots.iter().position(|(at, _)| *at == decl) else { continue };
1396                param.spot = Some(rucc_debug::Spot::Over(spots.remove(which).1));
1397            }
1398        }
1399        // Whatever is left, which is the locals that are not parameters, in the order the slots
1400        // were asked for. A number with nothing to look up is one whose declaration had no name,
1401        // which is a compound literal rather than anything the program can ask the value of.
1402        let mut locals = Vec::with_capacity(placed.len() + spots.len());
1403        // And which scope each of them was declared in, kept beside the list rather than on it,
1404        // because what goes on the entry is a place in this function's own table of scopes and that
1405        // table is not known until every local has been looked up.
1406        let mut wants: Vec<Option<usize>> = Vec::with_capacity(locals.capacity());
1407        for (decl, at) in placed {
1408            let Some(named) = origin.meaning.locals.get(&decl) else { continue };
1409            wants.push(named.scope);
1410            locals.push(rucc_debug::Local {
1411                name: named.name.clone(),
1412                ty: named.ty,
1413                decl: Some(rucc_debug::Place {
1414                    file: interned(&mut files, rewrite(&named.file)),
1415                    line: named.line,
1416                }),
1417                spot: rucc_debug::Spot::Always(rucc_debug::Held::Frame(i64::from(at))),
1418                scope: None,
1419            });
1420        }
1421        // And the ones with no slot at all, which are the locals the front end kept in a value.
1422        // Sorted by declaration, which is the order the program declared them in, so that what
1423        // comes out does not depend on the order the back end happened to hand registers out in.
1424        spots.sort_by_key(|(decl, _)| *decl);
1425        for (decl, spans) in spots {
1426            let Some(named) = origin.meaning.locals.get(&decl) else { continue };
1427            wants.push(named.scope);
1428            locals.push(rucc_debug::Local {
1429                name: named.name.clone(),
1430                ty: named.ty,
1431                decl: Some(rucc_debug::Place {
1432                    file: interned(&mut files, rewrite(&named.file)),
1433                    line: named.line,
1434                }),
1435                spot: rucc_debug::Spot::Over(spans),
1436                scope: None,
1437            });
1438        }
1439        // And the scopes the locals were declared in, which is where a name declared in an inner
1440        // block stops being one of the function's own. The numbers the walk over the tree handed out
1441        // are over the whole unit, and what goes on an entry is a place in this function's table, so
1442        // the two are joined here.
1443        let (scopes, at) = nests(&wants, &origin.meaning.scopes, extent, rows);
1444        for (local, want) in locals.iter_mut().zip(&wants) {
1445            local.scope = want.and_then(|want| at.get(&want).copied());
1446        }
1447        funcs.push(rucc_debug::Function {
1448            name: extent.name.clone(),
1449            len: extent.len as u64,
1450            rows: out,
1451            decl,
1452            sig,
1453            external: known.is_some_and(|known| known.external),
1454            locals,
1455            scopes,
1456        });
1457    }
1458    // And the file-scope variables, from the objects the back end laid out rather than from the
1459    // declarations, so that a name with an entry here is a name with a symbol to relocate against.
1460    // One the walk found and this did not is a `static` nothing read, and one this found and the
1461    // walk did not is a name the compiler made up rather than one the program wrote, a string
1462    // literal and a compound literal being the two: both are in the file and neither is a variable
1463    // anybody can ask the value of by name.
1464    let mut globals = Vec::new();
1465    for object in &data.objects {
1466        let Some(held) = origin.meaning.objects.get(&object.name) else { continue };
1467        globals.push(rucc_debug::Global {
1468            name: object.name.clone(),
1469            ty: held.ty,
1470            decl: Some(rucc_debug::Place {
1471                file: interned(&mut files, rewrite(&held.file)),
1472                line: held.line,
1473            }),
1474            external: held.external,
1475        });
1476    }
1477    let unit = rucc_debug::Unit {
1478        name: rewrite(origin.name),
1479        // A single dot when the process could not say where it was, which is a directory name every
1480        // debugger understands and which leaves a relative file name meaning what it already meant.
1481        dir: rewrite(opts.working_dir.as_deref().unwrap_or(".")),
1482        producer: format!("rucc {}", crate::VERSION),
1483        files,
1484        types: origin.meaning.types.clone(),
1485        funcs,
1486        globals,
1487        pointer: u8::try_from(target.pointer_width / 8).unwrap_or(8),
1488        // Whether a function can say where its frame base is, which it can when the build writes a
1489        // table that answers the question: the unwind table, or `.debug_frame` in its place. Read
1490        // off what was written rather than asked again, so the two cannot disagree about whether
1491        // the table a frame base is read through is there.
1492        frames: opts.unwinds() || frames.is_some(),
1493        mach_o: target.object_format == rucc_target::ObjectFormat::MachO,
1494    };
1495    let mut info = rucc_debug::write(&unit).map_err(|why| why.to_string())?;
1496    info.chunks.extend(frames.clone());
1497    Ok(info)
1498}
1499
1500/// Where each local the back end kept in a register is, as stretches of the function's addresses.
1501///
1502/// The back end names a stretch by the instruction at either end of it, because a machine
1503/// instruction has no length until something encodes it. This is where it gets one: the assembler
1504/// writes a row per instruction for the line table and the row says how far into the function the
1505/// instruction begins, so the row after it is where it ends. The last instruction of a function
1506/// ends where the function does.
1507///
1508/// Grouped by declaration on the way out, since one local is in one place over one stretch and
1509/// somewhere else over the next, and that is the shape the debugging information wants.
1510fn stretches(
1511    extent: &rucc_object::Extent,
1512    rows: &[rucc_asm::Row],
1513    built: &rucc_mir::Func,
1514    target: &TargetInfo,
1515) -> Vec<(u32, Vec<rucc_debug::Span>)> {
1516    // A target nobody has written a calling convention down for has no DWARF numbering either, so
1517    // there is no way to name the register a local is in and nothing to say.
1518    let (false, Some(regs)) = (built.kept.is_empty(), target.call_regs) else {
1519        return Vec::new();
1520    };
1521    let ends = ends(extent, rows);
1522    let mut bounds = vec![None; built.inst_count()];
1523    for (which, row) in rows.iter().enumerate() {
1524        let Some(inst) = row.inst else { continue };
1525        bounds[inst.index()] = Some((row.at as u64, ends[which]));
1526    }
1527    let mut spots: Vec<(u32, Vec<rucc_debug::Span>)> = Vec::new();
1528    for kept in &built.kept {
1529        let (Some((from, _)), Some((_, to))) = (bounds[kept.from.index()], bounds[kept.to.index()])
1530        else {
1531            continue;
1532        };
1533        if to <= from {
1534            continue;
1535        }
1536        let held = match kept.at {
1537            // A register is named by the number this target's DWARF numbering gives it, which is a
1538            // fact about the class and the register together rather than about either alone.
1539            rucc_mir::Where::Reg { reg, class } => match regs.dwarf(class, reg) {
1540                Some(number) => rucc_debug::Held::Reg(number),
1541                None => continue,
1542            },
1543            rucc_mir::Where::Frame(at) => rucc_debug::Held::Frame(i64::from(at)),
1544        };
1545        let span = rucc_debug::Span { from, len: to - from, held };
1546        match spots.iter_mut().find(|(decl, _)| *decl == kept.decl) {
1547            Some((_, spans)) => spans.push(span),
1548            None => spots.push((kept.decl, vec![span])),
1549        }
1550    }
1551    for (_, spans) in &mut spots {
1552        *spans = settle(std::mem::take(spans));
1553    }
1554    spots.retain(|(_, spans)| !spans.is_empty());
1555    spots
1556}
1557
1558/// Where the instruction each of a function's line table rows was written for ends.
1559///
1560/// The row after it, which is where the next instruction begins, and the end of the function for the
1561/// last one. The row after it at a different address rather than simply the row after it, because an
1562/// instruction that encodes to nothing leaves two rows on one byte and the one in front of it is not
1563/// where anything ends.
1564///
1565/// Backwards, because that is one pass rather than a search from each row for the next address that
1566/// differs, and a function the size of `sqlite3VdbeExec` has tens of thousands of rows.
1567fn ends(extent: &rucc_object::Extent, rows: &[rucc_asm::Row]) -> Vec<u64> {
1568    let mut out = vec![extent.len as u64; rows.len()];
1569    let mut next = extent.len as u64;
1570    for which in (0..rows.len()).rev() {
1571        let at = rows[which].at as u64;
1572        // The answer the row behind got, for a row sharing an address with the one in front of it,
1573        // since the two end in the same place and the one in front has already been asked.
1574        out[which] = match next > at {
1575            true => next,
1576            false => out.get(which + 1).copied().unwrap_or(extent.len as u64),
1577        };
1578        next = next.min(at);
1579    }
1580    out
1581}
1582
1583/// The scopes one function's locals were declared in, as the debug writer wants them, and which of
1584/// its entries each of the unit's scopes became.
1585///
1586/// Only the ones a local of this function is in, and their ancestors. The unit's table holds every
1587/// scope in the translation unit, and a function reaches its own by walking up from the locals the
1588/// back end handed over, which is both the filter and the answer to which function a scope belongs
1589/// to. A scope no local of this function is in is not this function's business even if the numbers
1590/// happen to sit next to each other.
1591///
1592/// The addresses come from the source. A scope is a run of source bytes, every row of the line table
1593/// says which source bytes its instruction was built for, and the rows already say where each
1594/// instruction is, so the addresses of a scope are the addresses of the instructions whose bytes are
1595/// inside it. Nothing had to be carried down the compiler for this, and the nesting comes out right
1596/// on its own: a scope's bytes hold the bytes of every scope inside it, so its addresses hold
1597/// theirs.
1598fn nests(
1599    wants: &[Option<usize>],
1600    scopes: &[crate::shapes::Scope],
1601    extent: &rucc_object::Extent,
1602    rows: &[rucc_asm::Row],
1603) -> (Vec<rucc_debug::Scope>, HashMap<usize, usize>) {
1604    let mut needed: Vec<usize> = Vec::new();
1605    for &want in wants {
1606        let mut up = want;
1607        while let Some(which) = up {
1608            if needed.contains(&which) {
1609                break;
1610            }
1611            needed.push(which);
1612            up = scopes.get(which).and_then(|scope| scope.parent);
1613        }
1614    }
1615    // In the order the unit wrote them, which puts a scope after the one it is inside, because that
1616    // is the order the writer wants and is what lets a parent be named by an entry already made.
1617    needed.sort_unstable();
1618    let at: HashMap<usize, usize> =
1619        needed.iter().enumerate().map(|(which, &scope)| (scope, which)).collect();
1620    let ends = ends(extent, rows);
1621    let out = needed
1622        .iter()
1623        .map(|&which| {
1624            let scope = &scopes[which];
1625            rucc_debug::Scope {
1626                parent: scope.parent.and_then(|parent| at.get(&parent).copied()),
1627                over: spread(scope.span, &ends, rows),
1628            }
1629        })
1630        .collect();
1631    (out, at)
1632}
1633
1634/// Which of a function's addresses were built for a run of its source bytes.
1635///
1636/// A row whose own bytes are inside the run is code the run asked for, and the addresses of a scope
1637/// are the addresses of every such row joined up. Two rows that meet or overlap are one stretch,
1638/// which is what almost all of a scope is: the rows of a block are next to each other unless
1639/// something moved them, and a block the back end split into pieces is exactly the case a list is
1640/// for.
1641fn spread(span: Span, ends: &[u64], rows: &[rucc_asm::Row]) -> Vec<rucc_debug::Reach> {
1642    let mut out: Vec<rucc_debug::Reach> = Vec::new();
1643    for (which, row) in rows.iter().enumerate() {
1644        if row.span.is_dummy() || row.span.lo < span.lo || row.span.hi > span.hi {
1645            continue;
1646        }
1647        let (from, to) = (row.at as u64, ends[which]);
1648        if to <= from {
1649            continue;
1650        }
1651        match out.last_mut() {
1652            Some(last) if last.from + last.len >= from => {
1653                last.len = to.saturating_sub(last.from).max(last.len);
1654            }
1655            _ => out.push(rucc_debug::Reach { from, len: to - from }),
1656        }
1657    }
1658    out
1659}
1660
1661/// One declaration's stretches with the disagreements taken out and the neighbours joined up.
1662///
1663/// Two stretches of one declaration can cover the same address. That is what a program that assigns
1664/// to a local from something already live looks like: both values are live across the assignment,
1665/// the old one because something else still reads it. A stretch never runs past the end of its
1666/// block, so two that overlap are in one block, where the addresses go the way the instructions
1667/// run, and one that starts inside the other starts where the declaration was given its value:
1668/// where the value was computed, or where the assignment was for a value it took from another
1669/// declaration. From there the declaration holds the new value and not the old one, so the one
1670/// that started first ends there.
1671///
1672/// What is still left is two stretches that start at the same address, which is two values both
1673/// live into a block with nothing here to say which of them the declaration holds. Where the two
1674/// agree the answer is the same either way and they become one stretch, and where they disagree the
1675/// address is left out, so a debugger says the variable is unavailable there rather than printing
1676/// whichever register this walk reached first. A wrong answer is worse than none.
1677fn settle(mut spans: Vec<rucc_debug::Span>) -> Vec<rucc_debug::Span> {
1678    spans.sort_by_key(|span| (span.from, span.len));
1679    for which in 0..spans.len() {
1680        let (from, end, held) =
1681            (spans[which].from, spans[which].from + spans[which].len, spans[which].held);
1682        let later = spans[which + 1..]
1683            .iter()
1684            .take_while(|later| later.from < end)
1685            .find(|later| later.from > from && later.held != held);
1686        if let Some(later) = later {
1687            spans[which].len = later.from - from;
1688        }
1689    }
1690    // Every address a stretch begins or ends at, which cuts the function into pieces no stretch is
1691    // partly over: a piece is inside a stretch or outside it and never half of each.
1692    let mut edges: Vec<u64> =
1693        spans.iter().flat_map(|span| [span.from, span.from + span.len]).collect();
1694    edges.sort_unstable();
1695    edges.dedup();
1696    let mut out: Vec<rucc_debug::Span> = Vec::new();
1697    let mut first = 0;
1698    for pair in edges.windows(2) {
1699        let (from, to) = (pair[0], pair[1]);
1700        // Nothing before this can cover this piece or any piece after it, since the pieces only
1701        // ever move forward. The list is in the order the stretches start in, so the walk below
1702        // stops at the first one that starts too late as well.
1703        while spans.get(first).is_some_and(|span| span.from + span.len <= from) {
1704            first += 1;
1705        }
1706        let mut held = None;
1707        let mut agreed = true;
1708        for span in &spans[first..] {
1709            if span.from >= to {
1710                break;
1711            }
1712            if span.from > from || span.from + span.len < to {
1713                continue;
1714            }
1715            match held {
1716                None => held = Some(span.held),
1717                Some(seen) => agreed &= seen == span.held,
1718            }
1719        }
1720        let (Some(held), true) = (held, agreed) else { continue };
1721        match out.last_mut() {
1722            Some(last) if last.from + last.len == from && last.held == held => {
1723                last.len += to - from
1724            }
1725            _ => out.push(rucc_debug::Span { from, len: to - from, held }),
1726        }
1727    }
1728    out
1729}
1730
1731/// Where a file name is in the table, putting it there if it is not there yet.
1732///
1733/// A walk rather than a map because the table holds the files one object's code came from, which is
1734/// a handful even for an amalgamation: everything the preprocessor opened and nothing was generated
1735/// out of stays out of it.
1736fn interned(files: &mut Vec<String>, name: String) -> usize {
1737    match files.iter().position(|have| *have == name) {
1738        Some(which) => which,
1739        None => {
1740            files.push(name);
1741            files.len() - 1
1742        }
1743    }
1744}
1745
1746/// What the command line decided about the file being written, in the words the assembler and the
1747/// object writer use.
1748///
1749/// Two spellings of the same facts, because the flags are the command line's and the answer the two
1750/// writers want is the object format's. The conversion is here rather than in either of them so
1751/// that the two output paths are handed the same thing and cannot come to disagree about what is
1752/// in a file.
1753///
1754/// The feature word is empty on a machine whose bits these are not. It is the x86 one, and a target
1755/// that wanted its control flow checked would want a property of its own with a key of its own, so
1756/// writing this one there would be recording something untrue rather than recording nothing.
1757fn output(opts: &Options, target: &TargetInfo) -> rucc_object::Output {
1758    let mut features = 0;
1759    if target.tuple.arch() == Arch::X86_64 {
1760        if opts.control.branch() {
1761            features |= rucc_object::Property::IBT;
1762        }
1763        if opts.control.ret() {
1764            features |= rucc_object::Property::SHSTK;
1765        }
1766    }
1767    rucc_object::Output {
1768        sections: rucc_object::Sections {
1769            functions: opts.function_sections,
1770            data: opts.data_sections,
1771        },
1772        property: rucc_object::Property { features },
1773    }
1774}
1775
1776/// What the object writer said, as the kind of news it is.
1777///
1778/// A format with no writer is a target this compiler is behind on, which is a program nobody can
1779/// compile today and not a mistake in the one being compiled. Anything else it refused is a bug
1780/// here, because every value it was handed came out of this compiler.
1781fn wrote(why: rucc_object::Error) -> Vec<Diagnostic> {
1782    match why {
1783        rucc_object::Error::Format { .. } => vec![unsupported(&why.to_string())],
1784        rucc_object::Error::Refused { .. } => vec![internal(&why.to_string())],
1785    }
1786}
1787
1788/// What the assembler said, as the kind of news it is.
1789///
1790/// Three of these are about a program and the rest are about this compiler. A thread-local
1791/// variable, an ifunc and a prologue the target's unwind table cannot describe are all valid C that
1792/// the back end does not build yet, and everything else the assembler refuses is something that
1793/// should never have reached it.
1794fn refused(why: rucc_asm::Error) -> Vec<Diagnostic> {
1795    match why {
1796        rucc_asm::Error::Thread { .. }
1797        | rucc_asm::Error::IFunc { .. }
1798        | rucc_asm::Error::Frame { .. } => {
1799            vec![unsupported(&why.to_string())]
1800        }
1801        _ => vec![internal(&why.to_string())],
1802    }
1803}
1804
1805/// A diagnostic about a program this compiler is not finished enough to compile.
1806///
1807/// Not an internal error, because nothing here is wrong: the program is valid C and the part of
1808/// the back end that would handle it has not been written. The note says so, so that a report
1809/// about one of these is filed against the milestone rather than as a miscompilation.
1810fn unsupported(message: &str) -> Diagnostic {
1811    unsupported_at(message, Span::DUMMY)
1812}
1813
1814/// The same, about somewhere in the file rather than about the file.
1815///
1816/// The note names the issue tracker rather than `spec/17-milestones.md`, which is a document
1817/// about the plan: a reader who follows it wants to know whether the construct in front of them
1818/// is already written down as work, and the milestone list does not answer that.
1819fn unsupported_at(message: &str, span: Span) -> Diagnostic {
1820    Diagnostic::error(message.to_owned(), span)
1821        .with_code("E0653")
1822        .note("this construct is not lowered yet, see https://github.com/tamnd/rucc/issues", span)
1823}
1824
1825/// A diagnostic about IR that was handed to us rather than built by us.
1826fn invalid(message: &str) -> Diagnostic {
1827    Diagnostic::error(message.to_owned(), Span::DUMMY).with_code("E0661")
1828}
1829
1830/// A diagnostic about this compiler rather than about the program it was given.
1831fn internal(message: &str) -> Diagnostic {
1832    Diagnostic::error(format!("internal error: {message}"), Span::DUMMY)
1833        .with_code("E0652")
1834        .note("this is a bug in rucc rather than in the program, please report it", Span::DUMMY)
1835}
1836
1837/// Every function's line, in the order they were compiled.
1838///
1839/// A function whose name has no place in the source, which only the tests and the IR reader
1840/// build, is reported against the file being compiled at line and column zero rather than left
1841/// out, since a report that is missing a function is one that reads as that function using
1842/// nothing.
1843fn su_file(stack: &StackUsage, sources: &SourceMap, file: &str) -> String {
1844    let mut out = String::new();
1845    for row in stack.rows() {
1846        let span = row.span();
1847        let at = (!span.is_dummy()).then(|| sources.presumed(span.lo)).flatten();
1848        let (name, line, column) = at.map_or((file, 0, 0), |at| (at.name, at.line, at.column));
1849        out.push_str(&row.line(name, line, column));
1850    }
1851    out
1852}
1853
1854/// A result that is nothing but one message, for the failures that happen before there is
1855/// anything to compile.
1856fn failure(message: String) -> Compiled {
1857    Compiled {
1858        artifact: Artifact::Nothing,
1859        messages: vec![format!("rucc: error: {message}")],
1860        errors: 1,
1861        fired: Fired::new(),
1862        pressure: Pressure::new(),
1863        lowerings: Lowerings::new(),
1864        dumps: Vec::new(),
1865        remarks: String::new(),
1866        deps: Vec::new(),
1867        temps: Temps::default(),
1868        timing: crate::trace::Timing::default(),
1869        stack_usage: String::new(),
1870    }
1871}
1872
1873#[cfg(test)]
1874mod tests {
1875    use rucc_session::{MemoryFileSystem, Std};
1876    use rucc_target::Triple;
1877
1878    use super::*;
1879
1880    fn options() -> Options {
1881        let mut opts = Options::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
1882        opts.emit = EmitKind::Tast;
1883        // The tests here read the code a function turns into, and a frame pointer in every one
1884        // of them is noise that says nothing about what each test is about.
1885        opts.frame_pointer = Some(false);
1886        opts
1887    }
1888
1889    fn run(opts: &Options, source: &str) -> Compiled {
1890        let mut fs = MemoryFileSystem::new();
1891        fs.insert("/main.c", source.to_owned().into_bytes());
1892        compile(opts, "/main.c", &fs)
1893    }
1894
1895    /// Options with the compiler's own headers on the search path and nothing else, which is
1896    /// what a freestanding compilation is. There is no file system underneath these tests,
1897    /// so a header that reached for one would fail to resolve and say so.
1898    fn freestanding() -> Options {
1899        let mut opts = options();
1900        opts.hosted = false;
1901        opts.search.push_system(rucc_session::runtime::DIR);
1902        opts
1903    }
1904
1905    /// The typed tree of a freestanding `source`, insisting that it compiled cleanly.
1906    fn shipped(source: &str) -> String {
1907        let result = run(&freestanding(), source);
1908        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
1909        result.text().to_owned()
1910    }
1911
1912    /// The typed tree of `source`, insisting that it compiled cleanly.
1913    fn tast(source: &str) -> String {
1914        let result = run(&options(), source);
1915        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
1916        result.text().to_owned()
1917    }
1918
1919    #[test]
1920    fn the_shipped_stdarg_declares_a_list_and_the_four_operators() {
1921        let text = shipped(concat!(
1922            "#include <stdarg.h>\n",
1923            "int sum(int n, ...) {\n",
1924            "  va_list ap, copy;\n",
1925            "  va_start(ap, n);\n",
1926            "  va_copy(copy, ap);\n",
1927            "  int total = va_arg(ap, int) + va_arg(copy, int);\n",
1928            "  va_end(ap);\n",
1929            "  va_end(copy);\n",
1930            "  return total;\n",
1931            "}\n",
1932        ));
1933        assert!(text.contains("va-start"), "{text}");
1934        assert!(text.contains("va-copy"), "{text}");
1935        assert!(text.contains("va-arg"), "{text}");
1936        assert!(text.contains("va-end"), "{text}");
1937    }
1938
1939    /// glibc includes `<stdarg.h>` this way from every header that declares a `vprintf`, and
1940    /// what it wants is the type without the four macro names. Answering the whole header
1941    /// would put `va_start` in the way of a program that has its own.
1942    #[test]
1943    fn stdarg_hands_out_the_type_alone_when_that_is_all_that_was_asked_for() {
1944        let text = shipped(concat!(
1945            "#define __need___va_list\n",
1946            "#include <stdarg.h>\n",
1947            "int vprint(const char *f, __gnuc_va_list ap);\n",
1948            "#ifdef va_start\n",
1949            "#error va_start should not be defined\n",
1950            "#endif\n",
1951            "#ifdef _VA_LIST_DEFINED\n",
1952            "#error va_list should not have been made\n",
1953            "#endif\n",
1954        ));
1955        assert!(text.contains("vprint"), "{text}");
1956    }
1957
1958    /// The same protocol on `<stddef.h>`, which glibc uses far more heavily: `<stdio.h>` asks
1959    /// for `size_t` and `NULL` and would be wrong to receive `offsetof` as well.
1960    #[test]
1961    fn stddef_answers_one_piece_at_a_time_and_the_next_request_still_gets_through() {
1962        let text = shipped(concat!(
1963            "#define __need_size_t\n",
1964            "#include <stddef.h>\n",
1965            "#ifdef offsetof\n",
1966            "#error offsetof should not be defined yet\n",
1967            "#endif\n",
1968            "#define __need_ptrdiff_t\n",
1969            "#include <stddef.h>\n",
1970            "#include <stddef.h>\n",
1971            "size_t a;\n",
1972            "ptrdiff_t b;\n",
1973            "wchar_t c;\n",
1974            "max_align_t d;\n",
1975            "void *e = NULL;\n",
1976            "struct P { int x; long y; };\n",
1977            "size_t f = offsetof(struct P, y);\n",
1978        ));
1979        assert!(text.contains("decl #0 a : unsigned long"), "{text}");
1980        assert!(text.contains("decl #1 b : long"), "{text}");
1981    }
1982
1983    #[test]
1984    fn the_shipped_limits_and_float_are_the_targets_own_answers() {
1985        let text = shipped(concat!(
1986            "#include <limits.h>\n",
1987            "#include <float.h>\n",
1988            "int bits = CHAR_BIT;\n",
1989            "long big = LONG_MAX;\n",
1990            "int low = INT_MIN;\n",
1991            "int radix = FLT_RADIX;\n",
1992            "int digits = DBL_MANT_DIG;\n",
1993        ));
1994        assert!(text.contains("const 8 : int"), "{text}");
1995        assert!(text.contains("const 9223372036854775807 : long"), "{text}");
1996        assert!(text.contains("const 2 : int"), "{text}");
1997        assert!(text.contains("const 53 : int"), "{text}");
1998    }
1999
2000    /// Freestanding, so there is no library header to chain to and `<stdint.h>` writes the
2001    /// whole set out itself. The widths are the ones the target picked, which is the only
2002    /// reason this header is the compiler's.
2003    #[test]
2004    fn the_shipped_stdint_writes_the_whole_set_when_there_is_no_library_to_defer_to() {
2005        let text = shipped(concat!(
2006            "#include <stdint.h>\n",
2007            "int64_t a = INT64_C(1);\n",
2008            "uint_least16_t b;\n",
2009            "intptr_t c;\n",
2010            "uintmax_t d = UINTMAX_MAX;\n",
2011            "int wide = sizeof(int_fast64_t);\n",
2012        ));
2013        assert!(text.contains("decl #0 a : long"), "{text}");
2014        assert!(text.contains("decl #1 b : unsigned short"), "{text}");
2015        assert!(text.contains("decl #2 c : long"), "{text}");
2016    }
2017
2018    /// `<mmintrin.h>` is the base of the vector header chain and the first one whose contents
2019    /// are C rather than declarations, so what this checks is that the C in it compiles: a
2020    /// header that is nothing but definitions fails as a whole or not at all.
2021    ///
2022    /// What the intrinsics answer is not checked here and cannot be, because the answer is
2023    /// only interesting next to another compiler's. Every intrinsic in the header was built
2024    /// and run against GCC 16.2.0 on the same inputs, at `-O0`, `-O1`, `-O2` and `-Os`, and
2025    /// gave the same bytes in all four. Carrying that comparison rather than repeating it by
2026    /// hand needs a facet in `tamnd/rucc-corpus` that works out the expected bytes itself,
2027    /// which is a second implementation of MMX and is `tamnd/rucc#1150`.
2028    #[test]
2029    fn the_shipped_mmintrin_defines_the_mmx_type_and_the_operations_over_it() {
2030        let text = shipped(concat!(
2031            "#include <mmintrin.h>\n",
2032            "__m64 add(__m64 a, __m64 b) { return _mm_add_pi16(a, b); }\n",
2033            "__m64 pack(__m64 a, __m64 b) { return _m_packsswb(a, b); }\n",
2034            "__m64 shift(__m64 a) { return _mm_srai_pi32(a, 3); }\n",
2035            "int low(__m64 a) { return _mm_cvtsi64_si32(a); }\n",
2036            "void done(void) { _mm_empty(); }\n",
2037        ));
2038        assert!(text.contains("add"), "{text}");
2039        assert!(text.contains("pack"), "{text}");
2040        assert!(text.contains("shift"), "{text}");
2041    }
2042
2043    /// The allocator beside the vector headers, which is the one piece of the family that is
2044    /// not a vector operation. It reaches for `<stddef.h>` and for three names out of the
2045    /// library, and the point of the test is that the reach resolves with nothing on the
2046    /// search path but the compiler's own directory.
2047    #[test]
2048    fn the_shipped_mm_malloc_asks_for_aligned_memory_and_gives_it_back() {
2049        let text = shipped(concat!(
2050            "#include <mm_malloc.h>\n",
2051            "void *get(void) { return _mm_malloc(64, 16); }\n",
2052            "void put(void *p) { _mm_free(p); }\n",
2053        ));
2054        assert!(text.contains("get"), "{text}");
2055        assert!(text.contains("put"), "{text}");
2056    }
2057
2058    /// `<xmmintrin.h>` is the next rung of the chain and pulls the other two in behind it, so a
2059    /// program that includes this one alone has to get all three. What the intrinsics answer is
2060    /// checked the same way `<mmintrin.h>` next door is checked and for the same reason: a
2061    /// hundred and forty eight lines of answers over nans, infinities, both zeros and values
2062    /// that do not fit in the integer they convert to, identical to GCC 16.2.0 at `-O0`, `-O1`,
2063    /// `-O2` and `-Os`.
2064    ///
2065    /// `_mm_rcp_ps` is the one answer in that run that is not identical, and is not meant to be.
2066    /// The instruction approximates a reciprocal and this computes one exactly, so the bits
2067    /// differ while both sit inside the relative error Intel documents, which the same program
2068    /// checks directly rather than by comparing bits.
2069    #[test]
2070    fn the_shipped_xmmintrin_defines_the_sse_type_and_the_operations_over_it() {
2071        let text = shipped(concat!(
2072            "#include <xmmintrin.h>\n",
2073            "__m128 add(__m128 a, __m128 b) { return _mm_add_ps(a, b); }\n",
2074            "__m128 one(__m128 a, __m128 b) { return _mm_max_ss(a, b); }\n",
2075            "__m128 mask(__m128 a, __m128 b) { return _mm_cmpnle_ps(a, b); }\n",
2076            "__m128 pick(__m128 a, __m128 b) { return _mm_shuffle_ps(a, b, _MM_SHUFFLE(0,1,2,3)); }\n",
2077            "int bits(__m128 a) { return _mm_movemask_ps(a); }\n",
2078            "int near(__m128 a) { return _mm_cvtss_si32(a); }\n",
2079            "__m128 wide(__m64 a) { return _mm_cvtpi16_ps(a); }\n",
2080            "void *room(void) { return _mm_malloc(64, 16); }\n",
2081            "void hint(const float *p) { _mm_prefetch(p, _MM_HINT_T0); _mm_sfence(); }\n",
2082        ));
2083        assert!(text.contains("add"), "{text}");
2084        assert!(text.contains("mask"), "{text}");
2085        assert!(text.contains("pick"), "{text}");
2086        assert!(text.contains("wide"), "{text}");
2087    }
2088
2089    /// The six names of gcc's header this one leaves out, each of which is an instruction whose
2090    /// answer no plain C reproduces exactly. Leaving them out is what turns a program that wants
2091    /// one into a diagnostic naming the function it called, rather than into a wrong answer, and
2092    /// this is what notices if one is ever quietly defined to something close.
2093    ///
2094    /// `tamnd/rucc#1157` is the square root, which brings the first four back.
2095    #[test]
2096    fn the_shipped_xmmintrin_leaves_out_the_names_that_need_an_instruction() {
2097        let text = rucc_session::runtime::header("xmmintrin.h").expect("xmmintrin.h is shipped");
2098        for absent in [
2099            "_mm_sqrt_ps",
2100            "_mm_sqrt_ss",
2101            "_mm_rsqrt_ps",
2102            "_mm_rsqrt_ss",
2103            "_mm_getcsr",
2104            "_mm_setcsr",
2105        ] {
2106            let defined = text.contains(&format!("{absent}("));
2107            assert!(!defined, "{absent} is defined and the header says it is not");
2108            assert!(text.contains(absent), "{absent} is absent and unexplained");
2109        }
2110    }
2111
2112    #[test]
2113    fn the_shipped_emmintrin_defines_both_sse2_types_and_the_operations_over_them() {
2114        let text = shipped(concat!(
2115            "#include <emmintrin.h>\n",
2116            "__m128i add(__m128i a, __m128i b) { return _mm_add_epi64(a, b); }\n",
2117            "__m128i wide(__m128i a, __m128i b) { return _mm_mul_epu32(a, b); }\n",
2118            "__m128i pick(__m128i a) { return _mm_shuffle_epi32(a, _MM_SHUFFLE(0,1,2,3)); }\n",
2119            "__m128i up(__m128i a) { return _mm_slli_epi64(a, 13); }\n",
2120            "__m128i down(__m128i a) { return _mm_srli_si128(a, 3); }\n",
2121            "__m128i pack(__m128i a, __m128i b) { return _mm_packus_epi16(a, b); }\n",
2122            "int bits(__m128i a) { return _mm_movemask_epi8(a); }\n",
2123            "__m128d sum(__m128d a, __m128d b) { return _mm_add_sd(a, b); }\n",
2124            "__m128d mask(__m128d a, __m128d b) { return _mm_cmpunord_pd(a, b); }\n",
2125            "__m128i near(__m128d a) { return _mm_cvtpd_epi32(a); }\n",
2126            "__m128d over(__m128 a) { return _mm_cvtps_pd(a); }\n",
2127            "__m128i half(__m64 a) { return _mm_movpi64_epi64(a); }\n",
2128            "__m128i grab(void const *p) { return _mm_loadu_si128(p); }\n",
2129            "void wall(void) { _mm_lfence(); _mm_mfence(); }\n",
2130        ));
2131        assert!(text.contains("wide"), "{text}");
2132        assert!(text.contains("pack"), "{text}");
2133        assert!(text.contains("near"), "{text}");
2134        assert!(text.contains("half"), "{text}");
2135    }
2136
2137    /// The umbrella header reaches the three underneath it. This is brotli's use of it, from
2138    /// `c/enc/matching_tag_mask.h`, which is the whole of what `tamnd/rucc#1236` was about: four
2139    /// SSE2 names that were already shipped and no way to get at them by the name gcc uses.
2140    #[test]
2141    fn the_shipped_immintrin_reaches_the_names_the_headers_under_it_define() {
2142        let text = shipped(concat!(
2143            "#include <immintrin.h>\n",
2144            "unsigned long long matching(unsigned char tag, unsigned char const *bucket) {\n",
2145            "  __m128i const want = _mm_set1_epi8((char)tag);\n",
2146            "  __m128i const chunk = _mm_loadu_si128((__m128i const *)(void const *)bucket);\n",
2147            "  __m128i const same = _mm_cmpeq_epi8(chunk, want);\n",
2148            "  return (unsigned long long)_mm_movemask_epi8(same);\n",
2149            "}\n",
2150            "__m64 narrow(__m64 a, __m64 b) { return _mm_add_pi32(a, b); }\n",
2151            "__m128 single(__m128 a, __m128 b) { return _mm_add_ps(a, b); }\n",
2152        ));
2153        assert!(text.contains("matching"), "{text}");
2154        assert!(text.contains("narrow"), "the MMX header is not reached: {text}");
2155        assert!(text.contains("single"), "the SSE header is not reached: {text}");
2156    }
2157
2158    /// The wider umbrella reaches everything the narrower one does, and the fence family with it.
2159    /// This is what mingw-w64's `<winnt.h>` includes and what it then uses, so a Windows program
2160    /// that has never heard of an intrinsic gets here through `<windows.h>`.
2161    #[test]
2162    fn the_shipped_x86intrin_reaches_the_fences_windows_headers_ask_it_for() {
2163        let text = shipped(concat!(
2164            "#include <x86intrin.h>\n",
2165            "void barriers(void *p) {\n",
2166            "  _mm_lfence();\n",
2167            "  _mm_sfence();\n",
2168            "  _mm_mfence();\n",
2169            "  _mm_pause();\n",
2170            "  _mm_clflush(p);\n",
2171            "}\n",
2172            "__m128i wide(__m128i a, __m128i b) { return _mm_add_epi32(a, b); }\n",
2173        ));
2174        assert!(text.contains("barriers"), "{text}");
2175        assert!(text.contains("wide"), "the SSE2 header is not reached: {text}");
2176    }
2177
2178    /// Including it twice is the same as including it once, and so is including it beside the
2179    /// header it reaches. A program that includes both spellings is the usual case rather than an
2180    /// odd one, because one of its own headers includes the umbrella and another includes SSE2.
2181    #[test]
2182    fn the_umbrella_and_the_header_under_it_can_both_be_included() {
2183        let text = shipped(concat!(
2184            "#include <immintrin.h>\n",
2185            "#include <emmintrin.h>\n",
2186            "#include <immintrin.h>\n",
2187            "#include <x86intrin.h>\n",
2188            "__m128i twice(__m128i a, __m128i b) { return _mm_add_epi32(a, b); }\n",
2189        ));
2190        assert!(text.contains("twice"), "{text}");
2191    }
2192
2193    /// The AArch64 intrinsics, as xxhash uses them in `XXH3_accumulate_512_neon`: a load, a
2194    /// reinterpretation, the halves of a vector and a widening multiply added into a sum.
2195    #[test]
2196    fn the_shipped_arm_neon_has_what_xxhash_asks_it_for() {
2197        let mut opts = freestanding();
2198        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2199        let source = concat!(
2200            "#include <arm_neon.h>\n",
2201            "uint64x2_t acc(uint64x2_t sum, const void *in, const void *key) {\n",
2202            "  uint8x16_t data = vld1q_u8((const uint8_t *)in);\n",
2203            "  uint8x16_t k = vld1q_u8((const uint8_t *)key);\n",
2204            "  uint64x2_t mixed = vreinterpretq_u64_u8(veorq_u8(data, k));\n",
2205            "  uint32x2_t lo = vmovn_u64(mixed);\n",
2206            "  uint32x2_t hi = vshrn_n_u64(mixed, 32);\n",
2207            "  return vmlal_u32(sum, lo, hi);\n",
2208            "}\n",
2209            "uint32x4x2_t pair(uint32x4_t a, uint32x4_t b) { return vzipq_u32(a, b); }\n",
2210            "uint32_t total(uint32x4_t a) { return vaddvq_u32(a); }\n",
2211        );
2212        let result = run(&opts, source);
2213        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
2214        assert!(result.text().contains("pair"), "{}", result.text());
2215        assert!(result.text().contains("total"), "{}", result.text());
2216    }
2217
2218    /// Off AArch64 the header says so, rather than failing on a type the target does not have.
2219    #[test]
2220    fn the_shipped_arm_neon_refuses_another_target() {
2221        let result = run(&freestanding(), "#include <arm_neon.h>\n");
2222        let said = result.messages.join("\n");
2223        assert!(said.contains("arm_neon.h is for AArch64"), "{said}");
2224    }
2225
2226    /// The float header omits four square roots and SSE2 omits the matching two, for the reason
2227    /// both headers write down. A later change that quietly defines one as an approximation
2228    /// would be a wrong answer nobody sees, so the absence is held in place here.
2229    #[test]
2230    fn the_shipped_emmintrin_leaves_out_the_two_square_roots() {
2231        let text = rucc_session::runtime::header("emmintrin.h").expect("emmintrin.h is shipped");
2232        for absent in ["_mm_sqrt_pd", "_mm_sqrt_sd"] {
2233            let defined = text.contains(&format!("{absent}("));
2234            assert!(!defined, "{absent} is defined and the header says it is not");
2235            assert!(text.contains(absent), "{absent} is absent and unexplained");
2236        }
2237    }
2238
2239    /// The CRC32C steps and the population counts are each one instruction, and the point of
2240    /// naming them rather than writing the loop in C is that instruction, so what is checked is
2241    /// the assembly and not only that the names resolve. `-msse4.2` is what PostgreSQL's
2242    /// configure passes, and it has to bring popcnt and crc32 with it the way gcc's does.
2243    #[test]
2244    fn the_shipped_nmmintrin_is_one_instruction_per_step_under_sse4_2() {
2245        let mut opts = freestanding();
2246        opts.emit = EmitKind::Asm;
2247        let mut choices = rucc_target::Choices::new();
2248        choices.read("sse4.2").expect("gcc knows sse4.2");
2249        opts.isa = choices.over(opts.isa);
2250        let source = concat!(
2251            "#include <nmmintrin.h>\n",
2252            "unsigned b(unsigned c, unsigned char v) { return _mm_crc32_u8(c, v); }\n",
2253            "unsigned w(unsigned c, unsigned short v) { return _mm_crc32_u16(c, v); }\n",
2254            "unsigned l(unsigned c, unsigned v) { return _mm_crc32_u32(c, v); }\n",
2255            "unsigned long long q(unsigned long long c, unsigned long long v) {\n",
2256            "  return _mm_crc32_u64(c, v);\n",
2257            "}\n",
2258            "int n(unsigned v) { return _mm_popcnt_u32(v); }\n",
2259            "long long m(unsigned long long v) { return _mm_popcnt_u64(v); }\n",
2260        );
2261        let result = run(&opts, source);
2262        assert_eq!(result.messages, Vec::<String>::new());
2263        let text = result.text();
2264        for step in ["crc32b", "crc32w", "crc32l", "crc32q", "popcntl", "popcntq"] {
2265            assert!(text.contains(step), "no {step} in:\n{text}");
2266        }
2267    }
2268
2269    /// Without the flag a function not built for the instruction cannot call it, which is gcc's
2270    /// refusal in gcc's words and the answer a configure probe reads.
2271    #[test]
2272    fn the_shipped_smmintrin_refuses_a_caller_not_built_for_the_checksum() {
2273        let result = run(
2274            &freestanding(),
2275            "#include <immintrin.h>\nunsigned f(unsigned c) { return _mm_crc32_u32(c, 1); }\n",
2276        );
2277        let said = result.messages.join("\n");
2278        let refusal = "inlining failed in call to 'always_inline' '_mm_crc32_u32': target \
2279                       specific option mismatch";
2280        assert!(said.contains(refusal), "{said}");
2281    }
2282
2283    /// A function carrying the attribute is built for the instruction whatever the unit is, which
2284    /// is how PostgreSQL writes its checksum: no flag, the attribute on the one function, and the
2285    /// step inlined into it as one instruction. PostgreSQL's probe writes the attribute only when
2286    /// `__has_attribute` says it is there, so that has to say so as well.
2287    #[test]
2288    fn a_function_built_for_sse4_2_calls_the_steps_without_a_flag() {
2289        let mut opts = freestanding();
2290        opts.emit = EmitKind::Asm;
2291        let source = concat!(
2292            "#include <nmmintrin.h>\n",
2293            "#if defined(__has_attribute) && __has_attribute (target)\n",
2294            "__attribute__((target(\"sse4.2\")))\n",
2295            "#endif\n",
2296            "unsigned l(unsigned c, unsigned v) { return _mm_crc32_u32(c, v); }\n",
2297            "__attribute__((target(\"popcnt\")))\n",
2298            "int n(unsigned v) { return _mm_popcnt_u32(v); }\n",
2299        );
2300        let result = run(&opts, source);
2301        assert_eq!(result.messages, Vec::<String>::new());
2302        let text = result.text();
2303        assert!(text.contains("crc32l") && text.contains("popcntl"), "{text}");
2304        let l = &text[text.find("\nl:").expect("l is defined")..];
2305        let l = &l[..l.find("ret").expect("l returns")];
2306        assert!(l.contains("crc32l") && !l.contains("call"), "{l}");
2307    }
2308
2309    /// PostgreSQL's two AVX-512 configure probes, as its `config/c-compiler.m4` writes them, with
2310    /// the functions made external so that each one is written out. Each compiles without a flag
2311    /// and every intrinsic in it is inlined into the one function, since a call left behind would
2312    /// be a call to a function built for an extension the caller may not have. Both were also run
2313    /// under Intel SDE as a Sapphire Rapids, with PostgreSQL's own files, and gave what gcc 16's
2314    /// build gives at `-O0` and `-O2`.
2315    #[test]
2316    fn the_shipped_avx512_headers_pass_postgres_probes() {
2317        let popcount = concat!(
2318            "#include <immintrin.h>\n",
2319            "#include <stdint.h>\n",
2320            "char buf[sizeof(__m512i)];\n",
2321            "#if defined(__has_attribute) && __has_attribute (target)\n",
2322            "__attribute__((target(\"avx512vpopcntdq,avx512bw\")))\n",
2323            "#endif\n",
2324            "int popcount_test(void)\n",
2325            "{\n",
2326            "  int64_t popcnt = 0;\n",
2327            "  __m512i accum = _mm512_setzero_si512();\n",
2328            "  __m512i val = _mm512_maskz_loadu_epi8((__mmask64) 0xf0f0f0f0f0f0f0f0, (const __m512i *) buf);\n",
2329            "  __m512i cnt = _mm512_popcnt_epi64(val);\n",
2330            "  accum = _mm512_add_epi64(accum, cnt);\n",
2331            "  popcnt = _mm512_reduce_add_epi64(accum);\n",
2332            "  return (int) popcnt;\n",
2333            "}\n",
2334        );
2335        let pclmul = concat!(
2336            "#include <immintrin.h>\n",
2337            "__m512i x;\n",
2338            "__m512i y;\n",
2339            "#if defined(__has_attribute) && __has_attribute (target)\n",
2340            "__attribute__((target(\"vpclmulqdq,avx512vl\")))\n",
2341            "#endif\n",
2342            "int avx512_pclmul_test(void)\n",
2343            "{\n",
2344            "  __m128i z;\n",
2345            "  x = _mm512_xor_si512(_mm512_zextsi128_si512(_mm_cvtsi32_si128(0)), x);\n",
2346            "  y = _mm512_clmulepi64_epi128(x, y, 0);\n",
2347            "  z = _mm_ternarylogic_epi64(\n",
2348            "            _mm512_castsi512_si128(y),\n",
2349            "            _mm512_extracti32x4_epi32(y, 1),\n",
2350            "            _mm512_extracti32x4_epi32(y, 2),\n",
2351            "            0x96);\n",
2352            "  return _mm_crc32_u64(0, _mm_extract_epi64(z, 0));\n",
2353            "}\n",
2354        );
2355        let checks: [(&str, &str, &[&str]); 2] = [
2356            (popcount, "popcount_test", &["kmovq", "vmovdqu8", "vpopcntq", "vpaddq", "vshufi64x2"]),
2357            (pclmul, "avx512_pclmul_test", &["vpxorq", "vpclmulqdq", "vpternlogq", "crc32q"]),
2358        ];
2359        for (source, name, wanted) in checks {
2360            for level in [rucc_session::OptLevel::O0, rucc_session::OptLevel::O2] {
2361                let mut opts = freestanding();
2362                opts.emit = EmitKind::Asm;
2363                opts.opt_level = level;
2364                let result = run(&opts, source);
2365                assert_eq!(result.messages, Vec::<String>::new(), "{name} at {level:?}");
2366                let text = result.text();
2367                let start = text.find(&format!("\n{name}:")).expect("the probe is written out");
2368                let body = &text[start..];
2369                let body = &body[..body.find(".size").unwrap_or(body.len())];
2370                for instruction in wanted {
2371                    assert!(
2372                        body.contains(instruction),
2373                        "no {instruction} at {level:?} in:\n{body}"
2374                    );
2375                }
2376                assert!(!body.contains("call"), "a call left behind at {level:?} in:\n{body}");
2377            }
2378        }
2379    }
2380
2381    /// A function not built for the extension cannot call one of its intrinsics, which is the
2382    /// refusal gcc gives in gcc's words, and what tells a probe without the attribute no.
2383    #[test]
2384    fn the_shipped_avx512_headers_refuse_a_caller_not_built_for_them() {
2385        let result = run(
2386            &freestanding(),
2387            "#include <immintrin.h>\n__m512i f(__m512i a) { return _mm512_popcnt_epi64(a); }\n",
2388        );
2389        let said = result.messages.join("\n");
2390        let refusal = "inlining failed in call to 'always_inline' '_mm512_popcnt_epi64': target \
2391                       specific option mismatch";
2392        assert!(said.contains(refusal), "{said}");
2393    }
2394
2395    /// Each of SSE3, SSSE3, SSE4.1 and SSE4.2 reached through `<immintrin.h>` from a function built
2396    /// for it, which is how a program that picks its path at run time writes them. Each is the
2397    /// instruction gcc writes, inlined, with its immediate a number in the text even when the
2398    /// caller wrote the immediate as the two flags `_MM_FROUND_*` are meant to be combined with.
2399    #[test]
2400    fn the_sse3_to_sse4_2_intrinsics_are_the_instructions_under_the_attribute() {
2401        let mut opts = freestanding();
2402        opts.emit = EmitKind::Asm;
2403        let source = concat!(
2404            "#include <immintrin.h>\n",
2405            "__attribute__((target(\"sse3\")))\n",
2406            "__m128i a(const __m128i *p) { return _mm_lddqu_si128(p); }\n",
2407            "__attribute__((target(\"sse3\")))\n",
2408            "__m128 b(__m128 x, __m128 y) { return _mm_hadd_ps(x, y); }\n",
2409            "__attribute__((target(\"ssse3\")))\n",
2410            "__m128i c(__m128i x, __m128i y) { return _mm_shuffle_epi8(_mm_abs_epi32(x), y); }\n",
2411            "__attribute__((target(\"ssse3\")))\n",
2412            "__m128i d(__m128i x, __m128i y) { return _mm_alignr_epi8(x, y, 5); }\n",
2413            "__attribute__((target(\"sse4.1\")))\n",
2414            "int e(__m128i x, __m128i y) {\n",
2415            "  return _mm_extract_epi32(_mm_min_epi32(_mm_mullo_epi32(x, y), y), 2);\n",
2416            "}\n",
2417            "__attribute__((target(\"sse4.1\")))\n",
2418            "__m128 f(__m128 x) { return _mm_round_ps(x, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); }\n",
2419            "__attribute__((target(\"sse4.1\")))\n",
2420            "__m128i g(__m128i x, __m128i y, __m128i m) { return _mm_blendv_epi8(x, y, m); }\n",
2421            "__attribute__((target(\"sse4.1\")))\n",
2422            "int h(__m128i x) { return _mm_testz_si128(x, x); }\n",
2423            "__attribute__((target(\"sse4.2\")))\n",
2424            "__m128i i(__m128i x, __m128i y) { return _mm_cmpgt_epi64(x, y); }\n",
2425            "__attribute__((target(\"sse4.2\")))\n",
2426            "int j(__m128i x, __m128i y) { return _mm_cmpistri(x, y, _SIDD_CMP_EQUAL_EACH); }\n",
2427        );
2428        let result = run(&opts, source);
2429        assert_eq!(result.messages, Vec::<String>::new());
2430        let text = result.text();
2431        for insn in [
2432            "lddqu",
2433            "haddps",
2434            "pabsd",
2435            "pshufb",
2436            "palignr $5,",
2437            "pmulld",
2438            "pminsd",
2439            "pextrd $2,",
2440            "roundps $8,",
2441            "pblendvb",
2442            "ptest",
2443            "pcmpgtq",
2444            "pcmpistri $8,",
2445        ] {
2446            assert!(text.contains(insn), "no {insn} in:\n{text}");
2447        }
2448        assert!(!text.contains("call"), "{text}");
2449    }
2450
2451    /// The same refusal as the checksum's for a caller built for less than the intrinsic wants,
2452    /// and `-mssse3` on the command line is enough for SSSE3 and SSE3 and not for SSE4.1.
2453    #[test]
2454    fn the_sse3_to_sse4_1_intrinsics_are_refused_a_caller_not_built_for_them() {
2455        let source = concat!(
2456            "#include <immintrin.h>\n",
2457            "__m128i f(__m128i x, __m128i y) { return _mm_shuffle_epi8(x, y); }\n",
2458        );
2459        let said = run(&freestanding(), source).messages.join("\n");
2460        let refusal = "inlining failed in call to 'always_inline' '_mm_shuffle_epi8': target \
2461                       specific option mismatch";
2462        assert!(said.contains(refusal), "{said}");
2463
2464        let mut opts = freestanding();
2465        let mut choices = rucc_target::Choices::new();
2466        choices.read("ssse3").expect("gcc knows ssse3");
2467        opts.isa = choices.over(opts.isa);
2468        let result =
2469            run(&opts, &format!("{source}__m128 g(__m128 x) {{ return _mm_movehdup_ps(x); }}\n"));
2470        assert_eq!(result.messages, Vec::<String>::new());
2471        let result = run(
2472            &opts,
2473            "#include <immintrin.h>\n__m128i h(__m128i x) { return _mm_abs_epi8(_mm_cvtepi8_epi32(x)); }\n",
2474        );
2475        let said = result.messages.join("\n");
2476        assert!(said.contains("'_mm_cvtepi8_epi32': target specific option mismatch"), "{said}");
2477    }
2478
2479    /// A string gcc does not know is refused in gcc's words, and AArch64's own strings are
2480    /// something x86-64 does not know either.
2481    #[test]
2482    fn a_target_string_gcc_does_not_know_is_refused() {
2483        for (string, name) in [("sse5", "sse5"), ("+crc", "+crc"), ("sse4.2,foo", "foo")] {
2484            let source =
2485                format!("__attribute__((target(\"{string}\"))) int f(void) {{ return 0; }}\n");
2486            let said = run(&freestanding(), &source).messages.join("\n");
2487            let wanted = format!("attribute 'target' argument '{name}' is unknown");
2488            assert!(said.contains(&wanted), "{string}: {said}");
2489        }
2490    }
2491
2492    /// Options for AArch64 Linux with the compiler's own headers, built for what `-march=`
2493    /// says, and written out as assembly.
2494    fn aarch64_asm(march: &str) -> Options {
2495        let mut opts = freestanding();
2496        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2497        opts.isa = rucc_target::Isa::aarch64_march(march);
2498        opts.emit = EmitKind::Asm;
2499        opts
2500    }
2501
2502    /// PostgreSQL's configure probe for the CRC32C intrinsics on AArch64, as
2503    /// `PGAC_ARMV8_CRC32C_INTRINSICS` in its `config/c-compiler.m4` writes it.
2504    const POSTGRES_ARMV8_PROBE: &str = concat!(
2505        "#include <arm_acle.h>\n",
2506        "unsigned int crc;\n",
2507        "int main(void) {\n",
2508        "  crc = __crc32cb(crc, 0);\n",
2509        "  crc = __crc32ch(crc, 0);\n",
2510        "  crc = __crc32cw(crc, 0);\n",
2511        "  crc = __crc32cd(crc, 0);\n",
2512        "  return crc == 0;\n",
2513        "}\n",
2514    );
2515
2516    /// tamnd/rucc#2006. Under `-march=armv8-a+crc`, and under `+crc+simd` and `armv8.1-a`, the
2517    /// probe compiles and every step is its one instruction, which is what the intrinsics are
2518    /// for. The CRC-32 ones beside them are the same with the other polynomial.
2519    #[test]
2520    fn the_shipped_arm_acle_is_one_instruction_per_step_under_crc() {
2521        for march in ["armv8-a+crc", "armv8-a+crc+simd", "armv8.1-a"] {
2522            let result = run(&aarch64_asm(march), POSTGRES_ARMV8_PROBE);
2523            assert_eq!(result.messages, Vec::<String>::new(), "{march}");
2524            let text = result.text();
2525            for step in ["crc32cb", "crc32ch", "crc32cw", "crc32cx"] {
2526                assert!(text.contains(step), "{march}: no {step} in:\n{text}");
2527            }
2528        }
2529        let source = concat!(
2530            "#include <arm_acle.h>\n",
2531            "#ifndef __ARM_FEATURE_CRC32\n",
2532            "#error \"no __ARM_FEATURE_CRC32\"\n",
2533            "#endif\n",
2534            "uint32_t b(uint32_t c, uint8_t v) { return __crc32b(c, v); }\n",
2535            "uint32_t h(uint32_t c, uint16_t v) { return __crc32h(c, v); }\n",
2536            "uint32_t w(uint32_t c, uint32_t v) { return __crc32w(c, v); }\n",
2537            "uint32_t d(uint32_t c, uint64_t v) { return __crc32d(c, v); }\n",
2538        );
2539        let result = run(&aarch64_asm("armv8-a+crc"), source);
2540        assert_eq!(result.messages, Vec::<String>::new());
2541        let text = result.text();
2542        for step in ["crc32b", "crc32h", "crc32w", "crc32x"] {
2543            assert!(text.contains(step), "no {step} in:\n{text}");
2544        }
2545    }
2546
2547    /// Plain Armv8-A is not built for the extension, so the probe is refused in gcc's words, which
2548    /// is the answer gcc gives it without a flag and the one that sends configure on to
2549    /// `-march=armv8-a+crc+simd`.
2550    #[test]
2551    fn the_shipped_arm_acle_refuses_a_caller_not_built_for_crc() {
2552        for march in ["armv8-a", "armv8-a+simd", "armv8.1-a+nocrc"] {
2553            let said = run(&aarch64_asm(march), POSTGRES_ARMV8_PROBE).messages.join("\n");
2554            let refusal = "inlining failed in call to 'always_inline' '__crc32cb': target \
2555                           specific option mismatch";
2556            assert!(said.contains(refusal), "{march}: {said}");
2557            assert!(said.contains("'-march=' with '+crc'"), "{march}: {said}");
2558        }
2559    }
2560
2561    /// A function carrying `target("+crc")` may call the intrinsics whatever the unit is built
2562    /// for, and each step is inlined into it as its instruction rather than left as a call, which
2563    /// is how a program that chooses its checksum at run time is written.
2564    #[test]
2565    fn a_function_built_for_crc_calls_the_steps_without_a_flag() {
2566        let source = concat!(
2567            "#include <arm_acle.h>\n",
2568            "#ifdef __ARM_FEATURE_CRC32\n",
2569            "#error \"plain armv8-a has no CRC32\"\n",
2570            "#endif\n",
2571            "__attribute__((target(\"+crc\")))\n",
2572            "uint32_t cd(uint32_t c, uint64_t v) { return __crc32cd(c, v); }\n",
2573            "__attribute__((target(\"arch=armv8.1-a\")))\n",
2574            "uint32_t cb(uint32_t c, uint8_t v) { return __crc32cb(c, v); }\n",
2575        );
2576        let result = run(&aarch64_asm("armv8-a"), source);
2577        assert_eq!(result.messages, Vec::<String>::new());
2578        let text = result.text();
2579        let cd = &text[text.find("\ncd:").expect("cd is defined")..];
2580        let cd = &cd[..cd.find("ret").expect("cd returns")];
2581        assert!(cd.contains("crc32cx") && !cd.contains("__crc32"), "{cd}");
2582        assert!(text.contains("crc32cb"), "{text}");
2583    }
2584
2585    /// The header is AArch64's alone, as gcc's is.
2586    #[test]
2587    fn the_shipped_arm_acle_refuses_another_target() {
2588        let result = run(&freestanding(), "#include <arm_acle.h>\n");
2589        let said = result.messages.join("\n");
2590        assert!(said.contains("arm_acle.h is for AArch64"), "{said}");
2591    }
2592
2593    /// AArch64 has strings of its own, which the x86-64 reading does not look at, so the
2594    /// checksum PostgreSQL builds there with `target("+crc")` still compiles.
2595    #[test]
2596    fn an_aarch64_target_string_is_still_accepted() {
2597        let mut opts = freestanding();
2598        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2599        let source = "__attribute__((target(\"+crc\"))) int f(void) { return 0; }\n";
2600        let result = run(&opts, source);
2601        assert_eq!(result.messages, Vec::<String>::new());
2602    }
2603
2604    #[test]
2605    fn the_three_formality_headers_still_have_to_work() {
2606        let text = shipped(concat!(
2607            "#include <stdbool.h>\n",
2608            "#include <stdalign.h>\n",
2609            "#include <iso646.h>\n",
2610            "#include <stdnoreturn.h>\n",
2611            "int t = true and not false;\n",
2612            "_Alignas(16) char buf[16];\n",
2613            "int a = alignof(long);\n",
2614        ));
2615        assert!(text.contains("decl #0 t : int"), "{text}");
2616        assert!(text.contains("const 8 : unsigned long"), "{text}");
2617    }
2618
2619    /// Including everything twice has to change nothing, because that is what happens in any
2620    /// program large enough to matter and a guard that is wrong shows up nowhere else.
2621    ///
2622    /// Stated as the two trees being the same rather than as a fact about what is in either
2623    /// one. A header that carries definitions puts them in the tree and moves everything
2624    /// after them along, so an assertion about where the program's own declaration landed is
2625    /// an assertion about how much `<mmintrin.h>` defines, which is not what is being asked.
2626    #[test]
2627    fn every_shipped_header_can_be_included_twice() {
2628        // This is x86-64, and `<arm_neon.h>` and `<arm_acle.h>` are for AArch64 only, so they are
2629        // held to the same thing by the AArch64 test below. `<intrin.h>`, `<setjmp.h>` and `<vadefs.h>` wrap the
2630        // library's, which they go on to find, and there is no library here.
2631        let once: String = rucc_session::runtime::names()
2632            .iter()
2633            .filter(|name| {
2634                !["arm_acle.h", "arm_neon.h", "intrin.h", "setjmp.h", "vadefs.h"].contains(*name)
2635            })
2636            .map(|name| format!("#include <{name}>\n"))
2637            .collect();
2638        let twice = once.repeat(2);
2639        assert_eq!(shipped(&format!("{once}int x;\n")), shipped(&format!("{twice}int x;\n")));
2640
2641        let mut opts = freestanding();
2642        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2643        let tree = |source: &str| {
2644            let result = run(&opts, source);
2645            assert_eq!(
2646                result.messages,
2647                Vec::<String>::new(),
2648                "expected this to compile:\n{source}"
2649            );
2650            result.text().to_owned()
2651        };
2652        let neon = "#include <arm_neon.h>\n#include <arm_acle.h>\n";
2653        assert_eq!(tree(&format!("{neon}int x;\n")), tree(&format!("{neon}{neon}int x;\n")));
2654    }
2655
2656    #[test]
2657    fn a_file_that_is_not_there_says_so_and_produces_nothing() {
2658        let fs = MemoryFileSystem::new();
2659        let result = compile(&options(), "/nope.c", &fs);
2660        assert!(result.failed());
2661        assert!(result.messages[0].contains("/nope.c"), "{:?}", result.messages);
2662        assert!(result.text().is_empty());
2663    }
2664
2665    #[test]
2666    fn an_object_comes_out_with_its_type_its_linkage_and_how_much_of_a_definition_it_is() {
2667        let text = tast("int x = 1;\n");
2668        let expected = "\
2669decl #0 x : int object external static defined
2670  init
2671    +0
2672      const 1 : int
2673";
2674        assert_eq!(text, expected);
2675    }
2676
2677    #[test]
2678    fn the_macros_are_expanded_before_anything_is_parsed() {
2679        // The whole pipeline in one line. The bound came out of a macro, so it was expanded,
2680        // converted from a preprocessing number to a constant of a type, parsed as an
2681        // expression, and folded to the number the array type carries.
2682        let text = tast("#define N 2\nint a[N];\n");
2683        assert!(text.starts_with("decl #0 a : int[2] object external static tentative"), "{text}");
2684    }
2685
2686    /// A pragma survives the preprocessor on purpose, since what one means is not its
2687    /// business, and nothing after it has a place for a `#` in the grammar. `pack` is the one
2688    /// the parser reads and every other line is walked past. Both spellings are here because
2689    /// they arrive by different routes and only one of them was ever on a line of its own in
2690    /// the source.
2691    #[test]
2692    fn a_pragma_is_not_a_declaration_and_the_parse_walks_past_the_ones_it_does_not_read() {
2693        let text = tast(concat!(
2694            "#pragma pack(4)\n",
2695            "struct s { int a; };\n",
2696            "#pragma pack()\n",
2697            "int b;\n",
2698            "_Pragma(\"GCC visibility push(default)\") int c;\n",
2699        ));
2700        assert!(text.contains("decl #0 b : int"), "{text}");
2701        assert!(text.contains("decl #1 c : int"), "{text}");
2702    }
2703
2704    /// The byte swaps and the bit counts of a constant are constants, which is how gcc has them, and
2705    /// every number here was read off gcc 16 on x86-64. `__builtin_clz(0)` and `__builtin_ctzll(0)`
2706    /// are undefined at run time and gcc folds them to the width.
2707    #[test]
2708    fn the_byte_swaps_and_the_bit_counts_of_a_constant_are_constants() {
2709        tast(concat!(
2710            "static const unsigned magic = __builtin_bswap32(0x11223344u);\n",
2711            "_Static_assert(__builtin_bswap16(0x1234) == 0x3412, \"16\");\n",
2712            "_Static_assert(__builtin_bswap32(0x11223344u) == 0x44332211u, \"32\");\n",
2713            "_Static_assert(__builtin_bswap64(0x0102030405060708ull) == 0x0807060504030201ull, \"64\");\n",
2714            "_Static_assert(__builtin_popcountll(-1ll) == 64 && __builtin_popcount(-1) == 32, \"ones\");\n",
2715            "_Static_assert(__builtin_parity(7) == 1 && __builtin_parity(3) == 0, \"parity\");\n",
2716            "_Static_assert(__builtin_ffs(0) == 0 && __builtin_ffs(8) == 4, \"ffs\");\n",
2717            "_Static_assert(__builtin_clrsb(0) == 31 && __builtin_clrsb(-1) == 31, \"clrsb\");\n",
2718            "_Static_assert(__builtin_clrsbl(1) == 62, \"clrsbl\");\n",
2719            "_Static_assert(__builtin_clz(1) == 31 && __builtin_clzl(1) == 63, \"clz\");\n",
2720            "_Static_assert(__builtin_ctzll(1ull << 40) == 40, \"ctz\");\n",
2721            "_Static_assert(__builtin_clz(0) == 32 && __builtin_ctzll(0) == 64, \"zero\");\n",
2722        ));
2723    }
2724
2725    /// Every number in these two tests was read off gcc 16 on x86-64 under `-std=gnu23`
2726    /// rather than reasoned about, which is why they are written as assertions the program
2727    /// makes about itself: a compilation with no messages is every one of them holding.
2728    ///
2729    /// This half is the attributes. `packed` takes the padding out, on the record or on one
2730    /// member, `aligned` raises and never lowers, and the two written together are the
2731    /// combination that packs and then aligns the whole thing.
2732    #[test]
2733    fn the_layout_attributes_move_the_members_and_the_record_the_way_gcc_lays_them_out() {
2734        tast(concat!(
2735            "struct A { char c; int i; } __attribute__((packed));\n",
2736            "_Static_assert(sizeof(struct A) == 5 && _Alignof(struct A) == 1, \"A\");\n",
2737            "_Static_assert(__builtin_offsetof(struct A, i) == 1, \"A.i\");\n",
2738            // `aligned` with nothing in the parentheses is the largest alignment the target
2739            // has, which gcc calls BIGGEST_ALIGNMENT and which is sixteen everywhere here.
2740            "struct B { char c; int i; } __attribute__((aligned));\n",
2741            "_Static_assert(sizeof(struct B) == 16 && _Alignof(struct B) == 16, \"B\");\n",
2742            "struct C { char c; int i __attribute__((packed)); };\n",
2743            "_Static_assert(sizeof(struct C) == 5 && _Alignof(struct C) == 1, \"C\");\n",
2744            "_Static_assert(__builtin_offsetof(struct C, i) == 1, \"C.i\");\n",
2745            "struct D { char c; int i; } __attribute__((packed, aligned(4)));\n",
2746            "_Static_assert(sizeof(struct D) == 8 && _Alignof(struct D) == 4, \"D\");\n",
2747            "_Static_assert(__builtin_offsetof(struct D, i) == 1, \"D.i\");\n",
2748            "struct E { char c; _Alignas(8) int i; };\n",
2749            "_Static_assert(sizeof(struct E) == 16 && _Alignof(struct E) == 8, \"E\");\n",
2750            "_Static_assert(__builtin_offsetof(struct E, i) == 8, \"E.i\");\n",
2751            "struct F { char c; int i __attribute__((aligned(8))); };\n",
2752            "_Static_assert(sizeof(struct F) == 16 && _Alignof(struct F) == 8, \"F\");\n",
2753            // Two the record already had, so the attribute asks for nothing new, and two
2754            // where four was already there, so the attribute is ignored rather than obeyed.
2755            "struct G { char c; short s; } __attribute__((aligned(2)));\n",
2756            "_Static_assert(sizeof(struct G) == 4 && _Alignof(struct G) == 2, \"G\");\n",
2757            "struct H { char c; int i; } __attribute__((aligned(2)));\n",
2758            "_Static_assert(sizeof(struct H) == 8 && _Alignof(struct H) == 4, \"H\");\n",
2759            // `packed` on a member takes the padding out in front of that member alone, so on
2760            // the first one it does nothing and on the second one it does all of it.
2761            "struct I { [[gnu::packed]] char c; int i; };\n",
2762            "_Static_assert(sizeof(struct I) == 8 && _Alignof(struct I) == 4, \"I\");\n",
2763            "struct J { char c; [[gnu::packed]] int i; };\n",
2764            "_Static_assert(sizeof(struct J) == 5 && _Alignof(struct J) == 1, \"J\");\n",
2765            "struct M { char c; int i : 5; int j : 20; } __attribute__((packed));\n",
2766            "_Static_assert(sizeof(struct M) == 5 && _Alignof(struct M) == 1, \"M\");\n",
2767            "struct N { char c; long long l; } __attribute__((aligned(32)));\n",
2768            "_Static_assert(sizeof(struct N) == 32 && _Alignof(struct N) == 32, \"N\");\n",
2769            "union L { char c; int i; } __attribute__((packed));\n",
2770            "_Static_assert(sizeof(union L) == 4 && _Alignof(union L) == 1, \"L\");\n",
2771            // The armoured spellings, which are the ones a system header writes, since a
2772            // program is entitled to a macro called `packed` and is not entitled to one called
2773            // `__packed__`. The two names are one attribute and the layout is the same one.
2774            "struct O { char c; int i; } __attribute__((__packed__));\n",
2775            "_Static_assert(sizeof(struct O) == 5 && _Alignof(struct O) == 1, \"O\");\n",
2776            "struct P { char c; int i; } __attribute__((__aligned__(8)));\n",
2777            "_Static_assert(sizeof(struct P) == 8 && _Alignof(struct P) == 8, \"P\");\n",
2778        ));
2779    }
2780
2781    /// The attribute that changes what a call means rather than what a record lays out.
2782    ///
2783    /// Both halves are here. A call hands a value to a parameter of the union type and the value
2784    /// goes into the member that takes it, which is a compound literal of the union and is the
2785    /// same object the GNU cast to a union builds. And a declaration written with a member's type
2786    /// declares the same function as one written with the union, which is what lets a pointer to
2787    /// either be assigned from the other, and is what gnulib's signature checks do.
2788    ///
2789    /// The `void *` member is last on purpose: the search takes a member whose type the value
2790    /// already has wherever it sits, and falls back to a pointer member that would take the value
2791    /// silently only when there is no such member, so `char *` reaches the catch-all past two
2792    /// members that are not it.
2793    #[test]
2794    fn a_transparent_union_takes_the_member_a_value_fits_and_is_declared_either_way() {
2795        let text = tast(concat!(
2796            "struct one { int x; };\n",
2797            "struct two { long y; };\n",
2798            "typedef union { struct one *a; struct two *b; void *any; }\n",
2799            "  __attribute__((__transparent_union__)) arg;\n",
2800            "int takes(arg v);\n",
2801            "int f(struct one *p, struct two *q, char *c) {\n",
2802            "  return takes(p) + takes(q) + takes(c) + takes(0);\n",
2803            "}\n",
2804            // The other half, which is about declarations and not about values.
2805            "int takes(struct one *p);\n",
2806            "int (*as_a_member)(struct one *) = takes;\n",
2807            "int (*as_the_union)(arg) = takes;\n",
2808        ));
2809        assert!(text.contains("compound-literal"), "{text}");
2810    }
2811
2812    /// The other place glibc writes it, which is the one that matters.
2813    ///
2814    /// `sys/socket.h` puts the attribute on the declarator of the typedef rather than after the
2815    /// closing brace, so a compiler that reads only the second position reads nothing at all of
2816    /// the eleven pointer union that `bind` and `connect` and five others take.
2817    #[test]
2818    fn the_attribute_on_the_declarator_of_a_typedef_is_the_one_glibc_writes() {
2819        let text = tast(concat!(
2820            "struct sockaddr { int family; };\n",
2821            "struct sockaddr_in { int family; int addr; };\n",
2822            "typedef union { struct sockaddr *plain; struct sockaddr_in *inet; }\n",
2823            "  addr_arg __attribute__((__transparent_union__));\n",
2824            "int bind_to(int fd, addr_arg where);\n",
2825            "int f(struct sockaddr_in *where) { return bind_to(0, where); }\n",
2826        ));
2827        assert!(text.contains("compound-literal"), "{text}");
2828    }
2829
2830    /// What the attribute promises has to be a promise this can keep, and is checked rather than
2831    /// believed.
2832    ///
2833    /// A union wider than its first member is not passed the way that member is, and a structure
2834    /// has no members that are alternatives to each other at all. gcc drops the attribute in both
2835    /// cases with a warning and compiles the program, because the type is still a perfectly good
2836    /// type and only the extra rule is gone.
2837    #[test]
2838    fn a_transparent_union_that_cannot_keep_the_promise_is_dropped_with_a_word_about_it() {
2839        let result = run(
2840            &options(),
2841            concat!(
2842                "union wider { int small; double large; } __attribute__((transparent_union));\n",
2843                "struct plain { int x; } __attribute__((transparent_union));\n",
2844            ),
2845        );
2846        assert_eq!(result.messages.len(), 2, "{:?}", result.messages);
2847        assert!(!result.failed(), "{:?}", result.messages);
2848        for message in &result.messages {
2849            assert!(message.contains("'transparent_union' attribute ignored"), "{message}");
2850        }
2851        assert!(result.messages[0].contains("first member"), "{:?}", result.messages);
2852        assert!(result.messages[1].contains("only a union"), "{:?}", result.messages);
2853    }
2854
2855    /// What an access to a packed member is allowed to assume about where it starts.
2856    ///
2857    /// C 6.2.8 gives an object of type `int` four byte alignment and `packed` takes it away: the
2858    /// member goes wherever the members in front of it ended, and an `int` one byte into a record
2859    /// is aligned to one. The number on the access has to say so, because it is what the back end
2860    /// picks instructions from and what judgement J1 of `spec/safe-memory/04-safety-model.md`
2861    /// tests at run time. Four on an address that is a multiple of one is the compiler refusing a
2862    /// program that is doing nothing wrong.
2863    #[test]
2864    fn an_access_to_a_packed_member_says_the_alignment_the_layout_left_it() {
2865        let packed = body(concat!(
2866            "struct P { char c; int v; } __attribute__((packed));\n",
2867            "int f(struct P *p) { return p->v; }\n",
2868        ));
2869        assert!(packed.contains("load.i32 %2, align 1,"), "{packed}");
2870        // The same record without the attribute, which is where the type's own answer is right.
2871        let plain = body(concat!(
2872            "struct P { char c; int v; };\n",
2873            "int f(struct P *p) { return p->v; }\n",
2874        ));
2875        assert!(plain.contains("load.i32 %2, align 4,"), "{plain}");
2876    }
2877
2878    /// The same, for the two ways of being further in than the member itself.
2879    ///
2880    /// An array member is stepped through rather than offset to, and a record member is offset to
2881    /// twice, and both have to carry the outer record's alignment with them. A step of a whole
2882    /// number of elements leaves what the element width and the address had in common, which for
2883    /// a one byte aligned base is one byte however wide the elements are.
2884    #[test]
2885    fn what_is_inside_a_packed_member_is_no_more_aligned_than_the_member_is() {
2886        let stepped = body(concat!(
2887            "struct P { char c; int v[4]; } __attribute__((packed));\n",
2888            "int f(struct P *p, int i) { return p->v[i]; }\n",
2889        ));
2890        assert!(stepped.contains(", align 1,"), "{stepped}");
2891        assert!(!stepped.contains(", align 4,"), "{stepped}");
2892        let nested = body(concat!(
2893            "struct Inner { int v; };\n",
2894            "struct P { char c; struct Inner in; } __attribute__((packed));\n",
2895            "int f(struct P *p) { return p->in.v; }\n",
2896        ));
2897        assert!(nested.contains(", align 1,"), "{nested}");
2898        assert!(!nested.contains(", align 4,"), "{nested}");
2899    }
2900
2901    /// The other way an access gets an alignment its type would not have given it, which is a
2902    /// typedef that lowered one.
2903    ///
2904    /// `aligned` raises on a declaration and replaces on a typedef, so `typedef aligned(1) U32
2905    /// unalign32` really is a four byte integer that may sit anywhere. Reading a word out of a
2906    /// buffer nothing aligned is what every compression library does and this is how they write
2907    /// it: zstd's `lib/common/mem.h` is four typedefs of exactly this shape and `MEM_read32` is
2908    /// `*(const unalign32 *)ptr`.
2909    ///
2910    /// What made this worth a test is where it went wrong. `__alignof__` was right the whole time,
2911    /// because that asks about the type and the type knew. The access was wrong, because the type
2912    /// of `*p` was worked out by resolving every typedef in `p`'s type rather than only the one on
2913    /// the pointer, so the thing being read came back as the `unsigned int` the typedef stands for
2914    /// and the alignment came off that. The number on the access is what judgement J1 tests, so
2915    /// the monitor refused fifty six of zstd's reads, all of them correct.
2916    #[test]
2917    fn an_access_through_a_typedef_that_lowered_its_alignment_says_the_one_the_typedef_asked_for() {
2918        let through = body(concat!(
2919            "typedef __attribute__((aligned(1))) unsigned int unalign32;\n",
2920            "unsigned int f(const void *p) { return *(const unalign32 *)p; }\n",
2921        ));
2922        assert!(through.contains("load.i32 %0, align 1,"), "{through}");
2923        // A subscript is `*(p + i)` and a member through an arrow is a dereference and then an
2924        // offset, so both read the pointee the same way and both have to come out the same.
2925        let stepped = body(concat!(
2926            "typedef __attribute__((aligned(1))) unsigned int unalign32;\n",
2927            "unsigned int f(unalign32 *p, int i) { return p[i]; }\n",
2928        ));
2929        assert!(stepped.contains(", align 1,"), "{stepped}");
2930        assert!(!stepped.contains(", align 4,"), "{stepped}");
2931        // And the same typedef without the attribute, which is where the type's own answer is the
2932        // right one and nothing above should have changed it.
2933        let plain = body(concat!(
2934            "typedef unsigned int word;\n",
2935            "unsigned int f(const void *p) { return *(const word *)p; }\n",
2936        ));
2937        assert!(plain.contains("load.i32 %0, align 4,"), "{plain}");
2938    }
2939
2940    /// The same thing where the object does not fit in a register, which is what `_mm_loadu_si128`
2941    /// is and is the reason the intrinsic header exists at all.
2942    ///
2943    /// `__m128i_u` is `__m128i` with `aligned(1)` on it and `_mm_loadu_si128` is one line,
2944    /// `return *(const __m128i_u *)__p;`. Two things had to be right for that to come out as the
2945    /// unaligned read it is. The dereference has to keep the typedef, which is what the test above
2946    /// covers, and then the return has to read the object as aligned as the object is rather than
2947    /// as aligned as the type it is being returned as: a vector comes back in registers on this
2948    /// ABI, so the sixteen bytes are read as two pieces of eight and the ABI's own alignment is
2949    /// what lays the two pieces out rather than what either read may claim.
2950    #[test]
2951    fn a_vector_read_through_a_typedef_that_lowered_its_alignment_comes_back_a_piece_at_a_time() {
2952        let prefix = concat!(
2953            "typedef long long v2di __attribute__((__vector_size__(16)));\n",
2954            "typedef long long v2di_u __attribute__((__vector_size__(16), __aligned__(1)));\n",
2955        );
2956        let loaded =
2957            body(&format!("{prefix}v2di f(const void *p) {{ return *(const v2di_u *)p; }}"));
2958        assert_eq!(loaded.matches("align 1\n").count(), 2, "{loaded}");
2959        assert!(!loaded.contains("align 16"), "{loaded}");
2960        // The store side, which travels as a copy into whatever the pointer names and so carries
2961        // one number for both ends of it.
2962        let stored = body(&format!("{prefix}void f(void *p, v2di b) {{ *(v2di_u *)p = b; }}"));
2963        assert!(stored.contains("memcpy %0, %3, size 16, align 1"), "{stored}");
2964        // And the aligned spelling of the same two, which is where sixteen is the right answer.
2965        let aligned =
2966            body(&format!("{prefix}v2di f(const void *p) {{ return *(const v2di *)p; }}"));
2967        assert!(aligned.contains("align 16"), "{aligned}");
2968    }
2969
2970    /// The same attribute on a declaration rather than on a type, which asks that this object or
2971    /// this function be at a multiple of that, and which is where a program that has to hand a
2972    /// buffer to hardware or keep two counters off one cache line writes it.
2973    ///
2974    /// A raise and never a lower, which is the one place it does not agree with `_Alignas`: below
2975    /// what the type already has, `_Alignas` is a constraint violation and this is ignored without
2976    /// a word. `__alignof__` of the object answers what the object got and not what its type has,
2977    /// because that is the question a program asking it is asking.
2978    #[test]
2979    fn the_aligned_attribute_on_a_declaration_raises_what_that_one_object_is_aligned_to() {
2980        tast(concat!(
2981            "int v __attribute__((aligned(64)));\n",
2982            "_Static_assert(__alignof__(v) == 64, \"v\");\n",
2983            // Written on the specifiers rather than after the declarator, which asks the same
2984            // thing and is the spelling a header is more likely to use.
2985            "__attribute__((aligned(32))) int w;\n",
2986            "_Static_assert(__alignof__(w) == 32, \"w\");\n",
2987            "[[gnu::aligned(16)]] int x;\n",
2988            "_Static_assert(__alignof__(x) == 16, \"x\");\n",
2989            // Two below the four an `int` already has, so nothing is asked for and nothing is
2990            // said, and the type still answers for the object.
2991            "int y __attribute__((aligned(2)));\n",
2992            "_Static_assert(__alignof__(y) == 4, \"y\");\n",
2993            // A local, which is the same question one scope down.
2994            "void f(void) { int a __attribute__((aligned(128)));\n",
2995            "_Static_assert(__alignof__(a) == 128, \"a\"); (void)a; }\n",
2996            // The type is untouched by any of it: `aligned` on a declaration says where that
2997            // declaration goes and says nothing about every other `int` in the program.
2998            "_Static_assert(__alignof__(int) == 4, \"int\");\n",
2999            // A function, which has no alignment of its own for this to be measured against and
3000            // takes whatever was asked for.
3001            "void g(void) __attribute__((aligned(256)));\n",
3002            "void g(void) {}\n",
3003            "_Static_assert(__alignof__(g) == 256, \"g\");\n",
3004        ));
3005    }
3006
3007    /// And what the object file says, which is the half that makes the answer above true. A
3008    /// function is at a fixed offset inside the text section, so it is at a multiple of two
3009    /// hundred and fifty six only if the section is at one too.
3010    #[test]
3011    fn what_a_declaration_asked_to_be_aligned_to_is_what_the_assembler_is_told() {
3012        let text = asm(concat!(
3013            "int v __attribute__((aligned(64)));\n",
3014            "void g(void) __attribute__((aligned(256)));\n",
3015            "void g(void) {}\n",
3016            "void plain(void) {}\n",
3017        ));
3018        assert!(text.contains("\t.p2align\t6\n\t.type\tv, @object\n"), "{text}");
3019        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "{text}");
3020        assert!(text.contains("\t.p2align\t4, 0x90\n\t.globl\tplain\n"), "{text}");
3021    }
3022
3023    /// The same question asked by the command line instead of by a declaration, which is
3024    /// `-falign-functions` and is what femtolisp's Makefile writes on every compile. The flag is a
3025    /// floor: a function that named a larger boundary itself keeps it, and one that named a
3026    /// smaller one is moved up, because the attribute is a requirement about one function and the
3027    /// flag is a preference about all of them.
3028    #[test]
3029    fn the_alignment_the_command_line_asked_of_every_function_is_a_floor_under_all_of_them() {
3030        let source = concat!(
3031            "void g(void) __attribute__((aligned(256)));\n",
3032            "void g(void) {}\n",
3033            "void small(void) __attribute__((aligned(4)));\n",
3034            "void small(void) {}\n",
3035            "void plain(void) {}\n",
3036        );
3037        let listing = |align: Option<u32>| {
3038            let mut opts = options();
3039            opts.emit = EmitKind::Asm;
3040            opts.align_functions = align;
3041            let result = run(&opts, source);
3042            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
3043            result.text().to_owned()
3044        };
3045
3046        let text = listing(Some(32));
3047        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "the larger one wins: {text}");
3048        assert!(text.contains("\t.p2align\t5, 0x90\n\t.globl\tsmall\n"), "{text}");
3049        assert!(text.contains("\t.p2align\t5, 0x90\n\t.globl\tplain\n"), "{text}");
3050
3051        // And the negative form, which asks for the smallest boundary the target has and is the
3052        // one spelling that takes a function below the sixteen bytes it would get anyway.
3053        let text = listing(Some(8));
3054        assert!(text.contains("\t.p2align\t3, 0x90\n\t.globl\tplain\n"), "{text}");
3055        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "{text}");
3056    }
3057
3058    /// And the one position where the attribute means something else. On a declaration it raises
3059    /// what that one object is aligned to, and on a typedef it says what the type is aligned to,
3060    /// which gcc lets it lower as well: `typedef int L __attribute__((aligned(2)))` really is an
3061    /// `int` at a multiple of two and a record with one in it really is smaller for it.
3062    ///
3063    /// The size is left alone, which is gcc's answer rather than an omission here. An aligned
3064    /// typedef whose alignment is larger than what it stands for keeps the size it stands for,
3065    /// and gcc refuses an array of one rather than padding the elements out to fit.
3066    #[test]
3067    fn an_aligned_typedef_says_what_an_object_of_it_is_aligned_to_and_may_lower_it() {
3068        tast(concat!(
3069            "typedef int L __attribute__((aligned(2)));\n",
3070            "_Static_assert(__alignof__(L) == 2, \"L\");\n",
3071            "_Static_assert(_Alignof(L) == 2, \"L alignof\");\n",
3072            // Below what an `int` has, which is the half a declaration cannot ask for.
3073            "_Static_assert(sizeof(L) == 4, \"L size\");\n",
3074            "struct T { char c; L x; };\n",
3075            "_Static_assert(sizeof(struct T) == 6, \"T\");\n",
3076            "_Static_assert(__builtin_offsetof(struct T, x) == 2, \"T.x\");\n",
3077            // And upwards, which is the ordinary direction and the one a header writes.
3078            "typedef int H __attribute__((aligned(16)));\n",
3079            "_Static_assert(__alignof__(H) == 16, \"H\");\n",
3080            "_Static_assert(sizeof(H) == 4, \"H size\");\n",
3081            "struct U { char c; H x; };\n",
3082            "_Static_assert(sizeof(struct U) == 32, \"U\");\n",
3083            "_Static_assert(__builtin_offsetof(struct U, x) == 16, \"U.x\");\n",
3084            // A typedef of a typedef, where the nearer one is the one the declaration was
3085            // written with and is the one that answers.
3086            "typedef L M __attribute__((aligned(8)));\n",
3087            "_Static_assert(__alignof__(M) == 8, \"M\");\n",
3088            // And one that asked for nothing, which still has whatever the one behind it asked
3089            // for because it is the same type spelled again.
3090            "typedef L N;\n",
3091            "_Static_assert(__alignof__(N) == 2, \"N\");\n",
3092            // The type it stands for is untouched by any of it.
3093            "_Static_assert(__alignof__(int) == 4, \"int\");\n",
3094        ));
3095        let text = asm(concat!(
3096            "typedef int L __attribute__((aligned(2)));\n",
3097            "typedef int H __attribute__((aligned(16)));\n",
3098            "L low;\n",
3099            "H high;\n",
3100        ));
3101        assert!(text.contains("\t.p2align\t1\n\t.type\tlow, @object\n"), "{text}");
3102        assert!(text.contains("\t.p2align\t4\n\t.type\thigh, @object\n"), "{text}");
3103    }
3104
3105    /// The attribute that builds a type rather than changing a layout. `vector_size(n)` says the
3106    /// declared type is `n` bytes of what was written, taken as lanes, and every operator over
3107    /// one is that operator over each lane.
3108    ///
3109    /// The size is in bytes and not in lanes, which is the part a reader gets backwards: sixteen
3110    /// of `int` is four lanes and sixteen of `char` is sixteen. A vector is aligned to its own
3111    /// size, which is what a machine that has the registers wants and what gcc gives one here.
3112    /// A conditional whose arms are two vectors of the same type, with a scalar condition that
3113    /// picks one of them whole. The typedef on one side and not the other does not make them two
3114    /// types, and a vector against a vector of a different lane is still refused.
3115    #[test]
3116    fn a_conditional_with_a_vector_in_each_arm_is_that_vector() {
3117        tast(concat!(
3118            "typedef long long v2di __attribute__((vector_size(16)));\n",
3119            "typedef long long m128i __attribute__((vector_size(16), may_alias));\n",
3120            "v2di id(v2di);\n",
3121            "v2di f(v2di x, v2di r, int c) { return c ? x : c > 1 ? id(x) : r; }\n",
3122            "m128i g(m128i x, v2di r, int c) { return c ? x : r; }\n",
3123            "_Static_assert(sizeof(1 ? (v2di){0} : (v2di){1}) == 16, \"whole\");\n",
3124        ));
3125        let refused = errors(concat!(
3126            "typedef long long v2di __attribute__((vector_size(16)));\n",
3127            "typedef int v4si __attribute__((vector_size(16)));\n",
3128            "v2di f(v2di x, v4si r, int c) { return c ? x : r; }\n",
3129        ));
3130        assert!(refused.iter().any(|m| m.contains("type mismatch in conditional")), "{refused:?}");
3131    }
3132
3133    #[test]
3134    fn the_vector_size_attribute_builds_a_type_of_lanes_and_measures_it_in_bytes() {
3135        tast(concat!(
3136            "typedef int __attribute__((vector_size(16))) v4si;\n",
3137            "_Static_assert(sizeof(v4si) == 16 && _Alignof(v4si) == 16, \"v4si\");\n",
3138            "typedef char __attribute__((vector_size(16))) v16qi;\n",
3139            "_Static_assert(sizeof(v16qi) == 16, \"v16qi\");\n",
3140            // One lane, which is a power of two and is a vector rather than the type it was
3141            // written on: the operators it takes are the vector's and not the scalar's.
3142            "typedef int __attribute__((vector_size(4))) v1si;\n",
3143            "_Static_assert(sizeof(v1si) == 4, \"v1si\");\n",
3144            // The armoured spelling and the bracket one, which are the same attribute.
3145            "typedef float __attribute__((__vector_size__(8))) v2sf;\n",
3146            "_Static_assert(sizeof(v2sf) == 8, \"v2sf\");\n",
3147            "typedef short [[gnu::vector_size(8)]] v4hi;\n",
3148            "_Static_assert(sizeof(v4hi) == 8, \"v4hi\");\n",
3149            // A lane is what a subscript answers with, and a vector is not a pointer: there is
3150            // nothing to decay and the lane type is the one the arithmetic happens in.
3151            "v4si g;\n",
3152            "_Static_assert(sizeof(g[0]) == 4, \"lane\");\n",
3153            "_Static_assert(sizeof(g + g) == 16, \"whole\");\n",
3154            // A scalar beside a vector stands for itself in every lane, so the answer is still
3155            // the vector and not the wider of the two types.
3156            "_Static_assert(sizeof(g + 1) == 16, \"broadcast\");\n",
3157            // An array of them, which is the ordinary way a program holds several.
3158            "_Static_assert(sizeof(v4si[3]) == 48, \"array\");\n",
3159        ));
3160    }
3161
3162    /// A whole vector written into an array of them, and a vector named by a type name rather
3163    /// than by a typedef.
3164    ///
3165    /// Both are the same question asked twice. A vector is filled like an array of its lanes when
3166    /// a list is written into it, so a braced element that is itself a vector has to be taken
3167    /// whole rather than started as the first lane, and the type of what was written is the only
3168    /// thing that says which was meant. And a type name is where a compound literal and a cast
3169    /// spell the type out, which a macro taking a lane type and a lane count does, so the
3170    /// attribute has to be read there and not only on a declaration.
3171    #[test]
3172    fn a_vector_is_written_whole_into_an_array_of_them_and_named_by_a_type_name() {
3173        tast(concat!(
3174            "typedef int __attribute__((vector_size(8))) v2si;\n",
3175            "v2si table[] = { (v2si){ 1, 2 }, (v2si){ 3, 4 } };\n",
3176            "_Static_assert(sizeof(table) == 16, \"two of them and not eight lanes\");\n",
3177            // The size written out rather than named, which is the spelling a macro expands to.
3178            "v2si written = (int __attribute__((vector_size(8)))){ 5, 6 };\n",
3179            "_Static_assert(sizeof((int __attribute__((vector_size(16)))){ 0 }) == 16, \"named\");\n",
3180            // A lane is still a lane, so a list of them fills the vector the way it always did
3181            // and the rule above did not turn brace elision off.
3182            "v2si lanes[2] = { 1, 2, 3, 4 };\n",
3183            "_Static_assert(sizeof(lanes) == 16, \"still elided\");\n",
3184        ));
3185    }
3186
3187    /// A lane written rather than read, and a shift whose two vectors are not the same type.
3188    ///
3189    /// Both are places where a vector is not the aggregate it looks like. A subscript of one is
3190    /// an lvalue because the vector it came from is an object, so a lane can be assigned to and
3191    /// has an address, and a qualifier written on the vector reaches every lane the way it does
3192    /// on an array. And a shift is the one lanewise operator whose sides are not brought to a
3193    /// single type, since the right side counts rather than computes.
3194    #[test]
3195    fn a_lane_is_assignable_and_a_shift_takes_a_count_of_its_own_lane() {
3196        let result = run(
3197            &options(),
3198            concat!(
3199                "typedef int __attribute__((vector_size(16))) v4si;\n",
3200                "typedef unsigned __attribute__((vector_size(16))) v4ui;\n",
3201                "void write(v4si *out, v4ui a, v4si b, int n) {\n",
3202                "  v4si v = { 1, 2, 3, 4 };\n",
3203                "  v[0] = n;\n",
3204                "  v[1] += n;\n",
3205                "  v[2]++;\n",
3206                "  *&v[3] = n;\n",
3207                // The count is signed and the value is not, which no other operator allows.
3208                "  v4ui shifted = a >> b;\n",
3209                "  shifted <<= b;\n",
3210                // A scalar stands in every lane on either side of a shift, which is the half
3211                // that looks wrong: the shape of the answer comes off the count here.
3212                "  *out = v + (v4si)shifted + (1 << b);\n",
3213                "}\n",
3214                // A qualifier on the vector is a qualifier on the lane, so there is nothing here
3215                // to write to.
3216                "void refused(const v4si c) {\n",
3217                "  c[0] = 1;\n",
3218                "}\n",
3219            ),
3220        );
3221        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
3222        assert!(result.messages[0].contains("assignment of read-only"), "{:?}", result.messages);
3223    }
3224
3225    /// The third layout attribute, and the one that moves nothing. It says the scalars in the
3226    /// record are stored in the byte order it names, so on a target whose order is the other one
3227    /// every load through a member swaps its bytes and so does every store. The record is the size
3228    /// and the alignment it would be without it and every member is where it would be, which is
3229    /// what gcc 16.2.0 does and what was measured before any of this was written.
3230    ///
3231    /// All four spellings are here because a header writes the armoured one, the attribute may be
3232    /// written in front of the body as well as behind it, and the C23 spelling in gcc's namespace
3233    /// is the same attribute a fourth way. The order the target already has is the fifth case and
3234    /// asks for nothing, since a program saying what would have happened anyway is entitled to be
3235    /// compiled as though it had said nothing.
3236    #[test]
3237    fn a_record_that_asks_for_the_other_byte_order_swaps_every_scalar_it_holds() {
3238        let read = "int f(struct s *p) { return p->i; }\n";
3239        let big = "struct s { int i; } __attribute__((scalar_storage_order(\"big-endian\")));\n";
3240        assert!(body(&format!("{big}{read}")).contains("bswap"), "{big}");
3241
3242        let armoured =
3243            "struct s { int i; } __attribute__((__scalar_storage_order__(\"big-endian\")));\n";
3244        assert!(body(&format!("{armoured}{read}")).contains("bswap"), "{armoured}");
3245
3246        let front = "struct __attribute__((scalar_storage_order(\"big-endian\"))) s { int i; };\n";
3247        assert!(body(&format!("{front}{read}")).contains("bswap"), "{front}");
3248
3249        let standard = "struct s { int i; } [[gnu::scalar_storage_order(\"big-endian\")]];\n";
3250        assert!(body(&format!("{standard}{read}")).contains("bswap"), "{standard}");
3251
3252        let same =
3253            "struct s { int i; } __attribute__((scalar_storage_order(\"little-endian\")));\n";
3254        assert!(!body(&format!("{same}{read}")).contains("bswap"), "{same}");
3255
3256        // A member one byte wide has only one order, and neither has the record itself.
3257        let byte = "struct s { char c; } __attribute__((scalar_storage_order(\"big-endian\")));\n";
3258        let source = format!("{byte}int f(struct s *p) {{ return p->c; }}\n");
3259        assert!(!body(&source).contains("bswap"), "{byte}");
3260
3261        tast(concat!(
3262            "struct s { int i; short h; char c; }",
3263            " __attribute__((scalar_storage_order(\"big-endian\")));\n",
3264            "_Static_assert(sizeof(struct s) == 8 && _Alignof(struct s) == 4, \"s\");\n",
3265            "_Static_assert(__builtin_offsetof(struct s, h) == 4, \"s.h\");\n",
3266            "_Static_assert(__builtin_offsetof(struct s, c) == 6, \"s.c\");\n",
3267        ));
3268    }
3269
3270    /// A bit-field in one of these records lies in the same bytes and is counted from the top of
3271    /// them rather than from the bottom. `execute/20230630-2.c` is the program that says so:
3272    /// `short i : 12` in front of four one bit fields holds 341 in the two bytes `15 5f`, so the
3273    /// twelve bits are the top twelve and reading them is a shift right by four rather than a mask
3274    /// alone. The plain record shifts nothing, since there the field is already at the bottom.
3275    #[test]
3276    fn a_bit_field_in_one_of_those_records_is_counted_from_the_top_of_its_bytes() {
3277        let members = "short i : 12; char c1 : 1; char c2 : 1; char c3 : 1; char c4 : 1;";
3278        let read = "int f(struct s *p) { return p->i; }\n";
3279        let plain = format!("struct s {{ {members} }};\n{read}");
3280        let reversed = format!(
3281            "struct s {{ {members} }} __attribute__((scalar_storage_order(\"big-endian\")));\n\
3282             {read}"
3283        );
3284        assert!(body(&plain).contains("shl"), "{}", body(&plain));
3285        assert!(!body(&plain).contains("bswap"), "{}", body(&plain));
3286        // The two loaded bytes the other way round and then the top twelve bits of them, which
3287        // is the arithmetic shift right on its own with nothing to move the field up to the top.
3288        let built = body(&reversed);
3289        assert!(built.contains("bswap"), "{built}");
3290        assert!(!built.contains("shl"), "{built}");
3291        assert!(built.contains("ashr"), "{built}");
3292    }
3293
3294    /// The one thing a program may not do with a member of one of these records. The bytes are
3295    /// there and they are the other way round, so a pointer to them is a pointer to a value of
3296    /// that type which is not the value the member holds. gcc refuses it in these words, and it
3297    /// refuses only the scalars: the address of a nested record or of an array member is an
3298    /// address of the bytes as they lie, and an access through it asks its own type which order
3299    /// it is in.
3300    #[test]
3301    fn the_address_of_a_scalar_stored_the_other_way_round_is_refused() {
3302        let opts = options();
3303        let record = "struct s { int i; int a[2]; struct in { int n; } w; }\n\
3304                      __attribute__((scalar_storage_order(\"big-endian\")));\n";
3305        let taken = format!("{record}int *f(struct s *p) {{ return &p->i; }}\n");
3306        assert_eq!(
3307            run(&opts, &taken).messages,
3308            ["/main.c:3:30: error: cannot take address of scalar with reverse storage order \
3309              [E0712]"]
3310        );
3311        let element = format!("{record}int *f(struct s *p) {{ return &p->a[0]; }}\n");
3312        let messages = run(&opts, &element).messages;
3313        assert!(messages[0].contains("[E0712]"), "{messages:?}");
3314
3315        let whole = format!("{record}int *f(struct s *p) {{ return (int *) &p->w; }}\n");
3316        assert_eq!(run(&opts, &whole).messages, Vec::<String>::new(), "{whole}");
3317    }
3318
3319    /// An argument that names neither order, which gcc answers with the two words it does take.
3320    /// A program that writes one of these is reading a wire format and would rather be told the
3321    /// spelling it got wrong than be handed a record laid out in the order it did not ask for.
3322    #[test]
3323    fn a_storage_order_that_names_neither_end_is_refused_with_the_two_words_that_are_taken() {
3324        let opts = options();
3325        let wrong = "struct s { int i; } __attribute__((scalar_storage_order(\"middle\")));\n";
3326        assert_eq!(
3327            run(&opts, wrong).messages,
3328            ["/main.c:1:36: error: 'scalar_storage_order' argument must be one of \"big-endian\" \
3329              or \"little-endian\" [E0688]"]
3330        );
3331        let bare = "struct s { int i; } __attribute__((scalar_storage_order));\n";
3332        let messages = run(&opts, bare).messages;
3333        assert!(messages[0].contains("[E0688]"), "{messages:?}");
3334    }
3335
3336    /// `ms_struct` and `gcc_struct` choose the bit-field rule for one record, the way gcc does on
3337    /// x86. `struct { unsigned m:3; char c; }` is eight bytes with the `char` at four under
3338    /// Microsoft's rule and four bytes with it at one under the Itanium rule, so each attribute
3339    /// gives on one target what the other target gives with no attribute at all. On AArch64 Linux
3340    /// gcc does not take either and neither does this, so there the record is what it always was.
3341    #[test]
3342    fn ms_struct_and_gcc_struct_choose_the_bit_field_rule_for_one_record() {
3343        let source = concat!(
3344            "struct __attribute__((ms_struct)) m { unsigned x : 3; char c; };\n",
3345            "struct g { unsigned x : 3; char c; } __attribute__((__gcc_struct__));\n",
3346            "struct p { unsigned x : 3; char c; };\n",
3347            "typedef struct { char c; int : 20; } __attribute__((gcc_struct)) u;\n",
3348            "_Static_assert(sizeof(struct m) == 8 && __builtin_offsetof(struct m, c) == 4, \"m\");\n",
3349            "_Static_assert(sizeof(struct g) == 4 && __builtin_offsetof(struct g, c) == 1, \"g\");\n",
3350            "_Static_assert(sizeof(u) == 4 && _Alignof(u) == 1, \"u\");\n",
3351        );
3352        let windows = "_Static_assert(sizeof(struct p) == 8, \"p\");\n";
3353        let linux = "_Static_assert(sizeof(struct p) == 4, \"p\");\n";
3354
3355        let mut opts = options();
3356        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
3357        let result = run(&opts, &format!("{source}{windows}"));
3358        assert_eq!(result.messages, Vec::<String>::new());
3359        let result = run(&options(), &format!("{source}{linux}"));
3360        assert_eq!(result.messages, Vec::<String>::new());
3361
3362        let mut opts = options();
3363        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3364        let ignored = concat!(
3365            "struct __attribute__((ms_struct)) m { unsigned x : 3; char c; };\n",
3366            "_Static_assert(sizeof(struct m) == 4, \"m\");\n",
3367        );
3368        assert_eq!(run(&opts, ignored).messages, Vec::<String>::new());
3369    }
3370
3371    /// The first of the two wins and the other is dropped with gcc's warning, in gcc's words.
3372    #[test]
3373    fn a_record_that_asks_for_both_rules_gets_the_first() {
3374        let source = concat!(
3375            "struct __attribute__((gcc_struct, ms_struct)) s { unsigned x : 3; char c; };\n",
3376            "_Static_assert(sizeof(struct s) == 4, \"s\");\n",
3377        );
3378        assert_eq!(
3379            run(&options(), source).messages,
3380            ["/main.c:1:35: warning: 'ms_struct' incompatible attribute ignored [E0746]"]
3381        );
3382        let same = "struct __attribute__((ms_struct, ms_struct)) s { unsigned x : 3; char c; };\n";
3383        assert_eq!(run(&options(), same).messages, Vec::<String>::new());
3384    }
3385
3386    /// The format archetypes gcc knows, which on Windows include the `ms_` ones mingw-w64's
3387    /// headers write through `__MINGW_PRINTF_FORMAT`. gcc on Linux does not know those and says
3388    /// so, and a name no target knows is warned about everywhere.
3389    #[test]
3390    fn format_takes_the_archetypes_gcc_knows_on_the_target() {
3391        let source = concat!(
3392            "int a(const char *, ...) __attribute__((format(ms_printf, 1, 2)));\n",
3393            "int b(const char *, ...) __attribute__((__format__(__gnu_printf__, 1, 2)));\n",
3394            "__attribute__((format(ms_scanf, 1, 2))) int c(const char *, ...);\n",
3395            "int d(const char *, ...) __attribute__((format(gnu_scanf, 1, 2)));\n",
3396            "unsigned long e(char *, unsigned long, const char *, const void *)\n",
3397            "    __attribute__((format(ms_strftime, 3, 0)));\n",
3398            "unsigned long f(char *, unsigned long, const char *, const void *)\n",
3399            "    __attribute__((format(gnu_strftime, 3, 0)));\n",
3400            "int g(const char *, ...) __attribute__((format(printf, 1, 2)));\n",
3401        );
3402        let mut opts = options();
3403        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
3404        assert_eq!(run(&opts, source).messages, Vec::<String>::new());
3405
3406        let linux = run(&options(), source).messages;
3407        assert_eq!(linux.len(), 3, "{linux:?}");
3408        assert!(
3409            linux[0]
3410                .ends_with("warning: 'ms_printf' is an unrecognized format function type [E0747]"),
3411            "{linux:?}"
3412        );
3413        assert!(linux[1].contains("'ms_scanf'"), "{linux:?}");
3414        assert!(linux[2].contains("'ms_strftime'"), "{linux:?}");
3415
3416        let bogus = "int h(const char *, ...) __attribute__((format(bogus, 1, 2)));\n";
3417        let messages = run(&opts, bogus).messages;
3418        assert_eq!(messages.len(), 1, "{messages:?}");
3419        assert!(messages[0].contains("'bogus' is an unrecognized format"), "{messages:?}");
3420    }
3421
3422    /// Where a bit-field goes, which packing decides and which is the part of all this that
3423    /// is not what the names suggest. A bit-field goes at the next free bit unless that would
3424    /// make it span more storage than its own type occupies, and then it moves to the next
3425    /// boundary of its alignment. Any packing at all takes that rule out, and `#pragma pack`
3426    /// counts even where it lowers nothing, which is the fourth and seventh cases here.
3427    ///
3428    /// Nothing in the language can be asked where a bit-field is, since `offsetof` refuses one
3429    /// and every size below comes out the same either way, so what is asked is the byte a read
3430    /// of the field loads from.
3431    #[test]
3432    fn packing_is_what_decides_whether_a_bit_field_may_straddle_its_own_storage() {
3433        // A `char` field after twelve bits, which will not straddle unpacked and does packed.
3434        assert_eq!(bit_field_byte("struct s { int x : 12; char y : 6; };"), 2);
3435        assert_eq!(
3436            bit_field_byte("struct s { int x : 12; char y : 6; } __attribute__((packed));"),
3437            1
3438        );
3439        assert_eq!(
3440            bit_field_byte("struct s { int x : 12; __attribute__((packed)) char y : 6; };"),
3441            1
3442        );
3443        assert_eq!(bit_field_byte("#pragma pack(4)\nstruct s { int x : 12; char y : 6; };"), 1);
3444        // A thirty bit field after a byte, which is the case the rule was written for.
3445        assert_eq!(bit_field_byte("struct s { char x; int y : 30; };"), 4);
3446        assert_eq!(bit_field_byte("struct s { char x; int y : 30; } __attribute__((packed));"), 1);
3447        // Four is what an `int` asked for anyway, so this caps nothing and still counts.
3448        assert_eq!(bit_field_byte("#pragma pack(4)\nstruct s { char x; int y : 30; };"), 1);
3449        assert_eq!(bit_field_byte("#pragma pack(2)\nstruct s { char x; int y : 30; };"), 1);
3450    }
3451
3452    /// The byte a read of `s.y` loads from, which is where the bit-field was placed.
3453    fn bit_field_byte(record: &str) -> u64 {
3454        let source = format!("{record}\nint f(struct s *p) {{ return p->y; }}\n");
3455        let body = body(&source);
3456        let Some((before, _)) = body.split_once("ptr_add") else { return 0 };
3457        let (_, constant) = before.rsplit_once("iconst.i64 ").expect("an offset constant");
3458        constant.lines().next().expect("a line").trim().parse().expect("a byte offset")
3459    }
3460
3461    /// An attribute in the middle of a specifier list, which is where a member usually carries
3462    /// one and which was read and then thrown away. The `[[...]]` spelling and whatever was
3463    /// written in front of the declaration are collected as the list is walked and the
3464    /// `__attribute__` spelling is put straight on the specifiers, and the two were assigned
3465    /// over each other rather than joined.
3466    #[test]
3467    fn an_attribute_among_the_specifiers_is_kept_beside_the_ones_written_in_front() {
3468        tast(concat!(
3469            "struct a { char c; __attribute__((aligned(8))) int i; };\n",
3470            "_Static_assert(sizeof(struct a) == 16 && _Alignof(struct a) == 8, \"a\");\n",
3471            "_Static_assert(__builtin_offsetof(struct a, i) == 8, \"a.i\");\n",
3472            "struct b { char c; __attribute__((packed)) int i; };\n",
3473            "_Static_assert(sizeof(struct b) == 5 && _Alignof(struct b) == 1, \"b\");\n",
3474            "_Static_assert(__builtin_offsetof(struct b, i) == 1, \"b.i\");\n",
3475            "typedef struct { char c; int i; } __attribute__((packed)) c;\n",
3476            "_Static_assert(sizeof(c) == 5 && _Alignof(c) == 1, \"c\");\n",
3477        ));
3478    }
3479
3480    /// The other half, which is `#pragma pack`. It caps a member's alignment where `packed`
3481    /// drops it, so `pack(2)` leaves a `short` where it was and moves an `int`, and it caps a
3482    /// member the program asked to align as well, which is where the two differ. It is read
3483    /// at the closing brace of the body, so a line written in the middle of one settles the
3484    /// whole record rather than the members after it, and `push` and `pop` nest.
3485    #[test]
3486    fn pragma_pack_caps_every_member_and_is_read_where_the_body_closes() {
3487        tast(concat!(
3488            "#pragma pack(1)\n",
3489            "struct A { char c; int i; };\n",
3490            "_Static_assert(sizeof(struct A) == 5 && _Alignof(struct A) == 1, \"A\");\n",
3491            "_Static_assert(__builtin_offsetof(struct A, i) == 1, \"A.i\");\n",
3492            "#pragma pack()\n",
3493            "struct B { char c; int i; };\n",
3494            "_Static_assert(sizeof(struct B) == 8 && _Alignof(struct B) == 4, \"B\");\n",
3495            "#pragma pack(2)\n",
3496            "struct C { char c; int i; double d; };\n",
3497            "_Static_assert(sizeof(struct C) == 14 && _Alignof(struct C) == 2, \"C\");\n",
3498            "_Static_assert(__builtin_offsetof(struct C, d) == 6, \"C.d\");\n",
3499            // A member the program aligned, which `pack` caps and `packed` would not.
3500            "struct K { char c; int i __attribute__((aligned(8))); };\n",
3501            "_Static_assert(sizeof(struct K) == 6 && _Alignof(struct K) == 2, \"K\");\n",
3502            "_Static_assert(__builtin_offsetof(struct K, i) == 2, \"K.i\");\n",
3503            // The record's own `aligned` is not a member's, so it is not capped.
3504            "struct J { char c; int i; } __attribute__((aligned(8)));\n",
3505            "_Static_assert(sizeof(struct J) == 8 && _Alignof(struct J) == 8, \"J\");\n",
3506            "#pragma pack()\n",
3507            "#pragma pack(push, 1)\n",
3508            "struct D { char c; short s; };\n",
3509            "_Static_assert(sizeof(struct D) == 3 && _Alignof(struct D) == 1, \"D\");\n",
3510            "#pragma pack(pop)\n",
3511            "struct E { char c; short s; };\n",
3512            "_Static_assert(sizeof(struct E) == 4 && _Alignof(struct E) == 2, \"E\");\n",
3513            // Written in the middle of a body, and it still settles the whole record.
3514            "struct H { char c;\n",
3515            "#pragma pack(1)\n",
3516            "  int i; };\n",
3517            "_Static_assert(sizeof(struct H) == 5 && _Alignof(struct H) == 1, \"H\");\n",
3518            "#pragma pack(1)\n",
3519            "struct I { char c;\n",
3520            "#pragma pack()\n",
3521            "  int i; };\n",
3522            "_Static_assert(sizeof(struct I) == 8 && _Alignof(struct I) == 4, \"I\");\n",
3523            "#pragma pack()\n",
3524            // Nested pushes, each one giving back what the one under it had.
3525            "#pragma pack(push, 8)\n",
3526            "#pragma pack(push, 1)\n",
3527            "struct P { char c; int i; };\n",
3528            "_Static_assert(sizeof(struct P) == 5 && _Alignof(struct P) == 1, \"P\");\n",
3529            "#pragma pack(pop)\n",
3530            "struct Q { char c; int i; };\n",
3531            "_Static_assert(sizeof(struct Q) == 8 && _Alignof(struct Q) == 4, \"Q\");\n",
3532            "#pragma pack(pop)\n",
3533            // A cap above what every member already asks for changes nothing at all.
3534            "#pragma pack(16)\n",
3535            "struct R { char c; int i; };\n",
3536            "_Static_assert(sizeof(struct R) == 8 && _Alignof(struct R) == 4, \"R\");\n",
3537            "#pragma pack()\n",
3538            "#pragma pack(1)\n",
3539            "struct S { char c; int i : 5; int j : 20; };\n",
3540            "_Static_assert(sizeof(struct S) == 5 && _Alignof(struct S) == 1, \"S\");\n",
3541            "union T { char c; int i; };\n",
3542            "_Static_assert(sizeof(union T) == 4 && _Alignof(union T) == 1, \"T\");\n",
3543            "#pragma pack()\n",
3544        ));
3545    }
3546
3547    /// A line the reader cannot make sense of is a warning and the line is dropped, which is
3548    /// what GCC does with one, and these are its words for each of them. The last line is the
3549    /// one nothing else would reach, since it stands after every record in the file.
3550    #[test]
3551    fn a_pack_line_that_is_not_one_is_reported_in_the_words_gcc_uses() {
3552        let result = run(
3553            &options(),
3554            concat!(
3555                "#pragma pack 4\n",
3556                "#pragma pack(pop)\n",
3557                "#pragma pack(3)\n",
3558                "#pragma pack(1) junk\n",
3559                "#pragma pack(push, 1\n",
3560                "#pragma pack(x)\n",
3561                // These two are well formed and say nothing. Zero is how a line asks for the
3562                // target's own alignments back without writing empty parentheses.
3563                "#pragma pack(0)\n",
3564                "#pragma pack(push)\n",
3565                "struct s { char c; int i; };\n",
3566                "#pragma pack(pop)\n",
3567                "#pragma pack(pop, foo)\n",
3568            ),
3569        );
3570        let expected = [
3571            "missing `(` after `#pragma pack` - ignored",
3572            "`#pragma pack (pop)` encountered without matching `#pragma pack (push)`",
3573            "alignment must be a small power of two, not 3",
3574            "junk at end of `#pragma pack`",
3575            "malformed `#pragma pack(push[, id][, <n>])` - ignored",
3576            "unknown action `x` for `#pragma pack` - ignored",
3577            "`#pragma pack(pop, foo)` encountered without matching `#pragma pack(push, foo)`",
3578        ];
3579        assert_eq!(result.messages.len(), expected.len(), "{:?}", result.messages);
3580        for (message, want) in result.messages.iter().zip(expected) {
3581            assert!(message.contains(want), "expected {want:?} in {message:?}");
3582        }
3583    }
3584
3585    /// A pragma line ends where the next line starts, so a macro that comes to nothing and was
3586    /// written first on that next line has to hand the line on rather than take it away. This
3587    /// is SQLite through mingw-w64's headers: `<stdarg.h>` leaves a `#pragma pack(pop)` behind
3588    /// it and `sqlite3.h` writes every declaration with `SQLITE_API` in front, which is empty.
3589    /// Without it the pragma swallows the declaration, the program is left without it, and the
3590    /// only thing said about any of it is that there was junk on the pragma.
3591    #[test]
3592    fn a_declaration_behind_an_empty_macro_is_not_eaten_by_the_pragma_above_it() {
3593        let result = run(
3594            &options(),
3595            concat!(
3596                "#pragma pack(push, 1)\n",
3597                "#pragma pack(pop)\n",
3598                "#define API\n",
3599                "API const char version[] = \"3.53.4\";\n",
3600                "const char *get(void) { return version; }\n",
3601            ),
3602        );
3603        assert!(result.messages.is_empty(), "{:?}", result.messages);
3604    }
3605
3606    /// The two typedef spellings of the 128 bit types. gcc offers them as keywords rather
3607    /// than as typedefs in a header, which is the only way a program that includes nothing at
3608    /// all can still use them, and Apple's `<mach/arm/_structs.h>` is one such program.
3609    #[test]
3610    fn the_wide_integer_answers_to_all_three_of_its_names() {
3611        let text = tast("__uint128_t a; __int128_t b; unsigned __int128 c;\n");
3612        assert!(text.contains("decl #0 a : unsigned __int128"), "{text}");
3613        assert!(text.contains("decl #1 b : __int128"), "{text}");
3614        assert!(text.contains("decl #2 c : unsigned __int128"), "{text}");
3615    }
3616
3617    #[test]
3618    fn every_conversion_the_language_performs_is_a_node_in_the_output() {
3619        // The point of a typed tree. The source has one operator and the output has the
3620        // widening that operator asked for, spelled out, so that nothing downstream has to
3621        // work out the conversion rules a second time.
3622        let text = tast("long f(int a, long b) { return a + b; }\n");
3623        assert!(text.contains("convert arithmetic"), "{text}");
3624    }
3625
3626    #[test]
3627    fn a_mistake_in_each_phase_reaches_the_caller_and_writes_no_tree() {
3628        for source in [
3629            "#error stop\n",
3630            "int f(void) { return 1 + ; }\n",
3631            "int f(void) { return undeclared; }\n",
3632        ] {
3633            let result = run(&options(), source);
3634            assert!(result.failed(), "expected this to fail:\n{source}");
3635            assert!(
3636                result.text().is_empty(),
3637                "a file that did not compile wrote a tree:\n{source}"
3638            );
3639        }
3640    }
3641
3642    #[test]
3643    fn one_undeclared_name_is_one_message_and_not_one_per_use() {
3644        // The poisoning rule from `spec/06-lexer-and-parser.md` section 6.8, seen from the
3645        // outside. Three uses of a name that was never declared, and the operators over them
3646        // say nothing at all.
3647        let result = run(&options(), "int f(void) { return nope + nope * nope; }\n");
3648        assert_eq!(result.errors, 1, "{:?}", result.messages);
3649    }
3650
3651    #[test]
3652    fn a_declaration_the_parser_skipped_does_not_become_an_undeclared_name_as_well() {
3653        // The reason the checking is skipped after a failed parse. The parser gave up on the
3654        // first line and there is no `x` in the tree, so a checker run over it would report
3655        // every use of `x` below as undeclared, which is a second message about one mistake.
3656        let result = run(&options(), "int x = ;\nint f(void) { return x; }\n");
3657        assert_eq!(result.errors, 1, "{:?}", result.messages);
3658    }
3659
3660    #[test]
3661    fn werror_turns_a_warning_into_an_error_in_the_count_and_in_the_word() {
3662        let source = "int f(void) { char c = 300; return c; }\n";
3663        let plain = run(&options(), source);
3664        assert_eq!(plain.errors, 0, "{:?}", plain.messages);
3665        assert_eq!(plain.messages.len(), 1, "expected a warning about the narrowed constant");
3666        assert!(!plain.text().is_empty(), "a warning is not a reason to write nothing");
3667
3668        let mut opts = options();
3669        opts.warnings_are_errors = true;
3670        let strict = run(&opts, source);
3671        assert!(strict.failed());
3672        assert!(strict.text().is_empty(), "and under -Werror it is a reason to write nothing");
3673        for message in &strict.messages {
3674            assert!(!message.contains("warning:"), "{message}");
3675        }
3676    }
3677
3678    #[test]
3679    fn w_drops_the_warning_before_werror_can_promote_it() {
3680        let source = "int f(void) { char c = 300; return c; }\n";
3681        let mut opts = options();
3682        opts.warnings = false;
3683        let quiet = run(&opts, source);
3684        assert_eq!(quiet.messages, Vec::<String>::new());
3685        assert_eq!(quiet.errors, 0);
3686        assert!(!quiet.text().is_empty(), "and the file still compiles");
3687
3688        // A build that passes both means it wants neither, and the order it wrote them in is not
3689        // something to make it think about.
3690        opts.warnings_are_errors = true;
3691        let both = run(&opts, source);
3692        assert_eq!(both.messages, Vec::<String>::new());
3693        assert!(!both.failed(), "-w -Werror is not an error about a warning nobody saw");
3694    }
3695
3696    #[test]
3697    fn the_dialect_reaches_the_keywords_and_the_checking() {
3698        // `typeof` is C23's and GNU's, so the same source is a declaration under one dialect
3699        // and a mistake under the other, which is the keyword table being built per dialect.
3700        let source = "typeof(1) x;\n";
3701        let mut opts = options();
3702        opts.std = Std::C23;
3703        opts.gnu_extensions = false;
3704        assert!(!run(&opts, source).failed(), "{:?}", run(&opts, source).messages);
3705
3706        opts.std = Std::C17;
3707        assert!(run(&opts, source).failed());
3708    }
3709
3710    #[test]
3711    fn asking_for_a_kind_that_is_not_written_yet_runs_the_front_end_and_writes_nothing() {
3712        let mut opts = options();
3713        opts.emit = EmitKind::Object;
3714        let result = run(&opts, "int x = 1;\n");
3715        assert!(!result.failed(), "{:?}", result.messages);
3716        assert!(result.text().is_empty());
3717        // And it still finds what the checking finds, so a later kind on a broken file is not
3718        // a silent success.
3719        assert!(run(&opts, "int f(void) { return undeclared; }\n").failed());
3720    }
3721
3722    /// The machine code of `source`, insisting that it compiled cleanly.
3723    fn mir(source: &str) -> String {
3724        let mut opts = options();
3725        opts.emit = EmitKind::MirFinal;
3726        let result = run(&opts, source);
3727        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3728        result.text().to_owned()
3729    }
3730
3731    /// The whole compiler in one assertion, which is what this emit kind is for.
3732    ///
3733    /// C in, machine instructions out, every register a real one and every frame offset a
3734    /// number. Everything between the two is checked somewhere else, one pass at a time. What is
3735    /// checked here is that the passes are joined up and that the driver runs them.
3736    #[test]
3737    fn a_function_goes_from_c_to_instructions_with_real_registers_in_them() {
3738        let text = mir("int add(int a, int b) { return a + b; }\n");
3739        assert!(text.starts_with("mfunc @add {"), "{text}");
3740        assert!(text.contains("x64.add_rr_32"), "{text}");
3741        assert!(text.contains("x64.ret"), "{text}");
3742        // A virtual register is what the allocator was there to remove, so one left in the
3743        // output is the difference between code and something that looks like code.
3744        assert!(!text.contains('%'), "{text}");
3745    }
3746
3747    /// A declaration has no body, so there is nothing to generate for one and nothing is.
3748    #[test]
3749    fn a_function_with_no_body_produces_no_machine_function() {
3750        let text = mir("int g(int);\nint f(int a) { return g(a); }\n");
3751        assert_eq!(text.matches("mfunc @").count(), 1, "{text}");
3752        assert!(text.contains("mfunc @f {"), "{text}");
3753        assert!(text.contains("x64.call"), "{text}");
3754    }
3755
3756    /// Two functions come out in the order the module holds them, which is source order.
3757    #[test]
3758    fn every_definition_in_the_file_is_generated_and_they_keep_their_order() {
3759        let text = mir("int a(int x) { return x; }\nint b(int x) { return x; }\n");
3760        let first = text.find("mfunc @a").expect("the first function");
3761        let second = text.find("mfunc @b").expect("the second function");
3762        assert!(first < second, "{text}");
3763    }
3764
3765    /// The target reaches the back end, so the same C is different instructions on Windows.
3766    #[test]
3767    fn the_target_decides_which_convention_the_generated_code_follows() {
3768        let mut opts = options();
3769        opts.emit = EmitKind::MirFinal;
3770        let linux = run(&opts, "int f(int a) { return a; }\n").text().to_owned();
3771        assert!(linux.contains("$rdi"), "{linux}");
3772
3773        opts.target = "x86_64-pc-windows-msvc".parse::<Triple>().unwrap();
3774        let windows = run(&opts, "int f(int a) { return a; }\n").text().to_owned();
3775        assert!(windows.contains("$rcx"), "{windows}");
3776        assert!(!windows.contains("$rdi"), "{windows}");
3777    }
3778
3779    /// And it reaches the front end, where it decides what an anonymous member is.
3780    ///
3781    /// This is the shape `<objidl.h>` writes and the Windows headers are full of: the union inside
3782    /// `STGMEDIUM` closes with `} DUMMYUNIONNAME;`, and the macro expands to nothing unless the
3783    /// program defined `NONAMELESSUNION`, so what is left is a union with a tag and no name. On a
3784    /// Windows target that is an anonymous member, and reading it as a declaration of nothing
3785    /// drops it, which loses the names and the eight bytes the member takes up both.
3786    #[test]
3787    fn a_tagged_member_with_no_name_is_a_member_on_windows_and_nothing_on_linux() {
3788        let source = concat!(
3789            "struct S { union U { int i; void *p; }; unsigned long tymed; };\n",
3790            "int size(void) { return sizeof(struct S); }\n",
3791            "int f(struct S *s) { s->i = 1; return s->i; }\n",
3792        );
3793
3794        let mut opts = options();
3795        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
3796        let windows = run(&opts, source);
3797        assert!(windows.messages.is_empty(), "{:?}", windows.messages);
3798
3799        let linux = run(&options(), source);
3800        assert_eq!(linux.messages.len(), 3, "{:?}", linux.messages);
3801        assert!(linux.messages[0].contains("does not declare anything"), "{:?}", linux.messages);
3802
3803        // And the flag answers for either of them, so a program built for Linux against a header
3804        // written for Windows can be read the way the header meant it.
3805        let mut opts = options();
3806        opts.ms_extensions = Some(true);
3807        let asked = run(&opts, source);
3808        assert!(asked.messages.is_empty(), "{:?}", asked.messages);
3809    }
3810
3811    /// A target with no back end says so rather than generating something for another machine.
3812    #[test]
3813    fn a_target_this_has_no_back_end_for_is_reported_rather_than_generated() {
3814        let mut opts = options();
3815        opts.emit = EmitKind::MirFinal;
3816        opts.target = "riscv64-unknown-linux-gnu".parse::<Triple>().unwrap();
3817        let result = run(&opts, "int f(int a) { return a; }\n");
3818        assert!(result.failed());
3819        assert!(result.messages[0].contains("no back end for riscv64"), "{:?}", result.messages);
3820        assert!(result.text().is_empty());
3821    }
3822
3823    /// AArch64 is written as its own assembly, with a function that calls keeping its return
3824    /// address in the frame record.
3825    #[test]
3826    fn an_aarch64_target_is_written_as_aarch64_assembly() {
3827        let mut opts = options();
3828        opts.emit = EmitKind::Asm;
3829        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3830        let source = "int g(int);\nint f(int a, int b) { return g(a) + b; }\n";
3831        let result = run(&opts, source);
3832        assert!(!result.failed(), "{:?}", result.messages);
3833        let text = result.text();
3834        for line in ["stp x29, x30, [sp, #-16]!", "mov x29, sp", "bl g", "ldp x29, x30, [sp], #16"]
3835        {
3836            assert!(text.contains(line), "{line} is not in\n{text}");
3837        }
3838        assert!(!text.contains('%'), "{text}");
3839    }
3840
3841    /// An object for AArch64, which is the listing read back by the assembler. The same object
3842    /// with debug information is refused rather than written without its line table.
3843    #[test]
3844    fn an_aarch64_target_reaches_an_object_file() {
3845        let mut opts = options();
3846        opts.emit = EmitKind::Object;
3847        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3848        let source = concat!(
3849            "int g(int);\n",
3850            "int table[4] = {1, 2, 3, 4};\n",
3851            "int f(int a, int b) { return g(a) + table[b & 3]; }\n",
3852        );
3853        let result = run(&opts, source);
3854        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3855        let bytes = match result.artifact {
3856            Artifact::Object { bytes, defines } => {
3857                assert_eq!(defines, ["f", "table"]);
3858                bytes
3859            }
3860            other => panic!("expected an object, got {other:?}"),
3861        };
3862        assert_eq!(&bytes[..4], b"\x7fELF");
3863        assert_eq!(&bytes[18..20], &183u16.to_le_bytes(), "EM_AARCH64");
3864
3865        // And with debug information, which the listing path builds from a label in front of
3866        // every instruction rather than refusing.
3867        opts.debug_info = true;
3868        let result = run(&opts, source);
3869        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3870        let bytes = match result.artifact {
3871            Artifact::Object { bytes, .. } => bytes,
3872            other => panic!("expected an object, got {other:?}"),
3873        };
3874        let has = |name: &[u8]| bytes.windows(name.len()).any(|at| at == name);
3875        assert!(has(b".debug_line\0") && has(b".debug_info\0"));
3876        assert!(!has(b"rucc_row"), "a row label reached the symbol table");
3877    }
3878
3879    /// Under `-fexceptions` a `cleanup` handler on AArch64 gets its landing pad, where it used to be
3880    /// refused. The object is the listing read back, so this is the reader keeping the personality
3881    /// routine and the call site table the listing names.
3882    #[test]
3883    fn an_aarch64_object_keeps_the_landing_pads_of_its_cleanups() {
3884        let mut opts = options();
3885        opts.emit = EmitKind::Object;
3886        opts.exceptions = true;
3887        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3888        let source = concat!(
3889            "void g(void);\n",
3890            "void done(int *p);\n",
3891            "void f(void) { int a __attribute__((cleanup(done))) = 1; g(); }\n",
3892        );
3893        let result = run(&opts, source);
3894        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3895        let Artifact::Object { bytes, .. } = result.artifact else { panic!("an object") };
3896        let has = |name: &[u8]| bytes.windows(name.len()).any(|at| at == name);
3897        assert!(has(b".gcc_except_table\0"), "no call site table");
3898        assert!(has(b"zPLR\0"), "no header naming the personality routine");
3899        assert!(has(b"DW.ref.__gcc_personality_v0\0"));
3900    }
3901
3902    /// gcc's AArch64 vector type names are there before any header, which glibc's `<math.h>`
3903    /// needs, a declaration can still hide one, and on x86-64 they are ordinary identifiers.
3904    #[test]
3905    fn the_aarch64_vector_type_names_are_declared_on_that_target_and_nowhere_else() {
3906        let mut opts = options();
3907        opts.emit = EmitKind::Asm;
3908        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3909        let source = "typedef __Float32x4_t f4;\n__SVFloat32_t sv(__SVFloat32_t, __SVBool_t);\n\
3910                      int n = sizeof(f4) + sizeof(__Int8x8_t);\n\
3911                      int f(f4 v) { int __Uint8x16_t = 3; return v[1] + __Uint8x16_t; }\n";
3912        let result = run(&opts, source);
3913        assert!(!result.failed(), "{:?}", result.messages);
3914        assert!(result.text().contains(".long\t24"), "{}", result.text());
3915        opts.target = "x86_64-unknown-linux-gnu".parse::<Triple>().unwrap();
3916        let result = run(&opts, "typedef __Float32x4_t f4;\n");
3917        assert!(result.failed());
3918        let result = run(&opts, "int __Float32x4_t = 1;\n");
3919        assert!(!result.failed(), "{:?}", result.messages);
3920    }
3921
3922    /// A structure too big for registers comes back through the address in x8, which AAPCS64 keeps
3923    /// apart from the arguments, so the argument after it is still in x0.
3924    #[test]
3925    fn an_aarch64_result_in_memory_is_reached_through_x8() {
3926        let mut opts = options();
3927        opts.emit = EmitKind::Asm;
3928        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3929        let source = "struct big { long a, b, c; };\nstruct big make(long v);\n\
3930                      long f(long v) { return make(v).c; }\n\
3931                      struct big g(long v) { struct big b = { v, v, v }; return b; }\n";
3932        let result = run(&opts, source);
3933        assert!(!result.failed(), "{:?}", result.messages);
3934        let text = result.text();
3935        assert!(text.contains("x8"), "{text}");
3936        assert!(text.contains("bl make"), "{text}");
3937    }
3938
3939    /// A remainder is two instructions on AArch64, the division and then a multiply subtract that
3940    /// reads the quotient the division wrote.
3941    #[test]
3942    fn an_aarch64_remainder_is_a_division_and_a_multiply_subtract() {
3943        let mut opts = options();
3944        opts.emit = EmitKind::Asm;
3945        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3946        let source = "int s(int a, int b) { return a % b; }\n\
3947                      unsigned long u(unsigned long a, unsigned long b) { return a % b; }\n";
3948        let result = run(&opts, source);
3949        assert!(!result.failed(), "{:?}", result.messages);
3950        let text = result.text();
3951        let at = |what: &str| text.find(what).unwrap_or_else(|| panic!("{what} is not in\n{text}"));
3952        assert!(at("sdiv w") < at("msub w"), "{text}");
3953        assert!(at("udiv x") < at("msub x"), "{text}");
3954    }
3955
3956    /// A dense `switch` on AArch64 reads a cell of a table after the function with `adr` and
3957    /// `ldrsw`, and each cell is the distance from the table to an arm.
3958    #[test]
3959    fn an_aarch64_jump_table_is_reached_with_adr() {
3960        let mut opts = options();
3961        opts.emit = EmitKind::Asm;
3962        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3963        let source = "int f(int x) { switch (x) { case 0: return 10; case 1: return 21; \
3964                      case 2: return 32; case 3: return 43; case 4: return 54; case 5: return 65; \
3965                      case 6: return 76; case 7: return 87; case 8: return 98; case 9: return 9; \
3966                      case 10: return 19; case 11: return 29; default: return 0; } }\n";
3967        let result = run(&opts, source);
3968        assert!(!result.failed(), "{:?}", result.messages);
3969        let text = result.text();
3970        let at = |what: &str| text.find(what).unwrap_or_else(|| panic!("{what} is not in\n{text}"));
3971        assert!(at("adr x") < at("ldrsw x"), "{text}");
3972        assert!(at("ldrsw x") < at("br x"), "{text}");
3973        assert!(text.contains("_j0:"), "{text}");
3974        assert!(text.contains(".long"), "{text}");
3975    }
3976
3977    /// An AArch64 Linux `va_start` fills in the five fields AAPCS64 gives a list. The two offsets
3978    /// count up to nothing from minus the size of what is left of each half of the save area, so
3979    /// with one integer named they start at minus fifty six and minus one hundred and twenty eight.
3980    #[test]
3981    fn an_aarch64_va_start_writes_the_five_fields_of_its_list() {
3982        let mut opts = options();
3983        opts.emit = EmitKind::Asm;
3984        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3985        let source = "typedef __builtin_va_list va_list;\n\
3986                      int f(int n, ...) { va_list ap; __builtin_va_start(ap, n); \
3987                      int x = __builtin_va_arg(ap, int); double d = __builtin_va_arg(ap, double); \
3988                      __builtin_va_end(ap); return x + (int)d; }\n";
3989        let result = run(&opts, source);
3990        assert!(!result.failed(), "{:?}", result.messages);
3991        let text = result.text();
3992        assert!(text.contains("#-56"), "{text}");
3993        assert!(text.contains("#-128"), "{text}");
3994        assert!(text.contains("#24]"), "{text}");
3995        assert!(text.contains("#28]"), "{text}");
3996        assert!(text.contains("str q"), "{text}");
3997    }
3998
3999    /// A `long double` on AArch64 Linux is a quad, moved with `ldr q` and `str q` and added with a
4000    /// call to the same routine libgcc has.
4001    #[test]
4002    fn an_aarch64_long_double_is_a_quad_in_a_vector_register() {
4003        let mut opts = options();
4004        opts.emit = EmitKind::Asm;
4005        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
4006        let source = "void f(long double *p, long double x) { *p = *p + x; }\n";
4007        let result = run(&opts, source);
4008        assert!(!result.failed(), "{:?}", result.messages);
4009        let text = result.text();
4010        assert!(text.contains("ldr q"), "{text}");
4011        assert!(text.contains("str q"), "{text}");
4012        assert!(text.contains("__addtf3"), "{text}");
4013    }
4014
4015    /// A thread-local variable on AArch64 Linux is initial exec: its offset comes out of the
4016    /// global offset table, the thread pointer out of `tpidr_el0`, and one `add` joins them.
4017    #[test]
4018    fn an_aarch64_thread_local_is_reached_through_tpidr_el0() {
4019        let mut opts = options();
4020        opts.emit = EmitKind::Asm;
4021        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
4022        let source = "__thread int n;\nint *f(void) { return &n; }\n\
4023                      void *g(void) { return __builtin_thread_pointer(); }\n";
4024        let result = run(&opts, source);
4025        assert!(!result.failed(), "{:?}", result.messages);
4026        let text = result.text();
4027        assert!(text.contains(":gottprel:n"), "{text}");
4028        assert!(text.contains(":gottprel_lo12:n]"), "{text}");
4029        assert_eq!(text.matches("mrs x").count(), 2, "{text}");
4030        assert!(text.contains("tpidr_el0"), "{text}");
4031    }
4032
4033    /// Apple's platforms reach a thread-local variable by calling through its descriptor, which
4034    /// is what clang writes on both machines, and the variable is the image and the descriptor.
4035    #[test]
4036    fn a_darwin_thread_local_is_reached_through_its_descriptor() {
4037        let source = "__thread int n = 5;\nint *f(void) { return &n; }\n";
4038        for (triple, wanted) in [
4039            ("aarch64-apple-darwin", &["_n@TLVPPAGE\n", "_n@TLVPPAGEOFF]\n", "\tblr x"][..]),
4040            ("x86_64-apple-darwin", &["_n@TLVP(%rip), %rdi\n", "\tcall\t*%"][..]),
4041        ] {
4042            let mut opts = options();
4043            opts.emit = EmitKind::Asm;
4044            opts.target = triple.parse::<Triple>().unwrap();
4045            let result = run(&opts, source);
4046            assert!(!result.failed(), "{triple}: {:?}", result.messages);
4047            let text = result.text();
4048            for want in wanted {
4049                assert!(text.contains(want), "{triple} wanted {want:?}:\n{text}");
4050            }
4051            assert!(text.contains("\n_n:\n\t.quad\t__tlv_bootstrap\n"), "{text}");
4052            assert!(!text.contains("tpidr_el0") && !text.contains("%fs"), "{text}");
4053        }
4054    }
4055
4056    /// The thread pointer itself is somewhere else on Apple's platforms and is still refused.
4057    #[test]
4058    fn the_thread_pointer_is_refused_on_darwin() {
4059        let mut opts = options();
4060        opts.emit = EmitKind::Asm;
4061        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
4062        let result = run(&opts, "void *f(void) { return __builtin_thread_pointer(); }\n");
4063        assert!(result.failed());
4064        assert!(result.messages[0].contains("thread pointer"), "{:?}", result.messages);
4065    }
4066
4067    /// Darwin's list is a plain pointer and its variadic arguments are all on the stack, so a
4068    /// variadic definition saves no registers and its `va_start` stores one address.
4069    #[test]
4070    fn a_darwin_variadic_definition_saves_nothing_and_walks_the_stack() {
4071        let mut opts = options();
4072        opts.emit = EmitKind::Asm;
4073        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
4074        let source = "int f(int n, ...) { __builtin_va_list ap; __builtin_va_start(ap, n);\n\
4075                      int r = __builtin_va_arg(ap, int); __builtin_va_end(ap); return r; }\n";
4076        let result = run(&opts, source);
4077        assert!(!result.failed(), "{:?}", result.messages);
4078        let text = result.text();
4079        assert!(!text.contains("str q"), "{text}");
4080        assert!(!text.contains("x7"), "{text}");
4081    }
4082
4083    /// A call on Darwin puts every argument past the named ones in memory, even with registers
4084    /// left over, so the `double` here is stored rather than put in `d0`.
4085    #[test]
4086    fn a_darwin_call_puts_its_variadic_arguments_in_memory() {
4087        let mut opts = options();
4088        opts.emit = EmitKind::Asm;
4089        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
4090        let source = "int printf(const char *, ...);\n\
4091                      int g(double x) { return printf(\"%d %f\", 7, x); }\n";
4092        let result = run(&opts, source);
4093        assert!(!result.failed(), "{:?}", result.messages);
4094        let text = result.text();
4095        assert!(text.contains("str d0, [sp, #8]"), "{text}");
4096    }
4097
4098    /// Apple's assembler asks for part of an address after the name, a variable another image
4099    /// defines is read through the table because nothing copies it in, and the directive that
4100    /// makes a zeroed variable is also its definition, so its binding goes above it.
4101    #[test]
4102    fn a_darwin_listing_is_one_apples_assembler_reads() {
4103        let mut opts = options();
4104        opts.emit = EmitKind::Asm;
4105        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
4106        // A tentative definition is common on Darwin unless told otherwise, and this is about the
4107        // one that is not.
4108        opts.common = Some(false);
4109        let source = "extern int ext;\n\
4110                      int g[4];\n\
4111                      int f(int i) { return g[i] + ext; }\n";
4112        let result = run(&opts, source);
4113        assert!(!result.failed(), "{:?}", result.messages);
4114        let text = result.text();
4115        assert!(text.contains(", _g@PAGE\n"), "{text}");
4116        assert!(text.contains(", _g@PAGEOFF\n"), "{text}");
4117        assert!(text.contains(", _ext@GOTPAGE\n"), "{text}");
4118        assert!(text.contains(", _ext@GOTPAGEOFF]\n"), "{text}");
4119        assert!(!text.contains(":lo12:"), "{text}");
4120        assert!(text.contains("\t.globl\t_g\n\t.zerofill\t__DATA,__bss,_g,16,2\n"), "{text}");
4121    }
4122
4123    /// A `signed char` read from memory and added to at 32 bits is widened with its sign first.
4124    ///
4125    /// The widening was being taken out as unneeded, because its source is written as a `w`
4126    /// register and was taken to have 32 bits in it, so `*p + 1` added one to the byte `ldrb` had
4127    /// loaded and -9 came out as 248. At every level, since the pass runs at `-O0` too.
4128    #[test]
4129    fn a_signed_char_on_aarch64_is_widened_with_its_sign_before_it_is_added_to() {
4130        for target in ["aarch64-linux-gnu", "aarch64-apple-darwin"] {
4131            let mut opts = options();
4132            opts.emit = EmitKind::Asm;
4133            opts.target = target.parse::<Triple>().unwrap();
4134            let source = "int f(signed char *p) { return *p + 1; }\n\
4135                          unsigned g(unsigned short *p) { return *p + 1u; }\n";
4136            let result = run(&opts, source);
4137            assert!(!result.failed(), "{:?}", result.messages);
4138            let text = result.text();
4139            let signed = text.contains("\tsxtb w") || text.contains("\tldrsb w");
4140            assert!(signed, "{target}: {text}");
4141        }
4142    }
4143
4144    /// A construct the rule set does not reach yet is named, along with the function it is in.
4145    ///
4146    /// The message is about this compiler being unfinished rather than about the program, which
4147    /// is valid C either way, so it carries the note that says where the work is tracked. Both
4148    /// functions are attempted, so a file that is ahead of the back end in three places says so
4149    /// three times rather than one recompilation at a time.
4150    ///
4151    /// The construct is a local of a fixed size wanting more alignment than a call leaves the
4152    /// stack pointer on, in a function whose frame also grows. The prologue would force the
4153    /// alignment and the array would move the stack pointer afterwards, and those are two frames
4154    /// that each want the one register the rest of the frame is counted from.
4155    #[test]
4156    fn a_construct_the_back_end_cannot_reach_yet_is_reported_against_its_function() {
4157        let mut opts = options();
4158        opts.emit = EmitKind::MirFinal;
4159        let source = "void a(int n) { int v[n]; struct __attribute__((aligned(32))) S { int x; } \
4160                      s; s.x = 1; v[0] = s.x; }\n\
4161                      void b(int n) { int v[n]; struct __attribute__((aligned(32))) S { int x; } \
4162                      s; s.x = 1; v[0] = s.x; }\n";
4163        let result = run(&opts, source);
4164        assert!(result.failed());
4165        assert_eq!(result.messages.len(), 2, "{:?}", result.messages);
4166        assert!(result.messages[0].contains("cannot generate code for 'a'"), "{:?}", result);
4167        assert!(result.messages[0].contains("wants more alignment"), "{:?}", result);
4168        assert!(result.messages[1].contains("cannot generate code for 'b'"), "{:?}", result);
4169        assert!(result.text().is_empty());
4170    }
4171
4172    /// A variable length array walks its pages under the flag that says every page is touched.
4173    ///
4174    /// The pages the prologue takes are touched by the prologue. The pages the array takes are
4175    /// however many the size worked out to, so touching them is a loop written around the
4176    /// declaration rather than anything a prologue can do. What says the loop is there is the
4177    /// ordered comparison it ends each step with, which nothing else in a function writes, and the
4178    /// touch behind it. Without the flag the declaration is still the one subtraction it always was.
4179    #[test]
4180    fn a_variable_length_array_walks_its_pages_where_every_page_of_the_frame_is_to_be_touched() {
4181        let mut opts = options();
4182        opts.emit = EmitKind::MirFinal;
4183        let source = "void a(int n) { int v[n]; v[0] = 1; }\n";
4184        let plain = run(&opts, source);
4185        assert!(!plain.failed(), "{:?}", plain.messages);
4186        assert!(!plain.text().contains("cmp_set_a_64"), "{}", plain.text());
4187
4188        opts.stack_clash = true;
4189        let result = run(&opts, source);
4190        assert!(!result.failed(), "{:?}", result.messages);
4191        assert!(result.text().contains("cmp_set_a_64"), "{}", result.text());
4192        assert!(result.text().contains("or_mi_8"), "{}", result.text());
4193    }
4194
4195    /// A function that keeps a frame pointer on Windows now has an unwind record and an object.
4196    ///
4197    /// The record that platform carries counts every slot in it from where the stack pointer ends
4198    /// the prologue, and it gets to that place by taking a constant off the frame pointer, so a
4199    /// register pushed after the pointer was established has no row the format can write. The order
4200    /// that does have one is the pushes, then the frame, and only then the pointer, which is what
4201    /// the back end writes there and only there. A variable length array and an `alloca` keep a
4202    /// pointer whatever the flags asked for, so before this they were the two shapes of C that
4203    /// could not be compiled for that target at all. See tamnd/rucc#1403.
4204    #[test]
4205    fn a_function_that_keeps_a_frame_pointer_on_windows_reaches_an_object_file() {
4206        let mut opts = options();
4207        opts.emit = EmitKind::Object;
4208        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
4209        let source = concat!(
4210            "void use(void *p);\n",
4211            "void array(int n) { int v[n]; v[0] = 1; use(v); }\n",
4212            "void taken(unsigned long n) { use(__builtin_alloca(n)); }\n",
4213        );
4214        let result = run(&opts, source);
4215        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
4216        let bytes = match result.artifact {
4217            Artifact::Object { bytes, .. } => bytes,
4218            other => panic!("expected an object, got {other:?}"),
4219        };
4220        assert_eq!(&bytes[..2], b"\x64\x86", "an object that says which machine it is for");
4221
4222        // And the same two functions for Linux, so that what the test is measuring is the target
4223        // rather than the program being one this compiler cannot reach yet.
4224        let mut opts = options();
4225        opts.emit = EmitKind::Object;
4226        assert_eq!(run(&opts, source).messages, Vec::<String>::new());
4227    }
4228
4229    /// The address of a name this file only declares, on the format with no table to read it out
4230    /// of.
4231    ///
4232    /// Every such name went into the table on every target, and COFF has no table, so the object
4233    /// writer was handed a relocation it has no way to write and refused the whole file. What the
4234    /// name stands for on this format is an address in the image whichever way the link supplies
4235    /// it, so the instruction pointer reaches it and gcc writes the same. Three shapes here, since
4236    /// the one that found it was a callback stored in a table of its own: a function passed as an
4237    /// argument, one put in a variable that lives past the call, and one called outright, which
4238    /// never needed the table and is here so the test says which of the three changed.
4239    #[test]
4240    fn the_address_of_a_function_this_file_only_declares_reaches_a_windows_object() {
4241        let source = concat!(
4242            "void other(void *p);\n",
4243            "void takes(void (*f)(void *));\n",
4244            "void (*held)(void *);\n",
4245            "void pass(void) { takes(other); }\n",
4246            "void keep(void) { held = other; }\n",
4247            "void call(void) { other(0); }\n",
4248        );
4249        let mut opts = options();
4250        opts.emit = EmitKind::Object;
4251        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
4252        let result = run(&opts, source);
4253        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
4254        let bytes = match result.artifact {
4255            Artifact::Object { bytes, .. } => bytes,
4256            other => panic!("expected an object, got {other:?}"),
4257        };
4258        assert_eq!(&bytes[..2], b"\x64\x86", "an object that says which machine it is for");
4259
4260        // And the same source for Linux, which does have a table and still uses it, so what this
4261        // measures is the format rather than the program.
4262        let mut opts = options();
4263        opts.emit = EmitKind::Object;
4264        assert_eq!(run(&opts, source).messages, Vec::<String>::new());
4265    }
4266
4267    /// `#pragma comment` reaches `.drectve` on Windows, spelled the way clang spells it, and in a
4268    /// listing as well as in an object. A kind nothing reads is taken without a word, and on Linux
4269    /// the whole thing is dropped, as it is by gcc and clang.
4270    #[test]
4271    fn a_pragma_comment_asks_the_windows_linker_for_a_library() {
4272        let source = concat!(
4273            "#pragma comment(lib, \"ws2_32\")\n",
4274            "#pragma comment(lib, \"my lib\")\n",
4275            "#pragma comment(lib, \"libz.a\")\n",
4276            "#pragma comment(linker, \"/include:x\")\n",
4277            "#pragma comment(user, \"nobody reads this\")\n",
4278            "int f(void) { return 0; }\n",
4279        );
4280        let wanted =
4281            " /DEFAULTLIB:ws2_32.lib /DEFAULTLIB:\"my lib.lib\" /DEFAULTLIB:libz.a /include:x";
4282        let mut opts = options();
4283        opts.emit = EmitKind::Object;
4284        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
4285        let result = run(&opts, source);
4286        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
4287        let Artifact::Object { bytes, .. } = result.artifact else { panic!("expected an object") };
4288        assert!(bytes.windows(wanted.len()).any(|at| at == wanted.as_bytes()), "no options");
4289
4290        opts.emit = EmitKind::Asm;
4291        let text = match run(&opts, source).artifact {
4292            Artifact::Text(text) => text,
4293            other => panic!("expected a listing, got {other:?}"),
4294        };
4295        assert!(text.contains("\t.ascii\t\" /DEFAULTLIB:\\\"my lib.lib\\\"\"\n"), "{text}");
4296
4297        let mut opts = options();
4298        opts.emit = EmitKind::Asm;
4299        let result = run(&opts, source);
4300        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
4301        let Artifact::Text(text) = result.artifact else { panic!("expected a listing") };
4302        assert!(!text.contains("DEFAULTLIB"), "{text}");
4303
4304        // And a line that says too little is a warning rather than a silent nothing.
4305        let result = run(&opts, "#pragma comment(lib)\nint f(void) { return 0; }\n");
4306        assert!(result.messages.iter().any(|m| m.contains("wants a string")), "{result:?}");
4307    }
4308
4309    /// An opcode the rule language has no word for is named anyway, and pointed at.
4310    ///
4311    /// The rule language's spelling is the better name when there is one, but an opcode it has
4312    /// no word for is exactly the opcode no rule lowers, so falling back to the opcode and the
4313    /// type is what makes the message say anything at all in the cases that happen. The span is
4314    /// the instruction's own, so the message lands on the line rather than on the file.
4315    ///
4316    /// The width of the float is what keeps the program refused. Everything else here is split into
4317    /// halves by `rucc_codegen::wide`, including the divisions and the conversions to a `float` and
4318    /// a `double`, which became calls into the compiler runtime. A `long double` is the eighty bit
4319    /// float on this target, the runtime has no conversion at that width because the back end has no
4320    /// register that holds one, which is tamnd/rucc#326, so a function converting to it is left with
4321    /// its wide values and reaches the selector the way every function of this width used to.
4322    #[test]
4323    fn an_opcode_with_no_name_in_the_rule_language_is_named_by_its_own_spelling() {
4324        let mut opts = options();
4325        opts.emit = EmitKind::MirFinal;
4326        let source =
4327            "long double f(int a) {\n  __int128 wide = a;\n  return (long double) wide;\n}\n";
4328        let result = run(&opts, source);
4329        assert!(result.failed());
4330        assert!(
4331            result.messages[0].contains("no rule lowers a `sext` producing a `i128`"),
4332            "{result:?}"
4333        );
4334        assert!(result.messages[0].contains(":2:"), "the line the widening is on: {result:?}");
4335        assert!(!result.messages[0].contains("this instruction"), "{result:?}");
4336    }
4337
4338    /// The note names the issue tracker, which is where a reader finds out whether it is known.
4339    #[test]
4340    fn the_note_on_unfinished_work_points_at_the_issues_rather_than_at_the_plan() {
4341        let mut opts = options();
4342        opts.emit = EmitKind::MirFinal;
4343        let source = "long double f(int a) { __int128 wide = a; return (long double) wide; }\n";
4344        let result = run(&opts, source);
4345        assert!(result.failed());
4346        let note = result.messages.iter().find(|line| line.contains("note:")).expect("a note");
4347        assert!(note.contains("https://github.com/tamnd/rucc/issues"), "{note}");
4348        assert!(!note.contains("spec/17-milestones.md"), "{note}");
4349    }
4350
4351    /// The two frame flags reach the frame, which is the only thing either of them does.
4352    #[test]
4353    fn the_frame_flags_on_the_command_line_reach_the_generated_frame() {
4354        let source = "int f(int a) { return a; }\n";
4355        assert!(!mir(source).contains("$rbp"), "a leaf needs no frame pointer when told so");
4356
4357        let mut opts = options();
4358        opts.emit = EmitKind::MirFinal;
4359        opts.frame_pointer = Some(true);
4360        let kept = run(&opts, source).text().to_owned();
4361        assert!(kept.contains("x64.push_64 $rbp"), "{kept}");
4362
4363        // Nothing said at -O0 is a frame pointer, which is what gcc keeps there.
4364        opts.frame_pointer = None;
4365        let kept = run(&opts, source).text().to_owned();
4366        assert!(kept.contains("x64.push_64 $rbp"), "{kept}");
4367    }
4368
4369    /// The assembly of `source`, insisting that it compiled cleanly.
4370    fn asm(source: &str) -> String {
4371        let mut opts = options();
4372        opts.emit = EmitKind::Asm;
4373        let result = run(&opts, source);
4374        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4375        result.text().to_owned()
4376    }
4377
4378    /// `-S`, which is the same compiler as the kind above it with a different last step.
4379    ///
4380    /// What the assembly says is checked in `rucc-asm`, one instruction at a time and against the
4381    /// target's own description of what an instruction is. What is checked here is that a C file
4382    /// goes all the way to a listing an assembler would take, which means the directives around
4383    /// the function as well as the instructions in it.
4384    #[test]
4385    fn a_function_goes_from_c_to_assembly_an_assembler_would_take() {
4386        let text = asm("int add(int a, int b) { return a + b; }\n");
4387        assert!(text.contains("\t.globl\tadd\n"), "{text}");
4388        assert!(text.contains("\t.type\tadd, @function\n"), "{text}");
4389        assert!(text.contains("\nadd:\n"), "{text}");
4390        assert!(text.contains("\taddl\t"), "{text}");
4391        assert!(text.contains("\tret\n"), "{text}");
4392        assert!(text.contains("\t.size\tadd, .-add\n"), "{text}");
4393        // Without this the stack the program runs on is executable, which is not a default
4394        // anybody chose and is not a thing a reader would notice missing.
4395        assert!(text.contains(".note.GNU-stack"), "{text}");
4396    }
4397
4398    /// A call through a function pointer, which is a different instruction from a call to a name.
4399    ///
4400    /// Both are in the one function on purpose. What is being read is that the two calls are told
4401    /// apart all the way down: one carries a name the linker resolves and one carries a register,
4402    /// and neither turns into the other on the way.
4403    #[test]
4404    fn a_call_through_a_function_pointer_goes_through_the_register_it_is_in() {
4405        let text = asm("int g(int);\nint f(int (*p)(int), int a) { return p(a) + g(a); }\n");
4406        assert!(text.contains("\tcall\t*%"), "{text}");
4407        assert!(text.contains("\tcall\tg\n"), "{text}");
4408        // The address arrived in the first argument register and the argument the call passes has
4409        // to end up there, so the two cannot be the same register and the compiler has to have
4410        // moved one of them.
4411        assert!(text.contains("%rdi"), "{text}");
4412    }
4413
4414    /// A name at file scope, which is the one address a function cannot compute for itself. The
4415    /// `lea` that computes it is folded into the load that reads through it, so what is left to
4416    /// read is the addressing mode, which is where the instruction pointer shows up.
4417    #[test]
4418    fn the_address_of_a_global_is_read_from_the_instruction_pointer() {
4419        let text = asm("extern int counter;\nint f(void) { return counter; }\n");
4420        assert!(text.contains("\tmovl\tcounter(%rip), %eax\n"), "{text}");
4421    }
4422
4423    /// Every comparison a branch can be on, which the machine jumps on without keeping a byte.
4424    ///
4425    /// Ten conditions, and each of them comes out as its opposite because the block falls into the
4426    /// arm the comparison is true for and jumps to the other one. That is the half of this most
4427    /// worth pinning: a jump on the condition rather than on its opposite compiles, encodes and
4428    /// runs, and gets every one of these ten functions backwards. The unsigned four and the signed
4429    /// four are separate for the same reason, since `jl` where `jb` was meant is a program that
4430    /// works until an address is above two gigabytes.
4431    #[test]
4432    fn a_branch_on_a_comparison_jumps_on_the_opposite_of_what_it_compared() {
4433        let arms = "return 1; return 2;";
4434        let signed = [("==", "jne"), ("!=", "je"), ("<", "jge"), ("<=", "jg"), (">", "jle")];
4435        for (operator, jump) in signed.into_iter().chain([(">=", "jl")]) {
4436            let text = asm(&format!("int f(int a, int b) {{ if (a {operator} b) {arms} }}\n"));
4437            assert!(
4438                text.contains(&format!("\tcmpl\t%esi, %edi\n\t{jump}\t")),
4439                "{operator}: {text}"
4440            );
4441            assert!(!text.contains("\tset"), "{operator}: {text}");
4442            assert!(!text.contains("\ttest"), "{operator}: {text}");
4443        }
4444        let unsigned = [("<", "jae"), ("<=", "ja"), (">", "jbe"), (">=", "jb")];
4445        for (operator, jump) in unsigned {
4446            let source =
4447                format!("int f(unsigned a, unsigned b) {{ if (a {operator} b) {arms} }}\n");
4448            let text = asm(&source);
4449            assert!(
4450                text.contains(&format!("\tcmpl\t%esi, %edi\n\t{jump}\t")),
4451                "{operator}: {text}"
4452            );
4453        }
4454
4455        // And against a constant, which is four comparisons in five and is where the saving
4456        // mostly is, since the byte that goes was the only reason the constant was in a register.
4457        let text = asm("int f(int a) { if (a < 7) return 1; return 2; }\n");
4458        assert!(text.contains("\tcmpl\t$7, %edi\n\tjge\t"), "{text}");
4459    }
4460
4461    /// The comparison whose answer is a value rather than a branch, which keeps its byte.
4462    ///
4463    /// The one that goes is the byte nothing but the branch reads. A comparison the program asked
4464    /// for the answer of is not that, and there is no branch behind it to fold into in any case,
4465    /// so this is here to say that what was taken out was taken out of one place and not two.
4466    #[test]
4467    fn a_comparison_whose_answer_the_program_wanted_still_writes_a_byte() {
4468        let text = asm("int f(int a, int b) { return a < b; }\n");
4469        assert!(text.contains("\tsetl\t"), "{text}");
4470    }
4471
4472    /// The same source at `-O2`, which is where the optimizer's passes are in the list.
4473    fn optimized(source: &str) -> String {
4474        let mut opts = options();
4475        opts.emit = EmitKind::Asm;
4476        opts.opt_level = rucc_session::OptLevel::O2;
4477        let result = run(&opts, source);
4478        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4479        result.text().to_owned()
4480    }
4481
4482    /// What each `switch` became is an `-fopt-info` remark, and `-Zswitch=` changes what it says.
4483    #[test]
4484    fn opt_info_says_what_each_switch_became_and_a_forced_shape_is_what_it_says() {
4485        let arms: String = (0..40)
4486            .map(|k| format!("case {}: return g({k});", k * 17))
4487            .collect::<Vec<_>>()
4488            .join(" ");
4489        let source = format!("int g(int);\nint f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n");
4490        let said = |shape: Option<&str>| {
4491            let mut opts = options();
4492            opts.emit = EmitKind::Asm;
4493            opts.opt_level = rucc_session::OptLevel::O2;
4494            opts.opt_info = vec![String::new()];
4495            opts.switch_shape = shape.map(str::to_owned);
4496            let result = run(&opts, &source);
4497            assert_eq!(result.messages, Vec::<String>::new());
4498            let lines: Vec<String> = result
4499                .remarks
4500                .lines()
4501                .filter(|line| line.contains("[switch-lowering]"))
4502                .map(str::to_owned)
4503                .collect();
4504            assert_eq!(lines.len(), 1, "{}", result.remarks);
4505            lines[0].clone()
4506        };
4507        assert!(said(None).contains(": f: optimized: switch of 40 cases lowered as a tree;"));
4508        assert!(said(Some("table")).contains("lowered as a table;"));
4509        assert!(said(Some("walk")).contains("lowered as a walk;"));
4510    }
4511
4512    /// A dense `switch` whose arms are a function of the label, which is arithmetic.
4513    ///
4514    /// Sixteen labels, and the arm for label `k` gives `k + 1`. What came out of this was a
4515    /// comparison and a jump for every one of them, which is tamnd/rucc#728. What comes out now is
4516    /// one comparison and one addition, and the count is the whole of the claim: it does not grow
4517    /// with the number of labels, so sixteen and a hundred and sixty compile to the same thing.
4518    ///
4519    /// The comparison is unsigned because the range check is the label minus the lowest one, which
4520    /// is a count and not a number the program wrote.
4521    #[test]
4522    fn a_switch_whose_arms_are_a_function_of_the_label_is_a_range_check_and_arithmetic() {
4523        let arms: String =
4524            (0..16).map(|k| format!("case {k}: return {};", k + 1)).collect::<Vec<_>>().join(" ");
4525        let text = optimized(&format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
4526        assert!(text.contains("\tcmpl\t$15, %edi\n\tja\t"), "{text}");
4527        assert!(text.contains("\taddl\t$1, %edi"), "{text}");
4528        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
4529    }
4530
4531    /// The same `switch` with one arm off the line, which is a table and not arithmetic.
4532    ///
4533    /// The answers being a line is what licenses the addition, since it answers for every label in
4534    /// the range at once. One label whose arm disagrees is a label it would answer wrongly, so this
4535    /// is here to say that the pass is reading the arms and not counting the labels. What it does
4536    /// instead is look the answer up: one comparison, no jump through a jump table, and the arm off
4537    /// the line is a cell of a constant array in `.rodata`, which is gcc's `CSWTCH` and its shape.
4538    #[test]
4539    fn a_dense_switch_whose_arms_are_not_a_line_is_a_load_from_a_table() {
4540        let arms: String = (0..16)
4541            .map(|k| format!("case {k}: return {};", if k == 9 { 100 } else { k + 1 }))
4542            .collect::<Vec<_>>()
4543            .join(" ");
4544        let text = optimized(&format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
4545        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
4546        assert!(!text.contains("\tjmp\t*"), "{text}");
4547        assert!(text.contains("leaq\tCSWTCH.0(%rip)"), "{text}");
4548        let table = &text[text.find("CSWTCH.0:").expect("the table is in the output")..];
4549        let section = text[..text.find("CSWTCH.0:").unwrap_or(0)].rfind("\t.section\t.rodata");
4550        assert!(section.is_some(), "{text}");
4551        assert_eq!(table.matches("\t.long\t").count(), 16, "{text}");
4552        assert!(table.contains("\t.long\t100\n"), "{text}");
4553    }
4554
4555    /// A `switch` whose arms give string literals is a table of how far each string is from it.
4556    ///
4557    /// gcc 16 keeps the compares here, because its table would hold addresses the loader has to
4558    /// write when the program starts, and that table would have to be in `.data.rel.ro`. This one
4559    /// holds four byte distances the linker writes once, so it stays in `.rodata` with the strings.
4560    #[test]
4561    fn a_switch_whose_arms_give_strings_is_a_table_of_how_far_away_they_are() {
4562        let text = optimized(
4563            "const char *f(int k) { switch (k) { case 0: return \"zero\"; \
4564             case 1: return \"one\"; case 2: return \"two\"; case 3: return \"three\"; } \
4565             return \"many\"; }\n",
4566        );
4567        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
4568        assert!(text.contains("leaq\tCSWTCH.0(%rip)"), "{text}");
4569        assert!(!text.contains(".data.rel.ro"), "{text}");
4570        let at = text.find("CSWTCH.0:").expect("the table is in the output");
4571        assert!(text[..at].rfind("\t.section\t.rodata").is_some(), "{text}");
4572        let table = &text[at..];
4573        assert_eq!(table.matches(" - .\n").count(), 4, "{text}");
4574        assert!(table.contains("\t.long\t.Lstr.1+4 - .\n"), "{text}");
4575    }
4576
4577    /// The same table at `-Os`, where a cell is a byte because every answer fits in one.
4578    ///
4579    /// gcc 16 narrows the cells at `-Os` and not at `-O2`, and so does rucc: sixteen answers under a
4580    /// hundred and twenty eight are sixteen bytes rather than sixty four, and the byte is widened
4581    /// back with its sign.
4582    #[test]
4583    fn a_table_at_os_has_cells_as_narrow_as_its_answers() {
4584        let arms: String = (0..16)
4585            .map(|k| format!("case {k}: return {};", if k == 9 { 100 } else { k + 1 }))
4586            .collect::<Vec<_>>()
4587            .join(" ");
4588        let mut opts = options();
4589        opts.emit = EmitKind::Asm;
4590        opts.opt_level = rucc_session::OptLevel::Os;
4591        let result = run(&opts, &format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
4592        assert_eq!(result.messages, Vec::<String>::new());
4593        let text = result.text();
4594        let table = &text[text.find("CSWTCH.0:").expect("the table is in the output")..];
4595        assert_eq!(table.matches("\t.byte\t").count(), 16, "{text}");
4596        assert!(text.contains("\tmovsbl\t"), "{text}");
4597    }
4598
4599    /// A table whose labels are every value the switched value can hold, which is the range check
4600    /// `rucc_opt::prune` takes out.
4601    ///
4602    /// The operand is `x & 3` and all four values are cases, so the `return -1` is dead. With the
4603    /// default out of the switch every case goes to the load, the switch is a jump, and what is
4604    /// left is the mask and the load with no compare in front of it.
4605    #[test]
4606    fn a_table_that_covers_its_operand_has_no_range_check() {
4607        let text = optimized(
4608            "int f(unsigned x) { switch (x & 3) { case 0: return 5; case 1: return 9; \
4609             case 2: return 2; case 3: return 7; } return -1; }\n",
4610        );
4611        assert!(text.contains("leaq\tCSWTCH.0(%rip)"), "{text}");
4612        assert!(!text.contains("\tcmp"), "{text}");
4613        assert!(!text.contains("$-1"), "{text}");
4614    }
4615
4616    /// A store one path makes to a local the loop has just read, which GCC also turns into a
4617    /// conditional move and an unconditional store. The branch was on data, so it was the one the
4618    /// machine gets wrong half the time. The move reads the flags of the comparison itself, so no
4619    /// byte is set and tested in between.
4620    #[test]
4621    fn a_store_to_a_local_the_loop_just_read_is_a_conditional_move() {
4622        let text = optimized(
4623            "int f(const int *v, int n, int k) { int best[8] = {0}; \
4624             for (int i = 0; i < n; i++) if (v[i] > best[i & 7]) best[i & 7] = v[i]; \
4625             return best[k & 7]; }\n",
4626        );
4627        assert!(text.contains("\tcmovgl"), "{text}");
4628        assert!(!text.contains("\tset"), "{text}");
4629        assert!(!text.contains("\ttestb"), "{text}");
4630    }
4631
4632    /// The same loop on a global keeps its branch, because another thread may own the slot.
4633    #[test]
4634    fn a_store_to_a_global_the_loop_just_read_keeps_its_branch() {
4635        let text = optimized(
4636            "int best[8]; void f(const int *v, int n) { \
4637             for (int i = 0; i < n; i++) if (v[i] > best[i & 7]) best[i & 7] = v[i]; }\n",
4638        );
4639        assert!(!text.contains("\tcmov"), "{text}");
4640    }
4641
4642    /// A conversion whose operand the optimizer turned into a constant, which is the whole of what
4643    /// `rucc_opt::fold` does with floating point.
4644    ///
4645    /// The cast is not a constant expression, so the front end leaves it alone and the pipeline is
4646    /// what has to see it. Load forwarding turns the local back into the constant that was stored
4647    /// into it, and the conversion then has an `fconst` in front of it. What came out before was
4648    /// the sixty four bit pattern moved into a register, moved into an `xmm`, and a `cvttsd2si`.
4649    #[test]
4650    fn a_conversion_from_a_constant_double_is_the_number_it_converts_to() {
4651        let text = optimized("int f(void) { double d = 2.75; return (int) d; }\n");
4652        assert!(text.contains("movl\t$2, %eax"), "{text}");
4653        assert!(!text.contains("cvttsd2si"), "{text}");
4654    }
4655
4656    /// A slot of a `const` table read at an index the optimizer works out, which is what
4657    /// `rucc_opt::image` is for.
4658    ///
4659    /// The subscript is not a constant expression and the front end does not fold it. What it
4660    /// writes is the index sign extended, multiplied by four and added to the address of the
4661    /// table, so the offset only exists once `fold` has run and the load only folds after that.
4662    /// What came out before was a `movl t+8(%rip), %eax`.
4663    #[test]
4664    fn a_slot_of_a_read_only_table_is_the_value_the_table_holds() {
4665        let text =
4666            optimized("static const int t[4] = {10, 20, 30, 40};\nint f(void) { return t[2]; }\n");
4667        assert!(text.contains("movl\t$30, %eax"), "{text}");
4668        assert!(!text.contains("t(%rip)"), "{text}");
4669    }
4670
4671    /// A byte of a string literal, which is the same fold reading literal bytes rather than the
4672    /// scalars an `int` array is written as.
4673    #[test]
4674    fn a_byte_of_a_read_only_string_is_the_byte_the_string_spells() {
4675        let text = optimized("static const char s[] = \"abc\";\nint f(void) { return s[1]; }\n");
4676        assert!(text.contains("movl\t$98, %eax"), "{text}");
4677    }
4678
4679    /// A global something can write to, which is the condition the fold turns on and therefore
4680    /// the one worth a test of its own. Nothing here is `const`, so the store in `g` could be the
4681    /// store that ran last and the load has to happen.
4682    #[test]
4683    fn a_table_that_is_not_read_only_keeps_its_load() {
4684        let text = optimized(
4685            "static int t[4] = {10, 20, 30, 40};\nvoid g(int x) { t[2] = x; }\nint f(void) { return t[2]; }\n",
4686        );
4687        assert!(!text.contains("movl\t$30, %eax"), "{text}");
4688    }
4689
4690    /// `gcc.c-torture/execute/20030216-1.c`, which is the program the whole of this is for.
4691    ///
4692    /// It calls a function nothing defines, guarded by a condition the optimizer is meant to prove
4693    /// false, so the program links exactly when the call has been folded away. Getting there is
4694    /// three folds standing on each other: the load of the `const double`, the conversion of it to
4695    /// an `int`, and the comparison against one.
4696    #[test]
4697    fn a_call_guarded_by_a_condition_a_read_only_object_settles_is_not_emitted() {
4698        let text = optimized(
4699            "void link_error(void);\nconst double one = 1.0;\nint main(void) { if ((int) one != 1) link_error(); return 0; }\n",
4700        );
4701        assert!(!text.contains("call\tlink_error"), "{text}");
4702    }
4703
4704    /// A cast between a pointer and an integer as wide as one, which is every one C writes here.
4705    #[test]
4706    fn a_cast_between_a_pointer_and_an_integer_leaves_the_value_where_it_is() {
4707        let text = asm("long f(void *p) { return (long)p; }\n");
4708        // Every instruction in the body is a full width move or the return. The copies are the
4709        // allocator taking no hints, and what matters here is what is not among them: nothing
4710        // narrows the value and nothing widens it again, which is what a cast that did something
4711        // would look like.
4712        for line in text.lines().filter(|line| line.starts_with('\t') && !line.contains('.')) {
4713            let mnemonic = line.split_whitespace().next().unwrap_or("");
4714            assert!(matches!(mnemonic, "movq" | "ret"), "{line} in\n{text}");
4715        }
4716    }
4717
4718    /// The arguments past the sixth arrive in the caller's memory rather than in a register, and
4719    /// where that memory is depends on what the prologue did, so this is checked at the end of the
4720    /// pipeline rather than in the middle of it.
4721    #[test]
4722    fn an_argument_past_the_last_register_is_read_out_of_the_caller_s_stack() {
4723        let six = "long a, long b, long c, long d, long e, long f";
4724        let text = asm(&format!("long f({six}, long g, long h) {{ return g + h; }}\n"));
4725
4726        // Nothing is pushed and no frame is taken, so the only thing between the stack pointer and
4727        // the caller's arguments is the return address the call pushed. Which is where gcc 16.2.0
4728        // reads them from too, at `-O0`, though it reads them in three instructions where this
4729        // reads them in two: the second read is the addition's own memory operand, which is
4730        // `rucc_codegen::combine`, and the offset in it is the one the frame layout wrote into the
4731        // load before the two were put together.
4732        assert!(text.contains("\tmovq\t8(%rsp), "), "{text}");
4733        assert!(text.contains("\taddq\t16(%rsp), "), "{text}");
4734
4735        // A narrower one is read at its own width, because the bits above it are bits the
4736        // convention says nothing about, and one in the other register file with the other file's
4737        // instruction.
4738        let narrow = asm(&format!("int f({six}, int g) {{ return g; }}\n"));
4739        assert!(narrow.contains("\tmovl\t8(%rsp), "), "{narrow}");
4740        let eight =
4741            "double a, double b, double c, double d, double e, double f, double g, double h";
4742        let float = asm(&format!("double f({eight}, double i) {{ return i; }}\n"));
4743        assert!(float.contains("\tmovsd\t8(%rsp), "), "{float}");
4744    }
4745
4746    /// The other end of the same thing. What the caller writes is at the stack pointer, because
4747    /// that is the bottom of its frame and the bottom of its frame is where the callee looks.
4748    #[test]
4749    fn a_call_writes_the_arguments_with_no_register_left_at_the_stack_pointer() {
4750        let six = "1, 2, 3, 4, 5, 6";
4751        let decl = "long g(long, long, long, long, long, long, long, long);\n";
4752        let text = asm(&format!("{decl}long f(void) {{ return g({six}, 7, 8); }}\n"));
4753
4754        assert!(text.contains("\tmovq\t%"), "{text}");
4755        assert!(text.contains(", (%rsp)\n"), "{text}");
4756        assert!(text.contains(", 8(%rsp)\n"), "{text}");
4757        // And it reserved the bytes it wrote into, so nothing else in the frame is on top of them.
4758        assert!(text.contains("\tsubq\t$"), "{text}");
4759
4760        // A narrower one is written at its own width, matching what the callee reads it back with.
4761        let narrow = "int g(int, int, int, int, int, int, int);\n";
4762        let text = asm(&format!("{narrow}int f(void) {{ return g({six}, 7); }}\n"));
4763        assert!(text.contains("\tmovl\t%"), "{text}");
4764        assert!(text.contains(", (%rsp)\n"), "{text}");
4765    }
4766
4767    /// The count a variadic callee on this convention reads is a count of vector registers, so a
4768    /// float that ran out of them and went to memory is not in it.
4769    #[test]
4770    fn a_variadic_call_counts_registers_and_not_arguments() {
4771        let nine = "1., 2., 3., 4., 5., 6., 7., 8., 9.";
4772        let decl = "int g(int, ...);\n";
4773        let text = asm(&format!("{decl}int f(void) {{ return g(0, {nine}); }}\n"));
4774
4775        assert!(text.contains("\tmovl\t$8, "), "eight registers, not nine: {text}");
4776        assert!(text.contains("\tmovsd\t%"), "{text}");
4777        assert!(text.contains(", (%rsp)\n"), "{text}");
4778    }
4779
4780    /// The callee's half of the same convention. Every argument register it was handed is written
4781    /// into its frame on the way in, because which of them hold anything is a thing only the caller
4782    /// knew, and the ones the signature does name are left out because `va_start` sets the offsets
4783    /// past them and nothing ever reads their slots.
4784    #[test]
4785    fn a_variadic_function_writes_the_argument_registers_it_was_handed_into_its_frame() {
4786        let body =
4787            "__builtin_va_list ap; __builtin_va_start(ap, n); __builtin_va_end(ap); return n;";
4788        let text = asm(&format!("int f(int n, ...) {{ {body} }}\n"));
4789
4790        // Five general purpose registers and eight vector ones, since the one parameter the
4791        // signature names took the first of the six.
4792        let stores = |mnemonic: &str| text.matches(&format!("\t{mnemonic}\t%")).count();
4793        assert!(text.contains(", 8(%r"), "the second slot, not the first: {text}");
4794        assert!(!text.contains(", 0(%r"), "{text}");
4795        // All sixteen bytes of each vector register, which is what gcc writes and what a `va_arg`
4796        // of a `_Float128` reads back, so the mnemonic is the one that moves a whole register.
4797        assert_eq!(stores("movaps"), 8, "every vector register: {text}");
4798        assert_eq!(stores("movsd"), 0, "and the whole of each one: {text}");
4799
4800        // And the area is one of the function's own stack objects, so the frame holds it.
4801        assert!(text.contains("\tsubq\t$"), "{text}");
4802    }
4803
4804    /// What `va_start` writes is the four fields of the list, and the two numbers among them are
4805    /// where the arguments the signature names left the walk over each file's registers.
4806    #[test]
4807    fn va_start_writes_the_four_fields_the_psabi_describes() {
4808        let start = "__builtin_va_list ap; __builtin_va_start(ap, d);";
4809        let params = "int a, int b, int c, double d";
4810        let text = asm(&format!("int f({params}, ...) {{ {start} return a; }}\n"));
4811
4812        // Three integers took three of the six general purpose registers, and one double took one
4813        // of the eight vector ones, so the walk starts at twenty four bytes into the first half and
4814        // sixteen bytes into the second, which begins at forty eight.
4815        assert!(text.contains("	movl	$24, "), "{text}");
4816        assert!(text.contains("	movl	$64, "), "{text}");
4817        // The other two fields are addresses rather than numbers, so each is stored as a word and
4818        // each is a `lea` away. One of them reaches above the frame, which is where the caller's
4819        // arguments are and is the only thing in this function that is not below the stack pointer.
4820        assert!(text.contains(", 8(%r"), "{text}");
4821        assert!(text.contains(", 16(%r"), "{text}");
4822        let frame: u32 = text
4823            .lines()
4824            .find_map(|line| line.trim().strip_prefix("subq	$")?.split(',').next()?.parse().ok())
4825            .expect("a variadic function takes a frame for the save area");
4826        let above = |line: &str| {
4827            let at: u32 = line.trim().strip_prefix("leaq	")?.split('(').next()?.parse().ok()?;
4828            Some(at > frame)
4829        };
4830        assert!(text.lines().filter_map(above).any(|it| it), "{frame}: {text}");
4831    }
4832
4833    /// A `va_arg` is a branch on whether the argument it wants is still in the save area, and which
4834    /// of the two halves it walks is the type's answer.
4835    #[test]
4836    fn va_arg_branches_on_whether_the_argument_is_still_in_the_save_area() {
4837        let read = "__builtin_va_list ap; __builtin_va_start(ap, n);";
4838        let ints = format!("int f(int n, ...) {{ {read} return __builtin_va_arg(ap, int); }}\n");
4839        let text = asm(&ints);
4840
4841        // The last general purpose slot begins at forty, so an offset above it is an argument the
4842        // caller left in its own memory instead.
4843        assert!(text.contains("$40, "), "{text}");
4844        assert!(text.contains("	cmpl	"), "{text}");
4845        // The jump is the unsigned one, since an offset is a count of bytes. It is the opposite
4846        // of the comparison the front end wrote, because the block falls into the half taken when
4847        // the argument is still in the save area and jumps to the other one.
4848        assert!(text.contains("	ja	"), "{text}");
4849
4850        let arg = "__builtin_va_arg(ap, double)";
4851        let text = asm(&format!("double f(int n, ...) {{ {read} return {arg}; }}\n"));
4852        assert!(text.contains("$160, "), "the last vector slot: {text}");
4853    }
4854
4855    /// A structure assigned is a copy of a known size, and a copy of a known size is a run of
4856    /// moves rather than a call to a library this compiler has no way to reach yet.
4857    #[test]
4858    fn a_structure_assignment_is_a_move_for_each_word_of_it() {
4859        let decl = "struct pair { long a, b; };\n";
4860        let body = "struct pair p = *q; return p.a + p.b;";
4861        let text = asm(&format!("{decl}long f(struct pair *q) {{ {body} }}\n"));
4862
4863        assert!(!text.contains("memcpy"), "nothing calls the library: {text}");
4864        assert!(!text.contains("\tcall"), "{text}");
4865        // Sixteen bytes aligned to eight is two words, and each is a load and a store.
4866        assert!(text.matches("\tmovq\t").count() >= 4, "two words each way: {text}");
4867    }
4868
4869    /// A word is as wide as the object is aligned to and no wider, so a character array is copied
4870    /// a byte at a time and a structure of longs eight bytes at a time.
4871    #[test]
4872    fn how_wide_a_word_of_a_copy_is_follows_the_alignment() {
4873        let decl = "struct bytes { char a[8]; };\n";
4874        let body = "struct bytes p = *q; return p.a[0];";
4875        let text = asm(&format!("{decl}int f(struct bytes *q) {{ {body} }}\n"));
4876
4877        // Eight bytes aligned to one is eight words, and each is a load and a store.
4878        assert!(text.matches("\tmovb\t").count() >= 16, "a byte at a time: {text}");
4879    }
4880
4881    /// What an initialiser does not name is zero, which the front end writes as a fill and this
4882    /// writes as the byte spread across each word.
4883    #[test]
4884    fn the_part_of_an_initialiser_that_names_nothing_is_stored_as_zero() {
4885        let decl = "struct wide { long a, b, c; };\n";
4886        let text = asm(&format!("{decl}long f(void) {{ struct wide w = {{ 7 }}; return w.c; }}\n"));
4887
4888        assert!(!text.contains("memset"), "nothing calls the library: {text}");
4889        // Either spelling of a zero in a register, the move of one or the exclusive or of the
4890        // register with itself that `rucc_codegen::shorten` writes instead where it is free. The
4891        // exclusive or is the thirty-two bit one whatever the width of the word, since the half of
4892        // the register it does not write is cleared rather than left alone.
4893        assert!(text.contains("\tmovq\t$0, ") || text.contains("\txorl\t"), "the zero: {text}");
4894    }
4895
4896    /// A copy too large to be worth unrolling is a call to the runtime, which is the C library on
4897    /// a hosted target and `rucc-builtins` on a freestanding one.
4898    #[test]
4899    fn a_copy_too_large_to_unroll_calls_the_runtime() {
4900        let decl = "struct huge { char a[4096]; };\n";
4901        let mut opts = options();
4902        opts.emit = EmitKind::Asm;
4903        let source = format!("{decl}void f(struct huge *p, struct huge *q) {{ *p = *q; }}\n");
4904        let result = run(&opts, &source);
4905        assert!(!result.failed(), "{:?}", result.messages);
4906        let text = result.text();
4907        assert!(text.contains("call") && text.contains("memcpy"), "{text}");
4908        // The size in the register the convention passes the third argument in, which is what
4909        // says the call was built from the convention and not from the shape of the IR.
4910        assert!(text.contains("4096"), "the size travels: {text}");
4911    }
4912
4913    /// And an object passed by value with more words in it than that is the same call again,
4914    /// written in front of the call the object is an argument of.
4915    ///
4916    /// The copy is one the caller owes the callee, since the callee is free to write to what it
4917    /// was handed, so it is not an optimization that the size decides but the only way the call
4918    /// can be made at all.
4919    #[test]
4920    fn a_structure_too_large_to_unroll_is_copied_into_the_argument_area_by_the_runtime() {
4921        let decl = "struct huge { char a[4096]; };\nint take(struct huge);\n";
4922        let text = asm(&format!("{decl}int f(struct huge *p) {{ return take(*p); }}\n"));
4923
4924        let copy = text.find("call\tmemcpy").expect("the copy");
4925        let call = text.find("call\ttake").expect("the call");
4926        assert!(copy < call, "the copy comes first: {text}");
4927        // Into the bottom of the outgoing area, which is where the stack pointer already is, and
4928        // with the size in the register the convention passes the third argument in. The address
4929        // of the bottom of the frame is the stack pointer itself, so what carries it is the move
4930        // rather than the address computation the selector wrote. See `rucc_codegen::shorten`.
4931        assert!(text.contains("movq\t%rsp, %rdi"), "the destination: {text}");
4932        assert!(text.contains("$4096, %edx"), "the size: {text}");
4933    }
4934
4935    /// A frame that had to force its own alignment cannot say how far away the caller's stack
4936    /// pointer was, so it reaches back through the frame pointer instead.
4937    #[test]
4938    fn a_realigned_frame_reads_them_through_the_frame_pointer() {
4939        let six = "long a, long b, long c, long d, long e, long f";
4940        let body = "_Alignas(32) long wide[4]; wide[0] = g; return wide[0];";
4941        let text = asm(&format!("long f({six}, long g) {{ {body} }}\n"));
4942
4943        // The frame pointer is saved and pointed at where it was saved before the alignment is
4944        // forced, so the caller's arguments stay a constant distance from it: one word for the
4945        // saved frame pointer and one for the return address.
4946        assert!(text.contains("\tandq\t$-32, %rsp"), "{text}");
4947        assert!(text.contains("\tmovq\t16(%rbp), "), "{text}");
4948        assert!(!text.contains("\tmovq\t16(%rsp), "), "{text}");
4949    }
4950
4951    /// The object format decides the directives, and the target decides the object format.
4952    #[test]
4953    fn the_target_decides_how_the_assembly_is_spelled() {
4954        let mut opts = options();
4955        opts.emit = EmitKind::Asm;
4956        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
4957        let text = run(&opts, "int f(void) { return 0; }\n").text().to_owned();
4958        assert!(text.contains("__TEXT,__text"), "{text}");
4959        assert!(text.contains("\n_f:\n"), "{text}");
4960        assert!(!text.contains(".note.GNU-stack"), "{text}");
4961    }
4962
4963    /// The object file of `source`, insisting that it compiled cleanly.
4964    fn obj(source: &str) -> Vec<u8> {
4965        let mut opts = options();
4966        opts.emit = EmitKind::Object;
4967        let result = run(&opts, source);
4968        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4969        match result.artifact {
4970            Artifact::Object { bytes, .. } => bytes,
4971            other => panic!("expected an object, got {other:?}"),
4972        }
4973    }
4974
4975    /// `-c`, which is the last step of the three the back end can end with.
4976    ///
4977    /// What is in the file is checked in `rucc-object`, a field at a time. What is checked here is
4978    /// that a C file goes all the way to one, which is the whole compiler in one line and the
4979    /// thing that stops working when a layer between them changes its mind about something.
4980    #[test]
4981    fn a_function_goes_from_c_to_an_object_a_linker_would_take() {
4982        let bytes = obj("int add(int a, int b) { return a + b; }\n");
4983        assert_eq!(&bytes[..4], b"\x7fELF", "an object file starts by saying it is one");
4984        let text = asm("int add(int a, int b) { return a + b; }\n");
4985        assert!(
4986            text.contains("\taddl\t"),
4987            "and the listing of it is the same instructions:\n{text}"
4988        );
4989    }
4990
4991    /// A variable this file defines, which is what a reference to one has to resolve against.
4992    #[test]
4993    fn a_variable_goes_from_c_to_the_section_it_belongs_in() {
4994        let text = asm("int counter = 42;\nstatic int hidden;\nconst int fixed = 7;\n");
4995        assert!(text.contains("\t.data\n\t.globl\tcounter\n"), "{text}");
4996        assert!(text.contains("\ncounter:\n\t.long\t42\n"), "{text}");
4997        assert!(text.contains("\t.size\tcounter, .-counter\n"), "{text}");
4998        // A zeroed variable carries its size and none of its bytes, and a `static` one is not
4999        // announced to the linker at all, which is the whole of what `static` means here.
5000        assert!(text.contains("\t.bss\n\t.p2align\t2\n"), "{text}");
5001        assert!(text.contains("\nhidden:\n\t.space\t4\n"), "{text}");
5002        assert!(!text.contains(".globl\thidden"), "{text}");
5003        // Nothing writes through it, so it goes in a page the loader can map read only and every
5004        // process running the program can share.
5005        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
5006    }
5007
5008    /// A bit-field with a value in it, which is written as the bytes the value lands in.
5009    ///
5010    /// The interesting one is the field whose lowest byte is zero. The bytes a bit-field
5011    /// initializer makes are put together first and then taken back out as the run they make,
5012    /// and taking them out starts at the byte the field starts at, so a zero byte at the front
5013    /// used to end the object up in `.bss` with the rest of its value thrown away.
5014    #[test]
5015    fn a_bit_field_initializer_writes_every_byte_of_the_value_and_not_only_the_ones_that_are_set() {
5016        let text = asm("struct s { unsigned f : 20; } x = { 0x12300 };\n");
5017        assert!(text.contains("\t.data\n"), "there is something to write: {text}");
5018        assert!(text.contains("\nx:\n\t.ascii\t\"\\000#\\001\"\n"), "and it is the value: {text}");
5019
5020        // Two fields, the first of them zero, which is the same thing said with the zero byte
5021        // inside the run rather than at the front of it.
5022        let text = asm("struct s { unsigned a : 8; unsigned b : 8; } x = { 0, 3 };\n");
5023        assert!(text.contains("\nx:\n\t.ascii\t\"\\000\\003\"\n"), "{text}");
5024
5025        // Wider than an `int`, which is the same code and is worth saying because the value no
5026        // longer fits in the thirty two bits a bit-field used to be read at.
5027        let text = asm("struct s { unsigned long long f : 40; } x = { 0x100000 };\n");
5028        assert!(text.contains("\nx:\n\t.ascii\t\"\\000\\000\\020\"\n\t.space\t5\n"), "{text}");
5029
5030        // Nothing in it, which still costs no bytes in the file.
5031        let text = asm("struct s { unsigned f : 20; } x = { 0 };\n");
5032        assert!(text.contains("\t.bss\n"), "an object of zeroes is zeroes: {text}");
5033        assert!(text.contains("\nx:\n\t.space\t4\n"), "{text}");
5034    }
5035
5036    /// A string literal, which is a variable the program never named.
5037    #[test]
5038    fn a_string_literal_is_a_variable_with_a_name_no_program_could_write() {
5039        let text = asm("const char *f(void) { return \"hi\"; }\n");
5040        assert!(text.contains("\t.ascii\t\"hi\\000\"\n"), "{text}");
5041        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
5042        let label = text
5043            .lines()
5044            .find(|line| line.starts_with(".Lstr"))
5045            .unwrap_or_else(|| panic!("a label for the literal in\n{text}"));
5046        assert!(!text.contains(&format!(".globl\t{}", label.trim_end_matches(':'))), "{text}");
5047    }
5048
5049    /// A variable holding the address of another one, which is the only hole an image has in it.
5050    #[test]
5051    fn an_address_in_an_initializer_is_left_to_the_linker() {
5052        let source = "int counter;\nint *p = &counter;\n";
5053        let text = asm(source);
5054        assert!(text.contains("\np:\n\t.quad\tcounter\n"), "{text}");
5055        // And in the object it is eight zero bytes and a relocation, which is what the two paths
5056        // being one description is for.
5057        let bytes = obj(source);
5058        assert!(bytes.windows(8).any(|w| w == b"counter\0"), "the object has to name it");
5059    }
5060
5061    /// A const table of function pointers, which is the shape that made SQLite link with a warning.
5062    ///
5063    /// The table is const so nothing in the program writes it, but the addresses in it are not
5064    /// numbers a link knows, so the loader writes it once at startup. Putting it in `.rodata`
5065    /// leaves a relocation in a section that is never writable, and what the linker does about
5066    /// that is set `DT_TEXTREL` on the whole image and say so. `.data.rel.ro` is writable for
5067    /// exactly as long as the loader is writing it and read only afterwards, which is what the
5068    /// program asked for in the first place.
5069    #[test]
5070    fn a_constant_holding_an_address_goes_in_the_section_the_loader_may_write_once() {
5071        // Both names are `static` and both are defined here, so nothing else can be the one that
5072        // defines them and the linker may lay the table out in the first pages of the segment.
5073        let text = asm("static void a(void) {}\nstatic void b(void) {}\n\
5074             struct m { void (*x)(void); void (*y)(void); };\n\
5075             const struct m t = { a, b };\n");
5076        assert!(text.contains("\t.section\t.data.rel.ro.local,\"aw\",@progbits\n"), "{text}");
5077        assert!(text.contains("\nt:\n\t.quad\ta\n\t.quad\tb\n"), "{text}");
5078
5079        // One name this file only declares is enough to lose the `.local` half, because a name the
5080        // link resolves from somewhere else is one another object may turn out to define.
5081        let text =
5082            asm("void a(void);\nstruct m { void (*x)(void); };\nconst struct m t = { a };\n");
5083        assert!(text.contains("\t.section\t.data.rel.ro,\"aw\",@progbits\n"), "{text}");
5084
5085        // And a constant with no address in it stays exactly where it was.
5086        let text = asm("const int fixed = 7;\n");
5087        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
5088    }
5089
5090    /// A thread-local variable, which is the whole of one: the storage and the way to reach it.
5091    ///
5092    /// The two halves are in one test on purpose. Either one alone is worse than neither: a
5093    /// definition with no way to reach it is a variable nothing can read, and a reference with no
5094    /// definition behind it is the bug this pair was written to prevent, where a thread-local is
5095    /// read as though it were an ordinary global and every thread quietly shares one copy.
5096    #[test]
5097    fn a_thread_local_variable_is_storage_a_thread_gets_a_copy_of_and_an_offset_into_it() {
5098        let text = asm("_Thread_local int x = 1;\nint read(void) { return x; }\n");
5099        // The storage: the section the loader makes a copy of for every thread, and the symbol
5100        // type that makes a linker refuse an ordinary relocation aimed at it.
5101        assert!(text.contains("\t.section\t.tdata,\"awT\",@progbits\n"), "{text}");
5102        assert!(text.contains("\t.type\tx, @tls_object\n"), "{text}");
5103        // The way to reach it: how far into a thread's block it sits, out of the table, plus where
5104        // this thread's block is, out of the segment register.
5105        assert!(text.contains("x@GOTTPOFF(%rip)"), "{text}");
5106        assert!(text.contains("%fs:0"), "{text}");
5107    }
5108
5109    /// The second half of that on its own, which is what a program asks for when the number it
5110    /// wants is the thread rather than anything in it.
5111    ///
5112    /// rpmalloc writes this to find its per thread cache, and it is the whole of what stood
5113    /// between that library and a build. gcc 16 writes the same one instruction.
5114    #[test]
5115    fn the_address_of_this_thread_s_own_storage_is_read_out_of_the_segment_register() {
5116        let text = asm("void *here(void) { return __builtin_thread_pointer(); }\n");
5117        assert!(text.contains("movq\t%fs:0, "), "{text}");
5118        // No table slot and no addition, because there is no variable to find inside the block.
5119        assert!(!text.contains("GOTTPOFF"), "{text}");
5120    }
5121
5122    /// `__builtin_sponentry` on AArch64, which is where the caller's stack arguments begin.
5123    ///
5124    /// The function below saves the frame pair and nothing else, so the stack pointer it was
5125    /// entered with is sixteen above the one it runs with, and that is what clang 18 writes for it
5126    /// too. On x86-64 the builtin is refused, as clang refuses it.
5127    #[test]
5128    fn sponentry_is_the_stack_pointer_the_function_was_entered_with() {
5129        let mut opts = freestanding();
5130        opts.emit = EmitKind::Asm;
5131        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
5132        let source = "void g(void *);\nvoid f(void) { g(__builtin_sponentry()); }\n";
5133        let result = run(&opts, source);
5134        assert_eq!(result.messages, Vec::<String>::new());
5135        let text = result.text();
5136        assert!(text.contains("add x0, sp, #16"), "{text}");
5137        let result = run(&freestanding(), source);
5138        assert_eq!(result.errors, 1, "{:?}", result.messages);
5139        assert!(result.messages[0].contains("only available on AArch64"), "{:?}", result.messages);
5140    }
5141
5142    /// The four hints and the one thing that decides between them, which is the locality.
5143    ///
5144    /// A prefetch promises nothing, so what is checked here is the instruction rather than any
5145    /// effect: the program runs the same whichever of the four it gets, and the whole point of
5146    /// writing one is which. The four spellings are what gcc 16.2.0 writes for the same four
5147    /// programs, measured on x86-64 rather than read off a manual.
5148    ///
5149    /// The write hint is not one of them. `prefetchw` is not in the base instruction set and gcc
5150    /// writes it only when the command line says the part has it, so a prefetch for a write is the
5151    /// same instruction as a prefetch for a read, which is the fourth line here.
5152    #[test]
5153    fn a_prefetch_is_one_of_four_instructions_and_the_locality_is_what_picks() {
5154        for (locality, wanted) in
5155            [(0, "prefetchnta"), (1, "prefetcht2"), (2, "prefetcht1"), (3, "prefetcht0")]
5156        {
5157            let source =
5158                format!("void warm(void *p) {{ __builtin_prefetch(p, 0, {locality}); }}\n");
5159            let text = asm(&source);
5160            assert!(text.contains(&format!("\t{wanted}\t")), "locality {locality}: {text}");
5161        }
5162        // The one argument form, which means a read that wants all of the data afterwards.
5163        let text = asm("void warm(void *p) { __builtin_prefetch(p); }\n");
5164        assert!(text.contains("\tprefetcht0\t"), "{text}");
5165        // A prefetch for a write, which on a part nobody said has `prefetchw` is the same
5166        // instruction as the read above.
5167        let text = asm("void warm(void *p) { __builtin_prefetch(p, 1); }\n");
5168        assert!(text.contains("\tprefetcht0\t"), "{text}");
5169        assert!(!text.contains("prefetchw"), "{text}");
5170    }
5171
5172    /// The same eight programs on AArch64, where the write hint is in the base instruction set and
5173    /// so is a different instruction, which is what gcc 16.2.0 writes for them.
5174    #[test]
5175    fn an_aarch64_prefetch_is_a_prfm_that_says_the_locality_and_whether_it_writes() {
5176        let mut opts = options();
5177        opts.emit = EmitKind::Asm;
5178        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
5179        for (write, kind) in [(0, "pld"), (1, "pst")] {
5180            for (locality, wanted) in [(0, "l1strm"), (1, "l3keep"), (2, "l2keep"), (3, "l1keep")] {
5181                let source = format!(
5182                    "void warm(void *p) {{ __builtin_prefetch(p, {write}, {locality}); }}\n"
5183                );
5184                let result = run(&opts, &source);
5185                assert_eq!(result.messages, Vec::<String>::new(), "{source}");
5186                let text = result.text();
5187                assert!(text.contains("prfm"), "{source}{text}");
5188                assert!(text.contains(&format!("{kind}{wanted}, [x0]")), "{source}{text}");
5189            }
5190        }
5191    }
5192
5193    /// The stop, which is the one instruction the machine is promised never to have a meaning for.
5194    ///
5195    /// What is checked is the instruction and not any effect, because the effect is a fault and a
5196    /// unit test has nowhere to take one. gcc 16.2.0 writes the same instruction for the same
5197    /// program, and it is not a call, which is the half that matters in a kernel and in a
5198    /// freestanding program: neither has an `abort` for a call to reach.
5199    ///
5200    /// The second half is the block going on after it. A statement written under a stop is
5201    /// compiled the way it would have been without one, so the addition is still there, and that
5202    /// is the front end declining to treat a stop as the end of a path.
5203    #[test]
5204    fn a_trap_is_the_instruction_the_machine_has_no_meaning_for() {
5205        let text = asm("void stop(void) { __builtin_trap(); }\n");
5206        assert!(text.contains("\tud2\n"), "{text}");
5207        assert!(!text.contains("\tcall"), "a stop is not a call to anything: {text}");
5208
5209        let text = asm("int stop(int a) { __builtin_trap(); return a + 1; }\n");
5210        assert!(text.contains("\tud2\n"), "{text}");
5211        assert!(text.contains("\taddl\t"), "the block goes on after a stop: {text}");
5212    }
5213
5214    /// `__builtin_cpu_init` is a call to libgcc's `__cpu_indicator_init` and nothing else, which
5215    /// is what gcc 16.2.0 writes for it. The name the program wrote does not reach the object
5216    /// file, because no library defines it.
5217    #[test]
5218    fn cpu_init_is_a_call_to_the_libgcc_function_that_fills_in_the_model() {
5219        let text = asm("void start(void) { __builtin_cpu_init(); }\n");
5220        assert!(text.contains("\tcall\t__cpu_indicator_init"), "{text}");
5221        assert!(!text.contains("__builtin_cpu_init"), "{text}");
5222    }
5223
5224    /// The two time stamp counter reads are the instruction where the call was, as gcc writes
5225    /// them, and not a call to anything. An object that called a routine in `librucc_builtins.a`
5226    /// for them did not link under gcc and did not load into a program gcc linked (#2191), so the
5227    /// object is checked as well as the listing: the instruction's bytes are in it and no name
5228    /// for either builtin or for the old routines is.
5229    #[test]
5230    fn the_time_stamp_counter_is_the_instruction_in_place() {
5231        let rdtsc = "unsigned long long f(void) { return __builtin_ia32_rdtsc(); }\n";
5232        let rdtscp =
5233            "unsigned long long f(unsigned int *aux) { return __builtin_ia32_rdtscp(aux); }\n";
5234        for (source, bytes) in [(rdtsc, "0x0f, 0x31"), (rdtscp, "0x0f, 0x01, 0xf9")] {
5235            let text = asm(source);
5236            assert!(text.contains(&format!("\t.byte\t{bytes}\n")), "{text}");
5237            assert!(!text.contains("\tcall\t"), "{text}");
5238            assert!(!text.contains("rdtsc"), "{text}");
5239        }
5240        // `rdtscp` leaves the processor's number in `ecx`, and that is what goes through the
5241        // pointer, as a four byte store.
5242        let text = asm(rdtscp);
5243        assert!(text.contains("%ecx"), "{text}");
5244
5245        for (source, bytes) in [(rdtsc, &[0x0f, 0x31][..]), (rdtscp, &[0x0f, 0x01, 0xf9][..])] {
5246            let object = obj(source);
5247            assert!(object.windows(bytes.len()).any(|w| w == bytes), "{bytes:02x?}");
5248            assert!(!object.windows(9).any(|w| w == b"__rucc_ia"), "an undefined name");
5249            assert!(!object.windows(5).any(|w| w == b"rdtsc"), "an undefined name");
5250        }
5251
5252        // Anywhere else there is no instruction to write and nothing to call, so it is refused
5253        // where it is written rather than left to fail at the link.
5254        let mut opts = options();
5255        opts.emit = EmitKind::Asm;
5256        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
5257        let said = run(&opts, rdtsc).messages.join("\n");
5258        assert!(said.contains("only available on x86-64"), "{said}");
5259    }
5260
5261    /// `__builtin_cpu_supports` is a load of the word the feature's bit is in and an `and` with
5262    /// the bit, and the answer is the bit where it stands, which is gcc 16.2.0's lowering.
5263    ///
5264    /// Three names, one from each place libgcc keeps the bits: sse4.2 is bit 8 of the last word of
5265    /// `__cpu_model`, vpclmulqdq is bit 1 of the first word of `__cpu_features2`, and xsave is bit
5266    /// 17 of its second word. The fourth is the top bit of a word, which gcc answers one for
5267    /// rather than the bit, so there is a compare after the `and`.
5268    #[test]
5269    fn cpu_supports_is_a_bit_of_the_words_libgcc_fills_in() {
5270        let text = asm("int f(void) { return __builtin_cpu_supports(\"sse4.2\"); }\n");
5271        assert!(text.contains("__cpu_model"), "{text}");
5272        assert!(text.contains("12(%"), "the fourth word of the model: {text}");
5273        assert!(text.contains("$256"), "{text}");
5274        assert!(!text.contains("\tcall"), "the answer is a read and not a call: {text}");
5275
5276        let text = asm("int f(void) { return __builtin_cpu_supports(\"vpclmulqdq\"); }\n");
5277        assert!(text.contains("__cpu_features2"), "{text}");
5278        assert!(text.contains("$2,"), "{text}");
5279
5280        let text = asm("int f(void) { return __builtin_cpu_supports(\"xsave\"); }\n");
5281        assert!(text.contains("__cpu_features2"), "{text}");
5282        assert!(text.contains("4(%"), "the second word of the second object: {text}");
5283        assert!(text.contains("$131072"), "{text}");
5284
5285        let text = asm("int f(void) { return __builtin_cpu_supports(\"avx512vbmi2\"); }\n");
5286        assert!(text.contains("set"), "the top bit is answered as a one: {text}");
5287    }
5288
5289    /// `__builtin_cpu_is` is a compare of one word of `__cpu_model` with a number: the vendor for
5290    /// `amd`, which is 2, and the subtype for `znver4`, which is 29.
5291    #[test]
5292    fn cpu_is_compares_one_word_of_the_model_with_a_number() {
5293        let text = asm("int f(void) { return __builtin_cpu_is(\"amd\"); }\n");
5294        assert!(text.contains("__cpu_model"), "{text}");
5295        assert!(text.contains("$2,"), "{text}");
5296
5297        let text = asm("int f(void) { return __builtin_cpu_is(\"znver4\"); }\n");
5298        assert!(text.contains("8(%"), "the subtype is the third word: {text}");
5299        assert!(text.contains("$29,"), "{text}");
5300    }
5301
5302    /// The name picks the word and the bit, so it has to be a string literal, and it has to be
5303    /// one gcc knows. Both are errors in gcc 16.2.0's words, and so is asking on a target other
5304    /// than x86-64, where nothing defines what these read.
5305    #[test]
5306    fn a_cpu_builtin_takes_a_name_it_knows_written_as_a_literal() {
5307        let mut opts = options();
5308        opts.emit = EmitKind::Ir;
5309        for (source, wanted) in [
5310            (
5311                "int f(const char *s) { return __builtin_cpu_supports(s); }\n",
5312                "parameter to builtin must be a string constant or literal",
5313            ),
5314            (
5315                "int f(void) { return __builtin_cpu_supports(\"sse5\"); }\n",
5316                "parameter to builtin not valid: sse5",
5317            ),
5318            (
5319                "int f(void) { return __builtin_cpu_is(\"sse\"); }\n",
5320                "parameter to builtin not valid: sse",
5321            ),
5322        ] {
5323            let result = run(&opts, source);
5324            assert!(
5325                result.messages.iter().any(|m| m.contains(wanted)),
5326                "{source}{:?}",
5327                result.messages
5328            );
5329        }
5330        // A cast in front of the literal is looked through, the way gcc looks through it.
5331        let text = asm("int f(void) { return __builtin_cpu_supports((const char *)\"avx2\"); }\n");
5332        assert!(text.contains("$1024"), "{text}");
5333
5334        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
5335        for source in [
5336            "void f(void) { __builtin_cpu_init(); }\n",
5337            "int f(void) { return __builtin_cpu_supports(\"sse4.2\"); }\n",
5338        ] {
5339            let result = run(&opts, source);
5340            assert!(
5341                result.messages.iter().any(|m| m.contains("only available on x86-64")),
5342                "{source}{:?}",
5343                result.messages
5344            );
5345        }
5346    }
5347
5348    /// The promise about the low bits of an address, whose value is the address.
5349    ///
5350    /// Nothing here reads an alignment fact about a value yet, so what the call leaves behind is
5351    /// its first argument and no instruction at all. The claim worth checking end to end is that
5352    /// the name is gone: a builtin nothing lowers reaches the assembler as a call to a name no
5353    /// object file defines, which is how this one used to fail to link out of glibc's string
5354    /// headers.
5355    ///
5356    /// The arguments behind the address are still evaluated, because gcc 16.2.0 evaluates them at
5357    /// every optimization level even though it has folded the call away. A constant has nothing to
5358    /// run and is dropped, and a call does, so the second half asks for the callee by name.
5359    #[test]
5360    fn assume_aligned_is_its_first_argument_and_keeps_the_rest() {
5361        let text = asm("void *aligned(char *p) { return __builtin_assume_aligned(p, 16); }\n");
5362        assert!(!text.contains("assume_aligned"), "{text}");
5363        assert!(!text.contains("\tcall"), "nothing is called for an alignment fact: {text}");
5364
5365        let source = "unsigned long width(void);\n\
5366                      void *aligned(char *p) { return __builtin_assume_aligned(p, width()); }\n";
5367        let text = asm(source);
5368        assert!(!text.contains("assume_aligned"), "{text}");
5369        assert!(text.contains("width"), "the argument that is not the answer still runs: {text}");
5370    }
5371
5372    /// Where a frame is, which on this machine is what the frame pointer holds.
5373    ///
5374    /// The first half is a function that would have kept no frame pointer at all, since it is a
5375    /// leaf with no locals, and keeps one because it asked where its frame is. The answer being
5376    /// `%rbp` rather than an offset off `%rsp` is the whole of the builtin at a depth of zero.
5377    ///
5378    /// The second half is the walk. Each link above zero is one load through the register the last
5379    /// one wrote, so a depth of two is two loads and a depth of three is three, which is what gcc
5380    /// 16.2.0 writes for the same programs at `-O2`.
5381    #[test]
5382    fn the_frame_address_is_the_frame_pointer_after_walking_that_many_links() {
5383        let text = asm("void *here(void) { return __builtin_frame_address(0); }\n");
5384        assert!(text.contains("pushq\t%rbp"), "a function that asks keeps a frame pointer: {text}");
5385        assert!(text.contains("movq\t%rbp, %rax"), "{text}");
5386        assert!(!text.contains("\tcall"), "a frame address is not a call to anything: {text}");
5387
5388        let walk = |depth: u32| {
5389            let source = format!("void *up(void) {{ return __builtin_frame_address({depth}); }}\n");
5390            asm(&source).matches("movq\t(%r").count()
5391        };
5392        assert_eq!(walk(1), 1, "one link is one load");
5393        assert_eq!(walk(3), 3, "three links are three loads");
5394    }
5395
5396    /// The address a frame returns to, which is one word above the frame the walk ended at.
5397    ///
5398    /// A word is eight bytes here and the `8(...)` is the whole claim: the call instruction pushed
5399    /// the return address and the prologue pushed the caller's frame pointer under it, so what the
5400    /// frame pointer points at is the link and what is above it is where control goes back to.
5401    /// gcc 16.2.0 writes `movq 8(%rbp), %rax` for the first of these, measured at `-O2`.
5402    ///
5403    /// The second half is the same walk the frame address does, with the load at the end of it
5404    /// reading one word further along rather than the register itself being the answer.
5405    #[test]
5406    fn the_return_address_is_one_word_above_the_frame_the_walk_ended_at() {
5407        let text = asm("void *back(void) { return __builtin_return_address(0); }\n");
5408        assert!(text.contains("pushq\t%rbp"), "a function that asks keeps a frame pointer: {text}");
5409        assert!(text.contains("movq\t8(%rbp), %rax"), "{text}");
5410        assert!(!text.contains("\tcall"), "a return address is not a call to anything: {text}");
5411
5412        let text = asm("void *back(void) { return __builtin_return_address(2); }\n");
5413        assert_eq!(text.matches("movq\t(%r").count(), 2, "two links are two loads: {text}");
5414        assert!(text.contains("movq\t8(%r"), "and the answer is above the last of them: {text}");
5415    }
5416
5417    /// A depth that is not a constant is refused, and so is one past the limit.
5418    ///
5419    /// The first is gcc's rule and not a convenience: what the call becomes is a walk that many
5420    /// links long, written out, so a number that is not known until the program runs has nothing
5421    /// to walk. gcc 16.2.0 says `invalid argument to '__builtin_return_address'` for the same
5422    /// program.
5423    ///
5424    /// The second is where this and gcc part company. gcc writes the walk however long it is, and
5425    /// this refuses a depth no program has a use for rather than filling an object file with loads
5426    /// that fault part way up.
5427    #[test]
5428    fn a_depth_that_is_not_a_small_constant_is_refused() {
5429        let mut opts = options();
5430        opts.emit = EmitKind::Ir;
5431        for source in [
5432            "void *up(int n) { return __builtin_return_address(n); }\n",
5433            "void *up(void) { return __builtin_frame_address(1000); }\n",
5434        ] {
5435            let messages = run(&opts, source).messages;
5436            let named = messages.iter().any(|m| m.contains("E0705"));
5437            assert!(named, "expected a refusal in {messages:?}");
5438        }
5439    }
5440
5441    /// Bytes off the frame, which is the stack pointer moving down and the answer being where it
5442    /// moved to.
5443    ///
5444    /// The rounding is the alignment: the size is taken up to the next sixteen before it is
5445    /// subtracted, so the pointer suits anything the program puts behind it. gcc 16.2.0 rounds the
5446    /// same way at `-O0` and spends a division doing it, which is the one place the two differ and
5447    /// is about how the rounding is written rather than about what it answers.
5448    ///
5449    /// There is no call anywhere in either program. An alloca that had reached the linker would
5450    /// have found the C library's, which is a real function with a real frame and is not what a
5451    /// program writing the builtin asked for.
5452    #[test]
5453    fn an_alloca_takes_the_bytes_off_the_stack_pointer_and_answers_where_they_are() {
5454        let text =
5455            asm("void use(void *p); void f(unsigned long n) { use(__builtin_alloca(n)); }\n");
5456        assert!(text.contains("andq\t$-16"), "the size is rounded up to sixteen: {text}");
5457        assert!(text.contains("subq\t%rdi, %rsp"), "and taken off the stack pointer: {text}");
5458        assert_eq!(text.matches("\tcall").count(), 1, "the only call is the one written: {text}");
5459
5460        // The plain name, which a program that declares it the way the C library does means the
5461        // same thing by. `gcc.c-torture/execute/20010122-1.c` is exactly this program.
5462        let plain = concat!(
5463            "extern void *alloca(__SIZE_TYPE__);\n",
5464            "void use(void *p);\n",
5465            "void f(unsigned long n) { use(alloca(n)); }\n",
5466        );
5467        let text = asm(plain);
5468        assert!(text.contains("subq\t%rdi, %rsp"), "the plain name is the same bytes: {text}");
5469        assert_eq!(text.matches("\tcall").count(), 1, "and is not a call either: {text}");
5470
5471        // And a program that means something of its own by the name keeps it, which is what the
5472        // declaration is looked at for.
5473        let own = concat!(
5474            "static void *alloca(unsigned long n) { return 0; }\n",
5475            "void *f(unsigned long n) { return alloca(n); }\n",
5476        );
5477        assert!(asm(own).contains("\tcall"), "a name the program took back is a call");
5478    }
5479
5480    /// A name nothing declared that the implementation knows the type of is declared with that
5481    /// type rather than with the `extern int f()` C89 6.3.2.2 writes down.
5482    ///
5483    /// That is gcc's rule and it is measurable: gcc 16.2.0 compiles an undeclared `alloca` with
5484    /// no call in it at all, and says `incompatible implicit declaration of built-in function`
5485    /// beside the implicit declaration warning. A C89 declaration would have made the call return
5486    /// an `int` and reach a function no C library defines, since every header that offers
5487    /// `alloca` offers it as a macro for the builtin. Four torture programs turn on it,
5488    /// `execute/20020314-1.c`, `20040223-1.c`, `941202-1.c` and `pr22061-1.c`, each of which
5489    /// calls `alloca` with nothing above it.
5490    ///
5491    /// The rule is the builtin table's rather than this one name's, so an undeclared `strlen` is
5492    /// the builtin too. What it is not is a declaration the program wrote that disagrees with the
5493    /// builtin's type, which gcc keeps and calls, and that was measured as well.
5494    #[test]
5495    fn a_builtin_the_program_never_declared_is_the_builtin_rather_than_the_one_c89_wrote_down() {
5496        // `-fpermissive`, because the implicit declaration itself is an error in every dialect
5497        // after C89 and the program would never get as far as a type without it. Each of the four
5498        // torture programs asks for either that or `-std=gnu89` on its own options line.
5499        let mut opts = options();
5500        opts.permissive = true;
5501        let undeclared = "void use(void *p);
5502void f(unsigned long n) { use(alloca(n)); }
5503";
5504        assert_eq!(
5505            run(&opts, undeclared).messages,
5506            [
5507                "/main.c:2:31: warning: implicit declaration of function 'alloca' [E0521]",
5508                "/main.c:2:31: warning: incompatible implicit declaration of built-in function \
5509                 'alloca' [E0713]",
5510            ]
5511        );
5512
5513        opts.emit = EmitKind::Asm;
5514        let text = run(&opts, undeclared).text().to_owned();
5515        assert!(text.contains("subq\t%rdi, %rsp"), "the bytes come off the stack: {text}");
5516        assert_eq!(text.matches("\tcall").count(), 1, "the only call is the one written: {text}");
5517
5518        // The table's rule and not this one name's, so a name whose whole answer is the library
5519        // function of the same name gets that function's type and still reaches it.
5520        let string = "unsigned long f(void) { return strlen(\"abc\"); }\n";
5521        let text = run(&opts, string).text().to_owned();
5522        assert!(text.contains("call\tstrlen"), "strlen is still a call: {text}");
5523
5524        // A declaration the program wrote is the program's, whatever the table says. gcc keeps
5525        // this one and writes the call, which is what makes the type worth looking at.
5526        let own = concat!(
5527            "static void *alloca(unsigned long n) { return 0; }\n",
5528            "void *f(unsigned long n) { return alloca(n); }\n",
5529        );
5530        assert!(asm(own).contains("\tcall"), "a name the program took back is a call");
5531    }
5532
5533    /// The bytes an alloca took live until the function returns and not until the end of the block
5534    /// the call was written in.
5535    ///
5536    /// That is what makes it different from a variable length array, and the way it is kept is that
5537    /// every scope open where the call was written stops giving the stack back. The second program
5538    /// is the mixed case: an array in the outer block and an alloca in the inner one, where the
5539    /// inner block gives nothing back either even though an array is in scope that ordinarily
5540    /// would. gcc 16.2.0 at `-O0` writes no restore at the end of either block, measured rather
5541    /// than read off the manual.
5542    #[test]
5543    fn the_bytes_an_alloca_took_are_still_there_at_the_end_of_the_block_that_took_them() {
5544        let inner = "{ use(__builtin_alloca(n)); }";
5545        for body in [inner.to_owned(), format!("int a[n]; {inner} use(a);")] {
5546            let source = format!("void use(void *p);\nvoid f(unsigned long n) {{ {body} }}\n");
5547            let text = asm(&source);
5548            // Every instruction that writes the stack pointer, which in a function that gives
5549            // nothing back is the alloca taking bytes and the epilogue putting the frame pointer
5550            // there. A restore would be a third kind, a move out of a register the save wrote.
5551            for line in text.lines().filter(|line| line.trim_end().ends_with(", %rsp")) {
5552                let taking = line.contains("subq");
5553                let leaving = line.contains("%rbp");
5554                assert!(taking || leaving, "nothing puts the stack back: {line} in {text}");
5555            }
5556        }
5557    }
5558
5559    /// Not a rewording of the check above: what the two paths agree about is the point.
5560    #[test]
5561    fn the_object_and_the_listing_are_two_spellings_of_one_compilation() {
5562        // A call, because it is the one thing whose spelling in the two differs completely: the
5563        // listing writes a name and the object writes four zero bytes and a relocation asking the
5564        // linker for the same name. If either path had lost the callee, one of these would fail.
5565        let source = "int callee(void); int g(void) { return callee(); }\n";
5566        let bytes = obj(source);
5567        assert!(
5568            bytes.windows(7).any(|w| w == b"callee\0"),
5569            "the object has to name the callee for the linker to find it"
5570        );
5571        let text = asm(source);
5572        assert!(text.contains("\tcall\tcallee\n"), "{text}");
5573    }
5574
5575    /// What a file of a link contributes is an object, and the default emit is a link.
5576    ///
5577    /// This is here because getting it wrong is silent in the worst way: an empty file is a valid
5578    /// empty linker script, so a link fed one gets as far as reporting every symbol of the file as
5579    /// undefined and says nothing about the compilation that produced nothing.
5580    #[test]
5581    fn compiling_for_an_executable_produces_an_object_and_not_a_dump() {
5582        let mut opts = options();
5583        // What a command line with no `-c` and no `-S` on it asks for.
5584        opts.emit = EmitKind::Executable;
5585        let result = run(&opts, "int main(void) { return 0; }\n");
5586        assert_eq!(result.messages, Vec::<String>::new());
5587        match result.artifact {
5588            Artifact::Object { bytes, .. } => assert_eq!(&bytes[..4], b"\x7fELF"),
5589            other => panic!("expected an object, got {other:?}"),
5590        }
5591    }
5592
5593    /// A target with a back end but no object writer says so rather than writing the wrong file.
5594    #[test]
5595    fn a_platform_with_no_object_writer_is_said_so_rather_than_written_as_elf() {
5596        let mut opts = options();
5597        opts.emit = EmitKind::Object;
5598        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
5599        let result = run(&opts, "int f(void) { return 0; }\n");
5600        assert!(result.failed(), "an object nobody can read is worse than a message");
5601        assert!(
5602            result.messages.iter().any(|m| m.contains("no object writer")),
5603            "{:?}",
5604            result.messages
5605        );
5606    }
5607
5608    /// The IR of `source`, insisting that it compiled cleanly.
5609    fn ir(source: &str) -> String {
5610        let mut opts = options();
5611        opts.emit = EmitKind::Ir;
5612        let result = run(&opts, source);
5613        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5614        result.text().to_owned()
5615    }
5616
5617    /// What was said about `source`, insisting that something was.
5618    fn errors(source: &str) -> Vec<String> {
5619        let mut opts = options();
5620        opts.emit = EmitKind::Ir;
5621        let result = run(&opts, source);
5622        assert!(result.failed(), "expected this to be refused:\n{source}");
5623        result.messages
5624    }
5625
5626    /// The body of the one function in `source`, which is what most of these are about.
5627    fn body(source: &str) -> String {
5628        let text = ir(source);
5629        let (_, rest) = text.split_once("{\n").expect("a function definition");
5630        let (body, _) = rest.rsplit_once("}\n").expect("a function definition");
5631        body.to_owned()
5632    }
5633
5634    /// What `-fgnu89-inline` is for, seen at the only place it shows: whether a body reached the
5635    /// module or only a declaration did.
5636    ///
5637    /// The C99 reading is the one an inline definition is written for and is not being changed
5638    /// here. What the flag is for is a program written before C99 swapped the two, which relies on
5639    /// `inline` alone leaving something behind for another unit to call, and there are twelve of
5640    /// those in the GCC torture suite alone.
5641    #[test]
5642    fn gnu89_inline_is_what_decides_whether_a_bare_inline_definition_reaches_the_module() {
5643        let source = "inline int f(int x) { return x + 1; }\n";
5644        let with = |flag: bool| {
5645            let mut opts = options();
5646            opts.emit = EmitKind::Ir;
5647            opts.gnu89_inline = flag;
5648            let result = run(&opts, source);
5649            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
5650            result.text().to_owned()
5651        };
5652
5653        // Under C's reading the module holds the declaration and the calls in this unit go to
5654        // whatever definition another unit has, which is C 6.7.4p7 and is what gcc does too.
5655        assert!(!with(false).contains("block0"), "no body: {}", with(false));
5656
5657        // Under GNU's it is an ordinary external definition, so the body is there and the symbol
5658        // is one the linker can resolve against.
5659        assert!(with(true).contains("block0"), "a body: {}", with(true));
5660    }
5661
5662    /// Every shape that reads or writes through a C type names that type.
5663    ///
5664    /// The tree itself is `rucc_lower::aliasing`'s and is tested there. What this is about is that
5665    /// the walk reaches it from every shape a program actually writes, since a node on the scalar
5666    /// load and nothing on the member load would be a layer that answers for a third of the
5667    /// accesses in a program and is not worth having.
5668    #[test]
5669    fn an_access_through_a_type_names_the_type_it_went_through() {
5670        let source = "\
5671struct s { int a; float b; };\n\
5672union u { int i; float f; };\n\
5673int scalar(int *p) { return *p; }\n\
5674float member(struct s *p) { p->a = 1; return p->b; }\n\
5675int element(int *a, long i) { return a[i]; }\n\
5676float through_a_union(union u *p) { p->i = 1; return p->f; }\n";
5677        let text = ir(source);
5678        assert!(text.contains(r#"!0 = tbaa "char""#), "the root: {text}");
5679        assert!(text.contains(r#"tbaa "int", parent !0"#), "int under it: {text}");
5680        assert!(text.contains(r#"tbaa "float", parent !0"#), "float under it: {text}");
5681        // One per access, and a function whose accesses all go through one type says so once per
5682        // access rather than once per function.
5683        let named = text.lines().filter(|line| line.contains(", tbaa !")).count();
5684        assert_eq!(named, 6, "six accesses: {text}");
5685    }
5686
5687    /// `-fno-strict-aliasing` is the front end leaving the name off.
5688    ///
5689    /// Nothing asks the alias analysis anything yet, so no program compiles differently for having
5690    /// passed this today. What this test is for is the day one does: the flag has to be the
5691    /// absence of the names rather than a condition somewhere downstream, since that is the only
5692    /// version of it that a pass added later cannot forget about.
5693    #[test]
5694    fn turning_strict_aliasing_off_leaves_the_type_off_every_access() {
5695        let source = "int punned(float *f, int *i) { *i = 1; *f = 2.0f; return *i; }\n";
5696        let mut opts = options();
5697        opts.emit = EmitKind::Ir;
5698        opts.strict_aliasing = false;
5699        let result = run(&opts, source);
5700        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
5701        let text = result.text().to_owned();
5702        assert!(!text.contains("tbaa"), "not even the root: {text}");
5703    }
5704
5705    /// `-finstrument-functions` puts one call to the entry hook in front of the body and one call
5706    /// to the exit hook in front of every return, each given the function's own address and the
5707    /// address it returns to. A function declared `no_instrument_function` gets neither, and the
5708    /// hooks are declared that way here as they are in `execute/eeprof-1.c`, since a hook that
5709    /// called itself would never get as far as its body.
5710    #[test]
5711    fn instrumenting_functions_calls_the_hooks_around_every_body_but_the_hooks() {
5712        let source = concat!(
5713            "#define NOCHK __attribute__((no_instrument_function))\n",
5714            "void __cyg_profile_func_enter(void *, void *) NOCHK;\n",
5715            "void __cyg_profile_func_exit(void *, void *) NOCHK;\n",
5716            "int calls;\n",
5717            "int pick(int x) { if (x) return 1; return 2; }\n",
5718            "void quiet(void) NOCHK;\n",
5719            "void quiet(void) { calls++; }\n",
5720            "void __cyg_profile_func_enter(void *fn, void *site) { calls++; }\n",
5721            "void __cyg_profile_func_exit(void *fn, void *site) { calls--; }\n",
5722        );
5723        let mut opts = options();
5724        opts.emit = EmitKind::Ir;
5725        opts.instrument_functions = true;
5726        let result = run(&opts, source);
5727        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
5728        let text = result.text().to_owned();
5729        let body = |name: &str| -> String {
5730            let open = format!("func @{name}(");
5731            let start = text.find(&open).unwrap_or_else(|| panic!("no {name}: {text}"));
5732            let rest = &text[start..];
5733            rest[..rest.find("\n}").unwrap_or(rest.len())].to_owned()
5734        };
5735        let pick = body("pick");
5736        assert_eq!(pick.matches("call @__cyg_profile_func_enter(").count(), 1, "{pick}");
5737        assert_eq!(pick.matches("call @__cyg_profile_func_exit(").count(), 2, "{pick}");
5738        assert!(pick.contains("return_address"), "{pick}");
5739        assert!(pick.contains("global_addr @pick"), "{pick}");
5740        for quiet in ["quiet", "__cyg_profile_func_enter", "__cyg_profile_func_exit"] {
5741            assert!(!body(quiet).contains("call "), "{quiet} is left alone: {text}");
5742        }
5743
5744        opts.instrument_functions = false;
5745        let result = run(&opts, source);
5746        assert!(!result.text().contains("call @__cyg_profile"), "off unless asked for");
5747    }
5748
5749    /// Calls whose open scopes owe the same handlers share one landing pad, as gcc's do, and a call
5750    /// after another object is declared, or once a scope has closed, gets the pad for what it owes
5751    /// then. Here that is two pads for six calls. A handler is a call like any other, so one that
5752    /// runs while an object further out still owes its own gets an edge to the pad for that.
5753    #[test]
5754    fn calls_that_owe_the_same_handlers_share_one_landing_pad() {
5755        let source = concat!(
5756            "void done(int *p);\n",
5757            "void work(int);\n",
5758            "void f(void) {\n",
5759            "  int a __attribute__((cleanup(done))) = 1;\n",
5760            "  work(1); work(2);\n",
5761            "  { int b __attribute__((cleanup(done))) = 2; work(3); work(4); }\n",
5762            "  work(5); work(6);\n",
5763            "}\n",
5764        );
5765        let mut opts = options();
5766        opts.emit = EmitKind::Ir;
5767        opts.exceptions = true;
5768        let result = run(&opts, source);
5769        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5770        let text = result.text();
5771        // Six for the calls to `work`, and one for the call to `done` that `b`'s scope makes on
5772        // the way out, which still owes `a`'s. The one `b`'s pad makes goes to `a`'s pad too.
5773        assert_eq!(text.matches("= unwound").count(), 8, "every call has its edge: {text}");
5774        assert_eq!(text.matches("= landing").count(), 2, "one pad for a, one for b and a: {text}");
5775    }
5776
5777    /// A `goto` out of two scopes runs their handlers in front of its branch, and under
5778    /// `-fexceptions` each one but the last is followed by an edge to the pad for the ones still
5779    /// owed, as a handler at the end of a scope is. The branch goes after them.
5780    #[test]
5781    fn a_goto_that_runs_handlers_gives_each_one_an_edge_to_what_is_still_owed() {
5782        let source = concat!(
5783            "void done(int *p);\n",
5784            "void f(int n) {\n",
5785            "  int a __attribute__((cleanup(done))) = 1;\n",
5786            "  { int b __attribute__((cleanup(done))) = 2;\n",
5787            "    { int c __attribute__((cleanup(done))) = 3; if (n) goto out; }\n",
5788            "  }\n",
5789            "out:\n",
5790            "  return;\n",
5791            "}\n",
5792        );
5793        let mut opts = options();
5794        opts.emit = EmitKind::Ir;
5795        opts.exceptions = true;
5796        let result = run(&opts, source);
5797        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5798        let text = result.text();
5799        // The goto's two for c and b, the two at the ends of the scopes of c and b, and the one
5800        // the pad for c makes after b's handler. The pad for b only runs a's, so it has none.
5801        assert_eq!(text.matches("= unwound").count(), 5, "{text}");
5802        assert_eq!(text.matches("= landing").count(), 2, "{text}");
5803    }
5804
5805    /// Under `-fexceptions` a `cleanup` handler is owed a call on an unwind as well. On x86-64 ELF
5806    /// a call inside a handler's scope gets a landing pad that runs the handler and resumes the
5807    /// unwind, a handler with no call in its scope needs none, and without the flag the same source
5808    /// compiles as it always did. Everywhere else the call is turned down by name, since no pad is
5809    /// built there.
5810    #[test]
5811    fn a_call_an_unwind_would_leave_a_cleanup_behind_gets_a_landing_pad_under_exceptions() {
5812        let source = concat!(
5813            "void done(int *p);\n",
5814            "void work(void);\n",
5815            "void calls(void) { int x __attribute__((cleanup(done))) = 1; work(); }\n",
5816            "int quiet(int y) { int x __attribute__((cleanup(done))) = y; return x + 1; }\n",
5817            "void after(void) { { int x __attribute__((cleanup(done))) = 1; } work(); }\n",
5818        );
5819        let mut opts = options();
5820        opts.emit = EmitKind::Ir;
5821        let result = run(&opts, source);
5822        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
5823        assert!(!result.text().contains("landing"), "{}", result.text());
5824
5825        opts.exceptions = true;
5826        let result = run(&opts, source);
5827        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5828        let text = result.text();
5829        assert_eq!(text.matches("= landing").count(), 1, "only the call in calls: {text}");
5830        assert!(text.contains("_Unwind_Resume"), "{text}");
5831
5832        opts.emit = EmitKind::Asm;
5833        let result = run(&opts, source);
5834        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5835        let text = result.text();
5836        assert!(text.contains(".cfi_personality 0x9b,DW.ref.__gcc_personality_v0"), "{text}");
5837        assert!(text.contains(".cfi_lsda 0x1b,.LLSDA_calls"), "{text}");
5838        assert!(text.contains(".gcc_except_table"), "{text}");
5839        assert_eq!(text.matches(".cfi_lsda").count(), 1, "{text}");
5840
5841        opts.emit = EmitKind::Object;
5842        let result = run(&opts, source);
5843        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5844        let bytes = result.artifact.bytes();
5845        let has = |what: &[u8]| bytes.windows(what.len()).any(|window| window == what);
5846        assert!(has(b".gcc_except_table\0"), "the call site table has a section");
5847        assert!(has(b"zPLR\0"), "a header naming the personality routine");
5848        assert!(has(b"zR\0"), "and the plain one for the functions with no pad");
5849        assert!(has(b"DW.ref.__gcc_personality_v0\0"), "the pointer the header reads through");
5850
5851        // A Mach-O target, whose unwind table is written without either, is refused rather than
5852        // given a pad the unwinder would never send it to.
5853        opts.emit = EmitKind::Ir;
5854        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
5855        let result = run(&opts, source);
5856        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
5857        assert!(result.messages[0].contains("landing pad"), "{:?}", result.messages);
5858        assert!(result.messages[0].contains(":3:"), "the call in calls: {:?}", result.messages);
5859    }
5860
5861    /// An `asm` at file scope with an instruction in it, which is how a unit writes a whole
5862    /// function in assembly. The template goes into the listing as it was written, between the
5863    /// markers gcc writes, and an object is assembled from that listing, so the function it
5864    /// defines is defined in the object and the C that calls it calls it there. tcc's
5865    /// `85_asm-outside-function.c` and `98_al_ax_extend.c` are this.
5866    #[test]
5867    fn an_asm_at_file_scope_with_an_instruction_in_it_is_assembled() {
5868        let source = concat!(
5869            "extern void vide(void);\n",
5870            "__asm__(\".text;.globl _us;_us:;movl $0x1234ABCD, %eax;ret\");\n",
5871            "__asm__(\"vide: ret\");\n",
5872            "unsigned short _us(void);\n",
5873            "int main(void) { vide(); return _us() == 0xABCD ? 0 : 1; }\n",
5874        );
5875        let mut opts = options();
5876        opts.emit = EmitKind::Ir;
5877        let result = run(&opts, source);
5878        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5879        assert_eq!(result.text().matches("module asm ").count(), 2, "{}", result.text());
5880
5881        opts.emit = EmitKind::Asm;
5882        let result = run(&opts, source);
5883        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5884        let text = result.text();
5885        assert!(text.contains("#APP\nvide: ret\n#NO_APP\n"), "{text}");
5886        let main = text.find("main:").expect("main");
5887        assert!(text.find("#NO_APP").expect("the markers") < main, "templates first: {text}");
5888
5889        opts.emit = EmitKind::Object;
5890        let result = run(&opts, source);
5891        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5892        let (bytes, defines) = match result.artifact {
5893            Artifact::Object { bytes, defines } => (bytes, defines),
5894            other => panic!("expected an object, got {other:?}"),
5895        };
5896        assert!(defines.iter().any(|name| name == "_us"), "{defines:?}");
5897        // `mov $0x1234abcd, %eax` and the `ret` after it, which only the assembler wrote.
5898        let us = [0xb8, 0xcd, 0xab, 0x34, 0x12, 0xc3];
5899        assert!(bytes.windows(us.len()).any(|window| window == us), "the template's bytes");
5900
5901        // Elsewhere there is no reader for the listing, so the template is still refused there.
5902        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
5903        opts.emit = EmitKind::Ir;
5904        let result = run(&opts, source);
5905        assert!(!result.messages.is_empty(), "refused on Mach-O");
5906        assert!(result.messages[0].contains("the instruction 'movl'"), "{:?}", result.messages);
5907    }
5908
5909    /// `return;` from a function that promised a value, which only C89 lets through and which
5910    /// therefore only reaches the IR builder under that dialect.
5911    ///
5912    /// Zero goes back. The alternatives are worse: an empty return list builds a `ret` the
5913    /// verifier refuses, which is what a torture case found, and `unreachable` would be a claim
5914    /// that the branch reaching this never runs, which is a claim about the program rather than
5915    /// about the value and lets the optimizer delete the path that led here.
5916    #[test]
5917    fn a_bare_return_from_a_function_that_promised_a_value_gives_back_a_zero() {
5918        let mut opts = options();
5919        opts.emit = EmitKind::Ir;
5920        opts.std = Std::C89;
5921        let compiled = |source: &str| {
5922            let result = run(&opts, source);
5923            assert_eq!(result.messages, Vec::<String>::new(), "C89 has nothing to say about this");
5924            result.text().to_owned()
5925        };
5926
5927        let text = compiled("int f(int x) { if (x) return; return 3; }\n");
5928        assert!(text.contains("iconst.i32 0\n    return"), "zero goes back: {text}");
5929        assert!(!text.contains("unreachable"), "the branch that reached it is kept: {text}");
5930
5931        // A floating point return needs the constant of its own kind rather than an integer one.
5932        let text = compiled("double f(int x) { if (x) return; return 1.0; }\n");
5933        assert!(text.contains("fconst.f64 0x0\n    return"), "a float zero goes back: {text}");
5934    }
5935
5936    /// What C89 6.3.2.2 declares for a call to a name nothing declared, seen in the IR rather than
5937    /// in what was said about it.
5938    ///
5939    /// `extern int f();`, so the call gives back an `int` and its arguments are promoted rather
5940    /// than converted to parameters there are none of. The declaration lasts for the file, which
5941    /// is what makes a second call to the same name ordinary and is why gcc says this once per
5942    /// file rather than once per call.
5943    #[test]
5944    fn a_call_to_a_name_nothing_declared_declares_it_as_c89_said_to() {
5945        let mut opts = options();
5946        opts.emit = EmitKind::Ir;
5947        opts.std = Std::C89;
5948        let compiled = |source: &str| {
5949            let result = run(&opts, source);
5950            assert_eq!(result.messages, Vec::<String>::new(), "C89 has nothing to say about this");
5951            result.text().to_owned()
5952        };
5953
5954        // An `int` back, which is the whole of what the implicit declaration says.
5955        let text = compiled("int f(void) { return g(); }\n");
5956        assert!(text.contains("call @g"), "the call is to the name that was written: {text}");
5957        assert!(text.contains("i32"), "and it gives back an int: {text}");
5958
5959        // No prototype, so a `char` argument arrives promoted to `int` the way an argument to a
5960        // function whose parameters are unspecified does.
5961        let text = compiled("int f(char c) { return g(c); }\n");
5962        assert!(text.contains("sext.i32"), "the argument is promoted: {text}");
5963
5964        // A name written as a value rather than called is still undeclared, since the rule is
5965        // about a call and nothing else.
5966        let mut opts = options();
5967        opts.std = Std::C89;
5968        let said = run(&opts, "int f(void) { return h; }\n").messages.join("\n");
5969        assert!(said.contains("'h' undeclared"), "not a call, so not declared: {said}");
5970    }
5971
5972    /// A file that calls a name above the definition of it, which is the shape the implicit
5973    /// declaration has to survive rather than swallow.
5974    ///
5975    /// The definition merges into the declaration the call already made rather than making a
5976    /// second one, so a declaration the tree does not carry at the top level takes the definition
5977    /// down with it: the body is attached to a node nothing walks and no function comes out.
5978    /// Nothing about the call itself looks wrong when that happens, and the program gets to the
5979    /// linker before anyone finds out, which is where `execute/cmpsi-1.c` in the torture suite
5980    /// found it, as an undefined reference to a name defined eleven lines further down.
5981    #[test]
5982    fn a_name_called_before_it_is_defined_still_gets_its_definition() {
5983        let mut opts = options();
5984        opts.emit = EmitKind::Ir;
5985        opts.std = Std::C89;
5986        let text = run(&opts, "int f(void) { return dummy(); }\ndummy () { return 7; }\n")
5987            .text()
5988            .to_owned();
5989        assert!(text.contains("func @f()"), "the caller is there: {text}");
5990        assert!(text.contains("func @dummy"), "and so is what it calls: {text}");
5991        assert!(text.contains("iconst.i32 7"), "with the body it was given: {text}");
5992    }
5993
5994    /// An old style definition whose parameter is narrower than what a call passes it.
5995    ///
5996    /// There is no prototype for a call to convert its argument to, so the argument is promoted
5997    /// and an `int` arrives for a parameter the body reads as an `unsigned char`. The entry block
5998    /// is where the two meet, and gcc writes the same pair of instructions there: store the low
5999    /// byte, read it back widened. `execute/950605-1.c` in the torture suite calls `f(-1)` and
6000    /// checks the parameter against `0xFF`, which is the difference between converting and not.
6001    #[test]
6002    fn an_old_style_parameter_is_converted_from_what_the_call_promoted_it_to() {
6003        let mut opts = options();
6004        opts.emit = EmitKind::Ir;
6005        opts.std = Std::C89;
6006        let compiled = |source: &str| run(&opts, source).text().to_owned();
6007
6008        let text = compiled("f (c) unsigned char c; { return c; }\n");
6009        assert!(text.contains("func @f(i32"), "an int arrives: {text}");
6010        assert!(text.contains("trunc.i8"), "and is cut down to what was declared: {text}");
6011        assert!(text.contains("zext.i32"), "then read back unsigned: {text}");
6012
6013        // A `short` is the same shape and signed, so it comes back the other way.
6014        let text = compiled("f (s) short s; { return s; }\n");
6015        assert!(text.contains("trunc.i16"), "cut down: {text}");
6016        assert!(text.contains("sext.i32"), "and read back signed: {text}");
6017
6018        // A `float` parameter is promoted to `double`, and without the conversion the multiply
6019        // below has one f64 operand and one f32, which the verifier refuses as invalid IR.
6020        let text = compiled("f (x) float x; { return x * 2; }\n");
6021        assert!(text.contains("func @f(f64"), "a double arrives: {text}");
6022        assert!(text.contains("fptrunc.f32"), "and is narrowed to the float: {text}");
6023
6024        // A parameter a prototype named arrives as itself and nothing is converted, which is the
6025        // case this must not have changed.
6026        let text = compiled("int f(unsigned char c) { return c; }\n");
6027        assert!(text.contains("func @f(i8)"), "the declared type arrives: {text}");
6028        assert!(!text.contains("trunc"), "so there is nothing to cut down: {text}");
6029    }
6030
6031    /// The six rules gcc 14 turned from a warning into an error, and the three answers each one
6032    /// gets depending on the dialect and on `-fpermissive`.
6033    ///
6034    /// The table is a measurement rather than a reading of the release notes. Six files, one per
6035    /// rule, put through gcc 16.2.0 on x86-64 Linux under each of the four command lines below
6036    /// with no `-W` flags on any of them, and what came back is what is written here. The three
6037    /// rules that say nothing under C89 are the three C89 did not have, and the three that warn
6038    /// there were constraint violations then as well.
6039    #[test]
6040    fn the_rules_gcc_promoted_are_decided_by_the_dialect_and_by_fpermissive() {
6041        // `-std=gnu89`, `-std=gnu17`, `-std=gnu17 -fpermissive`, and `-std=gnu23`.
6042        let modes = [(Std::C89, false), (Std::C17, false), (Std::C17, true), (Std::C23, false)];
6043        let cases = [
6044            ("static counted;\n", ["", "error", "warning", "error"]),
6045            ("int f(void) { return g(); }\n", ["", "error", "warning", "error"]),
6046            ("int f(x) { return x; }\n", ["", "error", "warning", "error"]),
6047            ("int *p;\nvoid h(void) { p = 1; }\n", ["warning", "error", "warning", "error"]),
6048            (
6049                "char *q;\nint *r;\nvoid k(void) { r = q; }\n",
6050                ["warning", "error", "warning", "error"],
6051            ),
6052            ("int f(void) { return; }\n", ["", "error", "warning", "error"]),
6053            ("void g(void) { return 1; }\n", ["warning", "error", "warning", "error"]),
6054        ];
6055
6056        for (source, wanted) in cases {
6057            for (&(std, permissive), wanted) in modes.iter().zip(wanted) {
6058                let mut opts = options();
6059                opts.std = std;
6060                opts.permissive = permissive;
6061                let said = run(&opts, source).messages.join("\n");
6062                let severity = if said.contains(": error: ") {
6063                    "error"
6064                } else if said.contains(": warning: ") {
6065                    "warning"
6066                } else {
6067                    ""
6068                };
6069                let how = if permissive { " -fpermissive" } else { "" };
6070                assert_eq!(
6071                    severity,
6072                    wanted,
6073                    "under -std={}{how}, {source} was answered with `{said}`",
6074                    std.as_str()
6075                );
6076                if wanted.is_empty() {
6077                    assert!(said.is_empty(), "nothing to say, but said `{said}`");
6078                }
6079            }
6080        }
6081    }
6082
6083    /// A first argument that is not a list, which the four variadic operators answer in two ways.
6084    ///
6085    /// gcc has `va_arg` as an operator, since it takes a type name and no function can, and the
6086    /// other three as builtin functions taking the address of a list. The difference is not a
6087    /// naming one: the operator's complaint is its own and is an error under every dialect, and
6088    /// the three functions go through the ordinary rule about an argument of the wrong type,
6089    /// which is one of the rules the table above is about. The same four command lines through
6090    /// gcc 16.2.0 on x86-64 Linux is where these came from.
6091    #[test]
6092    fn the_three_variadic_builtins_answer_a_bad_list_the_way_a_call_answers_a_bad_argument() {
6093        let modes = [(Std::C89, false), (Std::C17, false), (Std::C17, true), (Std::C23, false)];
6094        let cases = [
6095            (
6096                "int f(int n, ...) { char *p; return __builtin_va_arg(p, int); }\n",
6097                "first argument to 'va_arg' not of type 'va_list'",
6098                ["error", "error", "error", "error"],
6099            ),
6100            (
6101                "void f(int n, ...) { char *p; __builtin_va_start(p, n); }\n",
6102                "passing argument 1 of '__builtin_va_start' from incompatible pointer type",
6103                ["warning", "error", "warning", "error"],
6104            ),
6105            (
6106                "void f(int n, ...) { int x; __builtin_va_end(x); }\n",
6107                "passing argument 1 of '__builtin_va_end' makes pointer from integer without a \
6108                 cast",
6109                ["warning", "error", "warning", "error"],
6110            ),
6111            (
6112                "void f(int n, ...) { __builtin_va_list a; char *p; __builtin_va_copy(a, p); }\n",
6113                "passing argument 2 of '__builtin_va_copy' from incompatible pointer type",
6114                ["warning", "error", "warning", "error"],
6115            ),
6116        ];
6117
6118        for (source, message, wanted) in cases {
6119            for (&(std, permissive), wanted) in modes.iter().zip(wanted) {
6120                let mut opts = options();
6121                opts.std = std;
6122                opts.permissive = permissive;
6123                let said = run(&opts, source).messages.join("\n");
6124                let how = if permissive { " -fpermissive" } else { "" };
6125                assert!(
6126                    said.contains(&format!(": {wanted}: {message}")),
6127                    "under -std={}{how}, {source} was answered with `{said}`",
6128                    std.as_str()
6129                );
6130            }
6131        }
6132    }
6133
6134    /// The IR of `source` at one safety tier, insisting that it compiled cleanly.
6135    fn safe_ir(tier: rucc_session::Safety, source: &str) -> String {
6136        let mut opts = options();
6137        opts.emit = EmitKind::Ir;
6138        opts.safety = tier;
6139        let result = run(&opts, source);
6140        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
6141        result.text().to_owned()
6142    }
6143
6144    const READS_THROUGH_A_POINTER: &str = "int read(int *p) { return p[1]; }\n";
6145
6146    /// The IR for a source built with a tier and a padding mode.
6147    fn padded_ir(padding: Padding, source: &str) -> String {
6148        let mut opts = options();
6149        opts.emit = EmitKind::Ir;
6150        opts.safety = rucc_session::Safety::Detect;
6151        opts.padding = padding;
6152        let result = run(&opts, source);
6153        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
6154        result.text().to_owned()
6155    }
6156
6157    const FILLS_A_RECORD_A_MEMBER_AT_A_TIME: &str = "struct padded { char tag; int value; };\n\
6158         void fill(struct padded *p) { p->tag = 1; p->value = 2; }\n";
6159
6160    #[test]
6161    fn a_record_filled_a_member_at_a_time_comes_out_whole_when_padding_does_not_participate() {
6162        // Section 9.3 of document 09, and the reason the default is the one it gives library code.
6163        // Four bytes from the `char` and four from the `int` is the whole of an eight byte record,
6164        // so the `memcmp` or the hash or the `write` that reads it back is not refused.
6165        let text = padded_ir(Padding::Ignored, FILLS_A_RECORD_A_MEMBER_AT_A_TIME);
6166        assert_eq!(text.matches("owns 4").count(), 2, "{text}");
6167    }
6168
6169    #[test]
6170    fn a_store_says_only_what_it_wrote_when_padding_does_participate() {
6171        // The kernel profile's default, which is section 9.3's actual rule: the padding stays
6172        // unwritten and the read of the record that would leak it is the one that reports.
6173        let text = padded_ir(Padding::Tracked, FILLS_A_RECORD_A_MEMBER_AT_A_TIME);
6174        assert!(!text.contains("owns"), "{text}");
6175    }
6176
6177    #[test]
6178    fn a_member_of_a_union_owns_nothing_after_it() {
6179        // The bytes after a short member of a union belong to a longer member rather than to
6180        // padding, and saying a store through the short one wrote them would be saying the longer
6181        // one holds a value nobody put there.
6182        let text = padded_ir(
6183            Padding::Ignored,
6184            "union u { char tag; long wide; };\nvoid fill(union u *p) { p->tag = 1; }\n",
6185        );
6186        assert!(!text.contains("owns"), "{text}");
6187    }
6188
6189    #[test]
6190    fn an_inner_records_trailing_padding_reaches_the_outer_records() {
6191        // The composition. `in` owns four bytes of `outer` because `x` starts there, and `c` is
6192        // the last member of `in`, so what it owns is what `in` owns rather than its own one byte.
6193        // Without that the three bytes between them would stay unwritten and a read of the whole
6194        // thing would report.
6195        let text = padded_ir(
6196            Padding::Ignored,
6197            "struct inner { char c; };\n\
6198             struct outer { struct inner in; int x; };\n\
6199             void fill(struct outer *p) { p->in.c = 1; p->x = 2; }\n",
6200        );
6201        assert_eq!(text.matches("owns 4").count(), 2, "{text}");
6202    }
6203
6204    #[test]
6205    fn a_build_that_did_not_ask_for_the_monitor_is_compiled_the_way_it_always_was() {
6206        // This is the load bearing test of the whole flag. The monitor is being built in the open
6207        // and every build in the world is compiled by this compiler with the flag absent, so a
6208        // check that leaked into that path would be a regression for everybody.
6209        let text = ir(READS_THROUGH_A_POINTER);
6210        assert!(!text.contains("check_"), "{text}");
6211        assert!(!text.contains("cap_of"), "{text}");
6212    }
6213
6214    #[test]
6215    fn asking_for_a_tier_puts_the_checks_in_before_the_optimizer_sees_them() {
6216        let text = safe_ir(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
6217        assert!(text.contains("cap_of"), "{text}");
6218        assert!(text.contains("check_bounds"), "{text}");
6219        assert!(text.contains("check_live"), "{text}");
6220        // The subscript is address arithmetic, so J2 applies to it as well as J1.
6221        assert!(text.contains("check_deriv"), "{text}");
6222        // And the read names a type, so it asks the type plane about the bytes as well.
6223        assert!(text.contains("check_type"), "{text}");
6224    }
6225
6226    #[test]
6227    fn the_three_tiers_that_are_not_off_all_check_the_same_accesses_so_far() {
6228        // What separates them is the reporter and the boundary, which are milestones S2 and S3.
6229        // Pinning it here means the day they stop agreeing, this test says so rather than the
6230        // difference going unnoticed.
6231        let detect = safe_ir(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
6232        for tier in [rucc_session::Safety::Enforce, rucc_session::Safety::Kernel] {
6233            assert_eq!(safe_ir(tier, READS_THROUGH_A_POINTER), detect, "{tier}");
6234        }
6235    }
6236
6237    /// The safety summary of `source` at one tier, insisting that it compiled cleanly.
6238    fn summary(tier: rucc_session::Safety, source: &str) -> String {
6239        let mut opts = options();
6240        opts.emit = EmitKind::SafetySummary;
6241        opts.safety = tier;
6242        let result = run(&opts, source);
6243        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
6244        result.text().to_owned()
6245    }
6246
6247    #[test]
6248    fn the_summary_counts_the_checks_that_went_in_and_the_ones_still_standing() {
6249        let text = summary(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
6250        assert!(text.contains("\"tier\": \"detect\""), "{text}");
6251        // One load, so one of each of the two access checks, and the subscript is a derivation.
6252        assert!(
6253            text.contains("\"bounds\": { \"emitted\": 1, \"remaining\": 1, \"discharged\": 0 }"),
6254            "{text}"
6255        );
6256        assert!(
6257            text.contains(
6258                "\"derivation\": { \"emitted\": 1, \"remaining\": 1, \"discharged\": 0 }"
6259            ),
6260            "{text}"
6261        );
6262    }
6263
6264    #[test]
6265    fn a_build_without_the_monitor_summarises_as_a_build_with_no_checks_in_it() {
6266        // Which is the honest summary rather than an error. A build system that emits a summary
6267        // for every unit should get one for the units nobody asked to instrument too, and the
6268        // zeroes are what say that the guarantee over that file is nothing at all.
6269        let text = summary(rucc_session::Safety::Off, READS_THROUGH_A_POINTER);
6270        assert!(text.contains("\"tier\": \"off\""), "{text}");
6271        assert!(
6272            text.contains("\"bounds\": { \"emitted\": 0, \"remaining\": 0, \"discharged\": 0 }"),
6273            "{text}"
6274        );
6275    }
6276
6277    #[test]
6278    fn a_call_the_boundary_models_is_counted_apart_from_one_it_does_not() {
6279        let text = summary(
6280            rucc_session::Safety::Detect,
6281            "void *memcpy(void *, const void *, unsigned long);\n\
6282             int puts(const char *);\n\
6283             void f(char *d, char *s) { memcpy(d, s, 4); puts(d); }\n",
6284        );
6285        assert!(text.contains("\"interposed\": 1"), "{text}");
6286        assert!(text.contains("\"puts\""), "{text}");
6287        // The wrapper it was pointed at is ours, so it is not on the list of things this build
6288        // failed to model. Counting it there would make instrumenting a file look worse than
6289        // leaving it alone.
6290        assert!(!text.contains("__rucc_wrap_memcpy\""), "{text}");
6291    }
6292
6293    #[test]
6294    fn an_address_taken_of_a_library_function_is_counted_the_way_a_call_to_one_is() {
6295        // The shape SQLite's syscall table has, cut down to two rows. `memcpy` has a wrapper so the
6296        // table holds the wrapper's address and the build modelled it; `puts` has none, so what the
6297        // table holds is the real function and the build did not, and section 10.1 says the one it
6298        // did not is named rather than passed over.
6299        let text = summary(
6300            rucc_session::Safety::Detect,
6301            "void *memcpy(void *, const void *, unsigned long);\n\
6302             int puts(const char *);\n\
6303             void *table[2] = { (void *)memcpy, (void *)puts };\n\
6304             void *f(int i) { return table[i]; }\n",
6305        );
6306        assert!(text.contains("\"interposed\": 1"), "{text}");
6307        assert!(text.contains("\"puts\""), "{text}");
6308        assert!(!text.contains("\"memcpy\""), "{text}");
6309    }
6310
6311    #[test]
6312    fn the_two_directions_a_pointer_crosses_the_boundary_are_counted_apart() {
6313        // `f` is a name the linker can bind to and takes a pointer, so a pointer arrives there.
6314        // `notes_open` is a library this build did not instrument, so a pointer comes back from
6315        // it. Both are crossings and neither is the other, which is why there are two numbers.
6316        let text = summary(
6317            rucc_session::Safety::Detect,
6318            "void *notes_open(void);\n\
6319             char *f(char *p) { char *q = notes_open(); return q ? q : p; }\n",
6320        );
6321        assert!(text.contains("\"crossings\": { \"entered\": 1, \"returned\": 1 }"), "{text}");
6322        assert!(text.contains("\"notes_open\""), "{text}");
6323    }
6324
6325    #[test]
6326    fn a_static_function_nobody_takes_the_address_of_is_not_a_crossing() {
6327        // Nothing outside the file can reach it, so a witness on its parameters would be counting
6328        // a crossing that does not happen.
6329        let text = summary(
6330            rucc_session::Safety::Detect,
6331            "static int len(const char *p) { return p ? 1 : 0; }\n\
6332             int f(void) { return len(\"x\"); }\n",
6333        );
6334        assert!(text.contains("\"crossings\": { \"entered\": 0, \"returned\": 0 }"), "{text}");
6335    }
6336
6337    /// The granule report for `source`, insisting that it compiled cleanly.
6338    fn granules(source: &str) -> String {
6339        let mut opts = options();
6340        opts.emit = EmitKind::TypeGranules;
6341        let result = run(&opts, source);
6342        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
6343        result.text().to_owned()
6344    }
6345
6346    #[test]
6347    fn the_granule_report_names_every_record_and_both_keyings() {
6348        let text = granules(
6349            "struct hot { char *p; int a; int b; };\n\
6350             int f(struct hot *h) { return h->a; }\n",
6351        );
6352        assert!(text.contains("struct hot"), "{text}");
6353        // Both keyings are reported because which types count as one is a decision the design
6354        // has not made yet, and a report that picked one would be hiding the cost of the other.
6355        assert!(text.contains("every type distinct"), "{text}");
6356        assert!(text.contains("every pointer one type"), "{text}");
6357        assert!(text.contains("budget"), "{text}");
6358    }
6359
6360    #[test]
6361    fn a_record_nothing_uses_is_still_measured() {
6362        // The measurement is about what a program declares, not about what it runs, so a type
6363        // that is only ever declared still costs the plane whatever its layout costs.
6364        let text = granules("struct unused { long a; double b; };\nint f(void) { return 0; }\n");
6365        assert!(text.contains("struct unused"), "{text}");
6366    }
6367
6368    #[test]
6369    fn the_granule_report_stops_before_anything_is_lowered() {
6370        // A layout is settled at the closing brace, so lowering the function bodies would take
6371        // minutes on an amalgamation and answer nothing. The evidence that it stops is that a
6372        // body the back end has no way to compile still produces a report.
6373        let text = granules(
6374            "struct wide { long double d; };\n\
6375             long double f(long double x) { return x * x; }\n",
6376        );
6377        assert!(text.contains("struct wide"), "{text}");
6378    }
6379
6380    #[test]
6381    fn a_witness_reaches_the_assembler_as_a_call_to_the_runtime() {
6382        // The count only means anything if the call is really there, and a summary saying one is
6383        // there is not evidence that the back end emitted it.
6384        let text = safe_asm(rucc_session::Safety::Detect, "char *f(char *p) { return p; }\n");
6385        assert!(text.contains("\tcall\t__rucc_cap_witness\n"), "{text}");
6386    }
6387
6388    #[test]
6389    fn a_pointer_turned_into_an_integer_is_on_the_trust_set() {
6390        let text = summary(
6391            rucc_session::Safety::Detect,
6392            "unsigned long f(int *p) { return (unsigned long) p; }\n",
6393        );
6394        assert!(text.contains("\"exposed\": 1"), "{text}");
6395    }
6396
6397    /// The assembly of `source` at one safety tier, insisting that it compiled cleanly.
6398    fn safe_asm(tier: rucc_session::Safety, source: &str) -> String {
6399        let mut opts = options();
6400        opts.emit = EmitKind::Asm;
6401        opts.safety = tier;
6402        let result = run(&opts, source);
6403        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
6404        result.text().to_owned()
6405    }
6406
6407    #[test]
6408    fn a_check_reaches_the_assembler_as_a_call_to_the_runtime() {
6409        let text = safe_asm(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
6410        assert!(text.contains("\tcall\t__rucc_check_bounds\n"), "{text}");
6411        assert!(text.contains("\tcall\t__rucc_check_live\n"), "{text}");
6412        assert!(text.contains("\tcall\t__rucc_check_deriv\n"), "{text}");
6413        // The type check and the init check of one read reach the assembler as the one call that
6414        // asks both planes about it. `rucc_safety::lower::partner` is what recognises the pair.
6415        assert!(text.contains("\tcall\t__rucc_check_typed_init\n"), "{text}");
6416    }
6417
6418    #[test]
6419    fn every_check_that_reached_the_assembler_has_a_row_describing_it() {
6420        // Four calls and four descriptors, each in the section the runtime's reporter reads. The
6421        // width is `rucc_safety::lower::WIDTH` and the row is `rucc_safe_rt::fail::Descriptor`, and
6422        // the two agreeing is what makes the address a check is handed mean anything. Four rather
6423        // than five because the read's two plane questions are one call carrying one row, which the
6424        // two of them can share because a type check's row and an init check's row are identical.
6425        let text = safe_asm(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
6426        let section = format!("\t.section\t{},", rucc_safety::SECTION);
6427        assert_eq!(text.matches(&section).count(), 4, "{text}");
6428        for index in 0..4 {
6429            let name = format!("__rucc_safety_desc_{index}");
6430            // Defined once and referenced once, because a descriptor nothing points at describes
6431            // nothing and a reference with no definition does not link.
6432            assert!(text.contains(&format!("{name}:\n")), "{text}");
6433            assert!(text.contains(&format!("{name}(%rip)")), "{text}");
6434        }
6435        assert!(!text.contains("__rucc_safety_desc_4"), "{text}");
6436    }
6437
6438    /// `__builtin_constant_p` is answered in the front end and never reaches the IR.
6439    ///
6440    /// gcc folds it after optimization, so its answer for an argument that is not written as a
6441    /// constant can differ between `-O0` and `-O2`. What is checked here is the front end's
6442    /// answer, which is the same at every level, and the four cases where gcc gives the same
6443    /// answer at both levels are the ones measured on gcc 16: a literal is one, a variable is
6444    /// zero, a string literal is one and the address of an object is zero.
6445    #[test]
6446    fn builtin_constant_p_is_folded_where_it_is_written_rather_than_called() {
6447        let text = ir(concat!(
6448            "int g;\n",
6449            "int a = __builtin_constant_p(1);\n",
6450            "int b = __builtin_constant_p(g);\n",
6451            "int c = __builtin_constant_p(\"abc\");\n",
6452            "int d = __builtin_constant_p(&g);\n",
6453            "int e = __builtin_constant_p(1.5);\n",
6454            "int h = __builtin_choose_expr(__builtin_constant_p(3), 11, 22);\n",
6455        ));
6456        assert!(text.contains("global @a : i32 = 1,"), "{text}");
6457        assert!(text.contains("global @b : i32 = 0,"), "{text}");
6458        assert!(text.contains("global @c : i32 = 1,"), "{text}");
6459        assert!(text.contains("global @d : i32 = 0,"), "{text}");
6460        assert!(text.contains("global @e : i32 = 1,"), "{text}");
6461        assert!(text.contains("global @h : i32 = 11,"), "{text}");
6462        assert!(!text.contains("__builtin_constant_p"), "it is not a call to anything:\n{text}");
6463
6464        // The argument is not evaluated, which is what gcc does with it as well, so `i` is
6465        // still zero. The second constant is the answer, which nothing reads and which the
6466        // first pass that looks for dead code will take out.
6467        let text = body("int f(void) { int i = 0; __builtin_constant_p(i++); return i; }\n");
6468        assert_eq!(text, "block0:\n    %0 = iconst.i32 0\n    %1 = iconst.i32 0\n    return %0\n");
6469    }
6470
6471    /// A `pure` or `const` function whose value comes back through memory writes that memory, so
6472    /// neither promise reaches the IR for it and a call whose value is read is kept. Windows x64
6473    /// returns a `_Complex double` this way, which is `execute/20050121-1.c`.
6474    #[test]
6475    fn a_pure_function_that_returns_through_memory_is_still_called() {
6476        let text = optimized(concat!(
6477            "struct four { long a, b, c, d; };\n",
6478            "__attribute__((pure)) struct four made(int);\n",
6479            "__attribute__((const)) struct four counted(int);\n",
6480            "long f(int x) { return made(x).c + counted(x).d; }\n",
6481        ));
6482        assert!(text.contains("call\tmade"), "{text}");
6483        assert!(text.contains("call\tcounted"), "{text}");
6484    }
6485
6486    /// A condition built on a `__builtin_constant_p` the optimizer has still to answer is left to
6487    /// the optimizer, rather than decided in the front end on the no a constant expression gets.
6488    /// At `-O2` `size` has become the four it was set to by the time the question is asked, and
6489    /// gcc takes the first arm. `execute/builtin-constant.c` is the torture test that checks it.
6490    #[test]
6491    fn a_condition_on_a_builtin_constant_p_waits_for_the_optimizer() {
6492        let text = optimized(concat!(
6493            "void g(void);\n",
6494            "void f(void) { int size = sizeof(int); __builtin_constant_p(size) ? (void)0 : g(); }\n",
6495            "void h(void) { int size = sizeof(int); if (!__builtin_constant_p(size)) g(); }\n",
6496        ));
6497        assert!(!text.contains("call\tg") && !text.contains("jmp\tg"), "{text}");
6498    }
6499
6500    /// Falling off the end of a function that returns a value comes back to the caller, since C
6501    /// only makes it undefined to use the value. `execute/20020404-1.c` calls two such functions
6502    /// for what they do and lost everything after the first of them at `-O2`.
6503    #[test]
6504    fn a_call_to_a_function_that_falls_off_its_end_comes_back() {
6505        let text = optimized(concat!(
6506            "int calls;\n",
6507            "__attribute__((noinline)) static int no_answer(int x) { calls += x; }\n",
6508            "void after(void);\n",
6509            "void f(void) { no_answer(1); after(); }\n",
6510        ));
6511        assert!(text.contains("call\tafter") || text.contains("jmp\tafter"), "{text}");
6512    }
6513
6514    /// A library builtin is the library function of the same name, and the call says so.
6515    ///
6516    /// A program writes `__builtin_strlen` rather than `strlen` to reach the function the C
6517    /// library promises where its own name has been taken by a macro, and to say that the usual
6518    /// meaning is the one intended. So the name in the program and the name in the object file
6519    /// are two different names and the call carries the second one. gcc folds several of these
6520    /// when the arguments allow it, which is an optimization on top of a call that is already
6521    /// right rather than instead of it, so nothing here depends on any folding happening.
6522    #[test]
6523    fn a_call_to_a_library_builtin_reaches_the_library_function() {
6524        let text = body("void f(void) { __builtin_abort(); }\n");
6525        assert_eq!(text, "block0:\n    call @abort() : ()\n    return\n");
6526
6527        // Nothing declared either of these and nothing had to: the prefix is what says the name
6528        // belongs to the implementation, and the type comes out of `features.toml`.
6529        let text = ir("int f(const char *s) { return __builtin_puts(s) + __builtin_strlen(s); }\n");
6530        assert!(text.contains("call @puts(%0) : (ptr) -> i32"), "{text}");
6531        assert!(text.contains("call @strlen(%0) : (ptr) -> i64"), "{text}");
6532        assert!(!text.contains("__builtin_"), "the prefix is not part of any name here:\n{text}");
6533    }
6534
6535    /// A `_chk` builtin reaches the checking function in the library with the object size still
6536    /// on the end of it.
6537    ///
6538    /// This is what a fortified `string.h` turns every copy into, so it is what a program built
6539    /// the way a distribution builds one is full of, and the whole of what makes the call right
6540    /// is that the size goes with it. The checking function takes `(size_t) -1` to mean nothing
6541    /// is known and does no check, which is what the header passes when the destination's object
6542    /// is not in sight, so the unconditional call means the same thing in both cases and costs a
6543    /// call gcc would have folded away in the second.
6544    ///
6545    /// The name is the one place this family reads like an exception and is not one:
6546    /// `__builtin___memcpy_chk` with `__builtin_` taken off is `__memcpy_chk`.
6547    #[test]
6548    fn a_chk_builtin_reaches_the_checking_function_and_keeps_the_size() {
6549        let text = ir(concat!(
6550            "char d[8];\n",
6551            "void f(const char *s, unsigned long n) {\n",
6552            "  __builtin___memcpy_chk(d, s, n, __builtin_object_size(d, 0));\n",
6553            "  __builtin___strcpy_chk(d, s, __builtin_object_size(d, 1));\n",
6554            "  __builtin___memset_chk(d, 0, n, 8);\n",
6555            "}\n",
6556        ));
6557        assert!(text.contains("call @__memcpy_chk("), "{text}");
6558        assert!(text.contains("call @__strcpy_chk("), "{text}");
6559        assert!(text.contains("call @__memset_chk("), "{text}");
6560        assert!(text.contains("iconst.i64 8"), "the object size reaches the call: {text}");
6561        assert!(!text.contains("__builtin_"), "the prefix is not part of any name here:\n{text}");
6562    }
6563
6564    /// A checking call whose object size says nothing is known is the plain library call.
6565    ///
6566    /// That is the whole of the folding half of the family. The checking function reads the all
6567    /// ones value as do not check, so the call it was going to make is the function it guards with
6568    /// an argument nobody reads on the end of it, and gcc drops the argument and calls the plain
6569    /// function at every level including `-O0`. Where the size is a real number the checking call
6570    /// stands, because the check is the point.
6571    #[test]
6572    fn a_checking_call_whose_size_says_nothing_is_known_is_the_plain_library_call() {
6573        let text = ir(concat!(
6574            "extern char *p;\n",
6575            "char d[8];\n",
6576            "void f(const char *s, unsigned long n) {\n",
6577            "  __builtin___memcpy_chk(d, s, n, __builtin_object_size(d, 0));\n",
6578            "  __builtin___memcpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
6579            "  __builtin___strcpy_chk(p, s, __builtin_object_size(p, 0));\n",
6580            "  __builtin___stpncpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
6581            "  __builtin___sprintf_chk(p, 1, __builtin_object_size(p, 0), s);\n",
6582            "}\n",
6583        ));
6584
6585        // The destination whose object is in sight keeps its check, size and all.
6586        assert!(
6587            text.contains("call @__memcpy_chk(%2, %0, %1, %3) : (ptr, ptr, i64, i64)"),
6588            "{text}"
6589        );
6590
6591        // The three whose object is not lose the argument and the name along with it. The type of
6592        // the call goes with them, which is what says the argument is gone rather than ignored.
6593        assert!(text.contains("call @memcpy(%6, %0, %1) : (ptr, ptr, i64) -> ptr"), "{text}");
6594        assert!(text.contains("call @strcpy(%10, %0) : (ptr, ptr) -> ptr"), "{text}");
6595        assert!(text.contains("call @stpncpy(%14, %0, %1) : (ptr, ptr, i64) -> ptr"), "{text}");
6596
6597        // The formatted one never folds, whatever the size says, because refusing a `%n` in a
6598        // writable format is the other half of what it was asked to do.
6599        assert!(text.contains("call @__sprintf_chk("), "{text}");
6600
6601        // Nothing is left behind in the instructions either. The size the folded calls no longer
6602        // take is a constant nobody reads, and no instruction is written for one.
6603        let asm = asm(concat!(
6604            "void f(char *p, const char *s, unsigned long n) {\n",
6605            "  __builtin___memcpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
6606            "}\n",
6607        ));
6608        assert!(asm.contains("call\tmemcpy"), "{asm}");
6609        assert!(!asm.contains("$-1"), "the size that went away leaves no instruction:\n{asm}");
6610    }
6611
6612    /// The `v` spellings take a `__builtin_va_list`, which is the first type in the table the
6613    /// target chooses the shape of rather than the width of.
6614    ///
6615    /// On x86-64 it is an array of one, so what the prototype has to say is the pointer that
6616    /// array decays to, which is the same adjustment C makes to any parameter written as an array
6617    /// and is what a `va_list` parameter already holds. A prototype that kept the array would be
6618    /// one no argument could ever match.
6619    #[test]
6620    fn the_v_spellings_of_the_chk_family_take_the_list_a_va_list_parameter_holds() {
6621        let text = ir(concat!(
6622            "char d[64];\n",
6623            "int f(const char *fmt, ...) {\n",
6624            "  __builtin_va_list ap;\n",
6625            "  __builtin_va_start(ap, fmt);\n",
6626            "  int n = __builtin___vsprintf_chk(d, 1, __builtin_object_size(d, 0), fmt, ap);\n",
6627            "  __builtin_va_end(ap);\n",
6628            "  return n;\n",
6629            "}\n",
6630        ));
6631        assert!(text.contains("call @__vsprintf_chk("), "{text}");
6632        assert!(text.contains("iconst.i64 64"), "the object size reaches the call: {text}");
6633    }
6634
6635    /// The absolute value family is four instructions and not a call, whoever declared the name.
6636    ///
6637    /// `abs`, `labs` and `llabs` are reserved to the implementation, so a program that writes one
6638    /// means the one the C library promises and the compiler is allowed to know what it does. The
6639    /// program in `gcc.c-torture/execute/20021127-1.c` is the one that insists: it defines `llabs`
6640    /// to abort and expects the call not to reach it. Measured against gcc 16.2.0, which writes a
6641    /// `neg` and a `cmovns` and never calls the definition either.
6642    ///
6643    /// The most negative value comes back as itself, which is what the arithmetic gives and what
6644    /// gcc's pair of instructions gives, and C says the answer is undefined there.
6645    #[test]
6646    fn the_absolute_value_family_is_the_magnitude_and_not_a_call() {
6647        let text = body(concat!(
6648            "long long llabs(long long);\n",
6649            "long long f(long long x) { return llabs(x); }\n",
6650        ));
6651        assert!(text.contains("%1 = iconst.i64 63"), "{text}");
6652        assert!(text.contains("%2 = ashr %0, %1"), "{text}");
6653        assert!(text.contains("%3 = xor %0, %2"), "{text}");
6654        assert!(text.contains("%4 = sub %3, %2"), "{text}");
6655        assert!(!text.contains("call"), "the call does not happen:\n{text}");
6656
6657        // The narrower two, whose width comes from the type the library gives the name and not
6658        // from anything at the call.
6659        let text = body("int abs(int);\nint f(int x) { return abs(x); }\n");
6660        assert!(text.contains("iconst.i32 31"), "{text}");
6661        let text = body("long labs(long);\nlong f(long x) { return labs(x); }\n");
6662        assert!(text.contains("iconst.i64 63"), "{text}");
6663
6664        // The prefixed spelling is the same node, and it is what a program writes to reach the
6665        // library's meaning where the plain name has been taken.
6666        let text = body("long long f(long long x) { return __builtin_llabs(x); }\n");
6667        assert!(!text.contains("call"), "{text}");
6668
6669        // A definition of the name in the same file changes nothing, which is the whole point.
6670        let text = ir(concat!(
6671            "long long llabs(long long b);\n",
6672            "long long g(long long x) { return llabs(x); }\n",
6673            "long long llabs(long long b) { return 7; }\n",
6674        ));
6675        assert!(!text.contains("call @llabs"), "{text}");
6676    }
6677
6678    /// A byte swap is one instruction and not a call, and nothing had to declare it.
6679    ///
6680    /// SQLite writes these for its page headers and glibc's `<endian.h>` defines `htobe32` and its
6681    /// neighbours as exactly these, so a program that reads a file format reaches one without ever
6682    /// naming it. There is no object file anywhere that defines `__builtin_bswap32`, so a call left
6683    /// standing here would not link.
6684    #[test]
6685    fn a_byte_swap_is_arithmetic_and_not_a_call() {
6686        let text = body("unsigned f(unsigned x) { return __builtin_bswap32(x); }\n");
6687        assert_eq!(text, "block0(%0: i32):\n    %1 = bswap %0\n    return %1\n");
6688
6689        // The argument is converted by the prototype the way any other call's would be, so the
6690        // swap happens at the width the name says and not at the width the program wrote.
6691        let text = body("unsigned f(unsigned char c) { return __builtin_bswap32(c); }\n");
6692        assert!(text.contains("zext.i32 %0"), "widened first: {text}");
6693        assert!(text.contains("bswap %1"), "and swapped at four bytes: {text}");
6694    }
6695
6696    /// Each of the three reverses in the width its name says, which is the type of the node.
6697    ///
6698    /// The width matters more here than it looks. `__builtin_bswap16` is the two bytes of a
6699    /// `uint16_t` exchanged, and if the node came out at the machine's width instead then the bits
6700    /// above the value would be dragged into the answer and the result would be zero.
6701    #[test]
6702    fn the_byte_swaps_reverse_at_the_width_their_name_says() {
6703        for (name, ty, width) in [
6704            ("__builtin_bswap16", "unsigned short", "i16"),
6705            ("__builtin_bswap32", "unsigned", "i32"),
6706            ("__builtin_bswap64", "unsigned long long", "i64"),
6707        ] {
6708            let source = format!("{ty} f({ty} x) {{ return {name}(x); }}\n");
6709            let text = body(&source);
6710            assert_eq!(
6711                text,
6712                format!("block0(%0: {width}):\n    %1 = bswap %0\n    return %1\n"),
6713                "{name}"
6714            );
6715        }
6716    }
6717
6718    /// The three bit counts the IR has an instruction for are that instruction and not a call.
6719    ///
6720    /// Eighteen rows of `features.toml` come out of six questions, and three of the six are one
6721    /// instruction each. The kernel's bitmap search is built on them, ffmpeg counts leading zeroes
6722    /// in its bitstream reader and SQLite uses one to size a page, so a call left standing here
6723    /// would not link against anything and would be slow if it did.
6724    #[test]
6725    fn the_bit_counts_are_instructions_and_not_calls() {
6726        let text = body("int f(unsigned x) { return __builtin_clz(x); }\n");
6727        assert_eq!(text, "block0(%0: i32):\n    %1 = ctlz %0\n    return %1\n");
6728
6729        let text = body("int f(unsigned x) { return __builtin_ctz(x); }\n");
6730        assert_eq!(text, "block0(%0: i32):\n    %1 = cttz %0\n    return %1\n");
6731
6732        let text = body("int f(unsigned x) { return __builtin_popcount(x); }\n");
6733        assert_eq!(text, "block0(%0: i32):\n    %1 = ctpop %0\n    return %1\n");
6734    }
6735
6736    /// The width counted is the operand's and the width answered is `int`, which are two different
6737    /// things at every spelling but the narrowest.
6738    ///
6739    /// This is the mistake the family invites. `__builtin_clz` of a value counts the leading zeroes
6740    /// of it narrowed to `unsigned int` and `__builtin_clzll` counts them at sixty four bits, and
6741    /// those are different numbers for the same value. What decides it is the prototype the row
6742    /// carries, so the count happens after the conversion and the narrowing back to `int` happens
6743    /// after the count.
6744    #[test]
6745    fn the_bit_counts_ask_about_the_width_their_name_says() {
6746        let text = body("int f(unsigned long long x) { return __builtin_clzll(x); }\n");
6747        assert!(text.starts_with("block0(%0: i64):"), "counted at eight bytes: {text}");
6748        assert!(text.contains("%1 = ctlz %0"), "{text}");
6749        assert!(text.contains("trunc.i32 %1"), "and answered in an int: {text}");
6750
6751        // The same value asked about at the narrower width, which converts first and so counts
6752        // something else.
6753        let text = body("int f(unsigned long long x) { return __builtin_clz(x); }\n");
6754        assert!(text.contains("trunc.i32 %0"), "narrowed to what was asked about: {text}");
6755        assert!(text.contains("ctlz %1"), "and counted there: {text}");
6756
6757        let text = body("int f(unsigned long x) { return __builtin_popcountl(x); }\n");
6758        assert!(text.contains("%1 = ctpop %0"), "{text}");
6759        assert!(!text.contains("call"), "{text}");
6760    }
6761
6762    /// A parity is whether the count of set bits is odd, which is that count and its low bit.
6763    ///
6764    /// Not the machine's parity flag, which on x86-64 is over the low byte of a result and so is a
6765    /// different question, and not the count itself, since C says the answer is zero or one.
6766    #[test]
6767    fn a_parity_is_the_low_bit_of_the_set_bit_count() {
6768        let text = body("int f(unsigned x) { return __builtin_parity(x); }\n");
6769        assert!(text.contains("%1 = ctpop %0"), "{text}");
6770        assert!(text.contains("iconst.i32 1"), "{text}");
6771        assert!(text.contains("and %1, %2"), "the low bit of it: {text}");
6772    }
6773
6774    /// `__builtin_ffs` is the trailing zero count and one, kept only when there was a bit to find.
6775    ///
6776    /// The one in the family defined at zero, where it answers zero. Written as a mask rather than
6777    /// as a branch: the count and the comparison do not depend on each other and both are cheap, so
6778    /// a branch would buy nothing and cost two blocks and a join.
6779    #[test]
6780    fn the_first_set_bit_is_one_based_and_zero_for_a_zero() {
6781        let text = body("int f(int x) { return __builtin_ffs(x); }\n");
6782        assert!(text.contains("%1 = cttz %0"), "{text}");
6783        assert!(text.contains("%4 = add %1, %2"), "one more than the count: {text}");
6784        assert!(text.contains("%5 = icmp ne %0, %3"), "whether there was a bit at all: {text}");
6785        assert!(text.contains("%7 = sub %3, %6"), "spread to a mask: {text}");
6786        assert!(text.contains("%8 = and %4, %7"), "and kept only then: {text}");
6787        assert!(!text.contains("br_if"), "no branch: {text}");
6788    }
6789
6790    /// `__builtin_clrsb` is how many bits below the sign bit repeat it, which is a leading zero
6791    /// count of the value folded onto its own sign.
6792    ///
6793    /// Exclusive or with the sign spread over every bit turns a negative value into its complement
6794    /// and leaves one that is not negative alone, so in both cases the top bit is clear and there
6795    /// is one zero above the highest bit that does not repeat the sign. The answer is one less
6796    /// than that count, and the shift left is what takes the one off, with the low bit set on the
6797    /// way so that zero and minus one have something to count: both of them fold to a word with no
6798    /// bits in it, which is the one input a leading zero count says nothing about.
6799    #[test]
6800    fn the_redundant_sign_bit_count_is_instructions_and_not_a_call() {
6801        let text = body("int f(int x) { return __builtin_clrsb(x); }\n");
6802        assert!(text.contains("%1 = iconst.i32 31"), "{text}");
6803        assert!(text.contains("%2 = ashr %0, %1"), "the sign over every bit: {text}");
6804        assert!(text.contains("%3 = xor %0, %2"), "folded onto it: {text}");
6805        assert!(text.contains("%5 = shl %3, %4"), "one less than the count: {text}");
6806        assert!(text.contains("%6 = or %5, %4"), "with something to count at zero: {text}");
6807        assert!(text.contains("%7 = ctlz %6"), "{text}");
6808        assert!(!text.contains("call"), "{text}");
6809        assert!(!text.contains("br_if"), "no branch: {text}");
6810    }
6811
6812    /// The unsigned four are the same four instructions answering in the unsigned type.
6813    ///
6814    /// Which on a two's complement machine is the same bits, so what this checks is that the type
6815    /// of the answer is the unsigned one. The reason the family exists is the most negative value,
6816    /// whose magnitude is not representable in the signed type and is representable in this one.
6817    #[test]
6818    fn the_unsigned_absolute_value_family_answers_in_the_unsigned_type() {
6819        let text = body("unsigned f(int x) { return __builtin_uabs(x); }\n");
6820        assert!(text.contains("%1 = iconst.i32 31"), "{text}");
6821        assert!(text.contains("%4 = sub %3, %2"), "{text}");
6822        assert!(!text.contains("call"), "nothing declares uabs, so a call would not link: {text}");
6823
6824        let text = body("unsigned long long f(long long x) { return __builtin_ullabs(x); }\n");
6825        assert!(text.contains("iconst.i64 63"), "at the width the name says: {text}");
6826
6827        // The answer is the unsigned type and not the signed one, which is what a comparison
6828        // against it is decided by.
6829        let text = body("int f(int x) { return __builtin_uabs(x) > 2147483647u; }\n");
6830        assert!(text.contains("icmp ugt"), "compared unsigned: {text}");
6831    }
6832
6833    /// `intmax_t` is not a fixed type, so the two widest spellings ask the target what it is.
6834    ///
6835    /// `long` where that is sixty four bits wide and `long long` where it is not, which is the rule
6836    /// `rucc_pp::predef` writes `__INTMAX_TYPE__` out of. The three targets here are all LP64, so
6837    /// the answer is `long` and the shift is sixty three, and the point of the test is that the
6838    /// signature was understood at all rather than refused for naming a type the table could not
6839    /// spell.
6840    #[test]
6841    fn the_widest_absolute_value_is_whichever_type_the_target_makes_intmax_t() {
6842        let text = body("long f(long x) { return __builtin_imaxabs(x); }\n");
6843        assert!(text.contains("iconst.i64 63"), "{text}");
6844        assert!(text.contains("%4 = sub %3, %2"), "{text}");
6845        assert!(!text.contains("call"), "{text}");
6846
6847        let text = body("unsigned long f(long x) { return __builtin_umaxabs(x); }\n");
6848        assert!(text.contains("iconst.i64 63"), "{text}");
6849        assert!(!text.contains("call"), "{text}");
6850    }
6851
6852    /// The `_p` spellings ask the same question, write nothing, and do not evaluate the third
6853    /// argument.
6854    ///
6855    /// gcc says the third argument is there for its type alone, so a call is two operands and a
6856    /// type by the time it reaches the IR. What the type decides is the same thing it decides for
6857    /// the three that write: whether the exact answer would have fit there, which is why the
6858    /// second call below is done at a wider width than the first.
6859    #[test]
6860    fn an_overflow_predicate_writes_nothing_and_answers_the_bit_the_check_would() {
6861        let text =
6862            body("int f(int a, int b) { return __builtin_add_overflow_p(a, b, (int) 0); }\n");
6863        assert!(text.contains("%2, %3 = sadd_overflow.(i32, i1) %0, %1"), "{text}");
6864        assert!(!text.contains("store"), "nothing is written: {text}");
6865        assert!(!text.contains("call"), "{text}");
6866
6867        // A wider destination is a wider arithmetic, and the narrowing test that goes with it is
6868        // what says whether the answer got there, exactly as for the spelling that stores.
6869        let text =
6870            body("int f(int a, int b) { return __builtin_mul_overflow_p(a, b, (long long) 0); }\n");
6871        assert!(text.contains("smul_overflow.(i64, i1)"), "{text}");
6872        assert!(!text.contains("store"), "{text}");
6873
6874        // The third argument is a value and not a pointer, and a side effect written in it does
6875        // not happen, because what the argument is there for is its type.
6876        let text = body(concat!(
6877            "int g(void);\n",
6878            "int f(int a, int b) { return __builtin_sub_overflow_p(a, b, g()); }\n",
6879        ));
6880        assert!(!text.contains("call @g"), "the third argument is not evaluated: {text}");
6881    }
6882
6883    /// The three overflow checks are arithmetic and a flag, and not a call to anything.
6884    ///
6885    /// gcc has emitted these since 5.0 and there is no object file that defines one, so a call left
6886    /// standing here would not link. SQLite reaches all three within twenty lines of each other, in
6887    /// `sqlite3AddInt64` and its two neighbours, which is the reason they were done now.
6888    ///
6889    /// The IR instruction answers two things at once, the wrapped value and whether it wrapped,
6890    /// which is a shape nothing else in the IR has. The store is the builtin writing the answer
6891    /// through the pointer it was handed.
6892    #[test]
6893    fn an_overflow_check_is_arithmetic_and_not_a_call() {
6894        let text =
6895            body("int f(int a, int b, int *r) { return __builtin_add_overflow(a, b, r); }\n");
6896        assert!(text.contains("%3, %4 = sadd_overflow.(i32, i1) %0, %1"), "{text}");
6897        assert!(text.contains("store %3 -> %2"), "{text}");
6898        assert!(!text.contains("call"), "{text}");
6899
6900        let text =
6901            body("int f(int a, int b, int *r) { return __builtin_sub_overflow(a, b, r); }\n");
6902        assert!(text.contains("ssub_overflow.(i32, i1) %0, %1"), "{text}");
6903
6904        let text =
6905            body("int f(int a, int b, int *r) { return __builtin_mul_overflow(a, b, r); }\n");
6906        assert!(text.contains("smul_overflow.(i32, i1) %0, %1"), "{text}");
6907
6908        // Unsigned operands get the unsigned form, which is a different question about the same
6909        // arithmetic: an unsigned sum wraps where a signed one of the same bits does not.
6910        let text = body(
6911            "int f(unsigned a, unsigned b, unsigned *r) { return __builtin_add_overflow(a, b, r); }\n",
6912        );
6913        assert!(text.contains("uadd_overflow.(i32, i1) %0, %1"), "{text}");
6914    }
6915
6916    /// The arithmetic happens at a type that holds every value all three written types can hold.
6917    ///
6918    /// That is what makes the check exact. `unsigned int` and `int` in one call need thirty three
6919    /// bits between them, so the add is done at sixty four with each operand extended the way its
6920    /// own signedness says: the unsigned one zero extended, the signed one sign extended. Sign
6921    /// extending the unsigned one would turn three billion into a negative number before the
6922    /// addition ever saw it.
6923    #[test]
6924    fn an_overflow_check_is_done_at_a_type_that_holds_every_operand() {
6925        let text = body(
6926            "int f(unsigned a, int b, long long *r) { return __builtin_add_overflow(a, b, r); }\n",
6927        );
6928        assert!(text.contains("%3 = zext.i64 %0"), "the unsigned operand keeps its value: {text}");
6929        assert!(text.contains("%4 = sext.i64 %1"), "and so does the signed one: {text}");
6930        assert!(text.contains("sadd_overflow.(i64, i1) %3, %4"), "{text}");
6931
6932        // Three types that agree need no extension at all, which is what nearly every real call
6933        // is written as.
6934        let text = body(
6935            "int f(long long a, long long b, long long *r) { return __builtin_mul_overflow(a, b, r); }\n",
6936        );
6937        assert!(text.contains("smul_overflow.(i64, i1) %0, %1"), "{text}");
6938        assert!(!text.contains("sext."), "{text}");
6939        // The one widening left is the answer, which is a bit becoming the `int` C says it is.
6940        assert!(!text.contains("zext.i64"), "{text}");
6941    }
6942
6943    /// The wrapped answer is written through the pointer whether or not it fit.
6944    ///
6945    /// That is gcc's rule and it is what makes the builtin usable as a wrapping add with a flag on
6946    /// the side. A destination narrower than the arithmetic is narrowed and widened back, and the
6947    /// answer being different is the second half of the test: the instruction says whether the
6948    /// arithmetic itself needed more room, and the round trip says whether what came out survived
6949    /// the trip down to where it was going.
6950    #[test]
6951    fn an_overflow_check_writes_the_wrapped_answer_whether_or_not_it_fit() {
6952        let text =
6953            body("int f(int a, int b, char *r) { return __builtin_sub_overflow(a, b, r); }\n");
6954        assert!(text.contains("%3, %4 = ssub_overflow.(i32, i1) %0, %1"), "{text}");
6955        assert!(text.contains("%5 = trunc.i8 %3"), "narrowed to where it goes: {text}");
6956        assert!(text.contains("%6 = sext.i32 %5"), "and back: {text}");
6957        assert!(text.contains("%7 = icmp ne %6, %3"), "which is whether it fit: {text}");
6958        assert!(text.contains("store %5 -> %2"), "the narrowed value is stored either way: {text}");
6959        assert!(text.contains("%8 = or %4, %7"), "and either bit is an overflow: {text}");
6960    }
6961
6962    /// A call needing more than the widest type there is compiles, by not asking for such a type.
6963    ///
6964    /// One way to reach it: an unsigned `__int128` mixed with a signed type, which needs a hundred
6965    /// and twenty nine bits to represent both and so has nowhere left to go. That used to be refused
6966    /// by name. It is done now by carrying the sign of each operand alongside its value rather than
6967    /// inside it, which is what gcc does, so all three of the family compile for that mix.
6968    #[test]
6969    fn a_call_needing_more_than_the_widest_type_still_compiles() {
6970        for name in ["add", "sub", "mul"] {
6971            let source = format!(
6972                "int f(unsigned __int128 a, long long b, __int128 *r) {{\n    \
6973                 return __builtin_{name}_overflow(a, b, r);\n}}\n"
6974            );
6975            let mut opts = options();
6976            opts.emit = EmitKind::MirFinal;
6977            assert!(!run(&opts, &source).failed(), "{name} was refused or stopped the back end");
6978        }
6979    }
6980
6981    /// An operand that is not an integer at all is the older message, from the type checking every
6982    /// type generic builtin shares.
6983    #[test]
6984    fn an_overflow_check_over_something_that_is_not_an_integer_says_so() {
6985        let messages =
6986            errors("int f(double a, int b, int *r) { return __builtin_add_overflow(a, b, r); }\n");
6987        assert!(messages.iter().any(|line| line.contains("E0671")), "{messages:?}");
6988
6989        let messages =
6990            errors("int f(int a, int b, double *r) { return __builtin_add_overflow(a, b, r); }\n");
6991        assert!(messages.iter().any(|line| line.contains("E0671")), "{messages:?}");
6992    }
6993
6994    /// An ordered access is an ordered access in the IR, with the ordering the program wrote.
6995    ///
6996    /// Which is the point of the node existing at all. An ordering is not an argument anything is
6997    /// passed, it is a thing the IR says about an access, so the number in the source is read once
6998    /// in the front end and after that the ordering travels on the instruction where every pass
6999    /// that moves code can see it.
7000    ///
7001    /// SQLite is why these are done: `AtomicLoad` and `AtomicStore` in `sqlite3.c` are
7002    /// `__atomic_load_n` and `__atomic_store_n` at the relaxed ordering, and there are thirty five
7003    /// calls to the pair.
7004    #[test]
7005    fn an_ordered_access_is_ordered_in_the_ir() {
7006        let text = body("int f(int *p) { return __atomic_load_n(p, 0); }\n");
7007        assert!(text.contains("atomic_load.i32 %0, align 4, relaxed"), "{text}");
7008
7009        let text = body("long f(long *p) { return __atomic_load_n(p, 2); }\n");
7010        assert!(text.contains("atomic_load.i64 %0, align 8, acquire"), "{text}");
7011
7012        let text = body("void f(int *p, int v) { __atomic_store_n(p, v, 3); }\n");
7013        assert!(text.contains("atomic_store %1 -> %0, align 4, release"), "{text}");
7014
7015        let text = body("void f(int *p, int v) { __atomic_store_n(p, v, 5); }\n");
7016        assert!(text.contains("atomic_store %1 -> %0, align 4, seq_cst"), "{text}");
7017
7018        // The value is converted to what the pointer points at before it is stored, which is what
7019        // the call would have done if it had a prototype to convert against.
7020        let text = body("void f(char *p, int v) { __atomic_store_n(p, v, 0); }\n");
7021        assert!(text.contains("trunc.i8 %1"), "{text}");
7022        assert!(text.contains("atomic_store %2 -> %0, align 1, relaxed"), "{text}");
7023    }
7024
7025    /// On this machine the ordered access is the plain instruction, except at the strongest
7026    /// ordering of a store.
7027    ///
7028    /// x86-64 is total store order: every load is already an acquire and every store is already a
7029    /// release, and an aligned access no wider than a word is indivisible whether or not anybody
7030    /// asked. So the whole family is `mov` and the one thing the machine does not give away is a
7031    /// store staying in front of a later load, which is `mfence` behind the store. Every line below
7032    /// is what gcc 16.2.0 writes for the same function.
7033    #[test]
7034    fn an_ordered_access_is_the_plain_instruction_on_this_machine() {
7035        let text = asm("int f(int *p) { return __atomic_load_n(p, 5); }\n");
7036        assert!(text.contains("movl\t(%rdi), %eax"), "{text}");
7037        assert!(!text.contains("mfence"), "a load needs no barrier here: {text}");
7038
7039        let text = asm("void f(int *p, int v) { __atomic_store_n(p, v, 3); }\n");
7040        assert!(text.contains("movl\t%esi, (%rdi)"), "{text}");
7041        assert!(!text.contains("mfence"), "a release store needs no barrier here: {text}");
7042
7043        let text = asm("void f(int *p, int v) { __atomic_store_n(p, v, 5); }\n");
7044        let (before, after) = text.split_once("mfence").expect("a barrier: {text}");
7045        assert!(before.contains("movl\t%esi, (%rdi)"), "the store comes first: {text}");
7046        assert!(!after.contains("movl"), "and nothing else is between them: {text}");
7047    }
7048
7049    /// A barrier is one instruction at the strongest ordering and no instruction below it.
7050    ///
7051    /// The same reasoning the other way round. An acquire, a release and an acquire release fence
7052    /// are already true of every program running on this machine, and what a program wanted from
7053    /// one is that the compiler not move accesses across it, which is already so by the time any
7054    /// instruction is picked. Sequential consistency is the one that costs something.
7055    ///
7056    /// `__sync_synchronize` is the older family's spelling of the strongest one and compiles to
7057    /// exactly the same instruction, which is what SQLite calls twice in `sqlite3.c`.
7058    #[test]
7059    fn a_barrier_is_one_instruction_at_the_strongest_ordering_and_none_below_it() {
7060        assert!(asm("void f(void) { __atomic_thread_fence(5); }\n").contains("mfence"));
7061        assert!(asm("void f(void) { __sync_synchronize(); }\n").contains("mfence"));
7062
7063        for weaker in ["1", "2", "3", "4"] {
7064            let source = format!("void f(void) {{ __atomic_thread_fence({weaker}); }}\n");
7065            assert!(!asm(&source).contains("mfence"), "{weaker} costs nothing here");
7066        }
7067    }
7068
7069    /// The three x86 fences under gcc's names are that same barrier at that same ordering.
7070    ///
7071    /// Exact for `mfence` and stronger than asked for the other two, which is a safe answer: a
7072    /// program that wanted its stores ordered gets that and more. Narrowing the two is worth doing
7073    /// once an instruction can be named from there, which is the note the shipped `xmmintrin.h`
7074    /// already carries at `_mm_sfence`.
7075    ///
7076    /// Each carries a signature, so an argument written on one is reported like an argument
7077    /// written on any other call, which is the whole reason they have one.
7078    #[test]
7079    fn the_three_x86_fences_are_the_barrier_the_strongest_ordering_gives() {
7080        for name in ["__builtin_ia32_sfence", "__builtin_ia32_lfence", "__builtin_ia32_mfence"] {
7081            let source = format!("void f(void) {{ {name}(); }}\n");
7082            assert!(asm(&source).contains("mfence"), "{name} is a barrier");
7083            let text = body(&source);
7084            assert!(text.contains("fence seq_cst"), "{name}: {text}");
7085        }
7086
7087        let result = run(&options(), "void f(void) { __builtin_ia32_sfence(1); }\n");
7088        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
7089        assert!(result.messages[0].contains("too many arguments"), "{:?}", result.messages);
7090    }
7091
7092    /// The four compare and exchange names are one IR instruction producing two values.
7093    ///
7094    /// Which of the two the expression answers is the difference between three of the four names,
7095    /// and the fourth difference is the C11 pair writing what they found back through the pointer
7096    /// they were handed, which is the branch after the instruction.
7097    #[test]
7098    fn a_compare_and_exchange_is_one_instruction_answering_two_things() {
7099        // The older family, whose two names are the same instruction read two ways. Neither has a
7100        // memory order argument and both are a full barrier, which is what `seq_cst` says.
7101        let text =
7102            body("int f(int *p, int e, int d) { return __sync_val_compare_and_swap(p, e, d); }\n");
7103        assert!(text.contains("%3, %4 = cmpxchg.(i32, i1) %0, %1, %2, align 4, seq_cst"), "{text}");
7104        assert!(text.contains("return %3"), "the value it found: {text}");
7105
7106        let text =
7107            body("int f(int *p, int e, int d) { return __sync_bool_compare_and_swap(p, e, d); }\n");
7108        assert!(text.contains("%3, %4 = cmpxchg.(i32, i1) %0, %1, %2, align 4, seq_cst"), "{text}");
7109        assert!(text.contains("zext.i32 %4"), "whether it happened: {text}");
7110
7111        // The C11 form, whose value expected arrives by pointer and is read before the exchange,
7112        // and whose answer is whether it happened. The write back is on the path where it did not.
7113        let text = body(
7114            "int f(int *p, int *e, int d) { return __atomic_compare_exchange_n(p, e, d, 0, 4, 2); }\n",
7115        );
7116        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
7117        assert!(text.contains("%4, %5 = cmpxchg.(i32, i1) %0, %3, %2, align 4, acq_rel"), "{text}");
7118        assert!(text.contains("br_if %5, block2, block1"), "{text}");
7119        assert!(text.contains("store %4 -> %1, align 4"), "{text}");
7120
7121        // And the form that takes the value to put there by pointer as well, which is one more
7122        // read and is otherwise the same node.
7123        let text = body(
7124            "int f(int *p, int *e, int *d) { return __atomic_compare_exchange(p, e, d, 0, 5, 5); }\n",
7125        );
7126        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
7127        assert!(text.contains("%4 = load.i32 %2, align 4"), "{text}");
7128        assert!(text.contains("%5, %6 = cmpxchg.(i32, i1) %0, %3, %4, align 4, seq_cst"), "{text}");
7129    }
7130
7131    /// On this machine it is `lock cmpxchg`, at the width of the object and at every ordering.
7132    ///
7133    /// The `lock` is what makes the whole of it one step as far as every other processor is
7134    /// concerned, and it is also what makes the instruction a full barrier, which is why the
7135    /// ordering the program wrote changes nothing in what is written here. Every line below is what
7136    /// gcc 16.2.0 writes for the same function.
7137    #[test]
7138    fn a_compare_and_exchange_is_a_locked_instruction_at_the_width_of_the_object() {
7139        let widths = [("char", "b", "%dl"), ("short", "w", "%dx"), ("int", "l", "%edx")];
7140        for (ty, suffix, reg) in widths {
7141            let source = format!(
7142                "int f({ty} *p, {ty} e, {ty} d) {{ return __sync_bool_compare_and_swap(p, e, d); }}\n"
7143            );
7144            let text = asm(&source);
7145            assert!(text.contains("\tlock\n"), "{ty}: {text}");
7146            assert!(text.contains(&format!("cmpxchg{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
7147            assert!(text.contains("sete\t"), "{ty}: {text}");
7148        }
7149        let source =
7150            "int f(long *p, long e, long d) { return __sync_bool_compare_and_swap(p, e, d); }\n";
7151        assert!(asm(source).contains("cmpxchgq\t%rdx, (%rdi)"), "{}", asm(source));
7152
7153        // The ordering the program asked for changes nothing, because a locked instruction on this
7154        // machine orders everything whatever it was asked for, so there is never a barrier beside
7155        // it either.
7156        for order in ["0", "2", "3", "4", "5"] {
7157            let call = format!("__atomic_compare_exchange_n(p, e, d, 0, {order}, 0)");
7158            let source = format!("int f(int *p, int *e, int d) {{ return {call}; }}\n");
7159            let text = asm(&source);
7160            assert!(text.contains("cmpxchgl\t"), "{order}: {text}");
7161            assert!(!text.contains("mfence"), "{order} needs no barrier here: {text}");
7162        }
7163    }
7164
7165    /// A read modify write is one IR instruction, and a name that asks for the value afterwards is
7166    /// that instruction and one more operation.
7167    ///
7168    /// The instruction answers what was there before, which is the convention every machine and
7169    /// every language in this area uses. Half the names in the family ask for the value afterwards
7170    /// instead, and that is the answer and the operand put together again, which is arithmetic on
7171    /// two values already in registers rather than a second flavour of the instruction.
7172    ///
7173    /// The two lock names are here too. They are not read modify writes in the same sense: one is
7174    /// an exchange and the other is a store of a zero, and what makes them a pair is the ordering,
7175    /// which is the one place in the older family that is not sequential consistency.
7176    #[test]
7177    fn a_read_modify_write_is_one_instruction_and_the_arithmetic_a_name_asks_for() {
7178        let text = body("int f(int *p, int v) { return __atomic_fetch_add(p, v, 5); }\n");
7179        assert!(text.contains("%2 = atomic_rmw.i32 add %0, %1, align 4, seq_cst"), "{text}");
7180        assert!(text.contains("return %2"), "the value that was there: {text}");
7181
7182        let text = body("int f(int *p, int v) { return __atomic_add_fetch(p, v, 5); }\n");
7183        assert!(text.contains("%2 = atomic_rmw.i32 add %0, %1, align 4, seq_cst"), "{text}");
7184        assert!(text.contains("%3 = add %2, %1"), "and the value afterwards: {text}");
7185
7186        let text = body("int f(int *p, int v) { return __atomic_sub_fetch(p, v, 5); }\n");
7187        assert!(text.contains("%2 = atomic_rmw.i32 sub %0, %1, align 4, seq_cst"), "{text}");
7188        assert!(text.contains("%3 = sub %2, %1"), "{text}");
7189
7190        // The older family, which passes no ordering and is a full barrier.
7191        let text = body("int f(int *p, int v) { return __sync_fetch_and_sub(p, v); }\n");
7192        assert!(text.contains("%2 = atomic_rmw.i32 sub %0, %1, align 4, seq_cst"), "{text}");
7193
7194        // The exchange, and the older family's spelling of it, which is taking a lock and so is an
7195        // acquire rather than the full barrier the rest of that family is.
7196        let text = body("int f(int *p, int v) { return __atomic_exchange_n(p, v, 5); }\n");
7197        assert!(text.contains("%2 = atomic_rmw.i32 xchg %0, %1, align 4, seq_cst"), "{text}");
7198
7199        let text = body("int f(int *p, int v) { return __sync_lock_test_and_set(p, v); }\n");
7200        assert!(text.contains("%2 = atomic_rmw.i32 xchg %0, %1, align 4, acquire"), "{text}");
7201
7202        // Giving the lock back, which is one of the two names in the family that is handed no value
7203        // to put there, because what it puts there is a zero.
7204        let text = body("void f(int *p) { __sync_lock_release(p); }\n");
7205        assert!(text.contains("release"), "{text}");
7206        assert!(text.contains("%1 = iconst.i32 0"), "{text}");
7207
7208        // And with something after the pointer, which is the list of variables the call promises to
7209        // protect rather than a value to write. Reading it as a value would store whatever the
7210        // caller happened to name there, which is the one thing giving a lock back must not do.
7211        let text = body("void f(int *p, int guard) { __sync_lock_release(p, guard); }\n");
7212        assert!(text.contains("%2 = iconst.i32 0"), "{text}");
7213        assert!(text.contains("atomic_store %2 -> %0, align 4, release"), "{text}");
7214
7215        // The bitwise four, which look no different here from the arithmetic ones: what the machine
7216        // has an instruction for is a question further down and this level does not ask it.
7217        let text = body("int f(int *p, int v) { return __atomic_fetch_and(p, v, 5); }\n");
7218        assert!(text.contains("%2 = atomic_rmw.i32 and %0, %1, align 4, seq_cst"), "{text}");
7219
7220        let text = body("int f(int *p, int v) { return __sync_or_and_fetch(p, v); }\n");
7221        assert!(text.contains("%2 = atomic_rmw.i32 or %0, %1, align 4, seq_cst"), "{text}");
7222        assert!(text.contains("%3 = or %2, %1"), "and the value afterwards: {text}");
7223
7224        // The nand, which is the one of the six that is two operations. The flip is an exclusive or
7225        // against every bit set because the IR has no not and that is what one is.
7226        let text = body("int f(int *p, int v) { return __atomic_nand_fetch(p, v, 5); }\n");
7227        assert!(text.contains("%2 = atomic_rmw.i32 nand %0, %1, align 4, seq_cst"), "{text}");
7228        assert!(text.contains("%3 = and %2, %1"), "{text}");
7229        assert!(text.contains("%4 = iconst.i32 -1"), "{text}");
7230        assert!(text.contains("%5 = xor %3, %4"), "{text}");
7231    }
7232
7233    /// The four operations with no instruction on this machine are a loop around `lock cmpxchg`.
7234    ///
7235    /// The shape is the one every architecture manual writes out by hand: read the word, work out
7236    /// what should be there instead, put it back if nothing else got in first, and go round again
7237    /// when something did. What is checked is that the loop is there at every width, that the
7238    /// operation is inside it, and that no `xchg` or `xadd` got used for something neither of them
7239    /// does.
7240    ///
7241    /// gcc 16.2.0 writes the same loop for the same functions, down to which register holds the
7242    /// value that was read.
7243    #[test]
7244    fn a_bitwise_read_modify_write_is_a_loop_around_the_compare_and_exchange() {
7245        let widths = [("char", "b", "%dl"), ("short", "w", "%dx"), ("int", "l", "%edx")];
7246        for (ty, suffix, reg) in widths {
7247            for (name, call, insn) in [
7248                ("and", "__atomic_fetch_and(p, v, 5)", "and"),
7249                ("or", "__sync_fetch_and_or(p, v)", "or"),
7250                ("xor", "__atomic_xor_fetch(p, v, 5)", "xor"),
7251            ] {
7252                let source = format!("{ty} f({ty} *p, {ty} v) {{ return {call}; }}\n");
7253                let text = asm(&source);
7254                assert!(text.contains("\tlock\n"), "{ty} {name}: {text}");
7255                assert!(
7256                    text.contains(&format!("cmpxchg{suffix}\t{reg}, (%rdi)")),
7257                    "{ty} {name}: {text}"
7258                );
7259                assert!(text.contains(&format!("{insn}{suffix}\t")), "{ty} {name}: {text}");
7260                // The tab matters on the second of these, since `cmpxchg` ends in the other name.
7261                assert!(!text.contains("\txadd"), "{ty} {name} is not an add: {text}");
7262                assert!(!text.contains("\txchg"), "{ty} {name} is not an exchange: {text}");
7263            }
7264        }
7265        let source = "long f(long *p, long v) { return __atomic_fetch_or(p, v, 5); }\n";
7266        assert!(asm(source).contains("cmpxchgq\t%rdx, (%rdi)"), "{}", asm(source));
7267
7268        // The nand, which puts two instructions inside the loop rather than one. The flip is an
7269        // exclusive or against every bit set in the IR and the folder turns that into the `not` the
7270        // machine has, which is what gcc writes here too.
7271        let text = asm("int f(int *p, int v) { return __sync_fetch_and_nand(p, v); }\n");
7272        assert!(text.contains("cmpxchgl\t"), "{text}");
7273        assert!(text.contains("andl\t"), "{text}");
7274        assert!(text.contains("notl\t"), "{text}");
7275    }
7276
7277    /// The three names that pass a value through a pointer are the same access and one plain one.
7278    ///
7279    /// They exist for an object too big to come back in a register, and the front end takes them at
7280    /// their word rather than folding them into the `_n` spellings, because the extra access is real:
7281    /// the caller handed over somewhere to read from or write into and that is where the value has
7282    /// to come from or go. Both of those accesses are plain. The object at the end of the caller's
7283    /// pointer is the caller's own and no other thread has its address, which is what the whole
7284    /// shape is for.
7285    #[test]
7286    fn an_access_through_a_second_pointer_is_the_same_access_and_one_more() {
7287        let text = body("void f(int *p, int *r) { __atomic_load(p, r, 5); }\n");
7288        assert!(text.contains("%2 = atomic_load.i32 %0, align 4, seq_cst"), "{text}");
7289        assert!(text.contains("store %2 -> %1, align 4"), "and out through the place: {text}");
7290
7291        let text = body("void f(int *p, int *v) { __atomic_store(p, v, 3); }\n");
7292        assert!(text.contains("%2 = load.i32 %1, align 4"), "in through the place: {text}");
7293        assert!(text.contains("atomic_store %2 -> %0, align 4, release"), "{text}");
7294
7295        // The exchange, which reads through one pointer and writes through another and is the same
7296        // instruction in between as the spelling that takes and answers values.
7297        let text = body("void f(int *p, int *v, int *r) { __atomic_exchange(p, v, r, 5); }\n");
7298        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
7299        assert!(text.contains("%4 = atomic_rmw.i32 xchg %0, %3, align 4, seq_cst"), "{text}");
7300        assert!(text.contains("store %4 -> %2, align 4"), "{text}");
7301    }
7302
7303    /// The flag pair is an exchange of one byte and a store of a zero over the same byte.
7304    ///
7305    /// One byte whatever the pointer was written as, which is the standard's reading rather than a
7306    /// liberty: the object is an `atomic_flag`, there is no other way to read or write one, so the
7307    /// type the pointer carries says nothing about the access and the width is the implementation's
7308    /// to fix. gcc 16.2.0 writes `xchgb` here through an `int *` too.
7309    ///
7310    /// The answer is a comparison against zero rather than the byte itself, because the type of the
7311    /// call is `_Bool` and a byte that is neither zero nor one is not one. gcc answers the raw byte,
7312    /// and the two agree wherever the flag is only ever touched through this pair.
7313    #[test]
7314    fn a_flag_is_an_exchange_of_one_byte_and_a_store_of_a_zero_over_the_same_byte() {
7315        for pointer in ["char", "int", "void"] {
7316            let source = format!("int f({pointer} *p) {{ return __atomic_test_and_set(p, 5); }}\n");
7317            let text = body(&source);
7318            assert!(text.contains("%1 = iconst.i8 1"), "{pointer}: {text}");
7319            assert!(
7320                text.contains("%2 = atomic_rmw.i8 xchg %0, %1, align 1, seq_cst"),
7321                "{pointer}: {text}"
7322            );
7323            assert!(text.contains("%4 = icmp ne %2, %3"), "{pointer}: {text}");
7324
7325            let source = format!("void f({pointer} *p) {{ __atomic_clear(p, 3); }}\n");
7326            let text = body(&source);
7327            assert!(text.contains("atomic_store %2 -> %0, align 1, release"), "{pointer}: {text}");
7328        }
7329
7330        // And on this machine, where the exchange carries no `lock` because one with memory locks
7331        // the bus whether it was asked to or not. Both lines are what gcc 16.2.0 writes.
7332        let text = asm("int f(int *p) { return __atomic_test_and_set(p, 5); }\n");
7333        assert!(text.contains("xchgb\t%al, (%rdi)"), "{text}");
7334        assert!(text.contains("setne\t"), "{text}");
7335    }
7336
7337    /// On this machine it is `xchg` where the machine has an exchange and `lock xadd` where it has
7338    /// an add, at the width of the object.
7339    ///
7340    /// The exchange carries no prefix and the add carries one, which is the machine rather than an
7341    /// oversight: an exchange with memory locks the bus whether it is asked to or not. Both are
7342    /// therefore full barriers whatever ordering the program wrote, so no ordering costs an
7343    /// `mfence` beside them. Every line below is what gcc 16.2.0 writes for the same function.
7344    #[test]
7345    fn a_read_modify_write_is_an_exchange_or_a_locked_add_at_the_width_of_the_object() {
7346        let widths = [("char", "b", "%sil"), ("short", "w", "%si"), ("int", "l", "%esi")];
7347        for (ty, suffix, reg) in widths {
7348            let source =
7349                format!("{ty} f({ty} *p, {ty} v) {{ return __atomic_fetch_add(p, v, 5); }}\n");
7350            let text = asm(&source);
7351            assert!(text.contains("\tlock\n"), "{ty}: {text}");
7352            assert!(text.contains(&format!("xadd{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
7353
7354            let source =
7355                format!("{ty} f({ty} *p, {ty} v) {{ return __atomic_exchange_n(p, v, 5); }}\n");
7356            let text = asm(&source);
7357            assert!(text.contains(&format!("xchg{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
7358            assert!(!text.contains("\tlock\n"), "an exchange is locked already: {ty}: {text}");
7359        }
7360        let source = "long f(long *p, long v) { return __atomic_fetch_add(p, v, 5); }\n";
7361        assert!(asm(source).contains("xaddq\t%rsi, (%rdi)"), "{}", asm(source));
7362
7363        // A subtraction is the same instruction over the negated operand, which is right at every
7364        // width because the machine's arithmetic wraps.
7365        let source = "int f(int *p, int v) { return __atomic_fetch_sub(p, v, 5); }\n";
7366        let text = asm(source);
7367        assert!(text.contains("negl\t"), "{text}");
7368        assert!(text.contains("xaddl\t"), "{text}");
7369
7370        // The ordering changes nothing, for the reason it changes nothing for a compare and
7371        // exchange: a locked instruction on this machine orders everything whatever it was asked.
7372        for order in ["0", "2", "3", "4", "5"] {
7373            let source =
7374                format!("int f(int *p, int v) {{ return __atomic_fetch_add(p, v, {order}); }}\n");
7375            let text = asm(&source);
7376            assert!(text.contains("xaddl\t"), "{order}: {text}");
7377            assert!(!text.contains("mfence"), "{order} needs no barrier here: {text}");
7378        }
7379
7380        // And the lock pair, which is the exchange and a store of a zero. Neither is a barrier
7381        // instruction: the exchange is one already and the store is a release, which this machine
7382        // gives away.
7383        let text = asm("int f(int *p, int v) { return __sync_lock_test_and_set(p, v); }\n");
7384        assert!(text.contains("xchgl\t%esi, (%rdi)"), "{text}");
7385        // The zero goes through a register on the way, which is where every constant this
7386        // compiler stores goes: gcc writes the one instruction because it has a store that takes an
7387        // immediate and no rule here does. That is a rule this rule set is missing rather than
7388        // anything about the builtin, and it is the same two instructions a plain `*p = 0` makes.
7389        // The register gets its zero from an exclusive or with itself rather than from a move of a
7390        // zero, which is `rucc_codegen::shorten` writing the shorter of the two spellings.
7391        let text = asm("void f(int *p) { __sync_lock_release(p); }\n");
7392        assert!(text.contains("xorl\t%eax, %eax"), "{text}");
7393        assert!(text.contains("movl\t%eax, (%rdi)"), "{text}");
7394        assert!(!text.contains("mfence"), "a release store needs no barrier here: {text}");
7395    }
7396
7397    /// The two lock free questions are numbers in the program rather than calls to anything.
7398    ///
7399    /// Both answer from the size, which has to be a power of two no wider than the widest access
7400    /// this compiler writes, and from what the pointer says about the alignment. Sixteen bytes is
7401    /// no here and is no in gcc without `-mcx16`, because `cmpxchg16b` is not in the baseline and
7402    /// nothing here writes it. Three bytes is no because there is no three byte access at all.
7403    ///
7404    /// The whole point of both names is that the answer is available before the program runs, so
7405    /// what is checked is that a `mov` of a constant is the whole function and that no call was
7406    /// left behind. A call would be to `__atomic_is_lock_free` in libatomic, which is not a library
7407    /// this links against.
7408    #[test]
7409    fn the_lock_free_questions_are_answered_as_constants() {
7410        for size in ["1", "2", "4", "8"] {
7411            let source =
7412                format!("int f(void) {{ return __atomic_always_lock_free({size}, 0); }}\n");
7413            let text = asm(&source);
7414            assert!(text.contains("movb\t$1, %al"), "{size} bytes is lock free: {text}");
7415            assert!(!text.contains("call"), "and is not a call: {text}");
7416        }
7417        for size in ["3", "16", "sizeof(long double)"] {
7418            let source = format!("int f(void) {{ return __atomic_is_lock_free({size}, 0); }}\n");
7419            let text = asm(&source);
7420            assert!(text.contains("movb\t$0, %al"), "{size} bytes is not: {text}");
7421            assert!(!text.contains("call"), "and is not a call either: {text}");
7422        }
7423
7424        // A size the compiler cannot work out, which is no rather than a refusal, and an object
7425        // whose type is aligned under the size asked about, which is the whole of what the second
7426        // argument is for.
7427        let text = asm("int f(int n) { return __atomic_is_lock_free(n, 0); }\n");
7428        assert!(text.contains("movb\t$0, %al"), "a size nobody knows is not lock free: {text}");
7429        let text = asm("int f(int *p) { return __atomic_always_lock_free(8, p); }\n");
7430        assert!(text.contains("movb\t$0, %al"), "eight bytes at four is not: {text}");
7431        let text = asm("int f(long *p) { return __atomic_always_lock_free(8, p); }\n");
7432        assert!(text.contains("movb\t$1, %al"), "and at eight it is: {text}");
7433    }
7434
7435    /// A memory order an operation cannot carry is read as the strongest one, and said so about.
7436    ///
7437    /// There are three ways the number is not one the operation can take: it is not a constant at
7438    /// all, it is not one of the six the headers define, or it is one of them and means nothing for
7439    /// this operation, which is a release load or an acquire store. All three become sequential
7440    /// consistency, which is stronger than anything the program could have meant, so a program that
7441    /// wrote nonsense gets a correct answer rather than a fast one. gcc does the same.
7442    ///
7443    /// The last two also warn, because the number was written down and is wrong. The first does
7444    /// not: gcc takes a computed order, and so does the C11 spelling, so a warning there would fire
7445    /// on correct programs.
7446    #[test]
7447    fn a_memory_order_an_operation_cannot_carry_is_read_as_the_strongest() {
7448        let mut opts = options();
7449        opts.emit = EmitKind::Ir;
7450
7451        let acquire_store = run(&opts, "void f(int *p, int v) { __atomic_store_n(p, v, 2); }\n");
7452        assert!(acquire_store.text().contains("seq_cst"), "{:?}", acquire_store.text());
7453        assert!(acquire_store.messages[0].contains("[W0333]"), "{:?}", acquire_store.messages);
7454
7455        let nonsense = run(&opts, "int f(int *p) { return __atomic_load_n(p, 99); }\n");
7456        assert!(nonsense.text().contains("seq_cst"), "{:?}", nonsense.text());
7457        assert!(nonsense.messages[0].contains("[W0333]"), "{:?}", nonsense.messages);
7458
7459        let computed = run(&opts, "int f(int *p, int n) { return __atomic_load_n(p, n); }\n");
7460        assert!(computed.text().contains("seq_cst"), "{:?}", computed.text());
7461        assert_eq!(computed.messages, Vec::<String>::new(), "a computed order is not a mistake");
7462    }
7463
7464    /// A conversion between a float and the widest unsigned integer, which the machine has not got.
7465    ///
7466    /// Every other conversion between a float and an integer is the signed one at some width with a
7467    /// widening in front or a narrowing behind. These two are not, because there is no signed width
7468    /// that holds every value of an unsigned sixty four bit integer, so each is the signed
7469    /// conversion with arithmetic around it that brings the value into range and puts it back.
7470    ///
7471    /// What is checked here is that the conversion happens at all and that it happens without a
7472    /// branch. gcc writes a branch for both; this writes the choice as a mask, because every rewrite
7473    /// in that pass stays inside the block it started in. The arithmetic itself is checked in
7474    /// `rucc-codegen`, where it can be run against the answer rather than read in the assembly.
7475    #[test]
7476    fn a_conversion_between_a_float_and_the_widest_unsigned_integer_is_written_without_a_branch() {
7477        let text = asm("double f(unsigned long long x) { return (double)x; }\n");
7478        assert!(text.contains("cvtsi2sdq"), "the signed conversion is what runs: {text}");
7479        assert!(text.contains("shrq"), "with the value halved first: {text}");
7480        assert!(text.contains("addsd"), "and doubled after: {text}");
7481        assert!(!text.contains("\tj"), "and no branch anywhere: {text}");
7482
7483        let text = asm("unsigned long long f(double d) { return (unsigned long long)d; }\n");
7484        assert!(text.contains("cvttsd2siq"), "the signed conversion is what runs: {text}");
7485        assert!(text.contains("subsd"), "with half the range taken off first: {text}");
7486        assert!(text.contains("shlq\t$63"), "and the top bit put back: {text}");
7487        assert!(!text.contains("\tj"), "and no branch anywhere: {text}");
7488    }
7489
7490    /// The plain names are the library's only where nothing else has taken them.
7491    ///
7492    /// Four ways a program says it means something else. A `static` definition is its own
7493    /// function and the name outside the file is somebody else's. A declaration of another type
7494    /// is another function. `-fno-builtin` and `-fno-builtin-<name>` say so outright, and
7495    /// `-ffreestanding` says there is no C library for the name to be the name of. Every one of
7496    /// these was measured against gcc 16.2.0, which calls the program's function in all of them.
7497    ///
7498    /// The `__builtin_` spelling goes on meaning the library's function through all of it, which
7499    /// is what the prefix is for and what lets a freestanding build reach one deliberately.
7500    #[test]
7501    fn a_plain_name_the_program_took_is_the_programs_own_function() {
7502        let taken = concat!(
7503            "static long long llabs(long long b) { return 7; }\n",
7504            "long long f(long long x) { return llabs(x); }\n",
7505        );
7506        assert!(ir(taken).contains("call @llabs"), "a static definition is the program's own");
7507
7508        let retyped = concat!("int llabs(int b);\n", "int f(int x) { return llabs(x); }\n",);
7509        assert!(ir(retyped).contains("call @llabs"), "another type is another function");
7510
7511        let plain = concat!(
7512            "long long llabs(long long b);\n",
7513            "long long f(long long x) { return llabs(x); }\n",
7514        );
7515        let mut opts = options();
7516        opts.emit = EmitKind::Ir;
7517        assert!(!run(&opts, plain).text().contains("call @llabs"), "the library's by default");
7518
7519        opts.builtins = false;
7520        assert!(run(&opts, plain).text().contains("call @llabs"), "-fno-builtin");
7521
7522        opts.builtins = true;
7523        opts.no_builtin = vec!["llabs".to_owned()];
7524        assert!(run(&opts, plain).text().contains("call @llabs"), "-fno-builtin-llabs");
7525        let one = "long labs(long b);\nlong f(long x) { return labs(x); }\n";
7526        assert!(!run(&opts, one).text().contains("call @labs"), "one name and not the family");
7527
7528        // `-ffreestanding` reaches the front end as the same answer, which is what the driver
7529        // does with it in `compile`, and the prefixed spelling is untouched by any of it.
7530        opts.no_builtin = Vec::new();
7531        opts.builtins = false;
7532        let prefixed = "long long f(long long x) { return __builtin_llabs(x); }\n";
7533        assert!(!run(&opts, prefixed).text().contains("call @llabs"), "the prefix is a promise");
7534    }
7535
7536    /// The hint builtins are their first argument, and nothing is left of the hint.
7537    ///
7538    /// Which way a branch is expected to go is the whole of what they say, and there is nothing
7539    /// here that reads a branch weight yet, so what reaches the IR is the value and the hint is
7540    /// gone. The one thing the prototype has to keep doing is converting: gcc gives both of them
7541    /// a `long` result, so `sizeof(__builtin_expect((char)1, 1))` is eight and a narrower argument
7542    /// widens before it is answered with.
7543    ///
7544    /// Whether a side effect in the hint happens depends on the first argument, which is gcc's
7545    /// answer rather than a rule anybody designed. A constant first argument folds the whole call
7546    /// where it is written and the hint goes with it, and a first argument that is not a constant
7547    /// leaves the hint standing. Both halves are below and both were measured on gcc 16.2.0.
7548    #[test]
7549    fn the_hint_builtins_are_their_first_argument_and_the_hint_leaves_no_trace() {
7550        let text = ir(concat!(
7551            "long a = __builtin_expect(7, 1);\n",
7552            "long b = __builtin_expect_with_probability(9, 1, 0.9);\n",
7553            "unsigned long c = sizeof(__builtin_expect((char)1, 1));\n",
7554        ));
7555        assert!(text.contains("global @a : i64 = 7,"), "{text}");
7556        assert!(text.contains("global @b : i64 = 9,"), "{text}");
7557        assert!(text.contains("global @c : i64 = 8,"), "{text}");
7558        assert!(!text.contains("__builtin_expect"), "it is not a call to anything:\n{text}");
7559
7560        // A narrower argument is widened by the prototype before it is handed back, and it is
7561        // widened with its sign, since the parameter is a signed `long`.
7562        let text = body("long f(char c) { return __builtin_expect(c, 1); }\n");
7563        assert!(text.contains("sext"), "{text}");
7564
7565        // The first argument is a constant, so the second is not evaluated and `i` is still zero,
7566        // and neither is the third. What is left of each statement is the first argument widened,
7567        // which nothing reads and which the first pass that looks for dead code will take out.
7568        let one = "block0:\n    %0 = iconst.i32 0\n    %1 = iconst.i32 1\n    %2 = sext.i64 %1\n    return %0\n";
7569        assert_eq!(body("int f(void) { int i = 0; __builtin_expect(1, i++); return i; }\n"), one);
7570        let source = "int g(void) { int i = 0; __builtin_expect_with_probability(1, i++, 0.5); return i; }\n";
7571        assert_eq!(body(source), one);
7572
7573        // The first argument is not a constant, so the hint runs and `i` comes back one. There is
7574        // an increment in the body and the value it returns is the load after it, which is what
7575        // gcc gives for the same program, and the whole of tamnd/rucc#584 is that this used to
7576        // come out the same as the pair above.
7577        let kept = body("int f(int n) { int i = 0; __builtin_expect(n, i++); return i; }\n");
7578        assert!(kept.contains("add.nsw"), "the hint still runs: {kept}");
7579        assert!(kept.ends_with("return %3\n"), "and the answer is what it left behind: {kept}");
7580        let both = "int g(int n) { int i = 0; __builtin_expect_with_probability(n, i++, 0.5); return i; }\n";
7581        assert!(body(both).contains("add.nsw"), "and so does the one with three arguments");
7582    }
7583
7584    /// A point control does not arrive at, in both of the ways the compiler has one.
7585    ///
7586    /// `__builtin_unreachable()` is the promise written down, and a function whose body can run
7587    /// off the bottom is the walk arriving at the same place on its own. Neither writes an
7588    /// instruction, which is what gcc 16.2.0 does at `-O0`: it emits the epilogue and the `ret`
7589    /// for both of the functions below and nothing else, and the two of them come out byte for
7590    /// byte the same there.
7591    ///
7592    /// The `ret` is the part worth holding on to. It is not there because anything runs it, it is
7593    /// there because a function whose last instruction is not a return is one that falls into
7594    /// whatever the assembler puts after it.
7595    #[test]
7596    fn a_promise_that_control_does_not_arrive_writes_no_instruction() {
7597        let promised = "int f(int x) { if (x) return 1; __builtin_unreachable(); }\n";
7598        let text = ir(promised);
7599        assert!(text.contains("    unreachable_hint\n"), "{text}");
7600        assert!(!text.contains("call"), "it is not a call to anything:\n{text}");
7601
7602        // The statement after it is still lowered. Continuing to translate a path the program
7603        // promised is dead is one of the things a compiler may do with undefined behaviour, and
7604        // it is the one that keeps a program built at `-O0` behaving the way it was watched to.
7605        let after = body("int g(int x) { __builtin_unreachable(); return x; }\n");
7606        assert!(after.contains("return"), "{after}");
7607
7608        // Both functions are the same instructions, because the hint writes none of them and the
7609        // terminator underneath it writes none either.
7610        let text = asm(promised);
7611        let mine = text.split_once("\nf:\n").expect("a definition").1;
7612        let mine = mine.split_once("\t.size").expect("a definition").0;
7613        let plain = asm("int f(int x) { if (x) return 1; }\n");
7614        let plain = plain.split_once("\nf:\n").expect("a definition").1;
7615        let plain = plain.split_once("\t.size").expect("a definition").0;
7616        assert_eq!(mine, plain);
7617        // The last instruction, rather than the last line, because the unwind record is closed
7618        // after it and a directive is not something the machine runs.
7619        let last = mine.lines().rfind(|line| !line.trim_start().starts_with('.'));
7620        assert_eq!(last.map(str::trim), Some("ret"), "{mine}");
7621        assert!(!mine.contains("ud2"), "{mine}");
7622    }
7623
7624    /// The two names stay apart, which is what having both of them is for.
7625    ///
7626    /// The one the program wrote is what the call is checked against and what a diagnostic about
7627    /// it says, and the one the library defines is what the call ends up carrying. A compiler
7628    /// that kept only the second would report this against `abort`, which is a function the
7629    /// program never mentions.
7630    #[test]
7631    fn a_library_builtin_is_diagnosed_under_the_name_the_program_wrote() {
7632        let mut opts = options();
7633        opts.emit = EmitKind::Ir;
7634        let messages = run(&opts, "void f(void) { __builtin_abort(1); }\n").messages;
7635        assert!(
7636            messages.iter().any(|m| m.contains("__builtin_abort")),
7637            "expected the written name in {messages:?}"
7638        );
7639    }
7640
7641    /// A builtin nothing lowers is refused where it is written, rather than at the link.
7642    ///
7643    /// One name is left, which is the last of the atomic family that is refused and is also the
7644    /// one whose prefix is not `__builtin_`; its older half has nothing left in it at all, and so
7645    /// does the half of the family that carries a prototype. What the message has to carry is the
7646    /// name, because the whole complaint about the link error this replaces is that the name in it
7647    /// was one the compiler chose.
7648    #[test]
7649    fn a_builtin_nothing_lowers_is_refused_by_name() {
7650        let mut opts = options();
7651        opts.emit = EmitKind::Ir;
7652        let builtin = "__atomic_signal_fence";
7653        let source = format!("int counter;\nint f(void) {{ return ({builtin}(5), 0); }}\n");
7654        let messages = run(&opts, &source).messages;
7655        let named = messages.iter().any(|m| m.contains(builtin) && m.contains("E0686"));
7656        assert!(named, "expected {builtin} to be refused by name in {messages:?}");
7657    }
7658
7659    /// The refusal is about a call and not about the name, so a program that defines the name
7660    /// itself gets the function it wrote.
7661    ///
7662    /// That is not the reason the refusal exists, but a definition in front of us is a definition
7663    /// and the call to it links. It works here because the name is one with no prototype and no
7664    /// meaning the front end knows, which is what is left once the rest of the family is
7665    /// implemented: a `__builtin_` name the front end does answer is answered whatever the program
7666    /// declares, the way gcc answers one.
7667    #[test]
7668    fn what_is_refused_is_the_call_and_not_the_name() {
7669        let text = ir(concat!(
7670            "void __atomic_signal_fence(int order) { (void)order; }\n",
7671            "void f(void) { __atomic_signal_fence(5); }\n",
7672        ));
7673        assert!(text.contains("call @__atomic_signal_fence"), "{text}");
7674    }
7675
7676    /// How many bytes are behind an address is read off the layout, for every shape the walk
7677    /// covers.
7678    ///
7679    /// This is what `_FORTIFY_SOURCE` runs on, so the numbers matter one at a time rather than in
7680    /// aggregate: a size too small turns a correct copy into an abort, and a size too large turns
7681    /// a checked copy back into an unchecked one. Every answer here was measured against gcc
7682    /// 16.2.0 first. They are written as initializers so that each one is a constant in the
7683    /// output and the test reads as the table it is.
7684    #[test]
7685    fn the_object_size_of_an_address_is_what_the_layout_leaves_in_front_of_it() {
7686        let text = ir(concat!(
7687            "struct S { char a[8]; int n; char b[12]; };\n",
7688            "char g[32];\n",
7689            "struct S gs;\n",
7690            "unsigned long whole = __builtin_object_size(g, 0);\n",
7691            "unsigned long moved = __builtin_object_size(g + 4, 0);\n",
7692            "unsigned long back = __builtin_object_size(g + 30 - 2, 0);\n",
7693            "unsigned long outer = __builtin_object_size(gs.a, 0);\n",
7694            "unsigned long inner = __builtin_object_size(gs.a, 1);\n",
7695            "unsigned long scalar = __builtin_object_size(&gs.n, 1);\n",
7696            "unsigned long after = __builtin_object_size(&gs.n, 0);\n",
7697            "unsigned long into = __builtin_object_size(&gs.b[2], 1);\n",
7698            "unsigned long text = __builtin_object_size(\"hello\", 0);\n",
7699            "unsigned long dyn = __builtin_dynamic_object_size(gs.b, 1);\n",
7700        ));
7701        for (name, size) in [
7702            ("whole", 32),
7703            ("moved", 28),
7704            ("back", 4),
7705            ("outer", 24),
7706            ("inner", 8),
7707            ("scalar", 4),
7708            ("after", 16),
7709            ("into", 10),
7710            ("text", 6),
7711            ("dyn", 12),
7712        ] {
7713            let said = format!("global @{name} : i64 = {size},");
7714            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
7715        }
7716    }
7717
7718    /// A local is as knowable as a global, which is the whole point of asking on the way into a
7719    /// copy.
7720    ///
7721    /// A fortified header expands around the destination the caller wrote, and the destination a
7722    /// program most wants checked is the buffer on its own stack. Nothing in the answer depends on
7723    /// storage duration, unlike in a constant expression, where the address of a local is exactly
7724    /// what is not allowed.
7725    #[test]
7726    fn the_object_behind_an_address_can_be_one_with_automatic_storage() {
7727        let text = body(concat!(
7728            "struct S { char a[8]; int n; char b[12]; };\n",
7729            "unsigned long f(void) {\n",
7730            "  char loc[20];\n",
7731            "  struct S ls;\n",
7732            "  return __builtin_object_size(loc + 3, 0) + __builtin_object_size(ls.b + 2, 1);\n",
7733            "}\n",
7734        ));
7735        assert!(text.contains("iconst.i64 17"), "twenty bytes with three used: {text}");
7736        assert!(text.contains("iconst.i64 10"), "twelve bytes with two used: {text}");
7737    }
7738
7739    /// An address whose object the walk cannot see answers at whichever end of the range the kind
7740    /// asks for.
7741    ///
7742    /// The two bits are a question and the answer has to fit it. A kind wanting the largest object
7743    /// the address could be in has to name a size nothing is bigger than, and a kind wanting the
7744    /// smallest has to name a size nothing is smaller than, so the unknown answers are all ones
7745    /// and zero. That pair is what a fortified header compares against to decide whether to check
7746    /// at all, and getting either of them the wrong way round turns every unknown copy into an
7747    /// abort.
7748    #[test]
7749    fn an_address_with_no_object_in_sight_answers_at_the_end_of_the_range_its_kind_asks_for() {
7750        let text = ir(concat!(
7751            "struct T { int n; char f[]; };\n",
7752            "extern char *p;\n",
7753            "extern struct T *t;\n",
7754            "unsigned long largest = __builtin_object_size(p, 0);\n",
7755            "unsigned long nearest = __builtin_object_size(p, 1);\n",
7756            "unsigned long least = __builtin_object_size(p, 2);\n",
7757            "unsigned long tight = __builtin_object_size(p, 3);\n",
7758            "unsigned long flex = __builtin_object_size(t->f, 1);\n",
7759            "int says = __builtin_object_size(p, 0) == (unsigned long)-1;\n",
7760        ));
7761        for name in ["largest", "nearest", "flex"] {
7762            // All ones, printed as the signed rendering of the sixty four bits it is held in.
7763            // `says` is what pins the pattern itself, since it is the comparison a fortified
7764            // header writes and it folds only if every bit is set.
7765            let said = format!("global @{name} : i64 = -1,");
7766            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
7767        }
7768        for name in ["least", "tight"] {
7769            let said = format!("global @{name} : i64 = 0,");
7770            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
7771        }
7772        assert!(text.contains("global @says : i32 = 1,"), "{text}");
7773    }
7774
7775    /// The address is not evaluated, which is the rule `sizeof` follows and for the same reason.
7776    ///
7777    /// What the builtin reads is the shape of the expression rather than the value it would
7778    /// produce, so there is nothing to run. It matters because a fortified header writes the
7779    /// destination twice, once into the copy and once into the size, and a program whose
7780    /// destination is `*next()` would advance twice if this evaluated.
7781    #[test]
7782    fn the_address_an_object_size_is_asked_about_is_not_evaluated() {
7783        let text = body(concat!(
7784            "extern char *side(void);\n",
7785            "unsigned long f(void) { return __builtin_object_size(side(), 0); }\n",
7786        ));
7787        assert!(!text.contains("call"), "nothing is called: {text}");
7788    }
7789
7790    /// The kind has to be a constant in range, because it says which of four questions was asked.
7791    ///
7792    /// A number that is not known until the program runs decides nothing, and one outside the two
7793    /// bits names no question at all. gcc refuses both in one sentence and so does this.
7794    #[test]
7795    fn a_kind_that_is_not_one_of_the_four_is_refused() {
7796        for source in [
7797            "extern char *p;\nextern int k;\nunsigned long f(void) ".to_owned()
7798                + "{ return __builtin_object_size(p, k); }\n",
7799            "extern char *p;\nunsigned long f(void) { return __builtin_object_size(p, 4); }\n"
7800                .to_owned(),
7801            "extern char *p;\nunsigned long f(void) ".to_owned()
7802                + "{ return __builtin_dynamic_object_size(p, -1); }\n",
7803        ] {
7804            let messages = errors(&source);
7805            let named = messages.iter().any(|m| m.contains("E0709") && m.contains("0 to 3"));
7806            assert!(named, "expected a complaint about the kind in {messages:?}");
7807        }
7808    }
7809
7810    /// The pair that saves a place in a function and comes back to it, which is not a call.
7811    ///
7812    /// What the IR has to show is one instruction each and no call to anything: there is no
7813    /// function of either name for a call to reach, and a program that got one would fail to link.
7814    /// The save answers an `int`, which is the value that says how control got there.
7815    #[test]
7816    fn the_pair_that_saves_a_place_lowers_to_the_two_markers() {
7817        let text = ir(concat!(
7818            "void *buf[5];\n",
7819            "int f(void) {\n",
7820            "  if (__builtin_setjmp(buf)) return 2;\n",
7821            "  return 1;\n",
7822            "}\n",
7823            "void g(void) { __builtin_longjmp(buf, 1); }\n",
7824        ));
7825        assert!(text.contains("= setjmp_marker.i32 %0\n"), "the save answers a value: {text}");
7826        assert!(text.contains("    longjmp_marker %0\n"), "the restore answers nothing: {text}");
7827        assert!(!text.contains("call @"), "neither of them is a call: {text}");
7828    }
7829
7830    /// Every local of a function that saves a place lives in the frame, and not in a value.
7831    ///
7832    /// The edge a restore travels is not an edge of the graph, so a local the SSA construction
7833    /// renamed would answer the write that reached the read along the edges there are rather than
7834    /// the write that last ran. The second function here is the same code without the save, where
7835    /// the local is a value and there is no slot at all, which is what makes the first one a rule
7836    /// about the save and not about the shape of the code.
7837    #[test]
7838    fn a_local_of_a_function_that_saves_a_place_gets_a_slot() {
7839        let text = ir(concat!(
7840            "void *buf[5];\n",
7841            "int f(int x) { int a = 0; if (__builtin_setjmp(buf)) return a; a = 1; return x; }\n",
7842            "int g(int x) { int a = 0; if (x) return a; a = 1; return x; }\n",
7843        ));
7844        let (saves, plain) = text.split_once("func @g").expect("both functions");
7845        assert_eq!(saves.matches("= alloca").count(), 2, "the parameter and the local: {text}");
7846        assert!(saves.contains("store %9 -> %2"), "the local is written through: {text}");
7847        assert!(!plain.contains("alloca"), "nothing in the plain one needs a slot: {text}");
7848    }
7849
7850    /// A value set before a library `sigsetjmp` and read after the `siglongjmp` keeps a spill slot
7851    /// of its own.
7852    ///
7853    /// The shape of Postgres's `PG_TRY`. Five values are live across the call, one more than the
7854    /// callee saved registers left over, so some go to the stack. They are dead on the arm that
7855    /// runs first, and before this that arm's own values were given the same slots, so the arm the
7856    /// jump lands in read them back. Every slot is written by one value, so no offset is stored to
7857    /// twice.
7858    #[test]
7859    fn a_value_live_across_sigsetjmp_keeps_its_spill_slot() {
7860        each_spill_slot_written_once(&across("int __sigsetjmp(sigjmp_buf, int);\n", "__sigsetjmp"));
7861    }
7862
7863    /// The same shape through a function with a name nobody knows, which only the attribute says
7864    /// comes back twice. tamnd/rucc#2012.
7865    #[test]
7866    fn a_value_live_across_a_returns_twice_call_keeps_its_spill_slot() {
7867        let declared = "int save_here(sigjmp_buf, int) __attribute__((__returns_twice__));\n";
7868        each_spill_slot_written_once(&across(declared, "save_here"));
7869    }
7870
7871    /// A value set before `setjmp` and read after the `longjmp` keeps its slot to itself, at `-O0`
7872    /// and at `-O2`.
7873    ///
7874    /// The reduction in tamnd/rucc#2035, which glibc's `<setjmp.h>` turns into a call to
7875    /// `_setjmp`. `v` is dead on the arm that runs first, so that arm's own values were given its
7876    /// slot and the handler printed `v + 1`. The handler reads `v` from a slot, and nothing between
7877    /// the `setjmp` and the call that jumps back writes that slot.
7878    #[test]
7879    fn a_value_live_across_setjmp_shares_its_slot_with_nothing_in_the_first_arm() {
7880        let source = concat!(
7881            "typedef long jmp_buf[25];\n",
7882            "int _setjmp(jmp_buf);\n",
7883            "void longjmp(jmp_buf, int) __attribute__((noreturn));\n",
7884            "int printf(const char *, ...);\n",
7885            "static jmp_buf *stack;\n",
7886            "static volatile long long sink;\n",
7887            "static int cells[64];\n",
7888            "static volatile int seed_in = 3;\n",
7889            "static void work(void) { longjmp(*stack, 1); }\n",
7890            "int main(void) {\n",
7891            "  int seed = seed_in;\n",
7892            "  int v = seed * 2;\n",
7893            "  jmp_buf buf;\n",
7894            "  if (_setjmp(buf) == 0) {\n",
7895            "    stack = &buf;\n",
7896            "    int *p = &cells[seed + 3];\n",
7897            "    int a = v + 8;\n",
7898            "    int b = seed * 2005;\n",
7899            "    int *q = &cells[v + 1];\n",
7900            "    work();\n",
7901            "    sink = *p + a + b + *q;\n",
7902            "  } else {\n",
7903            "    printf(\"%d\\n\", v);\n",
7904            "  }\n",
7905            "  return 0;\n",
7906            "}\n",
7907        );
7908        for level in [rucc_session::OptLevel::O0, rucc_session::OptLevel::O2] {
7909            let mut opts = options();
7910            opts.emit = EmitKind::Asm;
7911            opts.opt_level = level;
7912            let result = run(&opts, source);
7913            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
7914            let text = result.text();
7915            let body = text.split_once("\nmain:\n").expect("the function").1;
7916            let lines: Vec<&str> = body.lines().map(str::trim).collect();
7917            let save = lines.iter().position(|l| *l == "call\t_setjmp").expect("the save");
7918            let jump = lines[save..]
7919                .iter()
7920                .position(|l| *l == "call\twork" || *l == "call\tlongjmp")
7921                .map(|at| save + at)
7922                .unwrap_or_else(|| panic!("the call that jumps back at {level:?}:\n{text}"));
7923            let printf = lines.iter().position(|l| *l == "call\tprintf").expect("the handler");
7924            // The load that hands `v` to `printf` as its second argument.
7925            let slot = lines[jump..printf]
7926                .iter()
7927                .rev()
7928                .find_map(|l| l.strip_suffix(", %rsi").or_else(|| l.strip_suffix(", %esi")))
7929                .and_then(|l| l.split_once('\t'))
7930                .map(|(_, place)| place)
7931                .filter(|place| place.ends_with("(%rsp)") || place.ends_with("(%rbp)"))
7932                .unwrap_or_else(|| panic!("the handler reads v from a slot at {level:?}:\n{text}"));
7933            let writes = |l: &&str| {
7934                !l.starts_with("cmp") && !l.starts_with("test") && l.ends_with(&format!(", {slot}"))
7935            };
7936            assert!(
7937                lines[..save].iter().any(writes),
7938                "{slot} is written before the save at {level:?}:\n{text}"
7939            );
7940            assert!(
7941                !lines[save..jump].iter().any(writes),
7942                "{slot} is written again before the jump at {level:?}:\n{text}"
7943            );
7944        }
7945    }
7946
7947    /// Five values live across a call to `save`, declared by `declared`, and five more that die
7948    /// before the jump back, which is enough to spill on x86-64.
7949    fn across(declared: &str, save: &str) -> String {
7950        asm(&format!(
7951            "typedef long sigjmp_buf[25];\n{declared}int id(int);\nvoid thrower(int);\n\
7952             int work(int n) {{\n\
7953             \x20 int v0 = id(n), v1 = id(n + 1), v2 = id(n + 2), v3 = id(n + 3), v4 = id(n + 4);\n\
7954             \x20 sigjmp_buf b;\n\
7955             \x20 if ({save}(b, 0) == 0) {{\n\
7956             \x20   int w0 = id(v0 + v1), w1 = id(v1 + v2), w2 = id(v2 + v3);\n\
7957             \x20   int w3 = id(v3 + v4), w4 = id(v4 + v0);\n\
7958             \x20   thrower(n);\n\
7959             \x20   return w0 ^ w1 ^ w2 ^ w3 ^ w4;\n\
7960             \x20 }}\n\
7961             \x20 return v0 + v1 + v2 + v3 + v4;\n\
7962             }}\n"
7963        ))
7964    }
7965
7966    /// No two spills in the text go to the same slot, and there is at least one.
7967    fn each_spill_slot_written_once(text: &str) {
7968        let mut stored = Vec::new();
7969        for line in text.lines().map(str::trim) {
7970            let Some(operands) = line.strip_prefix("movq\t%") else { continue };
7971            if let Some((_, place)) = operands.split_once(", ") {
7972                if place.ends_with("(%rsp)") {
7973                    assert!(!stored.contains(&place), "{place} is written twice:\n{text}");
7974                    stored.push(place);
7975                }
7976            }
7977        }
7978        assert!(!stored.is_empty(), "something should have been spilled:\n{text}");
7979    }
7980
7981    /// What the save writes and where it leaves control, which is a new block.
7982    ///
7983    /// Four words: the frame pointer, the address to come back to, the stack pointer, and the
7984    /// address of the word the answer arrives in, which is this compiler's own and is why the
7985    /// block after the save opens with a load. The frame pointer is kept although the function
7986    /// asked for nothing and calls nothing, since the epilogue has to find the caller's frame
7987    /// after control has come back, and the frame is grown although there is one word in it,
7988    /// since a function control comes back into cannot use the red zone.
7989    #[test]
7990    fn the_save_writes_four_words_and_carries_on_in_a_new_block() {
7991        let text =
7992            asm(concat!("void *buf[5];\n", "int f(void) { return __builtin_setjmp(buf); }\n",));
7993        let body = text.split_once("\nf:\n").expect("the function").1;
7994        assert!(body.contains("\tmovq\t%rsp, %rbp\n"), "a frame pointer whatever: {text}");
7995        assert!(body.contains("\tsubq\t$8, %rsp\n"), "no red zone: {text}");
7996        assert!(body.contains("\tmovq\t%rbp, (%rax)\n"), "the frame pointer: {text}");
7997        assert!(body.contains("\tmovq\t%rsp, 16(%rax)\n"), "the stack pointer: {text}");
7998        assert!(body.contains("\tleaq\t.Lf_1(%rip), %rcx\n"), "where to come back to: {text}");
7999        assert!(body.contains("\tmovq\t%rcx, 8(%rax)\n"), "and that goes in the buffer: {text}");
8000        let back = body.split_once(".Lf_1:\n").expect("the block control comes back to").1;
8001        assert!(back.starts_with("\tmovq\t(%rsp), %rax\n"), "the answer is read back: {text}");
8002    }
8003
8004    /// Nothing stays in a register across the save, which is said with a write of every one of
8005    /// them and shows up as the callee-saved registers the function saves and restores.
8006    ///
8007    /// The restore puts back two registers and no others, so a function coming back through one
8008    /// finds every other register holding whatever the code between the two put there. The pushes
8009    /// are what makes the epilogue right on that path: the values popped are the caller's, off the
8010    /// stack the restore put back, rather than whatever is in the registers when control arrives.
8011    #[test]
8012    fn a_save_destroys_every_register_the_allocator_hands_out() {
8013        let text =
8014            asm(concat!("void *buf[5];\n", "int f(void) { return __builtin_setjmp(buf); }\n",));
8015        for reg in ["%rbx", "%r12", "%r13", "%r14", "%r15"] {
8016            assert!(text.contains(&format!("\tpushq\t{reg}\n")), "{reg} is saved: {text}");
8017            assert!(text.contains(&format!("\tpopq\t{reg}\n")), "{reg} is restored: {text}");
8018        }
8019    }
8020
8021    /// The restore puts both registers back before it goes, at every level.
8022    ///
8023    /// The jump reads the two of them as well as the address it goes through, which is what keeps
8024    /// it behind them. Without that the two instructions write registers nothing reads, and the
8025    /// scheduler at `-O2` puts the jump in front of both and the program comes back to a frame
8026    /// that is not there.
8027    #[test]
8028    fn the_restore_puts_the_frame_back_before_it_jumps() {
8029        for level in [rucc_session::OptLevel::O0, rucc_session::OptLevel::O2] {
8030            let mut opts = options();
8031            opts.emit = EmitKind::Asm;
8032            opts.opt_level = level;
8033            let source = "void *buf[5];\nvoid g(void) { __builtin_longjmp(buf, 1); }\n";
8034            let result = run(&opts, source);
8035            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
8036            let text = result.text().to_owned();
8037            let jump = text.find("\tjmp\t*%").unwrap_or_else(|| panic!("an indirect jump: {text}"));
8038            let stack = text.find(", %rsp\n").unwrap_or_else(|| panic!("the stack back: {text}"));
8039            let frame = text.find(", %rbp\n").unwrap_or_else(|| panic!("the frame back: {text}"));
8040            assert!(stack < jump, "the stack goes back first at {level:?}: {text}");
8041            assert!(frame < jump, "and so does the frame at {level:?}: {text}");
8042        }
8043    }
8044
8045    /// The second argument of the restore has one allowed value, which gcc 16.2.0 also insists on.
8046    ///
8047    /// This pair does not carry a value back the way the library's `longjmp` does, because what
8048    /// the matching save answers is decided by which way control reached it. So the argument is a
8049    /// place-holder, and a program that wrote anything else meant the library's function.
8050    #[test]
8051    fn a_longjmp_whose_second_argument_is_not_one_is_turned_down() {
8052        for source in [
8053            "void *buf[5];\nvoid f(void) { __builtin_longjmp(buf, 0); }\n",
8054            "void *buf[5];\nextern int v;\nvoid f(void) { __builtin_longjmp(buf, v); }\n",
8055        ] {
8056            let messages = errors(source);
8057            let named = messages.iter().any(|m| m.contains("E0710"));
8058            assert!(named, "expected a complaint about the value in {messages:?}");
8059        }
8060    }
8061
8062    /// A `static` function nothing refers to is not emitted, and one that is refered to is.
8063    ///
8064    /// The pair is written as one program so that the two answers come out of one walk. What
8065    /// makes the difference is the call in `main` and nothing else about either definition.
8066    #[test]
8067    fn a_static_function_nothing_refers_to_is_not_emitted() {
8068        let text = ir("static int dropped(void) { return 1; }\n\
8069                       static int kept(void) { return 2; }\n\
8070                       int main(void) { return kept(); }\n");
8071        assert!(text.contains("func @kept"), "{text}");
8072        assert!(!text.contains("dropped"), "{text}");
8073    }
8074
8075    /// The set is transitive, so two of them that only call each other are both dropped.
8076    ///
8077    /// Counting the references to a name would keep this pair, since each is named once, and
8078    /// that is the mistake this is here to catch: what decides it is whether a root reaches the
8079    /// definition, and a root is something the file has a reason to emit on its own.
8080    #[test]
8081    fn two_static_functions_that_only_call_each_other_are_both_dropped() {
8082        let text = ir("static int ping(void);\n\
8083                       static int pong(void) { return ping(); }\n\
8084                       static int ping(void) { return pong(); }\n\
8085                       int main(void) { return 0; }\n");
8086        assert!(!text.contains("ping"), "{text}");
8087        assert!(!text.contains("pong"), "{text}");
8088    }
8089
8090    /// Everything that names a function keeps it, whether or not the name is being called.
8091    ///
8092    /// An address taken in a body, an image that holds one, and a body that is only reached
8093    /// through another `static` function are three different ways for a definition to be needed
8094    /// and none of them is a call at the top level of a reachable function.
8095    #[test]
8096    fn naming_a_static_function_anywhere_keeps_it() {
8097        let text = ir("static int by_address(void) { return 1; }\n\
8098                       static int in_an_image(void) { return 2; }\n\
8099                       static int deeper(void) { return 3; }\n\
8100                       static int reaches_deeper(void) { return deeper(); }\n\
8101                       static int (*table[1])(void) = {in_an_image};\n\
8102                       int main(void) {\n\
8103                         int (*p)(void) = by_address;\n\
8104                         return p() + table[0]() + reaches_deeper();\n\
8105                       }\n");
8106        for kept in ["by_address", "in_an_image", "deeper", "reaches_deeper"] {
8107            assert!(text.contains(&format!("func @{kept}")), "expected {kept} in:\n{text}");
8108        }
8109    }
8110
8111    /// An attribute that says something outside the file reaches it keeps the definition.
8112    ///
8113    /// None of the five is implemented as anything else yet, and this is the part of each of
8114    /// them that a program notices first: a symbol a linker script names or a function the
8115    /// run-up to `main` calls is not written about anywhere a C file can see.
8116    #[test]
8117    fn an_attribute_keeps_a_static_function_nothing_refers_to() {
8118        for attribute in ["used", "retain", "constructor", "destructor", "__used__"] {
8119            let source = format!(
8120                "__attribute__(({attribute})) static int kept(void) {{ return 1; }}\n\
8121                 int main(void) {{ return 0; }}\n"
8122            );
8123            let text = ir(&source);
8124            assert!(text.contains("func @kept"), "for {attribute}:\n{text}");
8125        }
8126    }
8127
8128    /// `nonnull` is answered yes and taken with or without operands, and a check the program
8129    /// makes on a parameter it names stays, since nothing is assumed from the claim.
8130    #[test]
8131    fn nonnull_is_answered_yes_and_taken_with_or_without_operands() {
8132        let text = ir("#if !__has_attribute(nonnull) || !__has_attribute(__nonnull__)\n\
8133             #error nonnull\n\
8134             #endif\n\
8135             __attribute__((nonnull)) int first(char *p);\n\
8136             int both(char *a, int n, char *b) __attribute__((__nonnull__(1, 3)));\n\
8137             int both(char *a, int n, char *b) { return first(a) + n + (b != 0); }\n");
8138        assert!(text.contains("func @both"), "{text}");
8139    }
8140
8141    /// A function with external linkage is emitted whatever this file does with it, because
8142    /// another one may call it, and that is what external linkage is.
8143    #[test]
8144    fn a_function_anything_could_call_is_emitted_without_being_called() {
8145        let text =
8146            ir("int nobody_here_calls_it(void) { return 1; }\nint main(void) { return 0; }\n");
8147        assert!(text.contains("func @nobody_here_calls_it"), "{text}");
8148    }
8149
8150    /// Four of the classification builtins are operators C already has, and become those.
8151    ///
8152    /// What the standard's macro promises over the operator is that it does not raise the
8153    /// invalid operation exception on a quiet NaN. This compiler does not model floating point
8154    /// exceptions, so there is nothing left for a node of its own to carry and a second way of
8155    /// spelling a comparison would be a second thing every pass has to know about.
8156    #[test]
8157    fn a_classification_c_has_an_operator_for_is_that_operator() {
8158        for (builtin, operator) in [
8159            ("__builtin_isgreater", "binary >"),
8160            ("__builtin_isgreaterequal", "binary >="),
8161            ("__builtin_isless", "binary <"),
8162            ("__builtin_islessequal", "binary <="),
8163        ] {
8164            let source = format!("int f(double x, double y) {{ return {builtin}(x, y); }}\n");
8165            let text = tast(&source);
8166            assert!(text.contains(&format!("{operator} : int")), "for {builtin}:\n{text}");
8167        }
8168    }
8169
8170    /// The rest of the family are comparisons in the IR and never a call to anything.
8171    ///
8172    /// `math.h` defines the macro of each of these names as the builtin of the same name, so
8173    /// there is no function under any of them for a call to reach. `isunordered` and
8174    /// `islessgreater` are predicates the IR's comparison already has, `isnan` is the value that
8175    /// is unordered with itself, and the two that ask about a magnitude are written against the
8176    /// infinities. `signbit` is the one that is not a question about the value, since a negative
8177    /// zero compares equal to a positive one, so its answer comes from the bits.
8178    #[test]
8179    fn the_classification_builtins_are_comparisons_and_not_calls() {
8180        let text = body("int f(double x, double y) { return __builtin_isunordered(x, y); }\n");
8181        assert_eq!(
8182            text,
8183            "block0(%0: f64, %1: f64):\n    %2 = fcmp uno %0, %1\n    %3 = zext.i32 \
8184                          %2\n    return %3\n"
8185        );
8186
8187        // Not `x != y`, which is true when the two are unordered and so is true of a NaN.
8188        let text = body("int f(double x, double y) { return __builtin_islessgreater(x, y); }\n");
8189        assert!(text.contains("fcmp one %0, %1"), "{text}");
8190
8191        let text = body("int f(double x) { return __builtin_isnan(x); }\n");
8192        assert!(text.contains("fcmp uno %0, %0"), "{text}");
8193
8194        let text = body("int f(double x) { return __builtin_isinf(x); }\n");
8195        assert!(text.contains("fconst.f64 0x7ff0000000000000"), "{text}");
8196        assert!(text.contains("fconst.f64 0xfff0000000000000"), "{text}");
8197        assert!(text.contains("%3 = fcmp oeq %0, %1"), "{text}");
8198        assert!(text.contains("%4 = fcmp oeq %0, %2"), "{text}");
8199        assert!(text.contains("%5 = or %3, %4"), "{text}");
8200
8201        // Strictly between the two infinities, which a NaN is not, because an ordered comparison
8202        // against either of them is false. That is what makes this one test rather than two.
8203        let text = body("int f(double x) { return __builtin_isfinite(x); }\n");
8204        assert!(text.contains("%3 = fcmp olt %2, %0"), "{text}");
8205        assert!(text.contains("%4 = fcmp olt %0, %1"), "{text}");
8206        assert!(text.contains("%5 = and %3, %4"), "{text}");
8207
8208        let text = body("int f(double x) { return __builtin_signbit(x); }\n");
8209        assert!(text.contains("%1 = bitcast.i64 %0"), "{text}");
8210        assert!(text.contains("icmp slt %1, %2"), "{text}");
8211
8212        // The same question of a value in the target's widest format, where the bits are eighty
8213        // and the object they sit in is sixteen bytes. No integer is that wide, so the sign is
8214        // read from the word at the top of the value once it is in memory.
8215        let text = body("int f(long double x) { return __builtin_signbitl(x); }\n");
8216        assert!(text.contains("load.i16"), "{text}");
8217        assert!(text.contains("icmp slt"), "{text}");
8218        assert!(!text.contains("i80"), "{text}");
8219
8220        // The operand is evaluated once however many times it is compared, which is the whole
8221        // reason these are nodes rather than a rewriting into the operators.
8222        let text = body("double g(void);\nint f(void) { return __builtin_isnan(g()); }\n");
8223        assert_eq!(text.matches("call @g()").count(), 1, "{text}");
8224    }
8225
8226    /// A spelling that names a width converts its argument before it asks.
8227    ///
8228    /// gcc gives `__builtin_isinff` a `float` parameter and `__builtin_isinf` no parameter type
8229    /// at all, and the difference is visible rather than academic: `1e300` does not fit in a
8230    /// `float`, so converting it first is an infinity and not converting it is not. Both numbers
8231    /// here are what gcc 16 gives.
8232    #[test]
8233    fn a_classification_spelling_that_names_a_width_converts_before_it_asks() {
8234        let text = ir(concat!(
8235            "int a = __builtin_isinff(1e300);\n",
8236            "int b = __builtin_isinf(1e300);\n",
8237            // Folded here rather than compared at run time, because a question about a value has
8238            // an answer as soon as the value is a constant, and an initializer for an object
8239            // with static storage duration has to have one.
8240            "int c = __builtin_isnan(0.0);\n",
8241            "int d = __builtin_signbit(-0.0);\n",
8242            "int e = __builtin_islessgreater(1.0, 2.0);\n",
8243        ));
8244        assert!(text.contains("global @a : i32 = 1,"), "{text}");
8245        assert!(text.contains("global @b : i32 = 0,"), "{text}");
8246        assert!(text.contains("global @c : i32 = 0,"), "{text}");
8247        assert!(text.contains("global @d : i32 = 1,"), "{text}");
8248        assert!(text.contains("global @e : i32 = 1,"), "{text}");
8249    }
8250
8251    /// An argument that is not floating point is refused, in gcc's words.
8252    #[test]
8253    fn a_classification_builtin_refuses_an_argument_that_is_not_floating_point() {
8254        let mut opts = options();
8255        opts.emit = EmitKind::Ir;
8256        let source = concat!(
8257            "int a(int x) { return __builtin_isnan(x); }\n",
8258            "int b(int x, int y) { return __builtin_isunordered(x, y); }\n",
8259            "int c(double x) { return __builtin_isnan(x, x); }\n",
8260        );
8261        let messages = run(&opts, source).messages;
8262        assert_eq!(
8263            messages,
8264            [
8265                "/main.c:1:23: error: non-floating-point argument in call to function \
8266                 '__builtin_isnan' [E0685]",
8267                "/main.c:2:30: error: non-floating-point arguments in call to function \
8268                 '__builtin_isunordered' [E0685]",
8269                "/main.c:3:26: error: too many arguments to function '__builtin_isnan' [E0511]",
8270            ]
8271        );
8272    }
8273
8274    /// The three of the family that need a constant of the format other than an infinity.
8275    ///
8276    /// `isnormal` is the one that needs the smallest normal, and it is asked of the magnitude, so
8277    /// the sign comes off first and what is left is the same shape as `isfinite`. `isinf_sign` is
8278    /// the one whose answer is a number: the two comparisons `isinf` builds, subtracted rather
8279    /// than combined. `fpclassify` is four questions of one value and five answers to pick from,
8280    /// and the picking is a mask because all five are constants and neither of them can have an
8281    /// effect.
8282    #[test]
8283    fn the_last_three_classification_builtins_are_comparisons_and_not_calls() {
8284        let text = body("int f(double x) { return __builtin_isnormal(x); }\n");
8285        // The sign off, which is the magnitude, and then the range, asked of the bits rather than
8286        // of the number, since the encoding of a value whose sign bit is clear rises with the
8287        // value in every format this compiles for.
8288        assert!(text.contains("%1 = bitcast.i64 %0"), "{text}");
8289        assert!(text.contains("%2 = iconst.i64 9223372036854775807"), "{text}");
8290        assert!(text.contains("%3 = and %1, %2"), "{text}");
8291        assert!(text.contains("%4 = iconst.i64 4503599627370496"), "{text}");
8292        assert!(text.contains("%5 = iconst.i64 9218868437227405312"), "{text}");
8293        assert!(text.contains("%6 = icmp uge %3, %4"), "{text}");
8294        assert!(text.contains("%7 = icmp ult %3, %5"), "{text}");
8295        assert!(text.contains("%8 = and %6, %7"), "{text}");
8296
8297        // The same question in the target's widest format, where the smallest normal has the
8298        // leading significand bit stored rather than implied, so its encoding is two bits and not
8299        // one. There is no integer that wide to compare the bits in, so it is the magnitude that
8300        // is compared, as a value.
8301        let text = body("int f(long double x) { return __builtin_isnormal(x); }\n");
8302        assert!(text.contains("fconst.f80 0x18000000000000000"), "{text}");
8303        assert!(text.contains("fconst.f80 0x7fff8000000000000000"), "{text}");
8304        assert!(text.contains("fcmp oge"), "{text}");
8305        assert!(text.contains("fcmp olt"), "{text}");
8306
8307        let text = body("int f(double x) { return __builtin_isinf_sign(x); }\n");
8308        assert!(text.contains("%3 = fcmp oeq %0, %1"), "{text}");
8309        assert!(text.contains("%4 = fcmp oeq %0, %2"), "{text}");
8310        assert!(text.contains("%7 = sub %5, %6"), "{text}");
8311
8312        let text = body("int f(double x) { return __builtin_fpclassify(0, 1, 2, 3, 4, x); }\n");
8313        assert!(text.contains("fcmp uno %0, %0"), "{text}");
8314        assert!(text.contains("fcmp oeq %0, %6"), "{text}");
8315        // Four questions, each of them a bit widened into the type of the answer and then spread
8316        // into a mask that picks between the answer and whatever the questions after it settled
8317        // on. Nothing sign extends, because no rule lowers a sign extension out of one bit.
8318        assert_eq!(text.matches(" = zext.i32 ").count(), 4, "{text}");
8319        assert_eq!(text.matches(" = xor ").count(), 4, "{text}");
8320        assert!(!text.contains("call"), "{text}");
8321
8322        // The value is evaluated once however many questions are asked of it, which is the whole
8323        // reason `fpclassify` is a node rather than the chain of tests it turns into.
8324        let text = body(concat!(
8325            "double g(void);\n",
8326            "int f(void) { return __builtin_fpclassify(0, 1, 2, 3, 4, g()); }\n",
8327        ));
8328        assert_eq!(text.matches("call @g()").count(), 1, "{text}");
8329    }
8330
8331    /// Each of the three answers a constant where its operand is one.
8332    ///
8333    /// glibc's `fpclassify` macro is exactly this builtin, so a program that writes
8334    /// `fpclassify(0.0)` in a static initializer is writing this, and it has to have a value at
8335    /// translation time or the program is refused rather than merely compiled slowly. Every
8336    /// number here is what gcc 16 gives.
8337    #[test]
8338    fn the_last_three_classification_builtins_fold_where_their_operand_is_a_constant() {
8339        let text = ir(concat!(
8340            "int a = __builtin_isnormal(1.0);\n",
8341            "int b = __builtin_isnormal(0.0);\n",
8342            "int c = __builtin_isnormal(1.0 / 0.0);\n",
8343            "int d = __builtin_isinf_sign(-1.0 / 0.0);\n",
8344            "int e = __builtin_isinf_sign(1.0);\n",
8345            "int g = __builtin_fpclassify(0, 1, 2, 3, 4, 0.0);\n",
8346            "int h = __builtin_fpclassify(0, 1, 2, 3, 4, 1.0);\n",
8347            "int i = __builtin_fpclassify(0, 1, 2, 3, 4, 1.0 / 0.0);\n",
8348        ));
8349        assert!(text.contains("global @a : i32 = 1,"), "{text}");
8350        assert!(text.contains("global @b : i32 = 0,"), "{text}");
8351        assert!(text.contains("global @c : i32 = 0,"), "{text}");
8352        assert!(text.contains("global @d : i32 = -1,"), "{text}");
8353        assert!(text.contains("global @e : i32 = 0,"), "{text}");
8354        assert!(text.contains("global @g : i32 = 4,"), "{text}");
8355        assert!(text.contains("global @h : i32 = 2,"), "{text}");
8356        assert!(text.contains("global @i : i32 = 1,"), "{text}");
8357    }
8358
8359    /// `fpclassify` refuses what gcc refuses, in gcc's words.
8360    ///
8361    /// The five answers have to be integer constant expressions, because what the builtin does is
8362    /// pick one of them and a pick between values that are not known here would be a chain of
8363    /// conditionals over expressions the call has already evaluated.
8364    #[test]
8365    fn fpclassify_refuses_an_answer_that_is_not_an_integer_constant() {
8366        let mut opts = options();
8367        opts.emit = EmitKind::Ir;
8368        let source = concat!(
8369            "int a(double x, int n) { return __builtin_fpclassify(0, 1, n, 3, 4, x); }\n",
8370            "int b(double x) { return __builtin_fpclassify(0, 1, 2, 3, x); }\n",
8371            "int c(int x) { return __builtin_fpclassify(0, 1, 2, 3, 4, x); }\n",
8372        );
8373        let messages = run(&opts, source).messages;
8374        assert_eq!(
8375            messages,
8376            [
8377                "/main.c:1:60: error: non-const integer argument 3 in call to function \
8378                 '__builtin_fpclassify' [E0687]",
8379                "/main.c:2:26: error: too few arguments to function '__builtin_fpclassify' \
8380                 [E0511]",
8381                "/main.c:3:23: error: non-floating-point argument in call to function \
8382                 '__builtin_fpclassify' [E0685]",
8383            ]
8384        );
8385    }
8386
8387    /// A builtin whose answer is a constant is one, and is not a call to the library.
8388    ///
8389    /// This is the reason the family is answered in the front end at all. `double x =
8390    /// __builtin_inf();` at file scope initializes an object with static storage duration, so
8391    /// there is no point in the program at which a call could be made, and a compiler that
8392    /// lowered it to one would reject a program gcc accepts. Every number here is the encoding
8393    /// gcc 16 gives on x86-64.
8394    #[test]
8395    fn a_builtin_whose_answer_is_a_constant_is_one_and_not_a_call() {
8396        let text = ir(concat!(
8397            "double a = __builtin_inf();\n",
8398            "float b = __builtin_huge_valf();\n",
8399            "long double c = __builtin_infl();\n",
8400            "double d = __builtin_huge_val();\n",
8401        ));
8402        assert!(text.contains("global @a : f64 = 0x7ff0000000000000,"), "{text}");
8403        assert!(text.contains("global @b : f32 = 0x7f800000,"), "{text}");
8404        assert!(text.contains("f80 0x7fff8000000000000000"), "{text}");
8405        assert!(text.contains("global @d : f64 = 0x7ff0000000000000,"), "{text}");
8406        assert!(!text.contains("call"), "{text}");
8407    }
8408
8409    /// A nan is written with the payload the program asked for.
8410    ///
8411    /// The string is read the way `strtoull` reads a number, which is what the library function
8412    /// of the same name does with it, and a string that is not one at all leaves the call for the
8413    /// library to answer at run time. A quiet nan has the high fraction bit set and a signalling
8414    /// one does not, except that a signalling nan with nothing in it would be an infinity, so it
8415    /// gets the next bit down instead. Every encoding here was measured against gcc 16, the two
8416    /// `long double` ones on a machine with the x87 format.
8417    #[test]
8418    fn a_nan_is_written_with_the_payload_the_program_asked_for() {
8419        let text = ir(concat!(
8420            "double a = __builtin_nan(\"\");\n",
8421            "double b = __builtin_nan(\"0x1\");\n",
8422            // Octal, since there is a leading zero, so this is eight and not ten.
8423            "double c = __builtin_nan(\"010\");\n",
8424            "double d = __builtin_nans(\"\");\n",
8425            "double e = __builtin_nans(\"0x1\");\n",
8426            "float f = __builtin_nanf(\"0x1\");\n",
8427            "float g = __builtin_nansf(\"\");\n",
8428            "long double h = __builtin_nansl(\"\");\n",
8429        ));
8430        assert!(text.contains("global @a : f64 = 0x7ff8000000000000,"), "{text}");
8431        assert!(text.contains("global @b : f64 = 0x7ff8000000000001,"), "{text}");
8432        assert!(text.contains("global @c : f64 = 0x7ff8000000000008,"), "{text}");
8433        assert!(text.contains("global @d : f64 = 0x7ff4000000000000,"), "{text}");
8434        assert!(text.contains("global @e : f64 = 0x7ff0000000000001,"), "{text}");
8435        assert!(text.contains("global @f : f32 = 0x7fc00001,"), "{text}");
8436        assert!(text.contains("global @g : f32 = 0x7fa00000,"), "{text}");
8437        assert!(text.contains("f80 0x7fffa000000000000000"), "{text}");
8438
8439        // A payload that is not a number, and one that is not known until run time, are both
8440        // left to the library, which is the same thing gcc emits for either of them.
8441        let text = ir(concat!(
8442            "double f(const char *p) { return __builtin_nan(p); }\n",
8443            "double g(void) { return __builtin_nans(\"1x\"); }\n",
8444        ));
8445        assert_eq!(text.matches("call @nan(").count(), 1, "{text}");
8446        assert_eq!(text.matches("call @nans(").count(), 1, "{text}");
8447    }
8448
8449    /// The length and the order of a string literal are known here.
8450    ///
8451    /// A program that asks for either of them is asking about something the translation already
8452    /// has in front of it, and folding is not only an optimization: `execute/921007-1.c` in the
8453    /// torture suite calls `__builtin_strcmp` in a file that defines its own `strcmp` with a
8454    /// different signature, so leaving the call behind is a name collision that gcc does not
8455    /// have. The comparison is over `unsigned char`, which is why the second one is negative.
8456    #[test]
8457    fn the_length_and_the_order_of_a_string_literal_are_known_here() {
8458        let text = ir(concat!(
8459            "unsigned long a = __builtin_strlen(\"hello\");\n",
8460            "unsigned long b = __builtin_strlen(\"a\\0bc\");\n",
8461            "int c = __builtin_strcmp(\"X\", \"X\\376\") < 0;\n",
8462            "int d = __builtin_strcmp(\"abc\", \"abc\");\n",
8463            "int e = __builtin_strcmp(\"abc\", \"ab\") > 0;\n",
8464        ));
8465        assert!(text.contains("global @a : i64 = 5,"), "{text}");
8466        assert!(text.contains("global @b : i64 = 1,"), "{text}");
8467        assert!(text.contains("global @c : i32 = 1,"), "{text}");
8468        assert!(text.contains("global @d : i32 = 0,"), "{text}");
8469        assert!(text.contains("global @e : i32 = 1,"), "{text}");
8470        assert!(!text.contains("call"), "{text}");
8471
8472        // An argument that is not a literal is the library's to answer, as it has to be.
8473        let text = ir("unsigned long f(const char *p) { return __builtin_strlen(p); }\n");
8474        assert!(text.contains("call @strlen("), "{text}");
8475    }
8476
8477    /// A sign builtin is a mask over the bits, and is not a call.
8478    ///
8479    /// `fabs` and `copysign` are in the math library rather than the C one, so a program that
8480    /// only ever wrote the prefixed spelling never asked for `-lm` and a call left behind here
8481    /// would not link. Neither needs anything the library has: one clears the sign bit and the
8482    /// other takes it from the second operand, and every other bit goes through untouched.
8483    #[test]
8484    fn a_sign_builtin_is_a_mask_over_the_bits_and_not_a_call() {
8485        let text = body("double f(double x) { return __builtin_fabs(x); }\n");
8486        assert!(text.contains("bitcast.i64 %0"), "{text}");
8487        assert!(text.contains("iconst.i64 9223372036854775807"), "{text}");
8488        assert!(text.contains("and %1, %2"), "{text}");
8489        assert!(text.contains("bitcast.f64 %3"), "{text}");
8490        assert!(!text.contains("call"), "{text}");
8491
8492        let text = body("double f(double x, double y) { return __builtin_copysign(x, y); }\n");
8493        assert!(text.contains("iconst.i64 -9223372036854775808"), "{text}");
8494        assert!(text.contains("%8 = or %4, %7"), "{text}");
8495        assert!(!text.contains("call"), "{text}");
8496
8497        // The x87 format, whose value is eighty bits sitting in an object of sixteen. There is no
8498        // integer that wide, so the mask is on the word at the top of the value, in memory.
8499        let text = body("long double f(long double x) { return __builtin_fabsl(x); }\n");
8500        assert!(text.contains("iconst.i16 32767"), "{text}");
8501        assert!(text.contains("load.f80"), "{text}");
8502        assert!(!text.contains("call"), "{text}");
8503
8504        // The width a name does not spell out is `double`, so a `float` argument widens first and
8505        // the answer is a `double`, which is what gcc's declaration of it says.
8506        let text = body("double f(float x) { return __builtin_fabs(x); }\n");
8507        assert!(text.contains("fpext.f64 %0"), "{text}");
8508        assert!(text.contains("bitcast.i64 %1"), "{text}");
8509    }
8510
8511    /// A shuffle reads each lane of the answer out of a copy of its sources, at the index the mask
8512    /// lane gives with only its low bits kept, and is not a call.
8513    ///
8514    /// The copy is what makes `*v = __builtin_shuffle(*v, m)` right, since the answer is written
8515    /// over the vector it reads, and the mask is what `pr85331.c` checks: gcc keeps as many bits
8516    /// of an index as it takes to name a lane, so `10000000001` picks lane one of two.
8517    #[test]
8518    fn a_shuffle_picks_each_lane_by_the_low_bits_of_the_mask() {
8519        let text = body(concat!(
8520            "typedef int v2 __attribute__((vector_size(8)));\n",
8521            "void f(v2 *v, v2 m) { *v = __builtin_shuffle(*v, m); }\n",
8522        ));
8523        assert!(text.contains("memcpy"), "{text}");
8524        assert_eq!(text.matches("iconst.i32 1\n").count(), 2, "{text}");
8525        assert_eq!(text.matches(" = and ").count(), 2, "{text}");
8526        assert!(!text.contains("call"), "{text}");
8527
8528        // Two sources of four lanes are eight to pick from, so three bits of each index are
8529        // kept, and a mask of bytes is widened to a word before it is masked.
8530        let text = body(concat!(
8531            "typedef char v4 __attribute__((vector_size(4)));\n",
8532            "v4 f(v4 a, v4 b, v4 m) { return __builtin_shuffle(a, b, m); }\n",
8533        ));
8534        assert_eq!(text.matches("iconst.i32 7\n").count(), 4, "{text}");
8535        assert!(text.contains("zext.i32"), "{text}");
8536        assert!(!text.contains("call"), "{text}");
8537    }
8538
8539    /// A function holding `__builtin_apply_args` writes every argument register into its frame
8540    /// before anything else runs, the ones its parameters took as well as the ones they did not,
8541    /// and the answer is the address of where it wrote them.
8542    #[test]
8543    fn the_arguments_a_function_was_called_with_are_saved_on_the_way_in() {
8544        let text =
8545            mir("void *f(int a, double b) { (void)a; (void)b; return __builtin_apply_args(); }\n");
8546        // Six words and the address the arguments in memory start at, and eight vectors.
8547        assert!(text.matches("x64.mov_mr_64").count() >= 7, "{text}");
8548        assert!(text.matches("x64.movaps_mr").count() >= 8, "{text}");
8549        for reg in ["$rdi", "$rsi", "$rdx", "$rcx", "$r8", "$r9", "$xmm0", "$xmm7"] {
8550            assert!(text.contains(reg), "{reg} is not saved in\n{text}");
8551        }
8552
8553        // And a function without one saves nothing.
8554        let text = mir("int f(int a) { return a; }\n");
8555        assert!(!text.contains("movaps_mr"), "{text}");
8556    }
8557
8558    /// `__builtin_apply` loads every argument register out of the block it is given, copies the
8559    /// bytes of arguments in memory it was told about, and calls through the address, with eight
8560    /// in `%al` since every vector register may hold an argument.
8561    #[test]
8562    fn a_call_built_from_saved_arguments_loads_every_argument_register() {
8563        let text = mir(concat!(
8564            "void *g(void *args, void (*h)()) {\n",
8565            "  return __builtin_apply(h, args, 64);\n",
8566            "}\n",
8567        ));
8568        assert!(text.matches("x64.mov_rm_64").count() >= 7, "{text}");
8569        assert!(text.matches("x64.movaps_rm").count() >= 8, "{text}");
8570        assert!(text.contains("call"), "{text}");
8571        // What came back is written out, two words and two vectors.
8572        assert!(text.matches("x64.movaps_mr").count() >= 2, "{text}");
8573
8574        // The size is a number the frame can be laid out with, and nothing else is.
8575        let mut opts = options();
8576        opts.emit = EmitKind::Ir;
8577        let result = run(
8578            &opts,
8579            "void *g(void *a, void (*h)(), int n) { return __builtin_apply(h, a, n); }\n",
8580        );
8581        assert!(result.failed(), "{:?}", result.messages);
8582        assert!(
8583            result
8584                .messages
8585                .iter()
8586                .any(|m| m.contains("the size given to '__builtin_apply' is a constant")),
8587            "{:?}",
8588            result.messages
8589        );
8590    }
8591
8592    /// A shuffle whose operands gcc would refuse is refused, in gcc's words.
8593    #[test]
8594    fn a_shuffle_refuses_what_gcc_refuses() {
8595        let mut opts = options();
8596        opts.emit = EmitKind::Ir;
8597        let source = concat!(
8598            "typedef int v4 __attribute__((vector_size(16)));\n",
8599            "typedef float f4 __attribute__((vector_size(16)));\n",
8600            "typedef short s8 __attribute__((vector_size(16)));\n",
8601            "typedef long long l4 __attribute__((vector_size(32)));\n",
8602            "void a(v4 x, f4 m) { __builtin_shuffle(x, m); }\n",
8603            "void b(int x, v4 m) { __builtin_shuffle(x, m); }\n",
8604            "void c(v4 x, f4 y, v4 m) { __builtin_shuffle(x, y, m); }\n",
8605            "void d(v4 x, s8 m) { __builtin_shuffle(x, m); }\n",
8606            "void e(f4 x, l4 m) { __builtin_shuffle(x, m); }\n",
8607            "void g(v4 x) { __builtin_shuffle(x); }\n",
8608        );
8609        let messages = run(&opts, source).messages;
8610        let wanted = [
8611            "last argument must be an integer vector [E0715]",
8612            "arguments must be vectors [E0715]",
8613            "argument vectors must be of the same type [E0715]",
8614            "number of elements of the argument vector(s) and the mask vector should be the same \
8615             [E0715]",
8616            "argument vector(s) inner type must have the same size as inner type of the mask \
8617             [E0715]",
8618            "too few arguments to function '__builtin_shuffle' [E0511]",
8619        ];
8620        assert_eq!(messages.len(), wanted.len(), "{messages:?}");
8621        for (message, wanted) in messages.iter().zip(wanted) {
8622            assert!(message.ends_with(wanted), "{message}");
8623        }
8624    }
8625
8626    /// The plain math library names are the same mask, which is what makes a program link.
8627    ///
8628    /// `math.h` declares `fabs` and never spells `__builtin_fabs`, so the plain name is the one
8629    /// every program that includes the header reaches. Recognising only the prefixed spelling
8630    /// leaves a call to the math library behind, and the math library is not on the link line
8631    /// unless the program asked for `-lm`. parson is the project that shows it: its makefile has
8632    /// no `-lm`, it does not need one under gcc, and `undefined reference to 'fabs'` is where the
8633    /// build stopped. That is issue 630.
8634    #[test]
8635    fn the_plain_math_names_are_the_same_mask_and_not_a_call() {
8636        let text =
8637            body(concat!("double fabs(double x);\n", "double f(double x) { return fabs(x); }\n",));
8638        assert!(text.contains("iconst.i64 9223372036854775807"), "{text}");
8639        assert!(!text.contains("call"), "{text}");
8640
8641        let text =
8642            body(concat!("float fabsf(float x);\n", "float f(float x) { return fabsf(x); }\n",));
8643        assert!(text.contains("bitcast.i32 %0"), "{text}");
8644        assert!(!text.contains("call"), "{text}");
8645
8646        let text = body(concat!(
8647            "double copysign(double x, double y);\n",
8648            "double f(double x, double y) { return copysign(x, y); }\n",
8649        ));
8650        assert!(text.contains("iconst.i64 -9223372036854775808"), "{text}");
8651        assert!(!text.contains("call"), "{text}");
8652
8653        let text = body(concat!(
8654            "float copysignf(float x, float y);\n",
8655            "float f(float x, float y) { return copysignf(x, y); }\n",
8656        ));
8657        assert!(!text.contains("call"), "{text}");
8658
8659        // The `long double` pair is left alone on purpose. The prefixed spelling of both stops in
8660        // the back end with `no rule lowers a bitcast producing an i80`, so expanding the plain
8661        // name would trade a link error for a worse one. They go in with issue 540.
8662        let text = ir(concat!(
8663            "long double fabsl(long double x);\n",
8664            "long double f(long double x) { return fabsl(x); }\n",
8665        ));
8666        assert!(text.contains("call @fabsl"), "{text}");
8667    }
8668
8669    /// A plain math name the program took is the program's own function.
8670    ///
8671    /// The same four ways as the absolute value family next door, asked again here because these
8672    /// two go through a different path: the plain names of this family are taken after the call
8673    /// has been checked against the declaration, and the declaration is the whole reason the
8674    /// question can be answered at all. Measured against gcc 16.2.0, which calls the program's
8675    /// function in every one of them.
8676    #[test]
8677    fn a_plain_math_name_the_program_took_is_the_programs_own_function() {
8678        let taken = concat!(
8679            "static double fabs(double b) { return 7; }\n",
8680            "double f(double x) { return fabs(x); }\n",
8681        );
8682        assert!(ir(taken).contains("call @fabs"), "a static definition is the program's own");
8683
8684        let retyped = concat!("int fabs(int b);\n", "int f(int x) { return fabs(x); }\n");
8685        assert!(ir(retyped).contains("call @fabs"), "another type is another function");
8686
8687        let plain = concat!("double fabs(double b);\n", "double f(double x) { return fabs(x); }\n");
8688        let mut opts = options();
8689        opts.emit = EmitKind::Ir;
8690        assert!(!run(&opts, plain).text().contains("call @fabs"), "the library's by default");
8691
8692        opts.builtins = false;
8693        assert!(run(&opts, plain).text().contains("call @fabs"), "-fno-builtin");
8694
8695        opts.builtins = true;
8696        opts.no_builtin = vec!["fabs".to_owned()];
8697        assert!(run(&opts, plain).text().contains("call @fabs"), "-fno-builtin-fabs");
8698        let one = concat!(
8699            "double copysign(double a, double b);\n",
8700            "double f(double x) { return copysign(x, 1.0); }\n",
8701        );
8702        assert!(!run(&opts, one).text().contains("call @copysign"), "one name and not the family");
8703
8704        // The prefixed spelling is untouched by any of it, which is what the prefix is for.
8705        opts.no_builtin = Vec::new();
8706        opts.builtins = false;
8707        let prefixed = "double f(double x) { return __builtin_fabs(x); }\n";
8708        assert!(!run(&opts, prefixed).text().contains("call @fabs"), "the prefix is not a library");
8709    }
8710
8711    /// The sign builtins answer a zero and a nan the way the bits say.
8712    ///
8713    /// This is why they are described over the bits rather than written with comparisons and
8714    /// negation. A negative zero compares equal to a positive one and has a sign bit to clear,
8715    /// and a nan compares equal to nothing at all and keeps its payload through both operations.
8716    /// `execute/ieee/copysign1.c` in the torture suite is the test that notices, because it
8717    /// compares its answers with `memcmp`. Every number here is what gcc 16 gives, the two in the
8718    /// x87 format measured on a machine that has it.
8719    #[test]
8720    fn the_sign_builtins_answer_a_zero_and_a_nan_the_way_the_bits_say() {
8721        let text = ir(concat!(
8722            "double a = __builtin_fabs(-3.5);\n",
8723            "double b = __builtin_copysign(1.0, -0.0);\n",
8724            "double c = __builtin_copysign(0.0, -2.0);\n",
8725            // The payload survives both, and only the sign bit moves.
8726            "double d = __builtin_copysign(-__builtin_nan(\"\"), 1.0);\n",
8727            "double e = __builtin_fabs(-__builtin_nan(\"0x1\"));\n",
8728            "float g = __builtin_copysignf(-0.0f, 2.0f);\n",
8729            "long double h = __builtin_copysignl(1.0L, -1.0L);\n",
8730            "long double i = __builtin_fabsl(-__builtin_infl());\n",
8731        ));
8732        assert!(text.contains("global @a : f64 = 0x400c000000000000,"), "{text}");
8733        assert!(text.contains("global @b : f64 = 0xbff0000000000000,"), "{text}");
8734        assert!(text.contains("global @c : f64 = 0x8000000000000000,"), "{text}");
8735        assert!(text.contains("global @d : f64 = 0x7ff8000000000000,"), "{text}");
8736        assert!(text.contains("global @e : f64 = 0x7ff8000000000001,"), "{text}");
8737        assert!(text.contains("global @g : f32 = 0x0,"), "{text}");
8738        assert!(text.contains("f80 0xbfff8000000000000000"), "{text}");
8739        assert!(text.contains("f80 0x7fff8000000000000000"), "{text}");
8740    }
8741
8742    /// The sign of a `long double` is read and written in the word at the top of it.
8743    ///
8744    /// The other formats have their sign tested and set on an integer as wide as the value, and
8745    /// there is no eighty bit integer for the x87 one to go to: no rule lowers it, and
8746    /// `execute/20080502-1.c` and `execute/ieee/copysign1.c` in the torture suite stopped on that.
8747    /// The value goes through memory instead, and the word holding its sign is what is looked at.
8748    #[test]
8749    fn the_sign_of_a_long_double_is_in_the_word_at_the_top_of_it() {
8750        for source in [
8751            "int f(long double x) { return __builtin_signbit(x); }\n",
8752            "long double f(long double x) { return __builtin_fabsl(x); }\n",
8753            "long double f(long double x, long double y) { return __builtin_copysignl(x, y); }\n",
8754            "int f(long double x) { return __builtin_isnormal(x); }\n",
8755        ] {
8756            let text = body(source);
8757            assert!(!text.contains("i80"), "{text}");
8758            assert!(text.contains("i16"), "{text}");
8759        }
8760    }
8761
8762    /// The complex builtins are the halves of the value, and are not a call.
8763    ///
8764    /// `conj`, `creal` and `cimag` are `~`, `__real__` and `__imag__` under the names `complex.h`
8765    /// gives them, so there is nothing for the math library to do that the translation cannot do
8766    /// with the object in front of it. Leaving the call behind would not link either, since all
8767    /// three are in the math library and a program that wrote one never had a reason to ask for
8768    /// `-lm`. Measured against gcc 16.2.0, which emits no call for any of them even at `-O0`.
8769    #[test]
8770    fn the_complex_builtins_are_the_halves_of_the_value_and_not_a_call() {
8771        let text = body("double f(_Complex double z) { return __builtin_creal(z); }\n");
8772        assert!(!text.contains("call"), "{text}");
8773        let text = body("double f(_Complex double z) { return __builtin_cimag(z); }\n");
8774        assert!(!text.contains("call"), "{text}");
8775
8776        // The conjugate is the imaginary half negated and the real half as it stands, so there is
8777        // one negation in it. A complex negation is the one with two.
8778        let text = body("_Complex double f(_Complex double z) { return __builtin_conj(z); }\n");
8779        assert_eq!(text.matches("fneg").count(), 1, "{text}");
8780        assert!(!text.contains("call"), "{text}");
8781        let negated = body("_Complex double f(_Complex double z) { return -z; }\n");
8782        assert_eq!(negated.matches("fneg").count(), 2, "{negated}");
8783
8784        // `~` on a complex operand is the same operator, which is the spelling the language has
8785        // had all along and the one a program that never included the header writes.
8786        let written = body("_Complex double f(_Complex double z) { return ~z; }\n");
8787        assert_eq!(written, text, "the name and the operator are the same thing");
8788
8789        // The plain names, which are the ones the header declares and so the ones programs write.
8790        let text = body(concat!(
8791            "double creal(_Complex double z);\n",
8792            "double f(_Complex double z) { return creal(z); }\n",
8793        ));
8794        assert!(!text.contains("call"), "{text}");
8795        let text = body(concat!(
8796            "_Complex float conjf(_Complex float z);\n",
8797            "_Complex float f(_Complex float z) { return conjf(z); }\n",
8798        ));
8799        assert_eq!(text.matches("fneg").count(), 1, "{text}");
8800        assert!(!text.contains("call"), "{text}");
8801
8802        // A program that took the name means its own function, the same four ways the absolute
8803        // value family next door asks it.
8804        let taken = concat!(
8805            "static double creal(_Complex double z) { return 7; }\n",
8806            "double f(_Complex double z) { return creal(z); }\n",
8807        );
8808        assert!(ir(taken).contains("call @creal"), "a static definition is the program's own");
8809        let retyped = concat!("int cimag(int z);\n", "int f(int z) { return cimag(z); }\n");
8810        assert!(ir(retyped).contains("call @cimag"), "another type is another function");
8811        let plain = concat!(
8812            "double cimag(_Complex double z);\n",
8813            "double f(_Complex double z) { return cimag(z); }\n",
8814        );
8815        let mut opts = options();
8816        opts.emit = EmitKind::Ir;
8817        opts.builtins = false;
8818        assert!(run(&opts, plain).text().contains("call @cimag"), "-fno-builtin");
8819        opts.builtins = true;
8820        opts.no_builtin = vec!["cimag".to_owned()];
8821        assert!(run(&opts, plain).text().contains("call @cimag"), "-fno-builtin-cimag");
8822
8823        // A constant folds, which is what a static initializer written with one needs.
8824        let text = ir(concat!(
8825            "double a = __builtin_creal(1.5 + 2.5i);\n",
8826            "double b = __builtin_cimag(1.5 + 2.5i);\n",
8827            "_Complex double c = __builtin_conj(1.5 + 2.5i);\n",
8828        ));
8829        assert!(text.contains("global @a : f64 = 0x3ff8000000000000,"), "{text}");
8830        assert!(text.contains("global @b : f64 = 0x4004000000000000,"), "{text}");
8831        assert!(
8832            text.contains("{ f64 0x3ff8000000000000, f64 0xc004000000000000 }"),
8833            "the conjugate of a constant is the constant with the second half negated: {text}"
8834        );
8835        assert!(!text.contains("call"), "{text}");
8836    }
8837
8838    /// A math library builtin handed a constant is the answer, and is not a call.
8839    ///
8840    /// This is the reason the family is answered in the front end at all. `double x =
8841    /// __builtin_ceil(1.5);` at file scope initializes an object with static storage duration, so
8842    /// there is no point in the program at which a call could be made, and a compiler that lowered
8843    /// it to one would refuse a program gcc accepts. Every number here is the encoding gcc 16.2.0
8844    /// gives on x86-64, read out of the object file one initializer at a time.
8845    #[test]
8846    fn a_math_library_builtin_of_a_constant_is_the_answer_and_not_a_call() {
8847        let text = ir(concat!(
8848            "double a = __builtin_ceil(1.5);\n",
8849            "double b = __builtin_floor(1.5);\n",
8850            "double c = __builtin_trunc(-1.5);\n",
8851            // A half goes away from zero and not to even, which is where C and the default
8852            // rounding of IEEE 754 part company.
8853            "double d = __builtin_round(2.5);\n",
8854            // The sign survives a number that rounds away to nothing, so this is a negative zero.
8855            "double e = __builtin_ceil(-0.5);\n",
8856            "double f = __builtin_fmax(1.0, 2.0);\n",
8857            "double g = __builtin_fmin(1.0, 2.0);\n",
8858            "float h = __builtin_ceilf(1.25f);\n",
8859            // The plain name is the same answer, which is what a program that included `math.h`
8860            // and never wrote a prefix reaches.
8861            "double ceil(double x);\n",
8862            "double i = ceil(2.25);\n",
8863        ));
8864        assert!(text.contains("global @a : f64 = 0x4000000000000000,"), "{text}");
8865        assert!(text.contains("global @b : f64 = 0x3ff0000000000000,"), "{text}");
8866        assert!(text.contains("global @c : f64 = 0xbff0000000000000,"), "{text}");
8867        assert!(text.contains("global @d : f64 = 0x4008000000000000,"), "{text}");
8868        assert!(text.contains("global @e : f64 = 0x8000000000000000,"), "{text}");
8869        assert!(text.contains("global @f : f64 = 0x4000000000000000,"), "{text}");
8870        assert!(text.contains("global @g : f64 = 0x3ff0000000000000,"), "{text}");
8871        assert!(text.contains("global @h : f32 = 0x40000000,"), "{text}");
8872        assert!(text.contains("global @i : f64 = 0x4008000000000000,"), "{text}");
8873        assert!(!text.contains("call"), "{text}");
8874    }
8875
8876    /// A math library builtin handed anything else is a call to the library function it is.
8877    ///
8878    /// gcc emits `jmp ceil` for `__builtin_ceil` on x86-64 at the default architecture, measured
8879    /// on gcc 16.2.0, and reaches the `roundsd` instruction only under `-msse4.1`. So the call is
8880    /// what a program gets from gcc too, and the name on it is the plain one, which is the whole
8881    /// point of the prefixed spelling: a program writing it reaches the library's function even
8882    /// where a macro or a definition of its own has taken the short name.
8883    #[test]
8884    fn a_math_library_builtin_of_anything_else_is_a_call_to_the_library() {
8885        let text = ir(concat!(
8886            "double f(double x) { return __builtin_ceil(x); }\n",
8887            "float g(float x) { return __builtin_floorf(x); }\n",
8888            "double h(double x, double y) { return __builtin_fmax(x, y); }\n",
8889        ));
8890        assert!(text.contains("call @ceil("), "{text}");
8891        assert!(text.contains("call @floorf("), "{text}");
8892        assert!(text.contains("call @fmax("), "{text}");
8893
8894        // The two the rounding mode decides are calls even when the argument is a constant, since
8895        // what they answer is not known until the program runs. gcc refuses a static initializer
8896        // written with one for that reason, so there is nothing to fold here either.
8897        let text = ir(concat!(
8898            "double f(void) { return __builtin_rint(2.5); }\n",
8899            "double g(void) { return __builtin_nearbyint(2.5); }\n",
8900        ));
8901        assert!(text.contains("call @rint("), "{text}");
8902        assert!(text.contains("call @nearbyint("), "{text}");
8903
8904        // A nan operand is the library's rule rather than the machine's, 7.12.12.2 saying the
8905        // answer is the other operand, and gcc will not fold that one either.
8906        let text = ir("double f(void) { return __builtin_fmin(__builtin_nan(\"\"), 1.0); }\n");
8907        assert!(text.contains("call @fmin("), "{text}");
8908
8909        // `-fno-builtin-ceil` is a program saying it means its own `ceil`, and it leaves the
8910        // prefixed spelling alone, which is what writing the prefix is for.
8911        let plain = concat!("double ceil(double x);\n", "double f(void) { return ceil(2.25); }\n");
8912        let mut opts = options();
8913        opts.emit = EmitKind::Ir;
8914        opts.no_builtin = vec!["ceil".to_owned()];
8915        assert!(run(&opts, plain).text().contains("call @ceil("), "-fno-builtin-ceil");
8916    }
8917
8918    /// A `constexpr` object is a named constant, which is the whole reason the keyword exists.
8919    ///
8920    /// C23 6.6p8 puts two of them on the list an integer constant expression is built from: one
8921    /// of an arithmetic type, and a member of one of a structure or union type. A subscript of
8922    /// one is not on the list and is a variably modified type in gcc 16 as well, and every
8923    /// number here is what gcc 16 gives on x86-64.
8924    #[test]
8925    fn a_constexpr_object_is_a_constant_wherever_one_is_required() {
8926        let text = ir(concat!(
8927            "constexpr int side = 4;\n",
8928            "constexpr int wider = side + 1;\n",
8929            "constexpr double half = 1.5;\n",
8930            "struct point { int x; int y; };\n",
8931            "constexpr struct point origin = { 5, 6 };\n",
8932            "int square[side * side];\n",
8933            "int rectangle[wider];\n",
8934            "int rounded[(int)half * 2];\n",
8935            "int across[origin.y];\n",
8936            "enum named { four = side };\n",
8937            "int e = four;\n",
8938        ));
8939        assert!(text.contains("global @square : bytes 64 ="), "{text}");
8940        assert!(text.contains("global @rectangle : bytes 20 ="), "{text}");
8941        assert!(text.contains("global @rounded : bytes 8 ="), "{text}");
8942        assert!(text.contains("global @across : bytes 24 ="), "{text}");
8943        assert!(text.contains("global @e : i32 = 4,"), "{text}");
8944
8945        // A `const` object is not one of them, which is what makes `int a[n];` a variable
8946        // length array in C and is the distinction the keyword was added to draw.
8947        let mut opts = options();
8948        opts.emit = EmitKind::Ir;
8949        let konst = "const int n = 1;\nint a[n];\n";
8950        let message = "/main.c:2:5: error: variably modified 'a' at file scope [E0538]";
8951        assert_eq!(run(&opts, konst).messages, [message]);
8952
8953        // Nor is a subscript of one, which gcc 16 refuses in the same words.
8954        let subscript = "constexpr int t[3] = { 1, 2, 3 };\nint a[t[1]];\n";
8955        assert_eq!(run(&opts, subscript).messages, [message]);
8956
8957        // And `constexpr` implies `const`, so the address of one is an address of a `const`.
8958        let address = "constexpr int c = 3;\nint *p = &c;\n";
8959        let warning = "/main.c:2:6: warning: initialization discards 'const' qualifier from \
8960             pointer target type [E0514]";
8961        assert_eq!(run(&opts, address).messages, [warning]);
8962    }
8963
8964    /// A member whose size was refused is not a flexible array member, whatever it looks like.
8965    ///
8966    /// The refusal leaves the member with no size, which is also how `int a[]` is written, so
8967    /// without the count that tells the two apart the rules about where a flexible array member
8968    /// may sit read the wreckage of the first error as a second mistake. gcc 16.2.0 says one
8969    /// thing about each of these and so does this, which is what the program can act on: adding
8970    /// a named member to `struct D` makes the message about `k` no clearer, and moving `a` to
8971    /// the end of `struct E` does not either.
8972    #[test]
8973    fn a_member_whose_size_was_refused_is_not_a_flexible_array_member() {
8974        let mut opts = options();
8975        opts.emit = EmitKind::Ir;
8976
8977        let alone = "int k;\nextern struct D { int a[k]; } ed;\n";
8978        let message = "/main.c:2:23: error: variably modified 'a' at file scope [E0538]";
8979        assert_eq!(run(&opts, alone).messages, [message]);
8980
8981        // And not one in the wrong place either, which is the other half of the same rule.
8982        let first = "int k;\nextern struct E { int a[k]; int b; } ee;\n";
8983        assert_eq!(run(&opts, first).messages, [message]);
8984
8985        // A size that is refused for a reason of its own, to show the count is about the
8986        // refusal rather than about the one message that happens to have been found first.
8987        let negative = "struct F { int a[-1]; };\n";
8988        let refused = "/main.c:1:18: error: size of array 'a' is negative [E0536]";
8989        assert_eq!(run(&opts, negative).messages, [refused]);
8990
8991        // The member that was written with no size at all is still a flexible array member, and
8992        // a structure with nothing else in it still has no named member to hang one off.
8993        let flexible = "struct G { int a[]; };\n";
8994        let named = "/main.c:1:16: error: flexible array member in a struct with no named \
8995             members [E0554]";
8996        assert_eq!(run(&opts, flexible).messages, [named]);
8997    }
8998
8999    /// A pointer to an array, where the qualifiers are on the element and the comparison is not.
9000    ///
9001    /// 6.7.3p10 says the qualifiers in an array declaration belong to the element, so `const int
9002    /// [4]` is an unqualified array of `const int` and not a qualified array of `int`. Compatibility
9003    /// then reads the element types, finds one `const` and one not, and calls the two arrays
9004    /// incompatible, which makes `const int (*)[4] = p` an incompatible pointer rather than a
9005    /// pointer that gained a qualifier. That is what the wording said before C23 and it is not what
9006    /// any compiler does: gcc and clang take it, C23 wrote the rule the way they read it, and the
9007    /// two directions are told apart the way they are everywhere else, which is that adding a
9008    /// qualifier is silent and dropping one is worth a word.
9009    ///
9010    /// Found in libwebp, where `src/enc/vp8l_enc.c` takes the address of a `HistogramBuckets` out of
9011    /// a structure into a `const HistogramBuckets *const`, and a whole file of a real library did
9012    /// not compile for it.
9013    #[test]
9014    fn a_pointer_to_an_array_gains_a_qualifier_the_same_way_a_pointer_to_anything_else_does() {
9015        let mut opts = options();
9016        opts.emit = EmitKind::Ir;
9017        let prefix = "typedef unsigned int B[4];\nstruct H { B category[2]; };\n";
9018
9019        // Adding it, which is the direction the library writes and the one nothing is owed for.
9020        let adding = format!("{prefix}const B *f(struct H *h) {{ return &h->category[0]; }}\n");
9021        assert_eq!(run(&opts, &adding).messages, [] as [String; 0]);
9022
9023        // And the same thing written out rather than through the typedef, since the typedef is a
9024        // spelling and the rule is about the array.
9025        let plain = concat!(
9026            "const unsigned int (*f(unsigned int (*p)[4]))[4] { return p; }\n",
9027            "const unsigned int (*g(unsigned int (*p)[2][3]))[2][3] { return p; }\n",
9028        );
9029        assert_eq!(run(&opts, plain).messages, [] as [String; 0]);
9030
9031        // Dropping it, which is the direction that is worth a word, and the word is the one every
9032        // other pointer target gets rather than a complaint about the types not matching.
9033        let dropping = format!("{prefix}B *f(const B *p) {{ return p; }}\n");
9034        let warning = "/main.c:3:27: warning: return discards 'const' qualifier from pointer target type \
9035             [E0514]";
9036        assert_eq!(run(&opts, &dropping).messages, [warning]);
9037
9038        // A pointer to an array of something else is still an incompatible pointer, because
9039        // nothing here is about the element being a different type.
9040        let wrong = "const unsigned int (*f(unsigned short (*p)[4]))[4] { return p; }\n";
9041        let error = "/main.c:1:61: error: returning 'unsigned short (*)[4]' from a function with \
9042             incompatible return type 'const unsigned int (*)[4]' [E0512]";
9043        assert_eq!(run(&opts, wrong).messages, [error]);
9044    }
9045
9046    /// A definition that names its parameters and then declares them under the list.
9047    ///
9048    /// The declarations say what the types are, 6.9.1p6, and what the function takes is those
9049    /// types with the default argument promotions over them, which is what a caller of an
9050    /// unprototyped function hands over. A prototype already in scope overrules the promoted
9051    /// types, since a header saying `int narrow(char);` over a definition written this way is
9052    /// the pairing all the code written this way relies on and 6.7.6.3p15 is read that way by
9053    /// every compiler.
9054    #[test]
9055    fn an_old_style_definition_takes_its_types_from_the_declarations_under_its_list() {
9056        // C17, since the default dialect is the one that warns about the form and this is
9057        // about what it means rather than about the warning.
9058        let mut opts = options();
9059        opts.std = Std::C17;
9060        let source = concat!(
9061            "int add(a, b)\n",
9062            "int a;\n",
9063            "int b;\n",
9064            "{ return a + b; }\n",
9065            "int promoted(c)\n",
9066            "char c;\n",
9067            "{ return c; }\n",
9068            "int narrow(char);\n",
9069            "int narrow(c)\n",
9070            "char c;\n",
9071            "{ return c; }\n",
9072            "int first(a)\n",
9073            "int a[4];\n",
9074            "{ return a[0]; }\n",
9075        );
9076        let result = run(&opts, source);
9077        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
9078        let text = result.text();
9079        assert!(text.contains("add : int(int, int) function external defined"), "{text}");
9080        assert!(text.contains("promoted : int(int) function external defined"), "{text}");
9081        // The body still sees the `char` it was declared as, whatever the caller hands over.
9082        assert!(text.contains("c : char object automatic defined"), "{text}");
9083        assert!(text.contains("narrow : int(char) function external defined"), "{text}");
9084        // An array parameter is a pointer here as much as it is in a prototype.
9085        assert!(text.contains("first : int(int *) function external defined"), "{text}");
9086    }
9087
9088    /// What the two halves of an old-style parameter list can disagree about.
9089    ///
9090    /// Each of these is a sentence gcc 16 has, and every message below is the one it prints,
9091    /// read off it on x86-64 rather than reasoned about. The last two are the dialect: a name
9092    /// with no declaration is an `int` in C89 and a diagnostic from C99 on, and the whole form
9093    /// left the language in C23, where gcc still takes it and warns.
9094    #[test]
9095    fn the_two_halves_of_an_old_style_parameter_list_have_to_agree() {
9096        let mut opts = options();
9097        opts.std = Std::C17;
9098        for (source, message) in [
9099            ("int f(a, a)\nint a;\n{ return a; }\n", "1:10: error: multiple parameters named 'a'"),
9100            (
9101                "int f(a)\nint a;\nint b;\n{ return a; }\n",
9102                "3:5: error: declaration for parameter 'b' but no such parameter",
9103            ),
9104            ("int f(a)\nint a;\nint a;\n{ return a; }\n", "3:5: error: redefinition of parameter"),
9105            ("int f(a)\nint a = 1;\n{ return a; }\n", "2:5: error: parameter 'a' is initialized"),
9106            (
9107                "int f(a)\nstatic int a;\n{ return a; }\n",
9108                "2:12: error: storage class specified for parameter 'a'",
9109            ),
9110            (
9111                "int f(char);\nint f(a)\nshort a;\n{ return a; }\n",
9112                "2:7: error: argument 'a' doesn't match prototype",
9113            ),
9114        ] {
9115            let result = run(&opts, source);
9116            assert!(result.failed(), "expected this to fail:\n{source}");
9117            assert!(result.messages[0].contains(message), "{:?}", result.messages);
9118        }
9119
9120        // A name the declarations never mention. C89 gave it an `int` and gcc still takes it
9121        // in that dialect, and every dialect after it made the same line a diagnostic.
9122        let implicit = "int f(a, b)\nint a;\n{ return a + b; }\n";
9123        let mut older = options();
9124        older.std = Std::C89;
9125        assert!(!run(&older, implicit).failed(), "{:?}", run(&older, implicit).messages);
9126        let result = run(&opts, implicit);
9127        assert!(
9128            result.messages[0].contains("1:10: error: type of 'b' defaults to 'int'"),
9129            "{:?}",
9130            result.messages
9131        );
9132
9133        // C23 took the form out of the language and gcc kept accepting it with a warning, and
9134        // a warning is what this is, because the code written this way is not going to be
9135        // rewritten and refusing it would put the compiler out of reach of it.
9136        let mut newer = options();
9137        newer.std = Std::C23;
9138        let plain = "int f(a)\nint a;\n{ return a; }\n";
9139        let result = run(&newer, plain);
9140        assert!(!result.failed(), "{:?}", result.messages);
9141        assert_eq!(
9142            result.messages,
9143            ["/main.c:1:5: warning: old-style function definition [E0412]"]
9144        );
9145        assert!(run(&opts, plain).messages.is_empty(), "and nothing to say in the dialects before");
9146    }
9147
9148    /// The two obsolete designators, which are silent until `-pedantic` asks about them.
9149    ///
9150    /// `[3] 7` is what GCC had for an array before C99 settled on `[3] = 7`, and `x: 7` is the
9151    /// same era's spelling for a member. Both are still in code written against a compiler of
9152    /// that era, and gcc 16 takes both without a word unless it is asked to be pedantic, which
9153    /// is where the columns below come from as well.
9154    #[test]
9155    fn the_obsolete_designators_are_taken_and_are_pedantic_warnings() {
9156        let array = "int a[8] = { [3] 7 };\n";
9157        let member = "struct s { int x; } v = { x: 7 };\n";
9158        for source in [array, member] {
9159            let result = run(&options(), source);
9160            assert!(!result.failed(), "{:?}", result.messages);
9161            assert!(result.messages.is_empty(), "nothing to say: {:?}", result.messages);
9162        }
9163
9164        let mut asked = options();
9165        asked.pedantic = true;
9166        assert_eq!(
9167            run(&asked, array).messages,
9168            ["/main.c:1:18: warning: obsolete designator, write `[i] =` instead [E0415]"]
9169        );
9170        assert_eq!(
9171            run(&asked, member).messages,
9172            ["/main.c:1:27: warning: obsolete designator, write `.field =` instead [E0413]"]
9173        );
9174    }
9175
9176    /// A type nothing is ever an object of is a type `sizeof` still has to answer about, which
9177    /// is what `991014-1.c` in the gcc.c-torture execution suite asks.
9178    ///
9179    /// The limit is `PTRDIFF_MAX` and it is the same one for an array and for a record, so a
9180    /// record of every byte an object may have is laid out and one byte more is refused. All
9181    /// four numbers are what gcc 16 gives on x86-64.
9182    #[test]
9183    fn a_type_is_refused_when_it_passes_the_largest_object_and_not_before() {
9184        let text = ir(concat!(
9185            "struct huge_struct { short buf[(1L << 62) - 256]; int a, b, c, d; };\n",
9186            "struct brim { char buf[9223372036854775807L]; };\n",
9187            "struct bitty { char buf[9223372036854775800L]; int x : 1; };\n",
9188            "unsigned long h = sizeof(struct huge_struct);\n",
9189            "unsigned long b = sizeof(struct brim);\n",
9190            "unsigned long y = sizeof(struct bitty);\n",
9191        ));
9192        assert!(text.contains("global @h : i64 = 9223372036854775312,"), "{text}");
9193        assert!(text.contains("global @b : i64 = 9223372036854775807,"), "{text}");
9194        assert!(text.contains("global @y : i64 = 9223372036854775804,"), "{text}");
9195
9196        let mut opts = options();
9197        opts.emit = EmitKind::Ir;
9198        let over = "struct over { char buf[9223372036854775800L]; char x[8]; };\n";
9199        let message = "/main.c:1:1: error: type 'struct over' is too large [E0560]";
9200        assert_eq!(run(&opts, over).messages, [message]);
9201        let array = "struct wide { short buf[1L << 62]; };\n";
9202        let message = "/main.c:1:25: error: size of array 'buf' exceeds \
9203             maximum object size '9223372036854775807' [E0537]";
9204        assert_eq!(run(&opts, array).messages[0], message);
9205    }
9206
9207    /// A byte in the source that is not part of a character, which only a literal may hold.
9208    ///
9209    /// The source cannot be a `&str` here, which is the whole point: a file is bytes and only
9210    /// mostly text.
9211    fn compile_bytes(source: &[u8]) -> Compiled {
9212        let mut opts = options();
9213        opts.emit = EmitKind::Ir;
9214        let mut fs = MemoryFileSystem::new();
9215        fs.insert("/main.c", source.to_vec());
9216        compile(&opts, "/main.c", &fs)
9217    }
9218
9219    /// A raw byte inside a string literal is that byte, which gcc has always taken and which is
9220    /// the only place in a source file where a byte does not have to be part of a character.
9221    /// Replacing it would give the object three bytes rather than one, since the replacement
9222    /// character is three bytes of UTF-8, so the object would not be the one that was written
9223    /// even where the diagnostic is ignored. Anywhere else the byte is still a mistake, which
9224    /// is where gcc draws the same line.
9225    #[test]
9226    fn a_byte_that_is_not_a_character_is_kept_in_a_literal_and_refused_outside_one() {
9227        let mut source = b"char s[] = \"a".to_vec();
9228        source.push(0xff);
9229        source.extend_from_slice(b"b\";\nchar c = '");
9230        source.push(0xff);
9231        source.extend_from_slice(b"';\n");
9232        let result = compile_bytes(&source);
9233        assert_eq!(result.messages, Vec::<String>::new(), "a raw byte in a literal is that byte");
9234        assert!(result.text().contains(r#"bytes "a\ffb\00""#), "{}", result.text());
9235        // Plain `char` is signed on this target, so the constant is minus one rather than 255.
9236        assert!(result.text().contains("global @c : i8 = -1,"), "{}", result.text());
9237
9238        let mut stray = b"int a".to_vec();
9239        stray.push(0xff);
9240        stray.extend_from_slice(b" = 1;\n");
9241        let result = compile_bytes(&stray);
9242        assert!(
9243            result.messages.iter().any(|m| m.contains("source is not valid UTF-8 here")),
9244            "{:?}",
9245            result.messages
9246        );
9247    }
9248
9249    #[test]
9250    fn an_object_becomes_a_global_with_an_image_and_a_function_becomes_a_func() {
9251        let text = ir("int x = 7;\nint add(int a, int b) { return a + b; }\n");
9252        assert!(text.contains("global @x : i32 = 7, align 4, linkage(external)\n"), "{text}");
9253        let expected = "\
9254func @add(i32, i32) -> i32, linkage(external) {
9255block0(%0: i32, %1: i32):
9256    %2 = add.nsw %0, %1
9257    return %2
9258}
9259";
9260        assert!(text.contains(expected), "{text}");
9261    }
9262
9263    #[test]
9264    fn a_local_nothing_takes_the_address_of_is_a_value_and_never_a_stack_slot() {
9265        let text = body("int f(int n) { int a = n + 1; int b = a * 2; return a + b; }\n");
9266        assert!(!text.contains("alloca"), "{text}");
9267        assert!(!text.contains("load"), "{text}");
9268        assert!(!text.contains("store"), "{text}");
9269    }
9270
9271    #[test]
9272    fn a_local_whose_address_is_taken_gets_a_slot_in_the_entry_block() {
9273        let text = body("int g(int *);\nint f(void) { int a = 1; return g(&a); }\n");
9274        let expected = "\
9275block0:
9276    %0 = alloca, size 4, align 4
9277    %1 = iconst.i32 1
9278    store %1 -> %0, align 4, tbaa !1
9279    %2 = call @g(%0) : (ptr) -> i32
9280    return %2
9281";
9282        assert_eq!(text, expected);
9283    }
9284
9285    #[test]
9286    fn a_loop_carries_what_it_changes_as_block_parameters() {
9287        // The whole point of building SSA during the walk rather than after it: `i` and
9288        // `total` are values that arrive on an edge, and neither has ever been in memory.
9289        let text = body(
9290            "int f(int n) {\n  int total = 0;\n  for (int i = 0; i < n; i++) total += i;\n  \
9291             return total;\n}\n",
9292        );
9293        assert!(!text.contains("alloca"), "{text}");
9294        assert!(text.contains("block1(%3: i32, %4: i32):"), "{text}");
9295        assert!(text.contains("jump block1("), "{text}");
9296    }
9297
9298    #[test]
9299    fn a_comparison_used_as_a_condition_is_not_widened_and_narrowed_again() {
9300        let text = body("int f(int a, int b) { if (a < b) return 1; return 0; }\n");
9301        assert!(text.contains("icmp slt %0, %1"), "{text}");
9302        assert!(!text.contains("zext"), "{text}");
9303    }
9304
9305    #[test]
9306    fn the_right_side_of_a_short_circuit_is_in_a_block_of_its_own() {
9307        let text = body("int f(int a, int b) { return a && b; }\n");
9308        let expected = "\
9309block0(%0: i32, %1: i32):
9310    %2 = iconst.i32 0
9311    %3 = icmp ne %0, %2
9312    %4 = iconst.i1 0
9313    br_if %3, block1, block2(%4)
9314
9315block1:
9316    %5 = iconst.i32 0
9317    %6 = icmp ne %1, %5
9318    jump block2(%6)
9319
9320block2(%7: i1):
9321    %8 = zext.i32 %7
9322    return %8
9323";
9324        assert_eq!(text, expected);
9325    }
9326
9327    #[test]
9328    fn code_after_a_return_is_not_built_and_does_not_leave_an_empty_block_behind() {
9329        let text = body("int f(int a) { if (a) return 1; else return 2; return 3; }\n");
9330        // Three blocks, the test and the two arms. The join the `return 3` would need is
9331        // never created, because a block nothing branches to is not a block.
9332        assert!(!text.contains("block3"), "{text}");
9333        assert!(!text.contains("iconst.i32 3"), "{text}");
9334    }
9335
9336    #[test]
9337    fn falling_off_the_end_returns_zero_from_main_and_nothing_from_a_void_function() {
9338        assert!(body("int main(void) { }\n").contains("iconst.i32 0\n    return"));
9339        assert_eq!(body("void f(void) { }\n"), "block0:\n    return\n");
9340        // Any other function comes back as well, with a zero, since only using the value is
9341        // undefined and a call made for what it does has to return to its caller.
9342        assert!(body("int f(void) { }\n").contains("iconst.i32 0\n    return"));
9343    }
9344
9345    #[test]
9346    fn a_structure_is_copied_rather_than_held_in_a_value() {
9347        let text = body(
9348            "struct point { int x, y; };\n\
9349             int f(void) { struct point p = { 1, 2 }; struct point q = p; return q.x; }\n",
9350        );
9351        assert!(text.contains("memcpy"), "{text}");
9352    }
9353
9354    #[test]
9355    fn an_initializer_that_leaves_part_of_an_object_unwritten_zeroes_it_first() {
9356        let text = body("int f(void) { int a[4] = { 1 }; return a[3]; }\n");
9357        assert!(text.contains("memset"), "{text}");
9358    }
9359
9360    #[test]
9361    fn a_switch_is_one_branch_and_a_case_that_falls_through_carries_what_it_wrote() {
9362        let text = body(
9363            "int f(int x) { int r = 0; switch (x) { case 1: r = 1; case 2: r += 2; break; \
9364             default: r = 4; } return r; }\n",
9365        );
9366        let expected = "\
9367block0(%0: i32):
9368    %1 = iconst.i32 0
9369    switch %0, block1, [1 => block2, 2 => block3(%1)]
9370
9371block1:
9372    %2 = iconst.i32 4
9373    jump block4(%2)
9374
9375block2:
9376    %3 = iconst.i32 1
9377    jump block3(%3)
9378
9379block3(%4: i32):
9380    %5 = iconst.i32 2
9381    %6 = add.nsw %4, %5
9382    jump block4(%6)
9383
9384block4(%7: i32):
9385    return %7
9386";
9387        assert_eq!(text, expected);
9388    }
9389
9390    #[test]
9391    fn a_case_range_is_tested_for_rather_than_put_in_the_table() {
9392        // GNU's `case 1 ... 9`. Nine table entries would be nine here and four billion for the
9393        // range a program is allowed to write, so it is a subtraction and one unsigned compare.
9394        let text = body("int f(int x) { switch (x) { case 1 ... 9: return 1; } return 0; }\n");
9395        assert!(text.contains("%2 = sub %0, %1"), "{text}");
9396        assert!(text.contains("icmp ule"), "{text}");
9397        assert!(!text.contains("switch"), "{text}");
9398    }
9399
9400    #[test]
9401    fn break_leaves_the_switch_and_continue_leaves_the_loop_around_it() {
9402        let text = body(
9403            "int f(int n) { int t = 0; for (int i = 0; i < n; i++) { switch (i) { \
9404             case 0: continue; case 1: break; default: t += i; } t++; } return t; }\n",
9405        );
9406        // The `continue` goes to the step and the `break` goes to the `t++` after the switch,
9407        // which is also where the default falls out to.
9408        assert!(text.contains("switch %3, block4, [0 => block5, 1 => block6]"), "{text}");
9409        assert!(text.contains("block5:\n    jump block7("), "{text}");
9410        assert!(text.contains("block6:\n    jump block8("), "{text}");
9411    }
9412
9413    #[test]
9414    fn a_switch_with_nothing_to_branch_on_still_runs_what_comes_after_it() {
9415        assert_eq!(body("void f(int x) { switch (x) { } }\n"), "block0(%0: i32):\n    return\n");
9416    }
9417
9418    #[test]
9419    fn a_label_a_loop_is_only_entered_through_builds_the_loop_around_it() {
9420        // A branch into the middle of a loop that nothing else reaches, the Duff's device shape.
9421        // The `while` is not reached in order, so the walk starts a block nothing branches to and
9422        // builds it from there. What comes out is the loop with an edge straight into its body,
9423        // and the header that nothing arrives at is pruned.
9424        let text = body(
9425            "int f(int x, int n) { switch (x) { case 1: break; while (n) { case 2: n--; } } \
9426             return n; }\n",
9427        );
9428        // `case 2` lands on the body, `case 1` and the default land on the return, and the test
9429        // at the bottom of the loop comes back round to the body.
9430        assert!(text.contains("switch %0, block1(%1), [1 => block2, 2 => block3(%1)]"), "{text}");
9431        assert!(text.contains("block3(%3: i32):\n    %4 = iconst.i32 1"), "{text}");
9432        assert!(text.contains("block4:\n    jump block3("), "{text}");
9433    }
9434
9435    #[test]
9436    fn a_goto_into_a_loop_body_enters_it_without_the_test() {
9437        // The same thing through a `goto`. The first pass through the body runs whatever the
9438        // label is on, and only then does the loop reach its own test.
9439        let text = body("int f(int x, int n) { goto in; while (n) { in: n--; } return n; }\n");
9440        assert!(text.starts_with("block0(%0: i32, %1: i32):\n    jump block1(%1)"), "{text}");
9441        assert!(text.contains("block1(%2: i32):\n    %3 = iconst.i32 1"), "{text}");
9442        assert!(text.contains("br_if %6, block2, block3"), "{text}");
9443    }
9444
9445    #[test]
9446    fn a_goto_is_a_jump_to_the_block_the_label_starts() {
9447        let text = body("int f(int x) { int r = 0; if (x) goto out; r = 1; out: return r; }\n");
9448        // Both edges into `out` carry what `r` holds on the way, and neither is a stack slot. The
9449        // block the `goto` jumps out of is empty and hands its edge on, which is what moves `out`
9450        // up the block list to second place.
9451        assert!(!text.contains("alloca"), "{text}");
9452        assert!(text.contains("block2(%4: i32):\n    return %4"), "{text}");
9453        assert_eq!(text.matches("jump block2(").count(), 2, "{text}");
9454    }
9455
9456    #[test]
9457    fn a_backward_goto_is_a_loop_and_carries_what_it_changes() {
9458        let text =
9459            body("int f(int n) { int i = 0; again: if (i < n) { i++; goto again; } return i; }\n");
9460        assert!(!text.contains("alloca"), "{text}");
9461        assert!(text.contains("block1(%2: i32):"), "{text}");
9462        assert!(text.contains("jump block1(%5)"), "{text}");
9463    }
9464
9465    #[test]
9466    fn a_label_nothing_reaches_is_taken_out_rather_than_left_for_the_verifier() {
9467        // A block nothing branches to is not a legal function, and which labels are dead is not
9468        // known until the last statement has been walked, since the `goto` is allowed to be it.
9469        assert_eq!(
9470            body("int f(int x) { return x; spare: return 0; }\n"),
9471            "block0(%0: i32):\n    return %0\n"
9472        );
9473    }
9474
9475    #[test]
9476    fn a_bit_field_is_read_by_loading_the_bytes_it_lies_in_and_shifting() {
9477        let text = body(
9478            "struct s { unsigned a : 3; signed b : 5; };\nint f(struct s *p) { return p->b; }\n",
9479        );
9480        // One byte holds both fields, and the signed one needs no mask: shifting it down
9481        // arithmetically is what says its top bit is a sign.
9482        assert_eq!(
9483            text,
9484            "\
9485block0(%0: ptr):
9486    %1 = load.i8 %0, align 1
9487    %2 = iconst.i8 3
9488    %3 = ashr %1, %2
9489    %4 = sext.i32 %3
9490    return %4
9491"
9492        );
9493    }
9494
9495    #[test]
9496    fn a_store_to_a_bit_field_does_not_write_a_byte_it_has_no_bit_in() {
9497        // C11 says an ordinary member beside a bit-field is a memory location of its own, so
9498        // the four byte store this would take is a data race in a program that has none. The
9499        // three bytes of `a` go in as two and one, and `c` is not touched.
9500        let text =
9501            body("struct s { int a : 24; char c; };\nvoid f(struct s *p, int v) { p->a = v; }\n");
9502        assert_eq!(
9503            text,
9504            "\
9505block0(%0: ptr, %1: i32):
9506    %2 = iconst.i32 16777215
9507    %3 = and %1, %2
9508    %4 = trunc.i16 %3
9509    store %4 -> %0, align 2
9510    %5 = iconst.i32 16
9511    %6 = lshr %3, %5
9512    %7 = trunc.i8 %6
9513    %8 = iconst.i64 2
9514    %9 = ptr_add %0, %8
9515    store %7 -> %9, align 1
9516    return
9517"
9518        );
9519    }
9520
9521    #[test]
9522    fn what_an_assignment_to_a_bit_field_is_worth_is_what_fits_in_it() {
9523        let text =
9524            body("struct s { unsigned b : 5; };\nunsigned f(struct s *p) { return p->b = 33; }\n");
9525        // 33 does not fit in five bits, and 1 is both what goes in the field and what the
9526        // assignment is worth.
9527        assert!(text.contains("%3 = iconst.i8 31\n    %4 = and %2, %3"), "{text}");
9528        assert!(text.ends_with("%9 = zext.i32 %4\n    return %9\n"), "{text}");
9529    }
9530
9531    #[test]
9532    fn an_assignment_a_statement_throws_away_builds_none_of_what_it_is_worth() {
9533        // The value of an assignment to a bit-field takes a shift to build, and a statement
9534        // has no use for it. Nothing here reads back what was stored.
9535        let text = body("struct s { signed b : 5; };\nvoid f(struct s *p) { p->b = 3; }\n");
9536        assert_eq!(text.matches("ashr").count(), 0, "{text}");
9537        assert!(text.ends_with("store %8 -> %0, align 1\n    return\n"), "{text}");
9538    }
9539
9540    #[test]
9541    fn a_bit_field_in_an_initializer_goes_in_over_bytes_that_were_zeroed_first() {
9542        // A bit-field writes part of a byte and leaves the rest of it alone, so the object has
9543        // to be zero before it goes in or what the initializer did not name is whatever the
9544        // stack held.
9545        let text = body(
9546            "struct s { int a : 3; int b; };\nint f(void) { struct s v = { 1 }; return v.b; }\n",
9547        );
9548        assert!(text.contains("memset %0, %1, size 8, align 4"), "{text}");
9549    }
9550
9551    #[test]
9552    fn the_image_of_a_static_bit_field_is_the_bytes_the_fields_share() {
9553        // Two fields in one byte are not two entries in the image, because an image is written
9554        // in bytes: they are the byte they are both in.
9555        let text = ir("struct s { unsigned a : 3; unsigned b : 5; } g = { 1, 2 };\n");
9556        assert!(
9557            text.contains("global @g : bytes 4 = { bytes \"\\11\", zero 3 }, align 4"),
9558            "{text}"
9559        );
9560    }
9561
9562    #[test]
9563    fn an_initialized_flexible_array_member_makes_the_object_larger_than_its_type() {
9564        // `sizeof` answers without the array and the definition has to hold what was written, so
9565        // the object is the size of its image. gcc 16 gives these four, three and two bytes and
9566        // so does this. The image used to be written at the size the type had, which left the
9567        // verifier looking at twenty bytes going into four.
9568        let text = ir(concat!(
9569            "struct a { int i; int j[]; } x = { 1, { 2, 0, 2, 3 } };\n",
9570            "struct b { char c; char p[]; } y = { 'o', \"wx\" };\n",
9571            "struct c { char c; char p[]; } z = { '9', { 'e', 'b' } };\n",
9572            "char s[2] = \"hi\";\n",
9573        ));
9574        assert!(
9575            text.contains("global @x : bytes 20 = { i32 1, i32 2, i32 0, i32 2, i32 3 }"),
9576            "{text}"
9577        );
9578        assert!(text.contains("global @y : bytes 4 = { i8 111, bytes \"wx\\00\" }"), "{text}");
9579        assert!(text.contains("global @z : bytes 3 = { i8 57, i8 101, i8 98 }"), "{text}");
9580        // The array with a length of its own still cuts the literal down to it, which is the
9581        // one case in C where a string initializer drops its terminator.
9582        assert!(text.contains("global @s : bytes 2 = { bytes \"hi\" }"), "{text}");
9583    }
9584
9585    #[test]
9586    fn a_definition_takes_a_parameter_it_left_unnamed() {
9587        // The entry block's parameters are the definition's, and one the front end dropped for
9588        // having no name left the two lists different lengths, which the walk read as an
9589        // old-style definition and refused. gcc has taken these for far longer than C23 has.
9590        let text = ir("int f(int a, int) { return a; }\n");
9591        assert!(text.contains("func @f(i32, i32) -> i32"), "{text}");
9592        assert!(text.contains("block0(%0: i32, %1: i32):"), "{text}");
9593
9594        // The unnamed one first, so that the named one is the second parameter of the entry
9595        // block and not the first: the list says the order and not only how many there are.
9596        let text = ir("int g(int, int n) { return n; }\n");
9597        assert!(text.contains("block0(%0: i32, %1: i32):\n    return %1\n"), "{text}");
9598    }
9599
9600    #[test]
9601    fn an_assignment_of_a_structure_is_the_object_it_wrote() {
9602        // `d = e = c` used to be refused, because the middle assignment is a value of structure
9603        // type and the walk had nowhere to read one from. What an assignment is worth is the
9604        // value it stored, so the object it stored into is the answer and the chain is three
9605        // copies out of the one source with no temporary in it.
9606        let text = body(concat!(
9607            "struct s { int f; int g; };\n",
9608            "void h(struct s *a, struct s *c, struct s *d, struct s *e)\n",
9609            "{ *d = *e = a[0] = *c; }\n",
9610        ));
9611        assert_eq!(text.matches("memcpy").count(), 3, "{text}");
9612        assert!(text.contains("memcpy %8, %1, size 8, align 4\n"), "{text}");
9613        assert!(text.contains("memcpy %3, %8, size 8, align 4\n"), "{text}");
9614        assert!(text.contains("memcpy %2, %3, size 8, align 4\n"), "{text}");
9615    }
9616
9617    #[test]
9618    fn a_string_literal_stops_at_the_end_of_the_array_it_is_filling() {
9619        // The excess used to be laid into the object anyway, so the row after was written over
9620        // and the image refused the entry that came to it. C 6.7.10p14 says the terminator goes
9621        // in only if there is room for it, and gcc discards the rest of a literal that is longer
9622        // still, which is what the first of these is and why it warns.
9623        let mut opts = options();
9624        opts.emit = EmitKind::Ir;
9625        let result = run(
9626            &opts,
9627            concat!(
9628                "const char a[2][3] = { \"1234\", \"xyz\" };\n",
9629                "static const char b[3][5] = { \"12345\", \"678\", \"9\" };\n",
9630                "union u { struct { char x[4]; char y[4]; }; struct { char z[8]; }; };\n",
9631                "const union u c = { { \"1234\", \"567\" } };\n",
9632            ),
9633        );
9634        let text = result.text();
9635        assert_eq!(
9636            result.messages,
9637            ["/main.c:1:24: warning: initializer-string for array of 'const char' is too long \
9638              (5 chars into 3 available) [E0637]"]
9639        );
9640        assert!(text.contains("global @a : bytes 6 = { bytes \"123\", bytes \"xyz\" }"), "{text}");
9641        assert!(
9642            text.contains(
9643                "global @b : bytes 15 = { bytes \"12345\", bytes \"678\\00\", zero 1, \
9644                 bytes \"9\\00\", zero 3 }"
9645            ),
9646            "{text}"
9647        );
9648        // The eight bytes are four, three and a terminator, and then the byte the shorter
9649        // literal left for the string in the other member of the union to end at.
9650        assert!(
9651            text.contains("global @c : bytes 8 = { bytes \"1234\", bytes \"567\\00\" }"),
9652            "{text}"
9653        );
9654    }
9655
9656    #[test]
9657    fn a_cast_of_a_record_to_its_own_type_is_the_object_that_was_cast() {
9658        // gcc accepts one and does nothing with it, which sema already had. Lowering asked for
9659        // the object under it and had no arm for a cast, so `(struct s)x` in an initializer was
9660        // refused with E0519. It is one copy out of the object named, not two.
9661        let text = body(concat!(
9662            "struct s { int a, b; };\nstruct v { struct s s; int t; };\n",
9663            "void g(struct v *);\n",
9664            "void f(struct s *p) { struct v w = { (struct s)*p, 5 }; g(&w); }\n",
9665        ));
9666        assert_eq!(text.matches("memcpy").count(), 1, "{text}");
9667    }
9668
9669    #[test]
9670    fn a_compound_literal_read_in_a_static_initializer_lays_its_bytes_into_the_image() {
9671        // C 6.7.11p4 says a compound literal at file scope has static storage duration, which
9672        // makes it a constant element, and tcc and c-testsuite both write one. Sema used to call
9673        // it a non constant because reading it is a node of its own and the read was what it
9674        // looked at, and lowering had no way to put an object where it wanted a number.
9675        let text = ir(concat!(
9676            "struct s { int x; };\n",
9677            "struct t { struct s s; int o; } a = { (struct s){ 2 }, 3 };\n",
9678            "int n = (int){ 7 };\n",
9679            "struct u { struct s p; struct s q; } b = { (struct s){ 1 }, (struct s){ } };\n",
9680        ));
9681        assert!(text.contains("global @a : bytes 8 = { i32 2, i32 3 }"), "{text}");
9682        assert!(text.contains("global @n : i32 = 7,"), "{text}");
9683        // The second literal names nothing, so what it puts in is the zeros of its own size and
9684        // not the tail of the object it went in, which would have been the same bytes by luck.
9685        assert!(text.contains("global @b : bytes 8 = { i32 1, zero 4 }"), "{text}");
9686    }
9687
9688    #[test]
9689    fn the_address_of_a_compound_literal_asks_for_the_object_it_points_at() {
9690        // Nothing declares a compound literal, so the reference is the only thing that can ask
9691        // for it to be emitted. The image named `.Lanon.0` and the module defined no such
9692        // symbol, which the link would have been the first to find out.
9693        let text = ir("struct s { int x; };\nstruct s *q = &(struct s){ 9 };\n");
9694        assert!(text.contains("global @.Lanon.0 : i32 = 9, align 4, linkage(internal)"), "{text}");
9695        assert!(text.contains("global @q : bytes 8 = { addr.8 @.Lanon.0 }"), "{text}");
9696    }
9697
9698    #[test]
9699    fn an_object_of_no_size_at_all_has_an_image_with_nothing_in_it() {
9700        // A zero length array, which gcc allows and real code uses as the tail of a structure.
9701        // The image is there and holds nothing, which is not the global that has no image at
9702        // all, and the IR reader used to stop on the empty one.
9703        let text = ir("unsigned char foo[1][0];\n");
9704        assert!(text.contains("global @foo : bytes 0 = {}, align 1"), "{text}");
9705    }
9706
9707    #[test]
9708    fn a_null_pointer_in_an_image_is_the_bits_an_address_has_room_for() {
9709        // `NULL` in a static initializer, which every program has. The IR type is `ptr` and a
9710        // `ptr` has no width of its own, so the width the bits are cut to is the target's.
9711        let text = ir("void *p = 0;\nchar *q = (char *) 4096;\n");
9712        assert!(text.contains("global @p : i64 = 0, align 8"), "{text}");
9713        assert!(text.contains("global @q : i64 = 4096, align 8"), "{text}");
9714    }
9715
9716    #[test]
9717    fn an_object_another_module_defines_may_be_one_that_cannot_be_written_through() {
9718        // Which the verifier used to refuse, having read a declaration as a definition with
9719        // nothing in it. `extern const` is how a program names something in the library's read
9720        // only data, and glibc and Darwin both have one in a header a real program includes.
9721        let text = ir("extern const int limit;\nint f(void) { return limit; }\n");
9722        assert!(
9723            text.contains("global @limit : bytes 4, align 4, linkage(external), constant"),
9724            "{text}"
9725        );
9726    }
9727
9728    #[test]
9729    fn a_conditional_whose_value_is_an_object_answers_where_the_object_is() {
9730        // A structure is not a value in the IR, so the two arms cannot be joined as one. The
9731        // addresses can, and the answer is the address of whichever arm was taken rather than
9732        // a copy of it into a third place: both arms outlive the expression, so a copy would
9733        // be one nothing could observe. SQLite's parser writes one of these.
9734        let text = body(
9735            "\
9736struct s { int a, b; };
9737struct s pick(int c, struct s x, struct s y) { return c ? x : y; }
9738",
9739        );
9740        // The join takes an address, each arm hands it the one it has, and nothing is copied.
9741        assert!(text.contains("block3(%7: ptr)"), "{text}");
9742        assert!(text.contains("jump block3(%3)") && text.contains("jump block3(%4)"), "{text}");
9743        assert!(!text.contains("memcpy"), "the arms are joined rather than copied: {text}");
9744    }
9745
9746    /// GNU's `a ?: b` evaluates `a` once, and the arm answers the value that was tested.
9747    ///
9748    /// The checking keeps one node for `a` and converts it in two directions, to the bit the
9749    /// branch is taken on and to the type the whole expression has. Walking into the arm used to
9750    /// reach that node a second time and build a second copy of whatever it says, so `++i ?: 10`
9751    /// incremented twice and `f() ?: 10` called twice. Measured against gcc 16.2.0, which
9752    /// increments once.
9753    #[test]
9754    fn the_left_side_of_a_conditional_with_no_middle_is_evaluated_once() {
9755        let text = body("int f(int i) { return ++i ?: 10; }\n");
9756        assert!(text.contains("jump block3(%2)"), "the arm is the value that was tested: {text}");
9757        assert_eq!(text.matches("add.nsw").count(), 1, "incremented once: {text}");
9758
9759        // The arm still converts, since what the whole expression is worth is a `long` here and
9760        // the node under it is an `int`. What it converts is the value in hand.
9761        let text = body("long f(int i) { return ++i ?: 10L; }\n");
9762        assert!(text.contains("%5 = sext.i64 %2"), "the arm widens what was tested: {text}");
9763        assert_eq!(text.matches("add.nsw").count(), 1, "incremented once: {text}");
9764
9765        // A call, which is where evaluating twice is a wrong answer rather than a slow one.
9766        let text = body("int g(void);\nint f(void) { return g() ?: 10; }\n");
9767        assert_eq!(text.matches("call @g").count(), 1, "called once: {text}");
9768
9769        // Written out in full it is two reads of `i`, which is what C says it is, so the middle
9770        // operand being absent is the whole of the difference.
9771        let text = body("int f(int i) { return ++i ? ++i : 10; }\n");
9772        assert_eq!(text.matches("add.nsw").count(), 2, "incremented twice: {text}");
9773    }
9774
9775    #[test]
9776    fn a_structure_that_fits_in_registers_travels_as_the_registers_it_fits_in() {
9777        // `struct pair` is two eightbytes on SysV, one of them integer, so the signature says
9778        // one `i64` in each direction and the body takes the object apart and puts it back
9779        // together around the call.
9780        let text = ir("\
9781struct pair { int a, b; };
9782struct pair make(int a, int b);
9783struct pair twice(struct pair p) { return make(p.a, p.b); }
9784");
9785        assert!(text.contains("func @make(i32, i32) -> i64"), "{text}");
9786        assert!(text.contains("func @twice(i64) -> i64"), "{text}");
9787    }
9788
9789    #[test]
9790    fn a_structure_too_large_for_the_registers_travels_as_where_its_bytes_are() {
9791        // Over two eightbytes the caller passes the bytes in the argument area, which is
9792        // `byval`, and passes somewhere to write the return value, which is `sret`. Neither is
9793        // a parameter the program wrote and both are parameters the function has.
9794        let text = ir("\
9795struct big { double v[8]; };
9796struct big grow(struct big b);
9797struct big twice(struct big b) { return grow(grow(b)); }
9798");
9799        assert!(
9800            text.contains("func @grow(ptr sret(64, align 8), ptr byval(64, align 8))"),
9801            "{text}"
9802        );
9803        assert!(text.contains("block0(%0: ptr, %1: ptr):"), "{text}");
9804        // The inner call writes into a slot and the outer one reads the same slot, so the
9805        // object between the two calls is never copied anywhere.
9806        assert_eq!(text.matches("call @grow").count(), 2, "{text}");
9807    }
9808
9809    #[test]
9810    fn a_structure_passed_to_a_variadic_function_says_so_at_the_call() {
9811        // The bytes travel in the argument area the same way they would for a parameter, and
9812        // `printf` has no parameter there to say it on, so the call says it instead. The one
9813        // that fits in registers says nothing, because travelling as the registers it fits in
9814        // is what an argument does when nothing says otherwise.
9815        let text = ir("\
9816struct big { double v[8]; };
9817struct pair { int a, b; };
9818int p(const char *, ...);
9819int f(struct big b, struct pair q) { return p(\"\", 1, b, q); }
9820");
9821        assert!(
9822            text.contains("call @p(%4, %5, %2 byval(64, align 8), %6) : (ptr, ...) -> i32"),
9823            "{text}"
9824        );
9825    }
9826
9827    #[test]
9828    fn what_a_call_produced_is_somewhere_before_anything_is_read_out_of_it() {
9829        // `make(1, 2).b` has no object to read a member of until one is made, and what makes it
9830        // is a slot the returned registers are written to.
9831        let body = body(
9832            "\
9833struct pair { int a, b; };
9834struct pair make(int a, int b);
9835int second(void) { return make(1, 2).b; }
9836",
9837        );
9838        assert!(body.starts_with("block0:\n    %0 = alloca, size 8, align 4\n"), "{body}");
9839        assert!(body.contains("store %3 -> %0, align 4\n"), "{body}");
9840    }
9841
9842    #[test]
9843    fn a_structure_of_floats_travels_in_floating_point_registers_on_aarch64() {
9844        // The same declaration, classified by a different ABI: three `float` members are an
9845        // eightbyte of two of them and a half eightbyte of the third on SysV, and three vector
9846        // registers on AAPCS64.
9847        let source = "\
9848struct hfa { float x, y, z; };
9849int take(struct hfa h);
9850int give(struct hfa h) { return take(h); }
9851";
9852        assert!(ir(source).contains("func @take(f64, f32) -> i32"), "{}", ir(source));
9853        let mut opts = options();
9854        opts.emit = EmitKind::Ir;
9855        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
9856        let result = run(&opts, source);
9857        assert_eq!(result.messages, Vec::<String>::new());
9858        assert!(result.text().contains("func @take(f32, f32, f32) -> i32"), "{}", result.text());
9859    }
9860
9861    #[test]
9862    fn an_array_whose_length_is_not_a_constant_is_a_slot_made_where_its_declaration_is() {
9863        // The size is a multiplication rather than a number, the slot is taken from the stack
9864        // where the declaration is, and the scope it was declared in gives it back.
9865        let source = "\
9866int use(int *);
9867void f(int n) {
9868  {
9869    int a[n];
9870    use(a);
9871  }
9872  use(0);
9873}
9874";
9875        let body = body(source);
9876        assert!(body.contains("mul.nsw"), "{body}");
9877        assert!(body.contains("stacksave"), "{body}");
9878        assert!(body.contains("alloca %"), "{body}");
9879        assert!(body.contains("stackrestore"), "{body}");
9880    }
9881
9882    #[test]
9883    fn a_goto_out_of_the_scope_of_one_gives_its_stack_back_on_the_way() {
9884        // The label is outside the block the array is in, so arriving there means the array is
9885        // gone, and the restore that says so goes in front of the branch. The `goto` is written
9886        // before the walk knows where the label is, which is why the restore is put there at
9887        // the end rather than built where the branch was.
9888        let source = "\
9889int use(int *);
9890int f(int n) {
9891  {
9892    int a[n];
9893    if (use(a)) goto out;
9894    use(0);
9895  }
9896out:
9897  return 0;
9898}
9899";
9900        let body = body(source);
9901        // Two ways out of the block and a restore on each: the jump and the end of the block.
9902        assert_eq!(body.matches("stackrestore").count(), 2, "{body}");
9903        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
9904        assert!(after.starts_with(" %4\n    jump block"), "{body}");
9905    }
9906
9907    #[test]
9908    fn a_goto_to_a_label_the_array_is_still_alive_at_leaves_the_stack_alone() {
9909        // The label is after the declaration and in the same block, so control that arrives
9910        // there arrives somewhere the array exists. Giving it back would be giving back an
9911        // object the next statement reads.
9912        let source = "\
9913int use(int *);
9914int f(int n) {
9915  int a[n];
9916again:
9917  if (use(a)) goto again;
9918  return 0;
9919}
9920";
9921        let body = body(source);
9922        assert!(body.contains("stacksave"), "{body}");
9923        assert!(!body.contains("stackrestore"), "{body}");
9924    }
9925
9926    #[test]
9927    fn a_goto_back_to_a_label_in_front_of_one_gives_it_back_every_time_round() {
9928        // A loop written out of a `goto`, with the array made inside it. The label is in the
9929        // same block as the declaration and before it, which is a place where the array does
9930        // not exist yet, so the jump there leaves its scope and has to give the stack back. A
9931        // compiler that skips this restore grows the stack once per iteration.
9932        let source = "\
9933int use(int *);
9934int f(int n) {
9935again:
9936  {
9937    int a[n];
9938    if (use(a)) goto again;
9939  }
9940  return 0;
9941}
9942";
9943        let body = body(source);
9944        assert_eq!(body.matches("stacksave").count(), 1, "{body}");
9945        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
9946        assert!(after.starts_with(" %4\n    jump block1\n"), "{body}");
9947    }
9948
9949    #[test]
9950    fn the_head_of_a_for_loop_is_a_scope_that_closes_where_the_loop_is_left() {
9951        // The scope opened for `for (int a[n];;)` used to stay open, and a scope left open is
9952        // not one mark nobody reads. The marks are a stack, so the next close took this one
9953        // instead of its own, and the body of the loop gave back nothing while the block after
9954        // the loop restored a pointer saved inside it. The verifier refused that, which is how
9955        // it was found.
9956        let source = "\
9957int f(void);
9958void t(void) {
9959  int count = 10;
9960  for (; count--;) {
9961    int b[f()];
9962    int i;
9963    for (i = 0; i < f(); i++) {
9964      b[i] = count;
9965    }
9966  }
9967}
9968";
9969        let body = body(source);
9970        // One save, in the body, and one restore for it, also in the body: the block the
9971        // restore is in is the one the inner loop leaves through, and it goes back round the
9972        // outer loop rather than out of it.
9973        assert_eq!(body.matches("stacksave").count(), 1, "{body}");
9974        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
9975        // The rest of the block the restore is in, which is the last block here, so there is not
9976        // always another one after it to split on.
9977        let next = after.split("\n\n").next().expect("the block the restore is in");
9978        assert!(next.contains("jump block1("), "{body}");
9979    }
9980
9981    #[test]
9982    fn how_long_one_of_those_is_was_decided_where_it_was_declared_and_not_where_it_is_asked() {
9983        // What C says about the length being evaluated once: `sizeof a` after `n` changed is
9984        // still as long as the array is, which is what `n` was when the array came into being.
9985        let source = "\
9986unsigned long f(int n) {
9987  int a[n];
9988  n = 0;
9989  return sizeof a;
9990}
9991";
9992        let body = body(source);
9993        // One read of the parameter, at the declaration, and the answer is built out of it.
9994        assert_eq!(body.matches("sext.i64 %0").count(), 2, "{body}");
9995    }
9996
9997    #[test]
9998    fn a_block_in_the_middle_of_an_expression_is_walked_where_the_expression_is() {
9999        // GNU's statement expression: the statements happen where they are written and the last
10000        // one is the value, so the temporary in it never becomes a slot and never is copied.
10001        let source = "\
10002int use(int);
10003int f(int x) {
10004  return ({
10005    int t = use(x);
10006    t * t;
10007  });
10008}
10009";
10010        let expected = "\
10011block0(%0: i32):
10012    %1 = call @use(%0) : (i32) -> i32
10013    %2 = mul.nsw %1, %1
10014    return %2
10015";
10016        assert_eq!(body(source), expected);
10017    }
10018
10019    #[test]
10020    fn a_comma_whose_value_is_an_object_names_the_object_the_right_side_named() {
10021        // What janet writes, which is a call that does not return and then a value after it so
10022        // that the arm is worth something. The left side happens for what it did and the answer
10023        // is where the right side is, so there is nothing to copy and no temporary for a copy.
10024        let source = "\
10025struct pair { int a, b; };
10026void bail(void);
10027int f(struct pair p) {
10028  return (bail(), p).b;
10029}
10030";
10031        let expected = "\
10032block0(%0: i64):
10033    %1 = alloca, size 8, align 4
10034    store %0 -> %1, align 4
10035    call @bail() : ()
10036    %2 = iconst.i64 4
10037    %3 = ptr_add %1, %2
10038    %4 = load.i32 %3, align 4, tbaa !1
10039    return %4
10040";
10041        assert_eq!(body(source), expected);
10042    }
10043
10044    #[test]
10045    fn one_of_those_that_control_never_leaves_is_lowered_and_what_follows_it_is_dropped() {
10046        // A macro that always jumps, which is what this shape is in real code. The value is
10047        // never taken, and the block the rest of the expression would have been built in is
10048        // one nothing branches to, so it goes with the other unreachable blocks.
10049        let source = "int f(int x) { return ({ return x; 0; }); }\n";
10050        assert_eq!(body(source), "block0(%0: i32):\n    return %0\n");
10051    }
10052
10053    #[test]
10054    fn one_argument_off_a_variable_argument_list_stays_an_intrinsic() {
10055        // What it becomes is the target's answer, and this is not where the target's answers
10056        // are, so the walk writes down which list and which type and leaves it at that. Two of
10057        // them are two instructions, since each moves the list on.
10058        let source = "double f(__builtin_va_list ap) { return __builtin_va_arg(ap, double) + __builtin_va_arg(ap, double); }\n";
10059        let expected = "\
10060block0(%0: ptr):
10061    %1 = va_arg.f64 %0
10062    %2 = va_arg.f64 %0
10063    %3 = fadd %1, %2
10064    return %3
10065";
10066        assert_eq!(body(source), expected);
10067    }
10068
10069    #[test]
10070    fn one_that_reads_a_structure_answers_where_the_object_is() {
10071        // An aggregate is not a value, so there is nothing for the result of `va_arg` to be and
10072        // the object form is a second instruction. What it answers is an address, so it is a
10073        // place already and the walk copies nothing out of it: the copy here is the one the
10074        // initializer asks for, into the variable being declared. The size and the alignment
10075        // travel with it because they are what steps the list on and what a target that has to
10076        // put registers somewhere needs to know. So does the classification, which says the two
10077        // halves of this one arrived in general purpose registers: that is an answer about a C
10078        // type, and this is the last place that still has one.
10079        //
10080        // The slot is aligned to sixteen and the copy into it to eight, which is not a
10081        // disagreement. Sixteen is what a local aggregate of sixteen bytes gets whatever its
10082        // members ask for, and eight is what the type asks for and so what the copy may assume
10083        // about the object it is reading from.
10084        let source = "\
10085struct s { int a; long b; };
10086long f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.b; }
10087";
10088        let expected = "\
10089block0(%0: ptr):
10090    %1 = alloca, size 16, align 16
10091    %2 = va_object %0, size 16, align 8, in(int 8 at 0, int 8 at 8)
10092    memcpy %1, %2, size 16, align 8
10093    %3 = iconst.i64 8
10094    %4 = ptr_add %1, %3
10095    %5 = load.i64 %4, align 8, tbaa !1
10096    return %5
10097";
10098        assert_eq!(body(source), expected);
10099    }
10100
10101    /// Which register file each eightbyte arrived in is the whole of what the classification adds,
10102    /// and an object with no slots at all is one it sent to the caller's argument area, which is
10103    /// what everything over two eightbytes is whatever its members are.
10104    #[test]
10105    fn the_classification_says_which_registers_the_object_arrived_in() {
10106        let source = "\
10107struct s { double a; double b; };
10108double f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.a; }
10109";
10110        assert!(
10111            body(source)
10112                .contains("va_object %0, size 16, align 8, in(float f64 at 0, float f64 at 8)"),
10113            "{}",
10114            body(source)
10115        );
10116
10117        let big = "\
10118struct s { long a[4]; };
10119long f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.a[0]; }
10120";
10121        assert!(body(big).contains("va_object %0, size 32, align 8\n"), "{}", body(big));
10122    }
10123
10124    #[test]
10125    fn a_jump_to_an_address_branches_to_every_label_the_function_takes_the_address_of() {
10126        // GNU's computed goto. Which label the address holds is not known here, so all of them
10127        // are listed, and the values arriving at one are passed on every edge the same way they
10128        // are on an ordinary branch.
10129        let source = "\
10130int f(int c) {
10131  void *p = c ? &&one : &&two;
10132  goto *p;
10133one:
10134  return 1;
10135two:
10136  return 2;
10137}
10138";
10139        let expected = "\
10140block0(%0: i32):
10141    %1 = iconst.i32 0
10142    %2 = icmp ne %0, %1
10143    br_if %2, block1, block2
10144
10145block1:
10146    %3 = block_addr block3
10147    jump block4(%3)
10148
10149block2:
10150    %4 = block_addr block5
10151    jump block4(%4)
10152
10153block3:
10154    %5 = iconst.i32 1
10155    return %5
10156
10157block4(%6: ptr):
10158    indirect_br %6, block3, block5
10159
10160block5:
10161    %7 = iconst.i32 2
10162    return %7
10163";
10164        assert_eq!(body(source), expected);
10165    }
10166
10167    /// An interpreter, cut down to the shape that matters: a table of labels, a few values the
10168    /// loop keeps in hand, and a jump through the table at the end of every one of them.
10169    fn dispatch(labels: usize) -> String {
10170        let mask = labels - 1;
10171        let mut source = String::from("int spin(int n)\n{\n\tstatic void *table[] = {");
10172        for index in 0..labels {
10173            source.push_str(&format!(" &&a{index},"));
10174        }
10175        source.push_str(" };\n\tint w = n, x = n + 1, y = n + 2, z = n + 3;\n");
10176        source.push_str(&format!("\tif (n < 0) return 0;\n\tgoto *table[n & {mask}];\n"));
10177        for index in 0..labels {
10178            let step = match index % 4 {
10179                0 => "w += x;",
10180                1 => "x += y;",
10181                2 => "y += z;",
10182                _ => "z += w;",
10183            };
10184            source.push_str(&format!("a{index}:\n\t{step}\n"));
10185            source.push_str("\tif (--n <= 0) return w + x + y + z;\n");
10186            source.push_str(&format!("\tgoto *table[n & {mask}];\n"));
10187        }
10188        source.push_str("}\n");
10189        source
10190    }
10191
10192    /// How many moves are written in front of the first jump through a register.
10193    fn in_front_of_the_jump(text: &str) -> usize {
10194        let (before, _) = text.split_once("\tjmp\t*%").expect("a jump through a register");
10195        before.lines().rev().take_while(|line| line.starts_with("\tmov")).count()
10196    }
10197
10198    /// What a branch writes in front of its jump is what it carries, not what every label it can
10199    /// reach would like to be handed.
10200    ///
10201    /// A label an indirect branch reaches is given its values in registers the branch writes
10202    /// before it goes, because the moves cannot go after a jump and cannot go across the register
10203    /// the jump reads. Writing a register for each parameter of each label costs the table's
10204    /// length on every dispatch, which is a few moves in a program with two labels and five
10205    /// hundred in an interpreter with seventy. The values are the same values, so the registers
10206    /// are the same registers, and the cost stays where the number of values puts it.
10207    #[test]
10208    fn a_jump_through_a_register_writes_what_it_carries_and_not_the_whole_table() {
10209        let small = in_front_of_the_jump(&asm(&dispatch(4)));
10210        let large = in_front_of_the_jump(&asm(&dispatch(32)));
10211        assert_eq!(small, large, "eight times the labels and the same values in hand");
10212        assert!(large <= 8, "the values the loop keeps, and not a set of them per label: {large}");
10213    }
10214
10215    /// The same interpreter with more values in hand than there are registers, which is what makes
10216    /// the allocator send some of them to the stack at every label.
10217    fn crowded(labels: usize) -> String {
10218        const VALUES: usize = 24;
10219        let mask = labels - 1;
10220        let mut source = String::from("int spin(int n)\n{\n\tstatic void *table[] = {");
10221        for index in 0..labels {
10222            source.push_str(&format!(" &&a{index},"));
10223        }
10224        source.push_str(" };\n\t");
10225        for value in 0..VALUES {
10226            source.push_str(&format!("int v{value} = n + {value}; "));
10227        }
10228        let sum: Vec<String> = (0..VALUES).map(|value| format!("v{value}")).collect();
10229        source.push_str(&format!("\n\tif (n < 0) return 0;\n\tgoto *table[n & {mask}];\n"));
10230        for index in 0..labels {
10231            let (to, from) = (index % VALUES, (index + 1) % VALUES);
10232            source.push_str(&format!("a{index}:\n\tv{to} += v{from};\n"));
10233            source.push_str(&format!("\tif (--n <= 0) return {};\n", sum.join(" + ")));
10234            source.push_str(&format!("\tgoto *table[n & {mask}];\n"));
10235        }
10236        source.push_str("}\n");
10237        source
10238    }
10239
10240    /// How many bytes of frame the first function in a listing opens.
10241    fn the_frame(text: &str) -> u64 {
10242        text.lines()
10243            .find_map(|line| {
10244                let (size, _) = line.strip_prefix("\tsubq\t$")?.split_once(", %rsp")?;
10245                size.parse().ok()
10246            })
10247            .expect("a function that opens a frame")
10248    }
10249
10250    /// A frame holds what a function wants at once, and an interpreter does not want the whole
10251    /// table at once.
10252    ///
10253    /// Every label a dispatch table reaches is handed the values the loop keeps, and what the
10254    /// allocator has no register for goes on the stack. They are the same few values one label at
10255    /// a time, so they are the same bytes. A slot each put forty kilobytes on the frame of lua's
10256    /// interpreter and ran the C stack out at a depth lua's own limit was supposed to catch,
10257    /// which is tamnd/rucc#1630.
10258    #[test]
10259    fn a_frame_holds_what_is_wanted_at_once_and_not_a_slot_for_every_label() {
10260        let small = the_frame(&asm(&crowded(16)));
10261        let large = the_frame(&asm(&crowded(64)));
10262        assert_eq!(small, large, "four times the labels and the same values: {small}, {large}");
10263    }
10264
10265    /// A template that saves the callee-saved registers by name, which is micropython's non local
10266    /// return and is tamnd/rucc#1583.
10267    ///
10268    /// Every register in it is one the template named rather than one the statement handed over,
10269    /// because the buffer is defined as holding those registers and there is no constraint letter
10270    /// that means `%rsp`. The instructions come out naming what the program named, and the
10271    /// allocator, which was told about the writes rather than left to find out, saves the ones the
10272    /// calling convention says belong to whoever called.
10273    #[test]
10274    fn a_template_that_names_its_own_registers_gets_the_ones_it_named() {
10275        let source = "void save(void *nlr) {
10276    __asm volatile (
10277        \"movq   %%rsp, 32(%%rdi)   \\n\"
10278        \"movq   %%rbx, 40(%%rdi)   \\n\"
10279        \"movq   %%r12, 48(%%rdi)   \\n\"
10280        : : \"D\" (nlr) : \"memory\");
10281}
10282";
10283        let text = asm(source);
10284        assert!(text.contains("\tmovq\t%rsp, 32(%rdi)\n"), "{text}");
10285        assert!(text.contains("\tmovq\t%rbx, 40(%rdi)\n"), "{text}");
10286        assert!(text.contains("\tmovq\t%r12, 48(%rdi)\n"), "{text}");
10287    }
10288
10289    #[test]
10290    fn a_jump_to_an_address_no_label_in_the_function_has_arrives_nowhere() {
10291        // The address came from outside the function, and a jump to a label in another function
10292        // is undefined. The expression is still evaluated, since a call in it has to happen.
10293        let source = "void **next(void);
10294void f(void) { goto *next(); }
10295";
10296        let expected = "\
10297block0:
10298    %0 = call @next() : () -> ptr
10299    unreachable
10300";
10301        assert_eq!(body(source), expected);
10302    }
10303
10304    #[test]
10305    fn an_asm_with_no_operands_is_volatile_and_the_clobbers_are_the_whole_of_what_it_says() {
10306        // Nothing reads a result, so the only thing that keeps it is that it is volatile, which
10307        // a basic asm implies.
10308        let source = "void f(void) { __asm__(\"mfence\" ::: \"memory\"); }\n";
10309        let expected = "\
10310block0:
10311    inline_asm.volatile \"mfence\", \"\", \"memory\"()
10312    return
10313";
10314        assert_eq!(body(source), expected);
10315    }
10316
10317    #[test]
10318    fn the_constraints_are_one_list_in_the_order_the_template_counts_the_operands() {
10319        // The outputs first and then the inputs, which is the numbering `%0` and `%1` use. An
10320        // output in a register is a result, and one that is read as well is an argument too.
10321        let source = "\
10322int f(int x, int y) {
10323  int r;
10324  __asm__(\"addl %2, %0\" : \"=r\"(r), \"+r\"(y) : \"r\"(x));
10325  return r + y;
10326}
10327";
10328        let expected = "\
10329block0(%0: i32, %1: i32):
10330    %2, %3 = inline_asm.(i32, i32) \"addl %2, %0\", \"=r,+r,r\", \"\"(%1, %0)
10331    %4 = add.nsw %2, %3
10332    return %4
10333";
10334        assert_eq!(body(source), expected);
10335    }
10336
10337    #[test]
10338    fn a_memory_operand_travels_as_the_address_of_an_object_that_is_given_a_slot() {
10339        // The assembly is handed a pointer, so the object cannot live in a value, and the scan
10340        // that runs before the walk has to have known that or there would be nothing to point
10341        // at. A structure travels this way whatever else its constraint allows, since there is
10342        // no register that holds one.
10343        let source = "\
10344struct pair { int a, b; };
10345int f(int x) {
10346  int slot = x;
10347  struct pair p = { x, x };
10348  __asm__(\"incl %0\" : \"+m\"(slot), \"=m\"(p));
10349  return slot + p.a;
10350}
10351";
10352        let text = body(source);
10353        assert!(text.contains("inline_asm \"incl %0\", \"+m,=m\", \"\"(%1, %2)\n"), "{text}");
10354        assert!(text.contains("%1 = alloca, size 4, align 4\n"), "{text}");
10355        assert!(text.contains("%2 = alloca, size 8, align 4\n"), "{text}");
10356    }
10357
10358    #[test]
10359    fn an_asm_goto_falls_through_to_its_first_target_and_writes_its_outputs_there() {
10360        // The output is only in scope where the instruction dominates, which is the fall through
10361        // block, so the edge to the label carries the value the object had before the assembly
10362        // ran. That is what document 11 asks for and it is what putting the fall through first
10363        // buys.
10364        let source = "\
10365int f(int x) {
10366  int r = 7;
10367  __asm__ goto(\"cbnz %0, %l1\" : \"=r\"(r) : \"r\"(x) :: away);
10368  return r;
10369away:
10370  return r;
10371}
10372";
10373        let expected = "\
10374block0(%0: i32):
10375    %1 = iconst.i32 7
10376    %2 = inline_asm.volatile \"cbnz %0, %l1\", \"=r,r\", \"\"(%0), labels [block1, block2]
10377
10378block1:
10379    return %2
10380
10381block2:
10382    return %1
10383";
10384        assert_eq!(body(source), expected);
10385    }
10386
10387    #[test]
10388    fn an_asm_statement_that_is_not_well_formed_is_reported_in_the_words_gcc_uses() {
10389        // The operands are checked here rather than by the assembler, because by the time the
10390        // assembler sees the template the operands have become registers and it has nothing left
10391        // to say about the C that named them.
10392        let mut opts = options();
10393        opts.emit = EmitKind::Ir;
10394        for (source, expected) in [
10395            (
10396                "void f(int x) { __asm__(\"\" : \"r\"(x)); }\n",
10397                "output operand constraint lacks '='",
10398            ),
10399            (
10400                "void f(int x) { __asm__(\"\" : \"=r\"(x + 1)); }\n",
10401                "lvalue required in 'asm' statement",
10402            ),
10403            (
10404                "const int g = 1;\nvoid f(void) { __asm__(\"\" : \"=r\"(g)); }\n",
10405                "read-only variable 'g' used as 'asm' output",
10406            ),
10407            (
10408                "void f(int x) { __asm__(\"\" : : \"=r\"(x)); }\n",
10409                "input operand constraint contains '='",
10410            ),
10411            (
10412                "void f(void) { __asm__(\"\" : : \"m\"(1)); }\n",
10413                "memory input 0 is not directly addressable",
10414            ),
10415            ("void f(void) { __asm__(L\"\"); }\n", "wide string literal in 'asm'"),
10416            (
10417                "void f(int x, int y) { __asm__(\"\" : [a] \"=r\"(x) : [a] \"r\"(y)); }\n",
10418                "duplicate asm operand name 'a'",
10419            ),
10420            ("void f(int x) { __asm__(\"%[in]\" : \"=r\"(x)); }\n", "undefined named operand 'in'"),
10421        ] {
10422            let result = run(&opts, source);
10423            assert!(result.failed(), "expected this to be reported:\n{source}");
10424            assert!(
10425                result.messages.iter().any(|m| m.contains(expected)),
10426                "{expected}\n{:?}",
10427                result.messages
10428            );
10429        }
10430    }
10431
10432    /// An `asm` at file scope whose template is directives is the whole of what the incbin
10433    /// header, an alias table and a hand written jump table each write, and what it says is a
10434    /// section holding named bytes. So it becomes the globals it names, in the order it names
10435    /// them, which is what `spec/11-asm-objects-debug.md` section 11.2 asks for.
10436    #[test]
10437    fn an_asm_at_file_scope_that_is_directives_becomes_the_objects_it_defines() {
10438        let text = ir(concat!(
10439            "__asm__(\n",
10440            "  \".section .rodata\\n\"\n",
10441            "  \".globl first\\n\"\n",
10442            "  \".balign 8\\n\"\n",
10443            "  \"first:\\n\"\n",
10444            "  \".long 1\\n\"\n",
10445            "  \".long 2\\n\"\n",
10446            "  \".globl last\\n\"\n",
10447            "  \"last:\\n\"\n",
10448            "  \".quad last - first\\n\");\n",
10449            "extern const int first[];\n",
10450            "extern const long last;\n",
10451        ));
10452        assert!(text.contains("global @first : bytes 8 = { i32 1, i32 2 }, align 8"), "{text}");
10453        assert!(text.contains("global @last : i64 = 8"), "{text}");
10454    }
10455
10456    /// The distance between two labels is what the incbin header hands a program as the size of
10457    /// the data, so a declaration of one of the names has to find the definition the template
10458    /// made rather than turn it back into something the linker is asked for.
10459    #[test]
10460    fn a_name_an_asm_at_file_scope_defined_is_not_undone_by_a_declaration_of_it() {
10461        let text = ir(concat!(
10462            "__asm__(\".data\\n.globl counter\\ncounter:\\n.long 7\\n\");\n",
10463            "extern int counter;\n",
10464            "int read(void) { return counter; }\n",
10465        ));
10466        assert!(text.contains("global @counter : i32 = 7"), "{text}");
10467    }
10468
10469    /// Bytes written before any label are a global with a name minted for them, in front of the
10470    /// label written under them, which is what makes the first byte of the name the one written
10471    /// under it. The block is the one tcc's test file writes, without the line of it that measures
10472    /// from one section to another.
10473    #[test]
10474    fn bytes_under_no_label_at_file_scope_are_a_global_in_front_of_the_label() {
10475        let text = ir(concat!(
10476            "__asm__(\".data\\n.byte 41\\nstuff:\\n661:\\n.byte 42\\n662:\\n",
10477            ".pushsection .data.ignore\\n.byte 7\\n.popsection\\n.byte 662b - 661b\\n\");\n",
10478            "extern unsigned char stuff[];\n",
10479            "int read(void) { return stuff[0]; }\n",
10480        ));
10481        let under = text.find("global @.Lasm.0 : i8 = 41").expect(&text);
10482        let named = text.find("global @stuff : i8 = 42").expect(&text);
10483        assert!(under < named, "the bytes under no label come first: {text}");
10484        assert!(text.contains("global @.Lasm.1 : i8 = 7, align 1, linkage(internal), section"));
10485        // The byte after the pop is a run of its own, because coming back to a section finishes
10486        // what was being written to it the way a label does. It is the next global of that
10487        // section all the same, so the byte lands where the template put it, which is the one
10488        // after the byte under `stuff`.
10489        let after = text.find("global @.Lasm.2 : i8 = 1").expect(&text);
10490        assert!(named < after, "{text}");
10491    }
10492
10493    /// How far a place is from the bytes holding the answer, which is what tcc's test file writes
10494    /// last and what the alternative instruction tables in a kernel header are made of. It is the
10495    /// linker's answer rather than the compiler's, because the two sections are placed by the
10496    /// linker, so the image holds a hole and a name for it.
10497    #[test]
10498    fn a_distance_from_here_at_file_scope_is_a_hole_naming_the_global_it_measures_to() {
10499        let text = ir(concat!(
10500            "__asm__(\".data\\n.byte 41\\nstuff:\\n661:\\n.byte 42\\n",
10501            ".pushsection .data.ignore\\n.long 661b - .\\n.popsection\\n\");\n",
10502            "extern unsigned char stuff[];\n",
10503            "int read(void) { return stuff[0]; }\n",
10504        ));
10505        // The label the template measured to is a local one and no symbol, so what the hole names
10506        // is the global it stands inside, which is the byte under `stuff`, and nothing further on
10507        // since it is the first byte of it.
10508        assert!(text.contains("global @.Lasm.1 : bytes 4 = { away.4 @stuff }"), "{text}");
10509    }
10510
10511    /// A `.set` says one name stands for another, which is a second symbol at the first one's
10512    /// address and is an alias and nothing else. What the directives around it said about the
10513    /// name is what the name gets, and a name the file defines itself keeps its own definition,
10514    /// which is what gcc's symbol table shows for the block tcc's test file writes.
10515    #[test]
10516    fn a_set_at_file_scope_is_a_second_name_for_what_it_names() {
10517        let text = ir(concat!(
10518            "void base(void) {}\n",
10519            "__asm__(\".weak one\\n.set one, base\");\n",
10520            "__asm__(\".globl two\\n.set two, base\");\n",
10521            "__asm__(\".set three, base\");\n",
10522            "void three(void) {}\n",
10523        ));
10524        assert!(text.contains("alias @one = @base, linkage(weak)"), "{text}");
10525        assert!(text.contains("alias @two = @base"), "{text}");
10526        assert!(!text.contains("alias @three"), "a definition of the name wins: {text}");
10527        assert!(text.contains("func @three"), "{text}");
10528    }
10529
10530    /// The target has to be something this file defines, because an alias is a symbol at an
10531    /// address in this object and a name only declared here has none to be at. The same rule and
10532    /// the same words as for `__attribute__((alias))`, since it is the same thing written another
10533    /// way.
10534    #[test]
10535    fn a_set_of_a_name_this_file_does_not_define_says_so() {
10536        let messages = errors("__asm__(\".set here, elsewhere\");\n");
10537        assert!(
10538            messages
10539                .iter()
10540                .any(|m| m.contains("'here' is aliased to undefined symbol 'elsewhere'")
10541                    && m.contains("E0697")),
10542            "{messages:?}"
10543        );
10544    }
10545
10546    /// `.incbin` is the one directive that reads something, and what it reads comes through the
10547    /// same file system the sources did.
10548    #[test]
10549    fn an_incbin_at_file_scope_is_the_bytes_of_the_file_it_names() {
10550        let mut opts = options();
10551        opts.emit = EmitKind::Ir;
10552        let mut fs = MemoryFileSystem::new();
10553        fs.insert(
10554            "/main.c",
10555            b"__asm__(\".data\\n.globl blob\\nblob:\\n.incbin \\\"seed\\\"\\n\");\n".to_vec(),
10556        );
10557        fs.insert("seed", b"hi".to_vec());
10558        let result = compile(&opts, "/main.c", &fs);
10559        assert_eq!(result.messages, Vec::<String>::new());
10560        let text = result.text();
10561        assert!(text.contains("global @blob : bytes 2 = { bytes \"hi\" }"), "{text}");
10562    }
10563
10564    /// A file that is not there is the mistake a build makes when it runs the compiler from the
10565    /// wrong directory, and it is worth saying which file rather than saying the template failed.
10566    #[test]
10567    fn an_incbin_naming_a_file_that_is_not_there_says_which_file() {
10568        let messages = errors("__asm__(\".data\\nb:\\n.incbin \\\"nowhere\\\"\\n\");\n");
10569        assert!(
10570            messages
10571                .iter()
10572                .any(|m| m.contains("cannot open 'nowhere' for reading") && m.contains("E0702")),
10573            "{messages:?}"
10574        );
10575    }
10576
10577    /// A template of directives the reader does not take is refused by name rather than dropped.
10578    /// One with an instruction in it goes to the assembler instead, which
10579    /// `an_asm_at_file_scope_with_an_instruction_in_it_is_assembled` covers.
10580    #[test]
10581    fn a_directive_in_an_asm_at_file_scope_is_refused_rather_than_ignored() {
10582        let source = "__asm__(\".data\\n.set alias, 4\\n\");\n";
10583        let messages = errors(source);
10584        assert!(
10585            messages
10586                .iter()
10587                .any(|m| m.contains("not supported yet") && m.contains("in an `asm` at file scope")),
10588            "{source}\n{messages:?}"
10589        );
10590    }
10591
10592    /// micropython's `nlr_push`, which is the program that asks for all of this. The body is the
10593    /// whole of the function: the return address is read out of `(%rsp)` where the call left it,
10594    /// the registers the convention preserves are saved by hand, and the frame that was just built
10595    /// is handed to a function written in C that never comes back.
10596    ///
10597    /// What is checked is what gcc writes for the same file. No prologue in front of the saves,
10598    /// since a push would move the return address the first of them reads. No epilogue and no
10599    /// `ret`, since the jump is where the function ends. And a `ud2` behind the jump, which is
10600    /// where control arrives if the jump is ever not taken and is exactly what gcc puts there.
10601    #[test]
10602    fn a_naked_function_is_its_own_prologue_and_its_own_ending() {
10603        let text = asm(concat!(
10604            "unsigned nlr_push_tail(void *nlr);\n",
10605            "__attribute__((naked)) unsigned nlr_push(void *nlr) {\n",
10606            "  __asm volatile(\n",
10607            "    \"movq (%rsp), %rax\\n\"\n",
10608            "    \"movq %rax, 16(%rdi)\\n\"\n",
10609            "    \"movq %rbx, 40(%rdi)\\n\"\n",
10610            "    \"jmp nlr_push_tail\\n\");\n",
10611            "}\n",
10612        ));
10613        assert!(text.contains("\tmovq\t(%rsp), %rax\n"), "{text}");
10614        assert!(text.contains("\tjmp\tnlr_push_tail\n"), "{text}");
10615        assert!(text.contains("\tud2\n"), "{text}");
10616        assert!(!text.contains("\tpushq\t"), "nothing is saved in front of it: {text}");
10617        assert!(!text.contains("\tret\n"), "the jump is where it ends: {text}");
10618    }
10619
10620    /// The three things a naked function may not ask for, each of which is a frame nothing sets up
10621    /// or a jump over an epilogue there is one of.
10622    #[test]
10623    fn what_a_function_without_a_prologue_cannot_be_given_is_refused() {
10624        let mut opts = options();
10625        opts.emit = EmitKind::Asm;
10626        for (source, why) in [
10627            (
10628                "__attribute__((naked)) void f(void) { volatile long a[8]; a[0] = 1; }\n",
10629                "bytes of frame",
10630            ),
10631            (
10632                "__attribute__((naked)) void f(int n) { char a[n]; __asm(\"nop\" ::\"r\"(a)); }\n",
10633                "has no prologue to point a frame pointer at it with",
10634            ),
10635            ("void elsewhere(void); void f(void) { __asm(\"jmp elsewhere\"); }\n", "jumps out of"),
10636        ] {
10637            let result = run(&opts, source);
10638            assert!(result.failed(), "expected this to be refused:\n{source}");
10639            assert!(
10640                result.messages.iter().any(|message| message.contains(why)),
10641                "{:?}",
10642                result.messages
10643            );
10644        }
10645    }
10646
10647    #[test]
10648    fn what_the_walk_cannot_build_yet_is_reported_rather_than_mislowered() {
10649        let mut opts = options();
10650        opts.emit = EmitKind::Ir;
10651        for source in [
10652            "int f(int n) { void *p = &&out; if (n) goto *p; { int a[n]; out: return 1; } }\n",
10653            "int f(int n) { int a[n]; __asm__ goto(\"\" ::::out); out: return a[0]; }\n",
10654        ] {
10655            let result = run(&opts, source);
10656            assert!(result.failed(), "expected this to be reported:\n{source}");
10657            assert!(
10658                result.messages.iter().any(|m| m.contains("not supported yet")),
10659                "{:?}",
10660                result.messages
10661            );
10662        }
10663    }
10664
10665    /// Compiles `source` to IR, reads that back as an input, and gives back both texts.
10666    fn round_trip(source: &str) -> (String, String) {
10667        let printed = ir(source);
10668        let mut opts = options();
10669        opts.emit = EmitKind::Ir;
10670        let mut fs = MemoryFileSystem::new();
10671        fs.insert("/main.ir", printed.clone().into_bytes());
10672        let result = compile_ir(&opts, "/main.ir", &fs);
10673        assert_eq!(result.messages, Vec::<String>::new(), "expected this to read back:\n{printed}");
10674        (printed, result.text().to_owned())
10675    }
10676
10677    #[test]
10678    fn ir_that_arrives_as_an_input_is_read_back_and_written_out_the_same() {
10679        // The other half of the round trip test below, through the driver rather than through
10680        // the library, which is what makes the property something to run over a real program
10681        // rather than over the modules a test builds.
10682        let (printed, again) = round_trip(
10683            "struct point { int x, y; };\n             static const char greeting[] = \"hi\";\n             int puts(const char *);\n             int f(int n) { struct point p = { n, 1 }; puts(greeting); return p.x; }\n",
10684        );
10685        assert_eq!(printed, again);
10686    }
10687
10688    #[test]
10689    fn ir_that_is_not_ir_says_which_line_stopped_it() {
10690        let mut opts = options();
10691        opts.emit = EmitKind::Ir;
10692        let mut fs = MemoryFileSystem::new();
10693        let text = "\
10694; ModuleID = 'a.c'
10695; format 0
10696target triple = \"x86_64-unknown-linux-gnu\"
10697target datalayout = \"e-p:64:64-i64:64-S128\"
10698
10699func @f(), linkage(external) {
10700block0:
10701    frobnicate
10702}
10703";
10704        fs.insert("/main.ir", text.as_bytes().to_vec());
10705        let result = compile_ir(&opts, "/main.ir", &fs);
10706        assert!(result.failed());
10707        assert!(result.messages[0].contains("/main.ir:8"), "{:?}", result.messages);
10708    }
10709
10710    #[test]
10711    fn ir_that_reads_but_does_not_hold_together_is_reported_by_the_verifier() {
10712        // A module that a person edited has not been through the verifier, and the return of
10713        // an `i32` from a function that returns nothing is the kind of thing editing produces.
10714        let mut opts = options();
10715        opts.emit = EmitKind::Ir;
10716        let mut fs = MemoryFileSystem::new();
10717        let text = "\
10718; ModuleID = 'a.c'
10719; format 0
10720target triple = \"x86_64-unknown-linux-gnu\"
10721target datalayout = \"e-p:64:64-i64:64-S128\"
10722
10723func @f(), linkage(external) {
10724block0:
10725    %0 = iconst.i32 1
10726    return %0
10727}
10728";
10729        fs.insert("/main.ir", text.as_bytes().to_vec());
10730        let result = compile_ir(&opts, "/main.ir", &fs);
10731        assert!(result.failed());
10732        assert!(result.messages[0].contains("invalid IR"), "{:?}", result.messages);
10733    }
10734
10735    #[test]
10736    fn a_typed_tree_is_not_something_an_input_of_ir_can_produce() {
10737        // The C that became this is not here any more, so there is nothing to print a tree of.
10738        let mut fs = MemoryFileSystem::new();
10739        fs.insert("/main.ir", Vec::new());
10740        let result = compile_ir(&options(), "/main.ir", &fs);
10741        assert!(result.failed());
10742        assert!(result.messages[0].contains("can only be emitted as IR"), "{:?}", result.messages);
10743    }
10744
10745    #[test]
10746    fn the_printed_ir_reads_back_as_the_same_module() {
10747        // The M2 exit criterion: the text is the module and nothing about it is lost by
10748        // writing it down. Anything the printer invents or the parser drops shows up here.
10749        let text = ir("\
10750struct point { int x, y; };
10751static const char greeting[] = \"hi\";
10752int table[4] = { 1, 2, 3 };
10753int puts(const char *);
10754double half(double x) { return x / 2.0; }
10755int f(int n) {
10756  int total = 0;
10757  for (int i = 0; i < n; i++) {
10758    if (i == 3) continue;
10759    total += table[i];
10760  }
10761  switch (n) {
10762    case 0: total = 1;
10763    case 1: total++; break;
10764    default: total = -total;
10765  }
10766  struct point p = { total, 1 };
10767  int *q = &p.y;
10768  puts(greeting);
10769  return p.x + *q;
10770}
10771int dispatch(int c) {
10772  void *p = c ? &&one : &&two;
10773  goto *p;
10774one:
10775  return 1;
10776two:
10777  return 2;
10778}
10779int assembly(int x, int *p) {
10780  int r;
10781  __asm__ volatile(\"xadd %0, %2\" : \"=r\"(r), \"+m\"(*p) : \"0\"(x) : \"cc\");
10782  __asm__ goto(\"cbnz %0, %l1\" : : \"r\"(r) : : away);
10783  return r;
10784away:
10785  return 0;
10786}
10787");
10788        let mut names = Interner::new();
10789        let module = rucc_ir::parse(&text, &mut names).expect("the printer writes what it reads");
10790        assert_eq!(rucc_ir::print(&module, &names), text);
10791    }
10792
10793    #[test]
10794    fn what_save_temps_keeps_is_the_text_that_was_compiled_and_the_assembly_that_was_assembled() {
10795        // The point of the flag is that these two are the compilation rather than a description
10796        // of one, so both come out of the run that produced the object rather than out of a
10797        // second run under different flags.
10798        let mut opts = options();
10799        opts.emit = EmitKind::Object;
10800        opts.save_temps = rucc_session::SaveTemps::Object;
10801        let result = run(&opts, "#define N 2\nint a[N];\n");
10802        assert_eq!(result.messages, Vec::<String>::new());
10803        let text = result.temps.preprocessed.expect("the preprocessed text");
10804        assert!(text.contains("int a[2];"), "{text}");
10805        assert!(text.starts_with("# 1 \"/main.c\""), "{text}");
10806        let asm = result.temps.assembly.expect("the assembly");
10807        assert!(asm.contains("a:"), "{asm}");
10808        assert!(matches!(result.artifact, Artifact::Object { .. }), "{:?}", result.artifact);
10809    }
10810
10811    #[test]
10812    fn nothing_is_kept_unless_the_flag_asked_for_it() {
10813        // A compilation that was not asked to keep anything must not pay for printing text
10814        // nobody will read, and the empty value is what says so.
10815        let mut opts = options();
10816        opts.emit = EmitKind::Object;
10817        assert_eq!(run(&opts, "int a;\n").temps, Temps::default());
10818    }
10819
10820    #[test]
10821    fn a_compilation_that_stops_before_the_back_end_keeps_the_text_and_no_assembly() {
10822        // `--emit=ir` never produces any, and the text is worth keeping all the same: it is
10823        // what a report about the file being read wrongly has to have in it.
10824        let mut opts = options();
10825        opts.emit = EmitKind::Ir;
10826        opts.save_temps = rucc_session::SaveTemps::Cwd;
10827        let result = run(&opts, "int a;\n");
10828        assert!(result.temps.preprocessed.is_some());
10829        assert_eq!(result.temps.assembly, None);
10830    }
10831
10832    /// A stretch of a local's life, written short because these tests are about nothing else.
10833    fn span(from: u64, len: u64, held: rucc_debug::Held) -> rucc_debug::Span {
10834        rucc_debug::Span { from, len, held }
10835    }
10836
10837    #[test]
10838    fn two_stretches_that_meet_and_agree_come_out_as_one() {
10839        let one = span(0, 4, rucc_debug::Held::Reg(3));
10840        let two = span(4, 4, rucc_debug::Held::Reg(3));
10841        assert_eq!(settle(vec![two, one]), vec![span(0, 8, rucc_debug::Held::Reg(3))]);
10842    }
10843
10844    #[test]
10845    fn a_stretch_another_starts_inside_and_disagrees_with_ends_where_the_other_starts() {
10846        let one = span(0, 8, rucc_debug::Held::Reg(3));
10847        let two = span(4, 8, rucc_debug::Held::Reg(4));
10848        // The second starts where the declaration was given its value, so from there it is the
10849        // second and not the first.
10850        let settled = settle(vec![one, two]);
10851        assert_eq!(
10852            settled,
10853            vec![span(0, 4, rucc_debug::Held::Reg(3)), span(4, 8, rucc_debug::Held::Reg(4))]
10854        );
10855    }
10856
10857    #[test]
10858    fn a_stretch_cut_by_one_that_ends_first_does_not_come_back_after_it() {
10859        // The old value is still live after the new one is done with, because something else
10860        // reads it, but the declaration stopped holding it where the new one started.
10861        let one = span(0, 16, rucc_debug::Held::Reg(3));
10862        let two = span(4, 4, rucc_debug::Held::Reg(4));
10863        assert_eq!(
10864            settle(vec![one, two]),
10865            vec![span(0, 4, rucc_debug::Held::Reg(3)), span(4, 4, rucc_debug::Held::Reg(4))]
10866        );
10867    }
10868
10869    #[test]
10870    fn a_stretch_inside_another_that_agrees_with_it_cuts_nothing() {
10871        let one = span(0, 16, rucc_debug::Held::Reg(3));
10872        let two = span(4, 4, rucc_debug::Held::Reg(3));
10873        assert_eq!(settle(vec![one, two]), vec![span(0, 16, rucc_debug::Held::Reg(3))]);
10874    }
10875
10876    #[test]
10877    fn a_stretch_two_others_disagree_over_the_whole_of_says_nothing_at_all() {
10878        let one = span(0, 8, rucc_debug::Held::Reg(3));
10879        let two = span(0, 8, rucc_debug::Held::Frame(-16));
10880        assert_eq!(settle(vec![one, two]), Vec::new());
10881    }
10882
10883    #[test]
10884    fn stretches_with_a_gap_between_them_keep_the_gap() {
10885        let one = span(0, 4, rucc_debug::Held::Reg(3));
10886        let two = span(16, 4, rucc_debug::Held::Reg(3));
10887        assert_eq!(settle(vec![one, two]), vec![one, two]);
10888    }
10889
10890    /// A function of `len` bytes, since that is the only thing about one these tests look at.
10891    fn extent(len: usize) -> rucc_object::Extent {
10892        rucc_object::Extent {
10893            name: "f".to_owned(),
10894            start: 0,
10895            len,
10896            align: 1,
10897            binding: rucc_object::Binding::Global,
10898            visibility: rucc_object::Visibility::Default,
10899            patch: None,
10900            landings: Vec::new(),
10901        }
10902    }
10903
10904    /// A line table row at `at` built for the source bytes `lo` to `hi`.
10905    fn row(at: usize, lo: u32, hi: u32) -> rucc_asm::Row {
10906        let span = Span::new(lo, hi);
10907        rucc_asm::Row { at, span, inst: None }
10908    }
10909
10910    #[test]
10911    fn a_row_ends_where_the_next_address_begins() {
10912        let rows = [row(0, 0, 1), row(4, 1, 2), row(10, 2, 3)];
10913        assert_eq!(ends(&extent(16), &rows), vec![4, 10, 16]);
10914    }
10915
10916    #[test]
10917    fn rows_sharing_an_address_all_end_where_the_next_address_begins() {
10918        // Two instructions that encoded to nothing sit on the address of the one after them, and
10919        // none of the three ends in front of that one.
10920        let rows = [row(0, 0, 1), row(4, 1, 2), row(4, 2, 3), row(4, 3, 4)];
10921        assert_eq!(ends(&extent(12), &rows), vec![4, 12, 12, 12]);
10922    }
10923
10924    #[test]
10925    fn the_rows_of_a_scope_that_are_next_to_each_other_come_out_as_one_stretch() {
10926        let rows = [row(0, 0, 4), row(4, 10, 14), row(8, 14, 18), row(12, 40, 44)];
10927        let ends = ends(&extent(16), &rows);
10928        let scope = Span::new(8, 20);
10929        assert_eq!(spread(scope, &ends, &rows), vec![rucc_debug::Reach { from: 4, len: 8 }]);
10930    }
10931
10932    #[test]
10933    fn a_scope_the_back_end_split_in_two_comes_out_as_two_stretches() {
10934        let rows = [row(0, 10, 14), row(4, 40, 44), row(8, 14, 18)];
10935        let ends = ends(&extent(12), &rows);
10936        let scope = Span::new(8, 20);
10937        let over = spread(scope, &ends, &rows);
10938        assert_eq!(
10939            over,
10940            vec![rucc_debug::Reach { from: 0, len: 4 }, rucc_debug::Reach { from: 8, len: 4 }]
10941        );
10942    }
10943
10944    #[test]
10945    fn a_row_with_no_source_of_its_own_belongs_to_no_scope() {
10946        // The prologue is the one of these every function has, and it is not inside any block.
10947        let rows = [rucc_asm::Row { at: 0, span: Span::DUMMY, inst: None }, row(4, 10, 14)];
10948        let ends = ends(&extent(8), &rows);
10949        let scope = Span::new(0, 20);
10950        assert_eq!(spread(scope, &ends, &rows), vec![rucc_debug::Reach { from: 4, len: 4 }]);
10951    }
10952
10953    /// A scope of the unit, written short because these tests are about nothing else.
10954    fn scope(parent: Option<usize>, lo: u32, hi: u32) -> crate::shapes::Scope {
10955        let span = Span::new(lo, hi);
10956        crate::shapes::Scope { parent, span }
10957    }
10958
10959    #[test]
10960    fn a_function_gets_the_scopes_its_own_locals_are_in_and_nothing_else() {
10961        // Two functions' worth of scopes in one table, and this one is in the second pair.
10962        let scopes = [scope(None, 0, 10), scope(None, 20, 30), scope(Some(1), 22, 26)];
10963        let rows = [row(0, 22, 24), row(4, 26, 28)];
10964        let (out, at) = nests(&[Some(2)], &scopes, &extent(8), &rows);
10965        // The one the local is in and the one that is inside, numbered from zero for this
10966        // function, with the parent named by the entry it became rather than by where it was.
10967        assert_eq!(at.get(&1), Some(&0));
10968        assert_eq!(at.get(&2), Some(&1));
10969        assert_eq!(at.get(&0), None);
10970        assert_eq!(out.len(), 2);
10971        assert_eq!(out[0].parent, None);
10972        assert_eq!(out[1].parent, Some(0));
10973        assert_eq!(out[0].over, vec![rucc_debug::Reach { from: 0, len: 8 }]);
10974        assert_eq!(out[1].over, vec![rucc_debug::Reach { from: 0, len: 4 }]);
10975    }
10976
10977    #[test]
10978    fn a_local_written_straight_into_the_body_pulls_no_scope_in() {
10979        let scopes = [scope(None, 20, 30)];
10980        let rows = [row(0, 22, 24)];
10981        let (out, at) = nests(&[None], &scopes, &extent(4), &rows);
10982        assert_eq!(out, Vec::new());
10983        assert!(at.is_empty());
10984    }
10985
10986    #[test]
10987    fn a_scope_whose_code_all_went_away_is_still_one_of_the_functions_scopes() {
10988        // Nothing was built for the bytes it covers, so there is nowhere to say its names were
10989        // live. The entry is written anyway, since dropping it would move a local up into the
10990        // function and make it answer to a name it was not declared under.
10991        let scopes = [scope(None, 20, 30)];
10992        let rows = [row(0, 40, 44)];
10993        let (out, at) = nests(&[Some(0)], &scopes, &extent(4), &rows);
10994        assert_eq!(at.get(&0), Some(&0));
10995        assert_eq!(out.len(), 1);
10996        assert_eq!(out[0].over, Vec::new());
10997    }
10998}