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;
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 = keywords.windows();
195    }
196    let mut diagnostics: Vec<Diagnostic> = Vec::new();
197    // Filled in by the back end when there is one, and empty for every kind that stops before it.
198    let mut fired = Fired::new();
199    // The same, and the other thing the back end is asked to record about itself.
200    let mut pressure = Pressure::new();
201    let mut lowerings = Lowerings::asked(opts.lowering_dump.is_some());
202    // And the frames it laid out, for `-fstack-usage`. Recorded whether or not the flag was given,
203    // since a row per function is nothing next to compiling the function, and written only if it
204    // was.
205    let mut stack = StackUsage::new();
206    // Filled in by the optimizer, and only when `-fdump-ir=` asked for something.
207    let mut dumps = Vec::new();
208    let mut remarks = String::new();
209    // How long each optimizer pass took, for `-frucc-trace`.
210    let mut passes = Vec::new();
211    // Filled in as the compilation goes past each of them, and only under `-save-temps`.
212    let mut temps = Temps::default();
213
214    let bytes = match fs.read(Path::new(name)) {
215        Ok(bytes) => bytes,
216        Err(e) => return failure(format!("{name}: {e}")),
217    };
218    let Ok(file) = sess.sources.add_shared(crate::phase::source_name(name), bytes, None) else {
219        return failure(format!("{name}: the source map has no room left for this file"));
220    };
221    clock.lap("read");
222
223    // Phases 1 to 4. The expanded stream is turned into pp-tokens straight away, because the
224    // include context borrows the source map that rendering a diagnostic reads and the borrow
225    // has to end before anything is rendered.
226    let mut pp = rucc_pp::Preprocessor::with_prefix_map(opts.prefix_map.macros.clone());
227    let predef = rucc_pp::Predef::for_options(opts);
228    let expanded: Vec<PpToken> = {
229        let mut tokens = Vec::new();
230        // The inner block is the borrow. The printer under `-save-temps` reads the source map
231        // that the include context is holding, so the context has to be gone before it runs, and
232        // nothing happens in between, which is what makes the text it prints the text that is
233        // compiled below rather than a second answer to the same question.
234        {
235            let mut cx =
236                rucc_pp::Context::new(&mut sess.interner, &mut sess.sources, fs, &opts.search);
237            cx.lex = rucc_lex::Options::for_dialect(opts.std, opts.gnu_extensions);
238            cx.pedantic = opts.pedantic;
239            if pp.predefine(&sess.target, &predef, &mut cx).is_err() {
240                return failure(format!(
241                    "{name}: the source map has no room for the built in macros"
242                ));
243            }
244            if pp.preinclude(&opts.preincludes, &mut tokens, &mut cx).is_err() {
245                return failure(format!("{name}: the source map has no room for the command line"));
246            }
247            tokens.append(&mut pp.run(file, &mut cx));
248        }
249        if opts.save_temps.wanted() {
250            temps.preprocessed = Some(rucc_pp::print(
251                file,
252                &tokens,
253                pp.line_directives(),
254                &sess.sources,
255                &sess.interner,
256                rucc_pp::PrintOptions { line_markers: opts.line_markers },
257            ));
258        }
259        tokens.iter().map(|token| token.to_pp()).collect()
260    };
261    diagnostics.extend(pp.take_diagnostics());
262    // Taken here rather than at the end, because the preprocessor is done with and everything
263    // after this is about the tree it produced.
264    let deps = pp.dependencies().to_vec();
265    clock.lap("preprocess");
266
267    // Phase 7, which is where a spelling becomes a keyword and a preprocessing number becomes
268    // a constant of a type.
269    let cx = Convert {
270        keywords: &keywords,
271        interner: &sess.interner,
272        target: &sess.target,
273        std: opts.std,
274        gnu: opts.gnu_extensions,
275        pedantic: opts.pedantic,
276    };
277    let (tokens, complaints) = convert(&expanded, &cx);
278    diagnostics.extend(complaints);
279    clock.lap("convert");
280
281    // Only the ones the file wrote, since a name nothing interned is one nothing can use.
282    let type_names: Vec<Symbol> =
283        sess.target.type_names().iter().filter_map(|&(name, _)| sess.interner.find(name)).collect();
284    let parsed = rucc_parse::parse(
285        &tokens,
286        rucc_parse::Context {
287            interner: &sess.interner,
288            std: opts.std,
289            gnu: opts.gnu_extensions,
290            pedantic: opts.pedantic,
291            error_limit: opts.error_limit as usize,
292            type_names: &type_names,
293        },
294    );
295    clock.lap("parse");
296    let parse_failed = parsed.diagnostics.iter().any(|d| d.severity.is_fatal());
297    diagnostics.extend(parsed.diagnostics);
298
299    let mut artifact = Artifact::Nothing;
300    // Zero when nothing instruments, which is the truthful summary of a file built without
301    // `-fsafety`: no checks went in, so none is standing, and every call it makes is unmodelled.
302    let mut instrumented = Instrumented::default();
303    if !parse_failed {
304        let mut checker = Checker::new(
305            &parsed.ast,
306            CheckContext {
307                names: &sess.interner,
308                target: &sess.target,
309                std: opts.std,
310                gnu: opts.gnu_extensions,
311                pedantic: opts.pedantic,
312                permissive: opts.permissive,
313                gnu89_inline: opts.gnu89_inline,
314                error_limit: opts.error_limit as usize,
315                // A freestanding program has no C library, so a name that is the library's
316                // everywhere else is the program's own here and means whatever it defined.
317                builtins: opts.builtins && opts.hosted,
318                no_builtin: &opts.no_builtin,
319                short_enums: opts.short_enums,
320                ms_extensions: sess.ms_extensions(),
321                trapping_math: opts.trapping_math,
322                isa: opts.isa,
323            },
324        );
325        checker.check_unit();
326        let checked = checker.finish();
327        clock.lap("check");
328        if !checked.failed() {
329            match opts.emit {
330                EmitKind::Tast => {
331                    artifact = Artifact::Text(rucc_sema::print(
332                        &checked.tast,
333                        &checked.types,
334                        &sess.interner,
335                    ));
336                }
337                // Nothing past the checker, because a granule is a fact about a layout and a
338                // layout is settled the moment the closing brace is seen. Lowering the
339                // function bodies would take minutes on an amalgamation and answer nothing.
340                EmitKind::TypeGranules => {
341                    artifact = Artifact::Text(rucc_types::granule_report(
342                        &checked.types,
343                        &sess.interner,
344                        &sess.target,
345                    ));
346                }
347                EmitKind::Ir
348                | EmitKind::MirFinal
349                | EmitKind::Asm
350                | EmitKind::Object
351                | EmitKind::Archive
352                | EmitKind::Executable
353                | EmitKind::SafetySummary => {
354                    // What a `.incbin` in an `asm` at file scope names is read through the same
355                    // file system the sources came through, and from where the compiler was run
356                    // rather than from beside the source, because that is where an assembler
357                    // looks for it.
358                    let mut read = |named: &str| {
359                        fs.read(Path::new(named))
360                            .map(|bytes| bytes.as_slice().to_vec())
361                            .map_err(|why| why.to_string())
362                    };
363                    // What the debug information will say about types and signatures, taken
364                    // here because this is the last place the checker's types are readable
365                    // without the back end's borrow of the interner in the way. Nothing at all
366                    // when the build asked for no debug information, since a translation unit
367                    // the size of an amalgamation has tens of thousands of types in it.
368                    let meaning = if opts.debug_info {
369                        crate::shapes::collect(
370                            &checked.tast,
371                            &checked.types,
372                            &sess.target,
373                            &sess.interner,
374                            &sess.sources,
375                        )
376                    } else {
377                        crate::shapes::Meaning::default()
378                    };
379                    let mut lowered = rucc_lower::lower(
380                        crate::phase::source_name(name),
381                        rucc_lower::Context {
382                            tast: &checked.tast,
383                            types: &checked.types,
384                            target: &sess.target,
385                            names: &mut sess.interner,
386                            visibility: match opts.visibility {
387                                Visibility::Default => IrVisibility::Default,
388                                Visibility::Hidden => IrVisibility::Hidden,
389                                Visibility::Protected => IrVisibility::Protected,
390                            },
391                            protector: match opts.protector {
392                                Protector::None => LowerProtector::None,
393                                Protector::Buffers => LowerProtector::Buffers,
394                                Protector::Strong => LowerProtector::Strong,
395                                Protector::All => LowerProtector::All,
396                            },
397                            wrapping: rucc_lower::Wrapping {
398                                signed: opts.wrapping.signed,
399                                pointer: opts.wrapping.pointer,
400                                trap: opts.wrapping.trap,
401                            },
402                            aliasing: opts.strict_aliasing,
403                            padding: opts.padding == Padding::Ignored,
404                            contract: match opts.fp_contract {
405                                Contract::Off => FpContract::Off,
406                                Contract::On => FpContract::On,
407                                Contract::Fast => FpContract::Fast,
408                            },
409                            align: opts.align_functions,
410                            instrument: opts.instrument_functions,
411                            exceptions: opts.exceptions,
412                            read: &mut read,
413                        },
414                    );
415                    // The walk reports what it cannot build, and what it did build is printed
416                    // anyway: a file with one construct missing from it is more use to read
417                    // than nothing at all, and the errors are what stop it being compiled.
418                    clock.lap("lower");
419                    let failed = lowered.diagnostics.iter().any(|d| d.severity.is_fatal());
420                    if !failed {
421                        // The verifier runs on everything the walk builds, always. It is the
422                        // one check that a bug in the walk cannot talk its way past, and a
423                        // wrong instruction found here costs a message rather than an hour
424                        // in front of a debugger over the assembly it turned into.
425                        if let Err(errors) = clock
426                            .time("verify", || rucc_ir::verify(&lowered.module, &sess.interner))
427                        {
428                            for error in errors {
429                                diagnostics.push(internal(&format!("invalid IR, {error}")));
430                            }
431                        } else if let Err(complaints) = clock
432                            .time("instrument", || {
433                                instrument(&mut lowered.module, &mut sess.interner, opts)
434                            })
435                            .map(|done| instrumented = done)
436                        {
437                            diagnostics.extend(complaints);
438                        } else if let Err(complaints) = clock
439                            .time("optimize", || {
440                                optimize(
441                                    &mut lowered.module,
442                                    &mut sess.interner,
443                                    &sess.target,
444                                    opts,
445                                    name,
446                                    &mut dumps,
447                                    &mut remarks,
448                                )
449                            })
450                            .map(|times| passes = times)
451                        {
452                            diagnostics.extend(complaints);
453                        } else if opts.emit == EmitKind::SafetySummary {
454                            // After the optimizer, because the number that matters is how many
455                            // checks are still standing and there is no way to know that before it
456                            // has run. Before the back end, because the back end turns a check into
457                            // a call and a summary of calls is not a summary of checks.
458                            artifact = Artifact::Text(
459                                rucc_safety::summarize(
460                                    &lowered.module,
461                                    &sess.interner,
462                                    name,
463                                    opts.safety.as_str(),
464                                    instrumented.checks,
465                                    instrumented.interposed,
466                                    instrumented.crossings,
467                                )
468                                .render(),
469                            );
470                        } else if opts.emit == EmitKind::Ir {
471                            // After the optimizer rather than before it, so that `--emit=ir -O2`
472                            // is the IR the back end will be given rather than the IR it would
473                            // have been given at `-O0`. There is no other way to see what a pass
474                            // did without reading the assembly it turned into.
475                            artifact =
476                                Artifact::Text(rucc_ir::print(&lowered.module, &sess.interner));
477                        } else {
478                            // The back end, which is every pass after the IR and which is
479                            // where a construct nothing has a rule for is finally noticed.
480                            let made = clock.time("generate", || {
481                                generate(
482                                    &mut lowered.module,
483                                    &mut sess.interner,
484                                    &sess.target,
485                                    opts,
486                                    &mut Recording {
487                                        fired: &mut fired,
488                                        pressure: &mut pressure,
489                                        lowerings: &mut lowerings,
490                                        stack: &mut stack,
491                                    },
492                                    &mut temps.assembly,
493                                    Origin { map: &sess.sources, name, meaning: &meaning },
494                                )
495                            });
496                            match made {
497                                Ok(made) => artifact = made,
498                                Err(complaints) => diagnostics.extend(complaints),
499                            }
500                        }
501                    }
502                    diagnostics.extend(lowered.diagnostics);
503                }
504                // The checker has said everything it has to say, and that is all that was asked.
505                EmitKind::SyntaxOnly => {}
506                _ => {}
507            }
508        }
509        diagnostics.extend(checked.diagnostics);
510    }
511    // The back end's remarks after the optimizer's, which is the order the work happened in. Only
512    // the `switch` lowering says anything yet, and what it says is a rewrite.
513    let mut wants = rucc_opt::Wants::none();
514    for spec in &opts.opt_info {
515        // Checked when the arguments were parsed, and again by the optimizer.
516        let _ = wants.add(spec);
517    }
518    if wants.wants(rucc_opt::stats::Kind::Optimized) {
519        remarks.push_str(&lowerings.remarks(name));
520    }
521
522    let mut messages = Vec::with_capacity(diagnostics.len());
523    let mut errors = 0;
524    for diag in &diagnostics {
525        // `-w` drops the warning here rather than at the several hundred places one is raised,
526        // and it drops it before the count, so `-w -Werror` compiles. A warning that was never
527        // raised is not a warning there is anything to promote. A warning about something in a
528        // header that came with the machine goes the same way for the same reason, unless
529        // `-Wsystem-headers` asked for it.
530        if rucc_diag::dropped(diag, &sess.sources, opts.warnings, opts.system_header_warnings) {
531            continue;
532        }
533        if diag.severity.is_fatal()
534            || (diag.severity == Severity::Warning && opts.warnings_are_errors)
535        {
536            errors += 1;
537        }
538        messages.push(render(diag, &sess.sources, opts.warnings_are_errors));
539    }
540    if errors > 0 {
541        // A tree built from a file that did not compile is not a tree anything should read.
542        artifact = Artifact::Nothing;
543    }
544    // Before the session goes, since the map that says where each function is goes with it. A
545    // file that did not compile gets an empty report, which is what gcc leaves for one.
546    let stack_usage = if opts.stack_usage && errors == 0 {
547        su_file(&stack, &sess.sources, crate::phase::source_name(name))
548    } else {
549        String::new()
550    };
551    // Kept even when the compilation failed, because a rule that fired did fire and a report about
552    // which rules a corpus reaches should not lose the ones a file with a mistake in it reached.
553    clock.passes(passes);
554    let timing = clock.finish();
555    Compiled {
556        artifact,
557        messages,
558        errors,
559        fired,
560        pressure,
561        lowerings,
562        dumps,
563        remarks,
564        deps,
565        temps,
566        timing,
567        stack_usage,
568    }
569}
570
571/// Reads one file of IR, checks it, and prints it back.
572///
573/// This is the compiler's own textual IR arriving as an input rather than leaving as an output,
574/// which is what makes the round trip in the M2 exit criterion something to run rather than
575/// something to believe: what the printer wrote is read back, verified, and written again, and
576/// the two files are either the same bytes or they are not.
577///
578/// The verifier runs here for the reason it runs after the walk. A module that was printed by
579/// this compiler has been through it once already, and one that a person edited has not.
580#[must_use]
581pub fn compile_ir(opts: &Options, name: &str, fs: &dyn FileSystem) -> Compiled {
582    let mut sess = Session::new(opts.clone());
583    if opts.emit != EmitKind::Ir {
584        return failure(format!(
585            "{name}: an input of IR can only be emitted as IR, and `--emit={}` asks for what \
586             the C in front of it became",
587            opts.emit.as_str()
588        ));
589    }
590    let bytes = match fs.read(Path::new(name)) {
591        Ok(bytes) => bytes,
592        Err(e) => return failure(format!("{name}: {e}")),
593    };
594    let Ok(text) = std::str::from_utf8(bytes.as_slice()) else {
595        return failure(format!("{name}: this is not text, so it is not IR"));
596    };
597
598    let module = match rucc_ir::parse(text, &mut sess.interner) {
599        Ok(module) => module,
600        Err(error) => {
601            return failure(format!("{name}:{}: {}", error.line, error.message));
602        }
603    };
604    let mut diagnostics: Vec<Diagnostic> = Vec::new();
605    if let Err(errors) = rucc_ir::verify(&module, &sess.interner) {
606        for error in errors {
607            diagnostics.push(invalid(&format!("invalid IR, {error}")));
608        }
609    }
610    let mut messages = Vec::with_capacity(diagnostics.len());
611    for diag in &diagnostics {
612        messages.push(render(diag, &sess.sources, opts.warnings_are_errors));
613    }
614    let errors = u32::try_from(messages.len()).unwrap_or(u32::MAX);
615    let artifact = if errors > 0 {
616        Artifact::Nothing
617    } else {
618        Artifact::Text(rucc_ir::print(&module, &sess.interner))
619    };
620    // Nothing here reaches the back end, so no rule fired and there is nothing to record.
621    Compiled {
622        artifact,
623        messages,
624        errors,
625        fired: Fired::new(),
626        pressure: Pressure::new(),
627        lowerings: Lowerings::new(),
628        dumps: Vec::new(),
629        remarks: String::new(),
630        deps: Vec::new(),
631        temps: Temps::default(),
632        timing: crate::trace::Timing::default(),
633        stack_usage: String::new(),
634    }
635}
636
637/// Puts the memory safety checks in and redirects the calls that cross the boundary, when
638/// `-fsafety=` asked for them.
639///
640/// Between the walk and the optimizer, which is where section 15.3 of
641/// `spec/safe-memory/15-integration.md` puts it and which is the whole design in one line: the
642/// checks go in while the addresses the program computes still exist, and the optimizer then
643/// discharges the ones it can prove. Every sanitizer that came before instruments after the
644/// optimizer so that its checks cannot be deleted, and pays for all of them forever.
645///
646/// The calls to the C library are redirected here too, and in the same window and for a related
647/// reason. `spec/safe-memory/10-boundaries.md` section 10.3 wants a `memcpy` modelled by a wrapper
648/// that performs the judgements, and `rucc_safety::wrap` is why that has to happen before the
649/// optimizer sees the call rather than after.
650///
651/// The verifier runs again afterwards, for the reason it runs after the walk. This pass rewrites
652/// every function in the module, and a pass that produced IR nothing else accepts should say so
653/// here rather than in the assembly it turned into.
654///
655/// # Errors
656///
657/// When the inserted checks left the module in a state the verifier refuses, which is a bug in
658/// this compiler and not in the program being compiled.
659fn instrument(
660    module: &mut rucc_ir::Module,
661    names: &mut Interner,
662    opts: &Options,
663) -> Result<Instrumented, Vec<Diagnostic>> {
664    if !opts.safety.instruments() {
665        return Ok(Instrumented::default());
666    }
667    let mut checks = rucc_safety::run(module, opts.subobject, opts.promise, opts.races);
668    // The one check that is about a call rather than about an access, so it is a walk of its own
669    // and it is here rather than in the walk above. `rucc_safety::ending` is why, and the short
670    // version is that deciding it means resolving a name, which takes the interner.
671    //
672    // Before the redirection for the same reason the redirection is before the optimizer: what this
673    // reads is the name the program wrote, and a pass that had already pointed the call somewhere
674    // else would leave it with a name this one has no row for.
675    checks.freed = rucc_safety::ending::checks(module, names);
676    // Before the optimizer rather than beside the check lowering, which is what
677    // `rucc_safety::wrap` argues out: `memcpy` is a name an optimizer knows things about, and a
678    // pass that turns a short copy into a pair of loads and stores would leave behind accesses the
679    // check insertion has already finished walking past.
680    let interposed = rucc_safety::redirect(module, names);
681    // After the redirection, so that a call this build models with a wrapper is not also counted
682    // as a crossing it did not model.
683    let crossings = rucc_safety::witness(module, names);
684    match rucc_ir::verify(module, names) {
685        Ok(()) => Ok(Instrumented { checks, interposed, crossings }),
686        Err(errors) => Err(errors
687            .iter()
688            .map(|e| internal(&format!("invalid IR after check insertion, {e}")))
689            .collect()),
690    }
691}
692
693/// What the instrumentation did, which nothing but the summary reads.
694///
695/// Carried out of [`instrument`] rather than recovered from the module afterwards because neither
696/// number survives the optimizer: a check that was discharged leaves nothing behind saying it was
697/// ever there, and a call that was pointed at a wrapper looks like a call that always named one.
698#[derive(Clone, Copy, Debug, Default)]
699struct Instrumented {
700    /// How many checks of each class went in.
701    checks: rucc_safety::Counts,
702    /// How many calls were pointed at an interposition wrapper.
703    interposed: usize,
704    /// How many places a pointer crosses to or from code this build did not instrument.
705    crossings: rucc_safety::Sites,
706}
707
708/// Runs the optimizer over the module, and collects whatever the dumps asked for.
709///
710/// The level chooses a pipeline, the `-f` flags edit it, and at `-O0` there is nothing in it, so
711/// this is a walk over an empty list rather than a branch on the level. See section 9.1 of
712/// `spec/09-optimizer.md` for why the pipelines are written out rather than assembled.
713///
714/// Gives back how long each pass took, for `-frucc-trace`.
715///
716/// # Errors
717///
718/// When a pass left the module in a state the verifier refuses, which is a bug in the pass and
719/// not in the program being compiled, so it is reported as an internal error the way a bad
720/// lowering is.
721fn optimize(
722    module: &mut rucc_ir::Module,
723    names: &mut Interner,
724    target: &TargetInfo,
725    opts: &Options,
726    file: &str,
727    dumps: &mut Vec<rucc_opt::Dump>,
728    remarks: &mut String,
729) -> Result<Vec<(&'static str, std::time::Duration)>, Vec<Diagnostic>> {
730    let mut settings = rucc_opt::Options::for_level(opts.opt_level);
731    // What the analyses that read a body may believe about it. The same question the back end asks
732    // about addresses, with one thing on top: `-fno-semantic-interposition` is the build promising
733    // that a name it exports is the one that will run, which is what every distribution builds a
734    // library with. It says nothing about how an address is reached, and gcc does not change that
735    // under the flag either, so the back end is not given this value.
736    settings.interposition = match opts.interposition {
737        true => replaceable(target, opts),
738        false => IrPic::Executable,
739    };
740    settings.toggles.clone_from(&opts.passes);
741    // The same pair the front end reads a call to a standard name with, which is section 20.1's
742    // three way split: `-ffreestanding` says the library is not there, `-fno-builtin` says it is
743    // there and is not to be assumed to do what the standard says, and a fold that leaves behind a
744    // call to `puts` needs both of those to be off.
745    settings.builtins = opts.builtins && opts.hosted;
746    settings.no_builtin.clone_from(&opts.no_builtin);
747    // What a function with no `target` attribute is built for, which the inliner compares a
748    // callee with one against.
749    settings.isa = opts.isa;
750    settings.fuel = opts.pass_fuel.iter().cloned().collect();
751    settings.global_fuel = opts.pass_fuel_global;
752    settings.verify |= opts.verify_each;
753    for (on, spec) in &opts.pass_gates {
754        // Same argument as the dumps below: every spelling in here was checked while the
755        // arguments were parsed, so a rejection now is this compiler disagreeing with itself.
756        if let Err(why) = settings.gates.add(*on, spec) {
757            return Err(vec![internal(&why)]);
758        }
759    }
760    for spec in &opts.dump_ir {
761        // Every spelling in here was checked while the arguments were parsed, so a rejection
762        // now is this compiler disagreeing with itself rather than the command line being wrong.
763        if let Err(why) = settings.dumps.add(spec) {
764            return Err(vec![internal(&why)]);
765        }
766    }
767    let mut wants = rucc_opt::Wants::none();
768    for spec in &opts.opt_info {
769        // Same argument as the dumps above: every spelling was checked while the arguments were
770        // parsed, so a rejection now is the compiler disagreeing with itself.
771        if let Err(why) = wants.add(spec) {
772            return Err(vec![internal(&why)]);
773        }
774    }
775    let report = rucc_opt::run(module, names, &settings);
776    remarks.push_str(&rucc_opt::optinfo::render(file, &report, names, wants));
777    dumps.extend(report.dumps);
778    match report.broke.is_empty() {
779        true => Ok(report.time),
780        false => Err(report.broke.iter().map(|why| internal(why)).collect()),
781    }
782}
783
784/// Runs the back end over every function in `module` and writes what came out.
785///
786/// One machine function per definition in the module, in the order the module holds them, every
787/// register physical and every frame offset a constant. A declaration has no body and is skipped,
788/// because there is nothing in it to compile.
789///
790/// What the last step is, is the only thing `--emit=mir-final`, `-S` and `-c` disagree about. The
791/// three read the same functions and differ in whether they are printed as machine IR, printed as
792/// assembly, or encoded and put in a file, which is the point of section 11.1 of
793/// `spec/11-asm-objects-debug.md`: a listing that disagrees with the object file beside it is
794/// worse than no listing, and the way to make that impossible is to have one description of an
795/// instruction and two ways of writing it down.
796///
797/// # Errors
798///
799/// One diagnostic per function the back end could not compile, or one about the target when no
800/// back end covers it at all. Every function is attempted rather than stopping at the first, so a
801/// file with three constructs missing from the rule set reports three rather than one at a time.
802///
803/// `assembly` is where `-save-temps` gets its listing from on the path that does not print one,
804/// which is the same functions written the other way rather than a second compilation of the same
805/// file. A listing that disagrees with the object beside it would be worse than none.
806/// Whether a name this file exports is one another object may define or replace.
807///
808/// The link that reads the object decides half of what is in it, and the command line is where that
809/// is said, which is why the flag reaches this far down. See #756.
810///
811/// ELF only, because it is a question about a format rather than about a machine and the other two
812/// answer it differently. Mach-O has a two level namespace, so a name a library defines is bound to
813/// that library and is not replaced by a definition loaded earlier, and it has no copy relocations,
814/// so a variable defined elsewhere needs the table whichever link is coming. COFF decides what
815/// leaves a DLL by an export table the linker is handed. Neither has an object writer here yet, so
816/// what this does is decline to say the ELF answer about them.
817fn replaceable(target: &TargetInfo, opts: &Options) -> IrPic {
818    match (target.tuple.os().object_format(), opts.pic) {
819        (Some(ObjectFormat::Elf), Pic::Library) => IrPic::Library,
820        _ => IrPic::Executable,
821    }
822}
823
824/// Where the file being generated came from, which is what the debug information is about.
825///
826/// The three together rather than separately because none of them is any use on its own here: a
827/// span without the map it points into is a pair of numbers, a name without the spans is a file
828/// nothing in the object refers to, and a signature without the name of the function it belongs to
829/// is an entry with nothing to attach it to.
830#[derive(Clone, Copy)]
831struct Origin<'a> {
832    /// Where every span in the module points.
833    map: &'a SourceMap,
834    /// What the command line called the file, which is what `DW_AT_name` says.
835    name: &'a str,
836    /// The types and the signatures, and empty where the build wanted no debug information.
837    meaning: &'a crate::shapes::Meaning,
838}
839
840fn generate(
841    module: &mut rucc_ir::Module,
842    names: &mut Interner,
843    target: &TargetInfo,
844    opts: &Options,
845    recording: &mut Recording<'_>,
846    assembly: &mut Option<String>,
847    origin: Origin<'_>,
848) -> Result<Artifact, Vec<Diagnostic>> {
849    let Some(machine) = Machine::for_target(target) else {
850        return Err(vec![unsupported(&format!(
851            "there is no back end for {} in this compiler yet, so there is nothing to generate",
852            target.tuple
853        ))]);
854    };
855    // Refused rather than dropped. A command line that asks for a stack protector on a target
856    // that has nowhere to keep the word one is compared against would otherwise get code with no
857    // protection in it and no indication that the flag did nothing, which is the one outcome worse
858    // than the error. Windows is the case: it has a protector and it is a different mechanism.
859    if opts.protector != Protector::None && machine.conv.guard.is_none() {
860        return Err(vec![unsupported(&format!(
861            "{} is not supported for {} yet, because the stack protector on that target is not \
862             the one this compiler writes",
863            opts.protector, target.tuple
864        ))]);
865    }
866    // The same answer for the same reason. What says a file was built to have its control flow
867    // checked is a note, the note is an ELF one, and a target whose objects are not ELF has nowhere
868    // to put it: the landing pads would go in and nothing would ever turn the check on. Windows has
869    // the same hardware and asks for it a different way, which is a bit in the image the linker is
870    // told to set rather than anything a compiler writes into an object.
871    if opts.control.any() && target.tuple.os().object_format() != Some(ObjectFormat::Elf) {
872        return Err(vec![unsupported(&format!(
873            "-fcf-protection={} is not supported for {} yet, because what says a file was built \
874             for it there is not the note this compiler writes",
875            opts.control, target.tuple
876        ))]);
877    }
878    // And once more. A profiled build is one whose functions call a routine the runtime provides,
879    // and a target whose runtime provides no such routine would get a call to a name nothing
880    // defines, which is a link error a long way from the flag that caused it. Windows profiles a
881    // build by calling something else, asked for a different way and taking its argument in a
882    // register, so it is not this hook spelled differently.
883    let profile = match machine.conv.trace {
884        Some(trace) => opts.profile.then(|| opts.hook.early(trace.fentry)),
885        None if opts.profile => {
886            return Err(vec![unsupported(&format!(
887                "-pg is not supported for {} yet, because the profiler's hook on that target is \
888                 not the one this compiler calls",
889                target.tuple
890            ))]);
891        }
892        None => None,
893    };
894    // And once more. The room a patcher was promised is only half the feature: the other half is a
895    // section listing where every function's room is, and both the section's shape and the way it
896    // points at the text it belongs to are ELF's. A format that has no such section would take the
897    // nops and quietly lose the list, which is a build that looks patchable and is not.
898    if opts.patchable.any() && target.tuple.os().object_format() != Some(ObjectFormat::Elf) {
899        return Err(vec![unsupported(&format!(
900            "-fpatchable-function-entry= is not supported for {} yet, because what records where \
901             the room is there is not the section this compiler writes",
902            target.tuple
903        ))]);
904    }
905    let flags = pipeline::Flags {
906        frame_pointer: opts.keeps_frame_pointer(),
907        red_zone: opts.red_zone,
908        stack_clash: opts.stack_clash,
909        landing: opts.control.branch(),
910        profile: match profile {
911            None => pipeline::Profile::No,
912            Some(true) => pipeline::Profile::Early,
913            Some(false) => pipeline::Profile::Late,
914        },
915        patch: pipeline::Room { after: opts.patchable.after(), before: opts.patchable.before },
916        // On at every level above `-O0`, which is where gcc turns `-freorder-blocks` on
917        // (`gcc/opts.cc:604`) and what `spec/optimizer/38-scheduling-and-layout.md` section 38.3
918        // reads off that: it is one of the earliest optimizations there is, it is nearly free,
919        // and it helps every target. `-O0` keeps the order the shape of the graph gives, so that
920        // the blocks come out in the order they were written and a person stepping through the
921        // code walks down the screen.
922        reorder: opts.reorder_blocks.unwrap_or_else(|| opts.opt_level.runs_optimizer()),
923        // On at every level above `-O0`, for the reason the line above is off at it. Sharing one
924        // run of bytes between two locals is a smaller frame and a worse debugger: a variable that
925        // is out of scope reads as whatever took its place, which is what `-O0` exists not to do.
926        // Above it the frame is the win, and `-fstack-reuse=` says either answer at any level.
927        reuse: opts.stack_reuse.unwrap_or_else(|| opts.opt_level.runs_optimizer()),
928        // On from `-O2`, which is where gcc turns `-fschedule-insns2` on and what
929        // `spec/optimizer/38-scheduling-and-layout.md` section 38.6 asks for. Not at `-O1`,
930        // because a schedule is a whole dependence graph per block and `-O1` is the level whose
931        // budget is roughly `-O0`'s. Not at `-O0` for the reason nothing else is.
932        schedule: opts.schedule_insns.unwrap_or_else(|| opts.opt_level.schedules()),
933        // Off unless asked for. gcc pads loops at `-O2` and `-O3`. gcc's padding here cost a third
934        // of a percent of the corpus's text and more than a percent of SQLite's for no speed
935        // anybody could measure, which is tamnd/rucc#1823. The padding this asks for now keeps a
936        // small loop inside one line, which is 18% on AMD EPYC and nothing on an Intel Core, so no
937        // level asks for it on every machine's behalf. See tamnd/rucc#1838.
938        align_loops: opts.align_loops.unwrap_or(false),
939        // Whatever the command line said, and the model's own answer when it said nothing.
940        accurate: opts.cycle_accurate_model,
941        // The same flag that turns the IR verifier on in a release build, since what it says is
942        // that this run should check itself and the back end has checks of its own.
943        verify: opts.verify_each,
944        // The backtracking allocator whenever the optimizer runs, and the single pass one at `-O0`,
945        // which is what section 39.7 keeps it for. `-Zregalloc=` picks either at any level. See
946        // `rucc_regalloc::backtrack` for what the backtracking one does differently.
947        allocator: if opts.backtracking.unwrap_or_else(|| opts.opt_level.runs_optimizer()) {
948            pipeline::Allocator::Backtracking
949        } else {
950            pipeline::Allocator::Single
951        },
952        // What the level asked for. The back end had no way to know until now, which is
953        // tamnd/rucc#741: `-Os` picked a shorter list of middle end passes and then compiled the
954        // result exactly as `-O2` would have. The level is asked whether it optimizes for size
955        // rather than matched against, so a level added later answers this without editing it.
956        goal: Goal::for_size(opts.opt_level.is_size()),
957        // Only when somebody is measuring, and checked when the arguments were parsed.
958        switch: opts.switch_shape.as_deref().and_then(rucc_codegen::switch::Force::named),
959        // On from `-O2` and at `-Os`, which is where gcc turns `-foptimize-sibling-calls` on.
960        sibling: opts.sibling_calls.unwrap_or_else(|| opts.opt_level.sibling_calls()),
961        debug: opts.debug_info,
962    };
963
964    // The checks become calls here rather than beside the insertion, because the id each one
965    // carries is an index into a table and a row for a check the optimizer deleted is a row nothing
966    // will ever name. Section 6.3.1 of `spec/safe-memory/06-instrumentation.md` is what this
967    // eventually becomes and `rucc_safety::lower` says why it is not that yet.
968    //
969    // It is inside the back end rather than beside the optimizer so that `--emit=ir` still shows
970    // the checks. The IR a person reads should say what the compiler decided, not how it spelled it
971    // for the machine.
972    if opts.safety.instruments() {
973        // Which calls hand back storage, which the lowering needs and `-O0` has not worked out.
974        // `rucc_opt::pipeline` runs this only when some pass in the run reads the summaries, since a
975        // flag nothing reads is noise in a dump, and at `-O0` nothing did. Something does now: the
976        // capability for a pointer an allocator just returned is the one capability that is exact
977        // and costs a load, and `rucc_safety::slot` finds those sites by the flag. The safety suite
978        // runs at `-O0`, so without this the cheap case would be the one case that never happens.
979        //
980        // Safe to run twice and safe to run late, because it only ever sets the flag and never
981        // clears one, so a build that had it already gets the same module back.
982        rucc_opt::heap::annotate(module, names);
983        // Which calls hand their capabilities to the callee and which say there are none. Here and
984        // not beside the insertion, because the rule is what each function still has left to check
985        // and the optimizer is what makes that small: running before it would give every callee a
986        // frame for checks that are about to be discharged. `rucc_safety::handover` is the rule and
987        // the pass both, and the census in `--emit=safety-summary` reads the same rule, so the
988        // buckets it prints describe the code that was actually built.
989        rucc_safety::handover::arrange(module);
990        rucc_safety::lower(module, names);
991        if let Err(errors) = rucc_ir::verify(module, names) {
992            return Err(errors
993                .iter()
994                .map(|e| internal(&format!("invalid IR after check lowering, {e}")))
995                .collect());
996        }
997    }
998
999    // Worked out before the loop and not inside it, because it reads the whole module and the loop
1000    // is holding one function of it. It has to be after the check lowering above, since that adds
1001    // calls to the runtime and so can add a name this file does not define.
1002    //
1003    // The link that reads the object decides half of what is in it, and the command line is where
1004    // that is said, which is why the flag reaches this far down. See #756. The format decides the
1005    // other half, since a table only exists on a format that has one to reach through.
1006    //
1007    // Only x86-64 copies a variable into the executable for a reference from the instruction
1008    // pointer, so on the other machines a variable this file only declares is read from the table.
1009    let copies = target.tuple.arch() == Arch::X86_64;
1010    let elsewhere = Elsewhere::of(module, replaceable(target, opts), target.object_format, copies);
1011
1012    let mut funcs = Vec::new();
1013    let mut complaints = Vec::new();
1014    for id in module.funcs() {
1015        if module[id].is_declaration() {
1016            continue;
1017        }
1018        match pipeline::compile_recording(
1019            &mut module[id],
1020            names,
1021            &machine,
1022            &elsewhere,
1023            flags,
1024            recording,
1025        ) {
1026            Ok(func) => funcs.push(func),
1027            Err(why) => {
1028                let name = names.resolve(module[id].name).to_owned();
1029                // The function knows where the instruction came from, so the message lands on
1030                // the line somebody wrote rather than on the file as a whole.
1031                let span = why.inst().map_or(Span::DUMMY, |inst| module[id].span(inst));
1032                let said = format!("cannot generate code for '{name}': {why}");
1033                complaints.push(unsupported_at(&said, span));
1034            }
1035        }
1036    }
1037    if !complaints.is_empty() {
1038        return Err(complaints);
1039    }
1040    // The variables the file defines, which go through the back end the way the functions did not:
1041    // there is nothing in a variable to select instructions for, so the module is what says what
1042    // one is right up to the point where it is written down.
1043    // The second names go the same way and for the same reason, and they are neither a function
1044    // nor a variable: an alias is an entry in the symbol table and no bytes of anything.
1045    let (globals, aliases) = match opts.emit {
1046        EmitKind::Asm | EmitKind::Object | EmitKind::Archive | EmitKind::Executable => (
1047            rucc_asm::globals(module, names, target.object_format).map_err(refused)?,
1048            rucc_asm::aliases(module, names).map_err(refused)?,
1049        ),
1050        _ => (rucc_asm::Globals::default(), Vec::new()),
1051    };
1052    // A failure in either of the last two is a bug here rather than a program this compiler is
1053    // behind on, because every instruction in a function that got this far came out of the same
1054    // description both of them read and every register in it has been allocated.
1055    let unwind = opts.unwinds();
1056    match opts.emit {
1057        EmitKind::Asm => {
1058            rucc_asm::print(&funcs, &globals, &aliases, names, target, unwind, output(opts, target))
1059                .map(Artifact::Text)
1060                .map_err(refused)
1061        }
1062        // An executable is an object as far as this gets: one is what each file of a link
1063        // contributes, and the linker is what turns them into the other. An archive is the same
1064        // again, with the archive writer in place of the linker.
1065        EmitKind::Object | EmitKind::Archive | EmitKind::Executable => {
1066            if opts.save_temps.wanted() {
1067                let listing = rucc_asm::print(
1068                    &funcs,
1069                    &globals,
1070                    &aliases,
1071                    names,
1072                    target,
1073                    unwind,
1074                    output(opts, target),
1075                );
1076                *assembly = Some(listing.map_err(refused)?);
1077            }
1078            // A template kept as text has no bytes until an assembler reads it. Most are read on
1079            // their own where they are, but one may jump to a label another statement's text
1080            // defines or switch section halfway through, and a unit with one of those in it is
1081            // assembled the way gcc assembles every unit: written out as a listing and read back.
1082            // A build that asked for debug information gets a label in front of every instruction,
1083            // and where the reader placed those is the row the encoder would have recorded.
1084            //
1085            // Every unit for AArch64 goes this way for now. The listing is already written from
1086            // the encoder's own tables, so reading it back is the encoder run over the same values,
1087            // and it is one path to get right rather than two.
1088            let aarch64 = target.tuple.arch() == Arch::Aarch64;
1089            if aarch64 || globals.kept() || rucc_asm::kept(&funcs, names, target) {
1090                // The reader keeps the frame rows of a listing but not the personality routine or
1091                // the call site tables, so a unit with a landing pad read back would unwind
1092                // straight past its cleanups. Saying so beats a program that skips them.
1093                if funcs.iter().any(|func| !func.landings.is_empty()) {
1094                    return Err(vec![unsupported(
1095                        "a cleanup that runs during an unwind, in a unit whose listing is read \
1096                         back by the assembler",
1097                    )]);
1098                }
1099                let print = if opts.debug_info { rucc_asm::print_marked } else { rucc_asm::print };
1100                let listing =
1101                    print(&funcs, &globals, &aliases, names, target, unwind, output(opts, target))
1102                        .map_err(refused)?;
1103                let arch = target.tuple.arch();
1104                let read =
1105                    rucc_asm::read_as(&listing, arch, target.object_format).map_err(|trouble| {
1106                        let what = if aarch64 {
1107                            "a unit for aarch64"
1108                        } else if globals.kept() {
1109                            "an `asm` at file scope"
1110                        } else {
1111                            "an `asm` template kept as text"
1112                        };
1113                        vec![unsupported(&format!(
1114                            "{what}, whose listing the assembler stopped at on line {}: {}",
1115                            trouble.line, trouble.why
1116                        ))]
1117                    })?;
1118                let info = if opts.debug_info {
1119                    let assembled =
1120                        placed(&read, &funcs, names, target).map_err(|why| vec![internal(&why)])?;
1121                    describe(&assembled, &globals.image(), &funcs, origin, opts, target)
1122                        .map_err(|why| vec![internal(&why)])?
1123                } else {
1124                    rucc_object::Info::default()
1125                };
1126                let defines = rucc_object::assembled_defines(&read);
1127                let bytes =
1128                    rucc_object::assembled_described(&read, target, &info).map_err(wrote)?;
1129                return Ok(Artifact::Object { bytes, defines });
1130            }
1131            let assembled = rucc_asm::assemble(&funcs, names, target, unwind, opts.debug_info)
1132                .map_err(refused)?;
1133            let data = globals.image();
1134            // The line table, from the spans the assembler kept beside the bytes. Empty when the
1135            // build asked for no debug information, which is the case the rows above are not even
1136            // recorded in.
1137            let info = if opts.debug_info {
1138                describe(&assembled, &data, &funcs, origin, opts, target)
1139                    .map_err(|why| vec![internal(&why)])?
1140            } else {
1141                rucc_object::Info::default()
1142            };
1143            let text = assembled.text;
1144            // A format with no writer is a target this compiler is behind on and anything else
1145            // the writer refused is a bug here, and the two are not the same news to get.
1146            let bytes =
1147                rucc_object::write(&text, &data, &aliases, target, output(opts, target), &info)
1148                    .map_err(wrote)?;
1149            // Asked of the writer rather than worked out from the same three values here, so that
1150            // what the archive's index says and what is in the member cannot come apart. It is
1151            // wanted only by `--emit=archive` and is cheap enough that the other two kinds are not
1152            // worth a second path.
1153            let defines = rucc_object::defines(&text, &data, &aliases, target).map_err(wrote)?;
1154            Ok(Artifact::Object { bytes, defines })
1155        }
1156        _ => Ok(Artifact::Text(rucc_mir::print(&funcs, names, target.regs))),
1157    }
1158}
1159
1160/// The rows a listing marked by [`rucc_asm::print_marked`] would have had from the encoder, read
1161/// off where the reader placed each label.
1162///
1163/// Each function is where its own symbol is and as long as its `.size` says, and each row is its
1164/// label's distance from the symbol. The row for the front of the function is the one the encoder
1165/// writes from `Func::declared`, and it is written here the same way.
1166///
1167/// # Errors
1168///
1169/// A function or a label the reader did not place, which is a listing this compiler wrote and got
1170/// wrong.
1171fn placed(
1172    read: &rucc_object::Assembled,
1173    funcs: &[rucc_mir::Func],
1174    names: &Interner,
1175    target: &TargetInfo,
1176) -> Result<rucc_asm::Assembled, String> {
1177    let at: HashMap<&str, &rucc_object::Name> =
1178        read.names.iter().map(|name| (name.name.as_str(), name)).collect();
1179    let offset = |name: &str| match at.get(name).map(|name| name.at) {
1180        Some(rucc_object::Held::In { part, offset }) => Some((part, offset)),
1181        _ => None,
1182    };
1183    let mut text = rucc_object::Text::default();
1184    let mut lines = Vec::with_capacity(funcs.len());
1185    // The name the listing gave each function, which on Mach-O has the underscore in front. The
1186    // debug information keeps the C name, and the object writer puts the underscore back on when
1187    // it looks one up.
1188    let symbol = rucc_asm::Directives::of(target.object_format).symbol();
1189    for (which, func) in funcs.iter().enumerate() {
1190        let name = names.resolve(func.name);
1191        let Some((part, start)) = offset(&format!("{symbol}{name}")) else {
1192            return Err(format!("the listing has no label for the function '{name}'"));
1193        };
1194        let mut rows = Vec::with_capacity(func.inst_count() + 1);
1195        if !func.declared.is_dummy() {
1196            rows.push(rucc_asm::Row { at: 0, span: func.declared, inst: None });
1197        }
1198        for block in func.blocks() {
1199            for inst in func.insts(block) {
1200                let label = rucc_asm::mark(target, which, inst);
1201                let Some((held, here)) = offset(&label) else {
1202                    return Err(format!("the listing has no label '{label}'"));
1203                };
1204                if held != part || here < start {
1205                    return Err(format!("the label '{label}' is not inside '{name}'"));
1206                }
1207                let at = usize::try_from(here - start).map_err(|why| why.to_string())?;
1208                rows.push(rucc_asm::Row { at, span: func.span(inst), inst: Some(inst) });
1209            }
1210        }
1211        // What `.size` said, or on a format without it, how far the label after the last
1212        // instruction is from the front.
1213        let size = at.get(format!("{symbol}{name}").as_str()).map_or(0, |name| name.size);
1214        let len = match offset(&rucc_asm::mark_end(target, which)) {
1215            Some((held, end)) if size == 0 && held == part && end >= start => end - start,
1216            _ => size,
1217        };
1218        text.funcs.push(rucc_object::Extent {
1219            name: name.to_owned(),
1220            start: usize::try_from(start).map_err(|why| why.to_string())?,
1221            len: usize::try_from(len).map_err(|why| why.to_string())?,
1222            align: func.align.unwrap_or(rucc_object::FUNC_ALIGN),
1223            binding: rucc_object::Binding::Global,
1224            visibility: rucc_object::Visibility::Default,
1225            patch: None,
1226            landings: Vec::new(),
1227        });
1228        lines.push(rows);
1229    }
1230    Ok(rucc_asm::Assembled { text, lines, frames: None })
1231}
1232
1233/// The debug sections for what was just assembled, as bytes and relocations.
1234///
1235/// This is where a span becomes a file and a line, and it is here rather than anywhere further down
1236/// because the source map is the driver's and because the paths in it are still paths at this point.
1237/// [`rucc_session::PrefixMap::apply`] is run over every one of them, which is the whole of what
1238/// `-fdebug-prefix-map=` and `-ffile-prefix-map=` asked for: a build is only reproducible if all of
1239/// the paths in it are rewritten rather than most, so the file names, the name of the unit and the
1240/// directory it was compiled in all go through it.
1241///
1242/// A row whose span is [`Span::DUMMY`] is dropped rather than written at line zero. Those are the
1243/// instructions a pass invented, a prologue and a spill among them, and a debugger asking what a
1244/// program counter is in the middle of is better told the line before than told a line that is not
1245/// in the file. The row that follows covers those bytes, which is the same answer gcc gives.
1246///
1247/// # Errors
1248///
1249/// Whatever the DWARF writer refused, which is a bug here rather than a program this compiler is
1250/// behind on.
1251fn describe(
1252    assembled: &rucc_asm::Assembled,
1253    data: &rucc_object::Data,
1254    machine: &[rucc_mir::Func],
1255    origin: Origin<'_>,
1256    opts: &Options,
1257    target: &TargetInfo,
1258) -> Result<rucc_object::Info, String> {
1259    let rucc_asm::Assembled { text, lines, frames } = assembled;
1260    let rewrite = |path: &str| opts.prefix_map.debug.apply(path).into_owned();
1261    // The file table, built as the rows are walked rather than up front, because what belongs in it
1262    // is the files the code came from and not the files the preprocessor opened. A header that
1263    // contributed nothing but declarations is not one of them, and one that holds a definition is
1264    // in it twice over: once for the rows and once for the line the definition is declared on.
1265    let mut files: Vec<String> = Vec::new();
1266    let mut funcs = Vec::with_capacity(text.funcs.len());
1267    for ((extent, rows), built) in text.funcs.iter().zip(lines).zip(machine) {
1268        let mut out: Vec<rucc_debug::Row> = Vec::with_capacity(rows.len());
1269        for row in rows {
1270            if row.span.is_dummy() {
1271                continue;
1272            }
1273            let Some(at) = origin.map.presumed(row.span.lo) else {
1274                continue;
1275            };
1276            let which = interned(&mut files, rewrite(at.name));
1277            let place = rucc_debug::Row {
1278                at: row.at as u64,
1279                file: which,
1280                line: at.line,
1281                column: at.column,
1282            };
1283            // Two rows at one address is one row, and the first of the two wins. The only place it
1284            // happens is the front of a function, where the row the assembler writes for the
1285            // declaration and the row for the first instruction land on the same byte, which is
1286            // what a function this compiler built no prologue for looks like: two instructions
1287            // cannot start at one address, so nowhere else has the question. The declaration is the
1288            // better answer there because it is the answer gcc gives, which it gives because gcc
1289            // always builds a frame at -O0 and so always has a byte of prologue for the brace to be
1290            // about. A breakpoint on a function wants the line of the function rather than the line
1291            // of whatever its first statement happened to be.
1292            match out.last() {
1293                Some(last) if last.at == place.at => {}
1294                _ => out.push(place),
1295            }
1296        }
1297        // And the front of the function, for a function whose declaration had no span to give. The
1298        // assembler writes a row there from `Func::declared` and that is the usual way this is
1299        // covered, but a function that came from something other than a C source has no such span,
1300        // and the front of one is the one part of it no row would otherwise cover. A program
1301        // counter in there would get no answer at all rather than a slightly early one, and no
1302        // answer is the worse of the two for anybody reading a backtrace.
1303        if let Some(first) = out.first_mut() {
1304            first.at = 0;
1305        }
1306        // And what the function is, for the one this unit holds a definition of. A function the
1307        // walk above found and this did not is one whose name in the object is not the name the
1308        // declaration had, which `__asm__` on a declaration is the way to arrange, and one whose
1309        // signature could not be described. Both get rows and no entry, which leaves a debugger
1310        // where it is for every function today rather than anywhere worse.
1311        let known = origin.meaning.funcs.get(&extent.name);
1312        let decl = known.map(|known| rucc_debug::Place {
1313            file: interned(&mut files, rewrite(&known.file)),
1314            line: known.line,
1315        });
1316        // And where each of its locals is, for the ones the frame gave a slot. The back end hands
1317        // back the declaration each of them is and how far below the frame base it ended up, and
1318        // this is where a number turns back into a name, a type and a line, because this is the
1319        // last place the checker's declarations are still in hand.
1320        //
1321        // A parameter goes on the entry the signature already wrote for it rather than getting one
1322        // of its own, which is what the parameter numbers on the function are for. Two entries of
1323        // one name in one scope is a debugger's problem rather than a reader's.
1324        let mut sig = known.and_then(|known| known.sig.clone());
1325        let mut placed: Vec<(u32, i32)> = built.locals.clone();
1326        let mut spots = stretches(extent, rows, built, target);
1327        // And a local in the frame that shares its bytes and has no stretch at all, which still
1328        // gets its entry so that a debugger says it is not available rather than that there is no
1329        // such name. That is a function whose instructions were scheduled, where no stretch can be
1330        // given, and the whole of it is then somewhere the local may not be.
1331        for &decl in &built.sharing {
1332            if !spots.iter().any(|(at, _)| *at == decl) {
1333                spots.push((decl, Vec::new()));
1334            }
1335        }
1336        if let (Some(sig), Some(known)) = (sig.as_mut(), known) {
1337            for (param, decl) in sig.params.iter_mut().zip(&known.params) {
1338                let Some(decl) = *decl else { continue };
1339                if let Some(which) = placed.iter().position(|&(at, _)| at == decl) {
1340                    let at = rucc_debug::Held::Frame(i64::from(placed.remove(which).1));
1341                    param.spot = Some(rucc_debug::Spot::Always(at));
1342                    continue;
1343                }
1344                // Or the stretches, for a parameter the front end kept in a value rather than in
1345                // the frame, which is what a scalar parameter whose address is never taken is at
1346                // every optimization level including this one.
1347                let Some(which) = spots.iter().position(|(at, _)| *at == decl) else { continue };
1348                param.spot = Some(rucc_debug::Spot::Over(spots.remove(which).1));
1349            }
1350        }
1351        // Whatever is left, which is the locals that are not parameters, in the order the slots
1352        // were asked for. A number with nothing to look up is one whose declaration had no name,
1353        // which is a compound literal rather than anything the program can ask the value of.
1354        let mut locals = Vec::with_capacity(placed.len() + spots.len());
1355        // And which scope each of them was declared in, kept beside the list rather than on it,
1356        // because what goes on the entry is a place in this function's own table of scopes and that
1357        // table is not known until every local has been looked up.
1358        let mut wants: Vec<Option<usize>> = Vec::with_capacity(locals.capacity());
1359        for (decl, at) in placed {
1360            let Some(named) = origin.meaning.locals.get(&decl) else { continue };
1361            wants.push(named.scope);
1362            locals.push(rucc_debug::Local {
1363                name: named.name.clone(),
1364                ty: named.ty,
1365                decl: Some(rucc_debug::Place {
1366                    file: interned(&mut files, rewrite(&named.file)),
1367                    line: named.line,
1368                }),
1369                spot: rucc_debug::Spot::Always(rucc_debug::Held::Frame(i64::from(at))),
1370                scope: None,
1371            });
1372        }
1373        // And the ones with no slot at all, which are the locals the front end kept in a value.
1374        // Sorted by declaration, which is the order the program declared them in, so that what
1375        // comes out does not depend on the order the back end happened to hand registers out in.
1376        spots.sort_by_key(|(decl, _)| *decl);
1377        for (decl, spans) in spots {
1378            let Some(named) = origin.meaning.locals.get(&decl) else { continue };
1379            wants.push(named.scope);
1380            locals.push(rucc_debug::Local {
1381                name: named.name.clone(),
1382                ty: named.ty,
1383                decl: Some(rucc_debug::Place {
1384                    file: interned(&mut files, rewrite(&named.file)),
1385                    line: named.line,
1386                }),
1387                spot: rucc_debug::Spot::Over(spans),
1388                scope: None,
1389            });
1390        }
1391        // And the scopes the locals were declared in, which is where a name declared in an inner
1392        // block stops being one of the function's own. The numbers the walk over the tree handed out
1393        // are over the whole unit, and what goes on an entry is a place in this function's table, so
1394        // the two are joined here.
1395        let (scopes, at) = nests(&wants, &origin.meaning.scopes, extent, rows);
1396        for (local, want) in locals.iter_mut().zip(&wants) {
1397            local.scope = want.and_then(|want| at.get(&want).copied());
1398        }
1399        funcs.push(rucc_debug::Function {
1400            name: extent.name.clone(),
1401            len: extent.len as u64,
1402            rows: out,
1403            decl,
1404            sig,
1405            external: known.is_some_and(|known| known.external),
1406            locals,
1407            scopes,
1408        });
1409    }
1410    // And the file-scope variables, from the objects the back end laid out rather than from the
1411    // declarations, so that a name with an entry here is a name with a symbol to relocate against.
1412    // One the walk found and this did not is a `static` nothing read, and one this found and the
1413    // walk did not is a name the compiler made up rather than one the program wrote, a string
1414    // literal and a compound literal being the two: both are in the file and neither is a variable
1415    // anybody can ask the value of by name.
1416    let mut globals = Vec::new();
1417    for object in &data.objects {
1418        let Some(held) = origin.meaning.objects.get(&object.name) else { continue };
1419        globals.push(rucc_debug::Global {
1420            name: object.name.clone(),
1421            ty: held.ty,
1422            decl: Some(rucc_debug::Place {
1423                file: interned(&mut files, rewrite(&held.file)),
1424                line: held.line,
1425            }),
1426            external: held.external,
1427        });
1428    }
1429    let unit = rucc_debug::Unit {
1430        name: rewrite(origin.name),
1431        // A single dot when the process could not say where it was, which is a directory name every
1432        // debugger understands and which leaves a relative file name meaning what it already meant.
1433        dir: rewrite(opts.working_dir.as_deref().unwrap_or(".")),
1434        producer: format!("rucc {}", crate::VERSION),
1435        files,
1436        types: origin.meaning.types.clone(),
1437        funcs,
1438        globals,
1439        pointer: u8::try_from(target.pointer_width / 8).unwrap_or(8),
1440        // Whether a function can say where its frame base is, which it can when the build writes a
1441        // table that answers the question: the unwind table, or `.debug_frame` in its place. Read
1442        // off what was written rather than asked again, so the two cannot disagree about whether
1443        // the table a frame base is read through is there.
1444        frames: opts.unwinds() || frames.is_some(),
1445        mach_o: target.object_format == rucc_target::ObjectFormat::MachO,
1446    };
1447    let mut info = rucc_debug::write(&unit).map_err(|why| why.to_string())?;
1448    info.chunks.extend(frames.clone());
1449    Ok(info)
1450}
1451
1452/// Where each local the back end kept in a register is, as stretches of the function's addresses.
1453///
1454/// The back end names a stretch by the instruction at either end of it, because a machine
1455/// instruction has no length until something encodes it. This is where it gets one: the assembler
1456/// writes a row per instruction for the line table and the row says how far into the function the
1457/// instruction begins, so the row after it is where it ends. The last instruction of a function
1458/// ends where the function does.
1459///
1460/// Grouped by declaration on the way out, since one local is in one place over one stretch and
1461/// somewhere else over the next, and that is the shape the debugging information wants.
1462fn stretches(
1463    extent: &rucc_object::Extent,
1464    rows: &[rucc_asm::Row],
1465    built: &rucc_mir::Func,
1466    target: &TargetInfo,
1467) -> Vec<(u32, Vec<rucc_debug::Span>)> {
1468    // A target nobody has written a calling convention down for has no DWARF numbering either, so
1469    // there is no way to name the register a local is in and nothing to say.
1470    let (false, Some(regs)) = (built.kept.is_empty(), target.call_regs) else {
1471        return Vec::new();
1472    };
1473    let ends = ends(extent, rows);
1474    let mut bounds = vec![None; built.inst_count()];
1475    for (which, row) in rows.iter().enumerate() {
1476        let Some(inst) = row.inst else { continue };
1477        bounds[inst.index()] = Some((row.at as u64, ends[which]));
1478    }
1479    let mut spots: Vec<(u32, Vec<rucc_debug::Span>)> = Vec::new();
1480    for kept in &built.kept {
1481        let (Some((from, _)), Some((_, to))) = (bounds[kept.from.index()], bounds[kept.to.index()])
1482        else {
1483            continue;
1484        };
1485        if to <= from {
1486            continue;
1487        }
1488        let held = match kept.at {
1489            // A register is named by the number this target's DWARF numbering gives it, which is a
1490            // fact about the class and the register together rather than about either alone.
1491            rucc_mir::Where::Reg { reg, class } => match regs.dwarf(class, reg) {
1492                Some(number) => rucc_debug::Held::Reg(number),
1493                None => continue,
1494            },
1495            rucc_mir::Where::Frame(at) => rucc_debug::Held::Frame(i64::from(at)),
1496        };
1497        let span = rucc_debug::Span { from, len: to - from, held };
1498        match spots.iter_mut().find(|(decl, _)| *decl == kept.decl) {
1499            Some((_, spans)) => spans.push(span),
1500            None => spots.push((kept.decl, vec![span])),
1501        }
1502    }
1503    for (_, spans) in &mut spots {
1504        *spans = settle(std::mem::take(spans));
1505    }
1506    spots.retain(|(_, spans)| !spans.is_empty());
1507    spots
1508}
1509
1510/// Where the instruction each of a function's line table rows was written for ends.
1511///
1512/// The row after it, which is where the next instruction begins, and the end of the function for the
1513/// last one. The row after it at a different address rather than simply the row after it, because an
1514/// instruction that encodes to nothing leaves two rows on one byte and the one in front of it is not
1515/// where anything ends.
1516///
1517/// Backwards, because that is one pass rather than a search from each row for the next address that
1518/// differs, and a function the size of `sqlite3VdbeExec` has tens of thousands of rows.
1519fn ends(extent: &rucc_object::Extent, rows: &[rucc_asm::Row]) -> Vec<u64> {
1520    let mut out = vec![extent.len as u64; rows.len()];
1521    let mut next = extent.len as u64;
1522    for which in (0..rows.len()).rev() {
1523        let at = rows[which].at as u64;
1524        // The answer the row behind got, for a row sharing an address with the one in front of it,
1525        // since the two end in the same place and the one in front has already been asked.
1526        out[which] = match next > at {
1527            true => next,
1528            false => out.get(which + 1).copied().unwrap_or(extent.len as u64),
1529        };
1530        next = next.min(at);
1531    }
1532    out
1533}
1534
1535/// The scopes one function's locals were declared in, as the debug writer wants them, and which of
1536/// its entries each of the unit's scopes became.
1537///
1538/// Only the ones a local of this function is in, and their ancestors. The unit's table holds every
1539/// scope in the translation unit, and a function reaches its own by walking up from the locals the
1540/// back end handed over, which is both the filter and the answer to which function a scope belongs
1541/// to. A scope no local of this function is in is not this function's business even if the numbers
1542/// happen to sit next to each other.
1543///
1544/// The addresses come from the source. A scope is a run of source bytes, every row of the line table
1545/// says which source bytes its instruction was built for, and the rows already say where each
1546/// instruction is, so the addresses of a scope are the addresses of the instructions whose bytes are
1547/// inside it. Nothing had to be carried down the compiler for this, and the nesting comes out right
1548/// on its own: a scope's bytes hold the bytes of every scope inside it, so its addresses hold
1549/// theirs.
1550fn nests(
1551    wants: &[Option<usize>],
1552    scopes: &[crate::shapes::Scope],
1553    extent: &rucc_object::Extent,
1554    rows: &[rucc_asm::Row],
1555) -> (Vec<rucc_debug::Scope>, HashMap<usize, usize>) {
1556    let mut needed: Vec<usize> = Vec::new();
1557    for &want in wants {
1558        let mut up = want;
1559        while let Some(which) = up {
1560            if needed.contains(&which) {
1561                break;
1562            }
1563            needed.push(which);
1564            up = scopes.get(which).and_then(|scope| scope.parent);
1565        }
1566    }
1567    // In the order the unit wrote them, which puts a scope after the one it is inside, because that
1568    // is the order the writer wants and is what lets a parent be named by an entry already made.
1569    needed.sort_unstable();
1570    let at: HashMap<usize, usize> =
1571        needed.iter().enumerate().map(|(which, &scope)| (scope, which)).collect();
1572    let ends = ends(extent, rows);
1573    let out = needed
1574        .iter()
1575        .map(|&which| {
1576            let scope = &scopes[which];
1577            rucc_debug::Scope {
1578                parent: scope.parent.and_then(|parent| at.get(&parent).copied()),
1579                over: spread(scope.span, &ends, rows),
1580            }
1581        })
1582        .collect();
1583    (out, at)
1584}
1585
1586/// Which of a function's addresses were built for a run of its source bytes.
1587///
1588/// A row whose own bytes are inside the run is code the run asked for, and the addresses of a scope
1589/// are the addresses of every such row joined up. Two rows that meet or overlap are one stretch,
1590/// which is what almost all of a scope is: the rows of a block are next to each other unless
1591/// something moved them, and a block the back end split into pieces is exactly the case a list is
1592/// for.
1593fn spread(span: Span, ends: &[u64], rows: &[rucc_asm::Row]) -> Vec<rucc_debug::Reach> {
1594    let mut out: Vec<rucc_debug::Reach> = Vec::new();
1595    for (which, row) in rows.iter().enumerate() {
1596        if row.span.is_dummy() || row.span.lo < span.lo || row.span.hi > span.hi {
1597            continue;
1598        }
1599        let (from, to) = (row.at as u64, ends[which]);
1600        if to <= from {
1601            continue;
1602        }
1603        match out.last_mut() {
1604            Some(last) if last.from + last.len >= from => {
1605                last.len = to.saturating_sub(last.from).max(last.len);
1606            }
1607            _ => out.push(rucc_debug::Reach { from, len: to - from }),
1608        }
1609    }
1610    out
1611}
1612
1613/// One declaration's stretches with the disagreements taken out and the neighbours joined up.
1614///
1615/// Two stretches of one declaration can cover the same address. That is what a program that assigns
1616/// to a local from something already live looks like: both values are live across the assignment,
1617/// the old one because something else still reads it. A stretch never runs past the end of its
1618/// block, so two that overlap are in one block, where the addresses go the way the instructions
1619/// run, and one that starts inside the other starts where the declaration was given its value:
1620/// where the value was computed, or where the assignment was for a value it took from another
1621/// declaration. From there the declaration holds the new value and not the old one, so the one
1622/// that started first ends there.
1623///
1624/// What is still left is two stretches that start at the same address, which is two values both
1625/// live into a block with nothing here to say which of them the declaration holds. Where the two
1626/// agree the answer is the same either way and they become one stretch, and where they disagree the
1627/// address is left out, so a debugger says the variable is unavailable there rather than printing
1628/// whichever register this walk reached first. A wrong answer is worse than none.
1629fn settle(mut spans: Vec<rucc_debug::Span>) -> Vec<rucc_debug::Span> {
1630    spans.sort_by_key(|span| (span.from, span.len));
1631    for which in 0..spans.len() {
1632        let (from, end, held) =
1633            (spans[which].from, spans[which].from + spans[which].len, spans[which].held);
1634        let later = spans[which + 1..]
1635            .iter()
1636            .take_while(|later| later.from < end)
1637            .find(|later| later.from > from && later.held != held);
1638        if let Some(later) = later {
1639            spans[which].len = later.from - from;
1640        }
1641    }
1642    // Every address a stretch begins or ends at, which cuts the function into pieces no stretch is
1643    // partly over: a piece is inside a stretch or outside it and never half of each.
1644    let mut edges: Vec<u64> =
1645        spans.iter().flat_map(|span| [span.from, span.from + span.len]).collect();
1646    edges.sort_unstable();
1647    edges.dedup();
1648    let mut out: Vec<rucc_debug::Span> = Vec::new();
1649    let mut first = 0;
1650    for pair in edges.windows(2) {
1651        let (from, to) = (pair[0], pair[1]);
1652        // Nothing before this can cover this piece or any piece after it, since the pieces only
1653        // ever move forward. The list is in the order the stretches start in, so the walk below
1654        // stops at the first one that starts too late as well.
1655        while spans.get(first).is_some_and(|span| span.from + span.len <= from) {
1656            first += 1;
1657        }
1658        let mut held = None;
1659        let mut agreed = true;
1660        for span in &spans[first..] {
1661            if span.from >= to {
1662                break;
1663            }
1664            if span.from > from || span.from + span.len < to {
1665                continue;
1666            }
1667            match held {
1668                None => held = Some(span.held),
1669                Some(seen) => agreed &= seen == span.held,
1670            }
1671        }
1672        let (Some(held), true) = (held, agreed) else { continue };
1673        match out.last_mut() {
1674            Some(last) if last.from + last.len == from && last.held == held => {
1675                last.len += to - from
1676            }
1677            _ => out.push(rucc_debug::Span { from, len: to - from, held }),
1678        }
1679    }
1680    out
1681}
1682
1683/// Where a file name is in the table, putting it there if it is not there yet.
1684///
1685/// A walk rather than a map because the table holds the files one object's code came from, which is
1686/// a handful even for an amalgamation: everything the preprocessor opened and nothing was generated
1687/// out of stays out of it.
1688fn interned(files: &mut Vec<String>, name: String) -> usize {
1689    match files.iter().position(|have| *have == name) {
1690        Some(which) => which,
1691        None => {
1692            files.push(name);
1693            files.len() - 1
1694        }
1695    }
1696}
1697
1698/// What the command line decided about the file being written, in the words the assembler and the
1699/// object writer use.
1700///
1701/// Two spellings of the same facts, because the flags are the command line's and the answer the two
1702/// writers want is the object format's. The conversion is here rather than in either of them so
1703/// that the two output paths are handed the same thing and cannot come to disagree about what is
1704/// in a file.
1705///
1706/// The feature word is empty on a machine whose bits these are not. It is the x86 one, and a target
1707/// that wanted its control flow checked would want a property of its own with a key of its own, so
1708/// writing this one there would be recording something untrue rather than recording nothing.
1709fn output(opts: &Options, target: &TargetInfo) -> rucc_object::Output {
1710    let mut features = 0;
1711    if target.tuple.arch() == Arch::X86_64 {
1712        if opts.control.branch() {
1713            features |= rucc_object::Property::IBT;
1714        }
1715        if opts.control.ret() {
1716            features |= rucc_object::Property::SHSTK;
1717        }
1718    }
1719    rucc_object::Output {
1720        sections: rucc_object::Sections {
1721            functions: opts.function_sections,
1722            data: opts.data_sections,
1723        },
1724        property: rucc_object::Property { features },
1725    }
1726}
1727
1728/// What the object writer said, as the kind of news it is.
1729///
1730/// A format with no writer is a target this compiler is behind on, which is a program nobody can
1731/// compile today and not a mistake in the one being compiled. Anything else it refused is a bug
1732/// here, because every value it was handed came out of this compiler.
1733fn wrote(why: rucc_object::Error) -> Vec<Diagnostic> {
1734    match why {
1735        rucc_object::Error::Format { .. } => vec![unsupported(&why.to_string())],
1736        rucc_object::Error::Refused { .. } => vec![internal(&why.to_string())],
1737    }
1738}
1739
1740/// What the assembler said, as the kind of news it is.
1741///
1742/// Three of these are about a program and the rest are about this compiler. A thread-local
1743/// variable, an ifunc and a prologue the target's unwind table cannot describe are all valid C that
1744/// the back end does not build yet, and everything else the assembler refuses is something that
1745/// should never have reached it.
1746fn refused(why: rucc_asm::Error) -> Vec<Diagnostic> {
1747    match why {
1748        rucc_asm::Error::Thread { .. }
1749        | rucc_asm::Error::IFunc { .. }
1750        | rucc_asm::Error::Frame { .. } => {
1751            vec![unsupported(&why.to_string())]
1752        }
1753        _ => vec![internal(&why.to_string())],
1754    }
1755}
1756
1757/// A diagnostic about a program this compiler is not finished enough to compile.
1758///
1759/// Not an internal error, because nothing here is wrong: the program is valid C and the part of
1760/// the back end that would handle it has not been written. The note says so, so that a report
1761/// about one of these is filed against the milestone rather than as a miscompilation.
1762fn unsupported(message: &str) -> Diagnostic {
1763    unsupported_at(message, Span::DUMMY)
1764}
1765
1766/// The same, about somewhere in the file rather than about the file.
1767///
1768/// The note names the issue tracker rather than `spec/17-milestones.md`, which is a document
1769/// about the plan: a reader who follows it wants to know whether the construct in front of them
1770/// is already written down as work, and the milestone list does not answer that.
1771fn unsupported_at(message: &str, span: Span) -> Diagnostic {
1772    Diagnostic::error(message.to_owned(), span)
1773        .with_code("E0653")
1774        .note("this construct is not lowered yet, see https://github.com/tamnd/rucc/issues", span)
1775}
1776
1777/// A diagnostic about IR that was handed to us rather than built by us.
1778fn invalid(message: &str) -> Diagnostic {
1779    Diagnostic::error(message.to_owned(), Span::DUMMY).with_code("E0661")
1780}
1781
1782/// A diagnostic about this compiler rather than about the program it was given.
1783fn internal(message: &str) -> Diagnostic {
1784    Diagnostic::error(format!("internal error: {message}"), Span::DUMMY)
1785        .with_code("E0652")
1786        .note("this is a bug in rucc rather than in the program, please report it", Span::DUMMY)
1787}
1788
1789/// Every function's line, in the order they were compiled.
1790///
1791/// A function whose name has no place in the source, which only the tests and the IR reader
1792/// build, is reported against the file being compiled at line and column zero rather than left
1793/// out, since a report that is missing a function is one that reads as that function using
1794/// nothing.
1795fn su_file(stack: &StackUsage, sources: &SourceMap, file: &str) -> String {
1796    let mut out = String::new();
1797    for row in stack.rows() {
1798        let span = row.span();
1799        let at = (!span.is_dummy()).then(|| sources.presumed(span.lo)).flatten();
1800        let (name, line, column) = at.map_or((file, 0, 0), |at| (at.name, at.line, at.column));
1801        out.push_str(&row.line(name, line, column));
1802    }
1803    out
1804}
1805
1806/// A result that is nothing but one message, for the failures that happen before there is
1807/// anything to compile.
1808fn failure(message: String) -> Compiled {
1809    Compiled {
1810        artifact: Artifact::Nothing,
1811        messages: vec![format!("rucc: error: {message}")],
1812        errors: 1,
1813        fired: Fired::new(),
1814        pressure: Pressure::new(),
1815        lowerings: Lowerings::new(),
1816        dumps: Vec::new(),
1817        remarks: String::new(),
1818        deps: Vec::new(),
1819        temps: Temps::default(),
1820        timing: crate::trace::Timing::default(),
1821        stack_usage: String::new(),
1822    }
1823}
1824
1825#[cfg(test)]
1826mod tests {
1827    use rucc_session::{MemoryFileSystem, Std};
1828    use rucc_target::Triple;
1829
1830    use super::*;
1831
1832    fn options() -> Options {
1833        let mut opts = Options::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
1834        opts.emit = EmitKind::Tast;
1835        // The tests here read the code a function turns into, and a frame pointer in every one
1836        // of them is noise that says nothing about what each test is about.
1837        opts.frame_pointer = Some(false);
1838        opts
1839    }
1840
1841    fn run(opts: &Options, source: &str) -> Compiled {
1842        let mut fs = MemoryFileSystem::new();
1843        fs.insert("/main.c", source.to_owned().into_bytes());
1844        compile(opts, "/main.c", &fs)
1845    }
1846
1847    /// Options with the compiler's own headers on the search path and nothing else, which is
1848    /// what a freestanding compilation is. There is no file system underneath these tests,
1849    /// so a header that reached for one would fail to resolve and say so.
1850    fn freestanding() -> Options {
1851        let mut opts = options();
1852        opts.hosted = false;
1853        opts.search.push_system(rucc_session::runtime::DIR);
1854        opts
1855    }
1856
1857    /// The typed tree of a freestanding `source`, insisting that it compiled cleanly.
1858    fn shipped(source: &str) -> String {
1859        let result = run(&freestanding(), source);
1860        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
1861        result.text().to_owned()
1862    }
1863
1864    /// The typed tree of `source`, insisting that it compiled cleanly.
1865    fn tast(source: &str) -> String {
1866        let result = run(&options(), source);
1867        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
1868        result.text().to_owned()
1869    }
1870
1871    #[test]
1872    fn the_shipped_stdarg_declares_a_list_and_the_four_operators() {
1873        let text = shipped(concat!(
1874            "#include <stdarg.h>\n",
1875            "int sum(int n, ...) {\n",
1876            "  va_list ap, copy;\n",
1877            "  va_start(ap, n);\n",
1878            "  va_copy(copy, ap);\n",
1879            "  int total = va_arg(ap, int) + va_arg(copy, int);\n",
1880            "  va_end(ap);\n",
1881            "  va_end(copy);\n",
1882            "  return total;\n",
1883            "}\n",
1884        ));
1885        assert!(text.contains("va-start"), "{text}");
1886        assert!(text.contains("va-copy"), "{text}");
1887        assert!(text.contains("va-arg"), "{text}");
1888        assert!(text.contains("va-end"), "{text}");
1889    }
1890
1891    /// glibc includes `<stdarg.h>` this way from every header that declares a `vprintf`, and
1892    /// what it wants is the type without the four macro names. Answering the whole header
1893    /// would put `va_start` in the way of a program that has its own.
1894    #[test]
1895    fn stdarg_hands_out_the_type_alone_when_that_is_all_that_was_asked_for() {
1896        let text = shipped(concat!(
1897            "#define __need___va_list\n",
1898            "#include <stdarg.h>\n",
1899            "int vprint(const char *f, __gnuc_va_list ap);\n",
1900            "#ifdef va_start\n",
1901            "#error va_start should not be defined\n",
1902            "#endif\n",
1903            "#ifdef _VA_LIST_DEFINED\n",
1904            "#error va_list should not have been made\n",
1905            "#endif\n",
1906        ));
1907        assert!(text.contains("vprint"), "{text}");
1908    }
1909
1910    /// The same protocol on `<stddef.h>`, which glibc uses far more heavily: `<stdio.h>` asks
1911    /// for `size_t` and `NULL` and would be wrong to receive `offsetof` as well.
1912    #[test]
1913    fn stddef_answers_one_piece_at_a_time_and_the_next_request_still_gets_through() {
1914        let text = shipped(concat!(
1915            "#define __need_size_t\n",
1916            "#include <stddef.h>\n",
1917            "#ifdef offsetof\n",
1918            "#error offsetof should not be defined yet\n",
1919            "#endif\n",
1920            "#define __need_ptrdiff_t\n",
1921            "#include <stddef.h>\n",
1922            "#include <stddef.h>\n",
1923            "size_t a;\n",
1924            "ptrdiff_t b;\n",
1925            "wchar_t c;\n",
1926            "max_align_t d;\n",
1927            "void *e = NULL;\n",
1928            "struct P { int x; long y; };\n",
1929            "size_t f = offsetof(struct P, y);\n",
1930        ));
1931        assert!(text.contains("decl #0 a : unsigned long"), "{text}");
1932        assert!(text.contains("decl #1 b : long"), "{text}");
1933    }
1934
1935    #[test]
1936    fn the_shipped_limits_and_float_are_the_targets_own_answers() {
1937        let text = shipped(concat!(
1938            "#include <limits.h>\n",
1939            "#include <float.h>\n",
1940            "int bits = CHAR_BIT;\n",
1941            "long big = LONG_MAX;\n",
1942            "int low = INT_MIN;\n",
1943            "int radix = FLT_RADIX;\n",
1944            "int digits = DBL_MANT_DIG;\n",
1945        ));
1946        assert!(text.contains("const 8 : int"), "{text}");
1947        assert!(text.contains("const 9223372036854775807 : long"), "{text}");
1948        assert!(text.contains("const 2 : int"), "{text}");
1949        assert!(text.contains("const 53 : int"), "{text}");
1950    }
1951
1952    /// Freestanding, so there is no library header to chain to and `<stdint.h>` writes the
1953    /// whole set out itself. The widths are the ones the target picked, which is the only
1954    /// reason this header is the compiler's.
1955    #[test]
1956    fn the_shipped_stdint_writes_the_whole_set_when_there_is_no_library_to_defer_to() {
1957        let text = shipped(concat!(
1958            "#include <stdint.h>\n",
1959            "int64_t a = INT64_C(1);\n",
1960            "uint_least16_t b;\n",
1961            "intptr_t c;\n",
1962            "uintmax_t d = UINTMAX_MAX;\n",
1963            "int wide = sizeof(int_fast64_t);\n",
1964        ));
1965        assert!(text.contains("decl #0 a : long"), "{text}");
1966        assert!(text.contains("decl #1 b : unsigned short"), "{text}");
1967        assert!(text.contains("decl #2 c : long"), "{text}");
1968    }
1969
1970    /// `<mmintrin.h>` is the base of the vector header chain and the first one whose contents
1971    /// are C rather than declarations, so what this checks is that the C in it compiles: a
1972    /// header that is nothing but definitions fails as a whole or not at all.
1973    ///
1974    /// What the intrinsics answer is not checked here and cannot be, because the answer is
1975    /// only interesting next to another compiler's. Every intrinsic in the header was built
1976    /// and run against GCC 16.2.0 on the same inputs, at `-O0`, `-O1`, `-O2` and `-Os`, and
1977    /// gave the same bytes in all four. Carrying that comparison rather than repeating it by
1978    /// hand needs a facet in `tamnd/rucc-corpus` that works out the expected bytes itself,
1979    /// which is a second implementation of MMX and is `tamnd/rucc#1150`.
1980    #[test]
1981    fn the_shipped_mmintrin_defines_the_mmx_type_and_the_operations_over_it() {
1982        let text = shipped(concat!(
1983            "#include <mmintrin.h>\n",
1984            "__m64 add(__m64 a, __m64 b) { return _mm_add_pi16(a, b); }\n",
1985            "__m64 pack(__m64 a, __m64 b) { return _m_packsswb(a, b); }\n",
1986            "__m64 shift(__m64 a) { return _mm_srai_pi32(a, 3); }\n",
1987            "int low(__m64 a) { return _mm_cvtsi64_si32(a); }\n",
1988            "void done(void) { _mm_empty(); }\n",
1989        ));
1990        assert!(text.contains("add"), "{text}");
1991        assert!(text.contains("pack"), "{text}");
1992        assert!(text.contains("shift"), "{text}");
1993    }
1994
1995    /// The allocator beside the vector headers, which is the one piece of the family that is
1996    /// not a vector operation. It reaches for `<stddef.h>` and for three names out of the
1997    /// library, and the point of the test is that the reach resolves with nothing on the
1998    /// search path but the compiler's own directory.
1999    #[test]
2000    fn the_shipped_mm_malloc_asks_for_aligned_memory_and_gives_it_back() {
2001        let text = shipped(concat!(
2002            "#include <mm_malloc.h>\n",
2003            "void *get(void) { return _mm_malloc(64, 16); }\n",
2004            "void put(void *p) { _mm_free(p); }\n",
2005        ));
2006        assert!(text.contains("get"), "{text}");
2007        assert!(text.contains("put"), "{text}");
2008    }
2009
2010    /// `<xmmintrin.h>` is the next rung of the chain and pulls the other two in behind it, so a
2011    /// program that includes this one alone has to get all three. What the intrinsics answer is
2012    /// checked the same way `<mmintrin.h>` next door is checked and for the same reason: a
2013    /// hundred and forty eight lines of answers over nans, infinities, both zeros and values
2014    /// that do not fit in the integer they convert to, identical to GCC 16.2.0 at `-O0`, `-O1`,
2015    /// `-O2` and `-Os`.
2016    ///
2017    /// `_mm_rcp_ps` is the one answer in that run that is not identical, and is not meant to be.
2018    /// The instruction approximates a reciprocal and this computes one exactly, so the bits
2019    /// differ while both sit inside the relative error Intel documents, which the same program
2020    /// checks directly rather than by comparing bits.
2021    #[test]
2022    fn the_shipped_xmmintrin_defines_the_sse_type_and_the_operations_over_it() {
2023        let text = shipped(concat!(
2024            "#include <xmmintrin.h>\n",
2025            "__m128 add(__m128 a, __m128 b) { return _mm_add_ps(a, b); }\n",
2026            "__m128 one(__m128 a, __m128 b) { return _mm_max_ss(a, b); }\n",
2027            "__m128 mask(__m128 a, __m128 b) { return _mm_cmpnle_ps(a, b); }\n",
2028            "__m128 pick(__m128 a, __m128 b) { return _mm_shuffle_ps(a, b, _MM_SHUFFLE(0,1,2,3)); }\n",
2029            "int bits(__m128 a) { return _mm_movemask_ps(a); }\n",
2030            "int near(__m128 a) { return _mm_cvtss_si32(a); }\n",
2031            "__m128 wide(__m64 a) { return _mm_cvtpi16_ps(a); }\n",
2032            "void *room(void) { return _mm_malloc(64, 16); }\n",
2033            "void hint(const float *p) { _mm_prefetch(p, _MM_HINT_T0); _mm_sfence(); }\n",
2034        ));
2035        assert!(text.contains("add"), "{text}");
2036        assert!(text.contains("mask"), "{text}");
2037        assert!(text.contains("pick"), "{text}");
2038        assert!(text.contains("wide"), "{text}");
2039    }
2040
2041    /// The six names of gcc's header this one leaves out, each of which is an instruction whose
2042    /// answer no plain C reproduces exactly. Leaving them out is what turns a program that wants
2043    /// one into a diagnostic naming the function it called, rather than into a wrong answer, and
2044    /// this is what notices if one is ever quietly defined to something close.
2045    ///
2046    /// `tamnd/rucc#1157` is the square root, which brings the first four back.
2047    #[test]
2048    fn the_shipped_xmmintrin_leaves_out_the_names_that_need_an_instruction() {
2049        let text = rucc_session::runtime::header("xmmintrin.h").expect("xmmintrin.h is shipped");
2050        for absent in [
2051            "_mm_sqrt_ps",
2052            "_mm_sqrt_ss",
2053            "_mm_rsqrt_ps",
2054            "_mm_rsqrt_ss",
2055            "_mm_getcsr",
2056            "_mm_setcsr",
2057        ] {
2058            let defined = text.contains(&format!("{absent}("));
2059            assert!(!defined, "{absent} is defined and the header says it is not");
2060            assert!(text.contains(absent), "{absent} is absent and unexplained");
2061        }
2062    }
2063
2064    #[test]
2065    fn the_shipped_emmintrin_defines_both_sse2_types_and_the_operations_over_them() {
2066        let text = shipped(concat!(
2067            "#include <emmintrin.h>\n",
2068            "__m128i add(__m128i a, __m128i b) { return _mm_add_epi64(a, b); }\n",
2069            "__m128i wide(__m128i a, __m128i b) { return _mm_mul_epu32(a, b); }\n",
2070            "__m128i pick(__m128i a) { return _mm_shuffle_epi32(a, _MM_SHUFFLE(0,1,2,3)); }\n",
2071            "__m128i up(__m128i a) { return _mm_slli_epi64(a, 13); }\n",
2072            "__m128i down(__m128i a) { return _mm_srli_si128(a, 3); }\n",
2073            "__m128i pack(__m128i a, __m128i b) { return _mm_packus_epi16(a, b); }\n",
2074            "int bits(__m128i a) { return _mm_movemask_epi8(a); }\n",
2075            "__m128d sum(__m128d a, __m128d b) { return _mm_add_sd(a, b); }\n",
2076            "__m128d mask(__m128d a, __m128d b) { return _mm_cmpunord_pd(a, b); }\n",
2077            "__m128i near(__m128d a) { return _mm_cvtpd_epi32(a); }\n",
2078            "__m128d over(__m128 a) { return _mm_cvtps_pd(a); }\n",
2079            "__m128i half(__m64 a) { return _mm_movpi64_epi64(a); }\n",
2080            "__m128i grab(void const *p) { return _mm_loadu_si128(p); }\n",
2081            "void wall(void) { _mm_lfence(); _mm_mfence(); }\n",
2082        ));
2083        assert!(text.contains("wide"), "{text}");
2084        assert!(text.contains("pack"), "{text}");
2085        assert!(text.contains("near"), "{text}");
2086        assert!(text.contains("half"), "{text}");
2087    }
2088
2089    /// The umbrella header reaches the three underneath it. This is brotli's use of it, from
2090    /// `c/enc/matching_tag_mask.h`, which is the whole of what `tamnd/rucc#1236` was about: four
2091    /// SSE2 names that were already shipped and no way to get at them by the name gcc uses.
2092    #[test]
2093    fn the_shipped_immintrin_reaches_the_names_the_headers_under_it_define() {
2094        let text = shipped(concat!(
2095            "#include <immintrin.h>\n",
2096            "unsigned long long matching(unsigned char tag, unsigned char const *bucket) {\n",
2097            "  __m128i const want = _mm_set1_epi8((char)tag);\n",
2098            "  __m128i const chunk = _mm_loadu_si128((__m128i const *)(void const *)bucket);\n",
2099            "  __m128i const same = _mm_cmpeq_epi8(chunk, want);\n",
2100            "  return (unsigned long long)_mm_movemask_epi8(same);\n",
2101            "}\n",
2102            "__m64 narrow(__m64 a, __m64 b) { return _mm_add_pi32(a, b); }\n",
2103            "__m128 single(__m128 a, __m128 b) { return _mm_add_ps(a, b); }\n",
2104        ));
2105        assert!(text.contains("matching"), "{text}");
2106        assert!(text.contains("narrow"), "the MMX header is not reached: {text}");
2107        assert!(text.contains("single"), "the SSE header is not reached: {text}");
2108    }
2109
2110    /// The wider umbrella reaches everything the narrower one does, and the fence family with it.
2111    /// This is what mingw-w64's `<winnt.h>` includes and what it then uses, so a Windows program
2112    /// that has never heard of an intrinsic gets here through `<windows.h>`.
2113    #[test]
2114    fn the_shipped_x86intrin_reaches_the_fences_windows_headers_ask_it_for() {
2115        let text = shipped(concat!(
2116            "#include <x86intrin.h>\n",
2117            "void barriers(void *p) {\n",
2118            "  _mm_lfence();\n",
2119            "  _mm_sfence();\n",
2120            "  _mm_mfence();\n",
2121            "  _mm_pause();\n",
2122            "  _mm_clflush(p);\n",
2123            "}\n",
2124            "__m128i wide(__m128i a, __m128i b) { return _mm_add_epi32(a, b); }\n",
2125        ));
2126        assert!(text.contains("barriers"), "{text}");
2127        assert!(text.contains("wide"), "the SSE2 header is not reached: {text}");
2128    }
2129
2130    /// Including it twice is the same as including it once, and so is including it beside the
2131    /// header it reaches. A program that includes both spellings is the usual case rather than an
2132    /// odd one, because one of its own headers includes the umbrella and another includes SSE2.
2133    #[test]
2134    fn the_umbrella_and_the_header_under_it_can_both_be_included() {
2135        let text = shipped(concat!(
2136            "#include <immintrin.h>\n",
2137            "#include <emmintrin.h>\n",
2138            "#include <immintrin.h>\n",
2139            "#include <x86intrin.h>\n",
2140            "__m128i twice(__m128i a, __m128i b) { return _mm_add_epi32(a, b); }\n",
2141        ));
2142        assert!(text.contains("twice"), "{text}");
2143    }
2144
2145    /// The AArch64 intrinsics, as xxhash uses them in `XXH3_accumulate_512_neon`: a load, a
2146    /// reinterpretation, the halves of a vector and a widening multiply added into a sum.
2147    #[test]
2148    fn the_shipped_arm_neon_has_what_xxhash_asks_it_for() {
2149        let mut opts = freestanding();
2150        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2151        let source = concat!(
2152            "#include <arm_neon.h>\n",
2153            "uint64x2_t acc(uint64x2_t sum, const void *in, const void *key) {\n",
2154            "  uint8x16_t data = vld1q_u8((const uint8_t *)in);\n",
2155            "  uint8x16_t k = vld1q_u8((const uint8_t *)key);\n",
2156            "  uint64x2_t mixed = vreinterpretq_u64_u8(veorq_u8(data, k));\n",
2157            "  uint32x2_t lo = vmovn_u64(mixed);\n",
2158            "  uint32x2_t hi = vshrn_n_u64(mixed, 32);\n",
2159            "  return vmlal_u32(sum, lo, hi);\n",
2160            "}\n",
2161            "uint32x4x2_t pair(uint32x4_t a, uint32x4_t b) { return vzipq_u32(a, b); }\n",
2162            "uint32_t total(uint32x4_t a) { return vaddvq_u32(a); }\n",
2163        );
2164        let result = run(&opts, source);
2165        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
2166        assert!(result.text().contains("pair"), "{}", result.text());
2167        assert!(result.text().contains("total"), "{}", result.text());
2168    }
2169
2170    /// Off AArch64 the header says so, rather than failing on a type the target does not have.
2171    #[test]
2172    fn the_shipped_arm_neon_refuses_another_target() {
2173        let result = run(&freestanding(), "#include <arm_neon.h>\n");
2174        let said = result.messages.join("\n");
2175        assert!(said.contains("arm_neon.h is for AArch64"), "{said}");
2176    }
2177
2178    /// The float header omits four square roots and SSE2 omits the matching two, for the reason
2179    /// both headers write down. A later change that quietly defines one as an approximation
2180    /// would be a wrong answer nobody sees, so the absence is held in place here.
2181    #[test]
2182    fn the_shipped_emmintrin_leaves_out_the_two_square_roots() {
2183        let text = rucc_session::runtime::header("emmintrin.h").expect("emmintrin.h is shipped");
2184        for absent in ["_mm_sqrt_pd", "_mm_sqrt_sd"] {
2185            let defined = text.contains(&format!("{absent}("));
2186            assert!(!defined, "{absent} is defined and the header says it is not");
2187            assert!(text.contains(absent), "{absent} is absent and unexplained");
2188        }
2189    }
2190
2191    /// The CRC32C steps and the population counts are each one instruction, and the point of
2192    /// naming them rather than writing the loop in C is that instruction, so what is checked is
2193    /// the assembly and not only that the names resolve. `-msse4.2` is what PostgreSQL's
2194    /// configure passes, and it has to bring popcnt and crc32 with it the way gcc's does.
2195    #[test]
2196    fn the_shipped_nmmintrin_is_one_instruction_per_step_under_sse4_2() {
2197        let mut opts = freestanding();
2198        opts.emit = EmitKind::Asm;
2199        let mut choices = rucc_target::Choices::new();
2200        choices.read("sse4.2").expect("gcc knows sse4.2");
2201        opts.isa = choices.over(opts.isa);
2202        let source = concat!(
2203            "#include <nmmintrin.h>\n",
2204            "unsigned b(unsigned c, unsigned char v) { return _mm_crc32_u8(c, v); }\n",
2205            "unsigned w(unsigned c, unsigned short v) { return _mm_crc32_u16(c, v); }\n",
2206            "unsigned l(unsigned c, unsigned v) { return _mm_crc32_u32(c, v); }\n",
2207            "unsigned long long q(unsigned long long c, unsigned long long v) {\n",
2208            "  return _mm_crc32_u64(c, v);\n",
2209            "}\n",
2210            "int n(unsigned v) { return _mm_popcnt_u32(v); }\n",
2211            "long long m(unsigned long long v) { return _mm_popcnt_u64(v); }\n",
2212        );
2213        let result = run(&opts, source);
2214        assert_eq!(result.messages, Vec::<String>::new());
2215        let text = result.text();
2216        for step in ["crc32b", "crc32w", "crc32l", "crc32q", "popcntl", "popcntq"] {
2217            assert!(text.contains(step), "no {step} in:\n{text}");
2218        }
2219    }
2220
2221    /// Without the flag a function not built for the instruction cannot call it, which is gcc's
2222    /// refusal in gcc's words and the answer a configure probe reads.
2223    #[test]
2224    fn the_shipped_smmintrin_refuses_a_caller_not_built_for_the_checksum() {
2225        let result = run(
2226            &freestanding(),
2227            "#include <immintrin.h>\nunsigned f(unsigned c) { return _mm_crc32_u32(c, 1); }\n",
2228        );
2229        let said = result.messages.join("\n");
2230        let refusal = "inlining failed in call to 'always_inline' '_mm_crc32_u32': target \
2231                       specific option mismatch";
2232        assert!(said.contains(refusal), "{said}");
2233    }
2234
2235    /// A function carrying the attribute is built for the instruction whatever the unit is, which
2236    /// is how PostgreSQL writes its checksum: no flag, the attribute on the one function, and the
2237    /// step inlined into it as one instruction. PostgreSQL's probe writes the attribute only when
2238    /// `__has_attribute` says it is there, so that has to say so as well.
2239    #[test]
2240    fn a_function_built_for_sse4_2_calls_the_steps_without_a_flag() {
2241        let mut opts = freestanding();
2242        opts.emit = EmitKind::Asm;
2243        let source = concat!(
2244            "#include <nmmintrin.h>\n",
2245            "#if defined(__has_attribute) && __has_attribute (target)\n",
2246            "__attribute__((target(\"sse4.2\")))\n",
2247            "#endif\n",
2248            "unsigned l(unsigned c, unsigned v) { return _mm_crc32_u32(c, v); }\n",
2249            "__attribute__((target(\"popcnt\")))\n",
2250            "int n(unsigned v) { return _mm_popcnt_u32(v); }\n",
2251        );
2252        let result = run(&opts, source);
2253        assert_eq!(result.messages, Vec::<String>::new());
2254        let text = result.text();
2255        assert!(text.contains("crc32l") && text.contains("popcntl"), "{text}");
2256        let l = &text[text.find("\nl:").expect("l is defined")..];
2257        let l = &l[..l.find("ret").expect("l returns")];
2258        assert!(l.contains("crc32l") && !l.contains("call"), "{l}");
2259    }
2260
2261    /// PostgreSQL's two AVX-512 configure probes, as its `config/c-compiler.m4` writes them, with
2262    /// the functions made external so that each one is written out. Each compiles without a flag
2263    /// and every intrinsic in it is inlined into the one function, since a call left behind would
2264    /// be a call to a function built for an extension the caller may not have. Both were also run
2265    /// under Intel SDE as a Sapphire Rapids, with PostgreSQL's own files, and gave what gcc 16's
2266    /// build gives at `-O0` and `-O2`.
2267    #[test]
2268    fn the_shipped_avx512_headers_pass_postgres_probes() {
2269        let popcount = concat!(
2270            "#include <immintrin.h>\n",
2271            "#include <stdint.h>\n",
2272            "char buf[sizeof(__m512i)];\n",
2273            "#if defined(__has_attribute) && __has_attribute (target)\n",
2274            "__attribute__((target(\"avx512vpopcntdq,avx512bw\")))\n",
2275            "#endif\n",
2276            "int popcount_test(void)\n",
2277            "{\n",
2278            "  int64_t popcnt = 0;\n",
2279            "  __m512i accum = _mm512_setzero_si512();\n",
2280            "  __m512i val = _mm512_maskz_loadu_epi8((__mmask64) 0xf0f0f0f0f0f0f0f0, (const __m512i *) buf);\n",
2281            "  __m512i cnt = _mm512_popcnt_epi64(val);\n",
2282            "  accum = _mm512_add_epi64(accum, cnt);\n",
2283            "  popcnt = _mm512_reduce_add_epi64(accum);\n",
2284            "  return (int) popcnt;\n",
2285            "}\n",
2286        );
2287        let pclmul = concat!(
2288            "#include <immintrin.h>\n",
2289            "__m512i x;\n",
2290            "__m512i y;\n",
2291            "#if defined(__has_attribute) && __has_attribute (target)\n",
2292            "__attribute__((target(\"vpclmulqdq,avx512vl\")))\n",
2293            "#endif\n",
2294            "int avx512_pclmul_test(void)\n",
2295            "{\n",
2296            "  __m128i z;\n",
2297            "  x = _mm512_xor_si512(_mm512_zextsi128_si512(_mm_cvtsi32_si128(0)), x);\n",
2298            "  y = _mm512_clmulepi64_epi128(x, y, 0);\n",
2299            "  z = _mm_ternarylogic_epi64(\n",
2300            "            _mm512_castsi512_si128(y),\n",
2301            "            _mm512_extracti32x4_epi32(y, 1),\n",
2302            "            _mm512_extracti32x4_epi32(y, 2),\n",
2303            "            0x96);\n",
2304            "  return _mm_crc32_u64(0, _mm_extract_epi64(z, 0));\n",
2305            "}\n",
2306        );
2307        let checks: [(&str, &str, &[&str]); 2] = [
2308            (popcount, "popcount_test", &["kmovq", "vmovdqu8", "vpopcntq", "vpaddq", "vshufi64x2"]),
2309            (pclmul, "avx512_pclmul_test", &["vpxorq", "vpclmulqdq", "vpternlogq", "crc32q"]),
2310        ];
2311        for (source, name, wanted) in checks {
2312            for level in [rucc_session::OptLevel::O0, rucc_session::OptLevel::O2] {
2313                let mut opts = freestanding();
2314                opts.emit = EmitKind::Asm;
2315                opts.opt_level = level;
2316                let result = run(&opts, source);
2317                assert_eq!(result.messages, Vec::<String>::new(), "{name} at {level:?}");
2318                let text = result.text();
2319                let start = text.find(&format!("\n{name}:")).expect("the probe is written out");
2320                let body = &text[start..];
2321                let body = &body[..body.find(".size").unwrap_or(body.len())];
2322                for instruction in wanted {
2323                    assert!(
2324                        body.contains(instruction),
2325                        "no {instruction} at {level:?} in:\n{body}"
2326                    );
2327                }
2328                assert!(!body.contains("call"), "a call left behind at {level:?} in:\n{body}");
2329            }
2330        }
2331    }
2332
2333    /// A function not built for the extension cannot call one of its intrinsics, which is the
2334    /// refusal gcc gives in gcc's words, and what tells a probe without the attribute no.
2335    #[test]
2336    fn the_shipped_avx512_headers_refuse_a_caller_not_built_for_them() {
2337        let result = run(
2338            &freestanding(),
2339            "#include <immintrin.h>\n__m512i f(__m512i a) { return _mm512_popcnt_epi64(a); }\n",
2340        );
2341        let said = result.messages.join("\n");
2342        let refusal = "inlining failed in call to 'always_inline' '_mm512_popcnt_epi64': target \
2343                       specific option mismatch";
2344        assert!(said.contains(refusal), "{said}");
2345    }
2346
2347    /// Each of SSE3, SSSE3, SSE4.1 and SSE4.2 reached through `<immintrin.h>` from a function built
2348    /// for it, which is how a program that picks its path at run time writes them. Each is the
2349    /// instruction gcc writes, inlined, with its immediate a number in the text even when the
2350    /// caller wrote the immediate as the two flags `_MM_FROUND_*` are meant to be combined with.
2351    #[test]
2352    fn the_sse3_to_sse4_2_intrinsics_are_the_instructions_under_the_attribute() {
2353        let mut opts = freestanding();
2354        opts.emit = EmitKind::Asm;
2355        let source = concat!(
2356            "#include <immintrin.h>\n",
2357            "__attribute__((target(\"sse3\")))\n",
2358            "__m128i a(const __m128i *p) { return _mm_lddqu_si128(p); }\n",
2359            "__attribute__((target(\"sse3\")))\n",
2360            "__m128 b(__m128 x, __m128 y) { return _mm_hadd_ps(x, y); }\n",
2361            "__attribute__((target(\"ssse3\")))\n",
2362            "__m128i c(__m128i x, __m128i y) { return _mm_shuffle_epi8(_mm_abs_epi32(x), y); }\n",
2363            "__attribute__((target(\"ssse3\")))\n",
2364            "__m128i d(__m128i x, __m128i y) { return _mm_alignr_epi8(x, y, 5); }\n",
2365            "__attribute__((target(\"sse4.1\")))\n",
2366            "int e(__m128i x, __m128i y) {\n",
2367            "  return _mm_extract_epi32(_mm_min_epi32(_mm_mullo_epi32(x, y), y), 2);\n",
2368            "}\n",
2369            "__attribute__((target(\"sse4.1\")))\n",
2370            "__m128 f(__m128 x) { return _mm_round_ps(x, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); }\n",
2371            "__attribute__((target(\"sse4.1\")))\n",
2372            "__m128i g(__m128i x, __m128i y, __m128i m) { return _mm_blendv_epi8(x, y, m); }\n",
2373            "__attribute__((target(\"sse4.1\")))\n",
2374            "int h(__m128i x) { return _mm_testz_si128(x, x); }\n",
2375            "__attribute__((target(\"sse4.2\")))\n",
2376            "__m128i i(__m128i x, __m128i y) { return _mm_cmpgt_epi64(x, y); }\n",
2377            "__attribute__((target(\"sse4.2\")))\n",
2378            "int j(__m128i x, __m128i y) { return _mm_cmpistri(x, y, _SIDD_CMP_EQUAL_EACH); }\n",
2379        );
2380        let result = run(&opts, source);
2381        assert_eq!(result.messages, Vec::<String>::new());
2382        let text = result.text();
2383        for insn in [
2384            "lddqu",
2385            "haddps",
2386            "pabsd",
2387            "pshufb",
2388            "palignr $5,",
2389            "pmulld",
2390            "pminsd",
2391            "pextrd $2,",
2392            "roundps $8,",
2393            "pblendvb",
2394            "ptest",
2395            "pcmpgtq",
2396            "pcmpistri $8,",
2397        ] {
2398            assert!(text.contains(insn), "no {insn} in:\n{text}");
2399        }
2400        assert!(!text.contains("call"), "{text}");
2401    }
2402
2403    /// The same refusal as the checksum's for a caller built for less than the intrinsic wants,
2404    /// and `-mssse3` on the command line is enough for SSSE3 and SSE3 and not for SSE4.1.
2405    #[test]
2406    fn the_sse3_to_sse4_1_intrinsics_are_refused_a_caller_not_built_for_them() {
2407        let source = concat!(
2408            "#include <immintrin.h>\n",
2409            "__m128i f(__m128i x, __m128i y) { return _mm_shuffle_epi8(x, y); }\n",
2410        );
2411        let said = run(&freestanding(), source).messages.join("\n");
2412        let refusal = "inlining failed in call to 'always_inline' '_mm_shuffle_epi8': target \
2413                       specific option mismatch";
2414        assert!(said.contains(refusal), "{said}");
2415
2416        let mut opts = freestanding();
2417        let mut choices = rucc_target::Choices::new();
2418        choices.read("ssse3").expect("gcc knows ssse3");
2419        opts.isa = choices.over(opts.isa);
2420        let result =
2421            run(&opts, &format!("{source}__m128 g(__m128 x) {{ return _mm_movehdup_ps(x); }}\n"));
2422        assert_eq!(result.messages, Vec::<String>::new());
2423        let result = run(
2424            &opts,
2425            "#include <immintrin.h>\n__m128i h(__m128i x) { return _mm_abs_epi8(_mm_cvtepi8_epi32(x)); }\n",
2426        );
2427        let said = result.messages.join("\n");
2428        assert!(said.contains("'_mm_cvtepi8_epi32': target specific option mismatch"), "{said}");
2429    }
2430
2431    /// A string gcc does not know is refused in gcc's words, and AArch64's own strings are
2432    /// something x86-64 does not know either.
2433    #[test]
2434    fn a_target_string_gcc_does_not_know_is_refused() {
2435        for (string, name) in [("sse5", "sse5"), ("+crc", "+crc"), ("sse4.2,foo", "foo")] {
2436            let source =
2437                format!("__attribute__((target(\"{string}\"))) int f(void) {{ return 0; }}\n");
2438            let said = run(&freestanding(), &source).messages.join("\n");
2439            let wanted = format!("attribute 'target' argument '{name}' is unknown");
2440            assert!(said.contains(&wanted), "{string}: {said}");
2441        }
2442    }
2443
2444    /// AArch64 has strings of its own, which the x86-64 reading does not look at, so the
2445    /// checksum PostgreSQL builds there with `target("+crc")` still compiles.
2446    #[test]
2447    fn an_aarch64_target_string_is_still_accepted() {
2448        let mut opts = freestanding();
2449        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2450        let source = "__attribute__((target(\"+crc\"))) int f(void) { return 0; }\n";
2451        let result = run(&opts, source);
2452        assert_eq!(result.messages, Vec::<String>::new());
2453    }
2454
2455    #[test]
2456    fn the_three_formality_headers_still_have_to_work() {
2457        let text = shipped(concat!(
2458            "#include <stdbool.h>\n",
2459            "#include <stdalign.h>\n",
2460            "#include <iso646.h>\n",
2461            "#include <stdnoreturn.h>\n",
2462            "int t = true and not false;\n",
2463            "_Alignas(16) char buf[16];\n",
2464            "int a = alignof(long);\n",
2465        ));
2466        assert!(text.contains("decl #0 t : int"), "{text}");
2467        assert!(text.contains("const 8 : unsigned long"), "{text}");
2468    }
2469
2470    /// Including everything twice has to change nothing, because that is what happens in any
2471    /// program large enough to matter and a guard that is wrong shows up nowhere else.
2472    ///
2473    /// Stated as the two trees being the same rather than as a fact about what is in either
2474    /// one. A header that carries definitions puts them in the tree and moves everything
2475    /// after them along, so an assertion about where the program's own declaration landed is
2476    /// an assertion about how much `<mmintrin.h>` defines, which is not what is being asked.
2477    #[test]
2478    fn every_shipped_header_can_be_included_twice() {
2479        // This is x86-64, and `<arm_neon.h>` is for AArch64 only, so it is held to the same
2480        // thing by the AArch64 test below.
2481        let once: String = rucc_session::runtime::names()
2482            .iter()
2483            .filter(|name| **name != "arm_neon.h")
2484            .map(|name| format!("#include <{name}>\n"))
2485            .collect();
2486        let twice = once.repeat(2);
2487        assert_eq!(shipped(&format!("{once}int x;\n")), shipped(&format!("{twice}int x;\n")));
2488
2489        let mut opts = freestanding();
2490        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2491        let tree = |source: &str| {
2492            let result = run(&opts, source);
2493            assert_eq!(
2494                result.messages,
2495                Vec::<String>::new(),
2496                "expected this to compile:\n{source}"
2497            );
2498            result.text().to_owned()
2499        };
2500        let neon = "#include <arm_neon.h>\n";
2501        assert_eq!(tree(&format!("{neon}int x;\n")), tree(&format!("{neon}{neon}int x;\n")));
2502    }
2503
2504    #[test]
2505    fn a_file_that_is_not_there_says_so_and_produces_nothing() {
2506        let fs = MemoryFileSystem::new();
2507        let result = compile(&options(), "/nope.c", &fs);
2508        assert!(result.failed());
2509        assert!(result.messages[0].contains("/nope.c"), "{:?}", result.messages);
2510        assert!(result.text().is_empty());
2511    }
2512
2513    #[test]
2514    fn an_object_comes_out_with_its_type_its_linkage_and_how_much_of_a_definition_it_is() {
2515        let text = tast("int x = 1;\n");
2516        let expected = "\
2517decl #0 x : int object external static defined
2518  init
2519    +0
2520      const 1 : int
2521";
2522        assert_eq!(text, expected);
2523    }
2524
2525    #[test]
2526    fn the_macros_are_expanded_before_anything_is_parsed() {
2527        // The whole pipeline in one line. The bound came out of a macro, so it was expanded,
2528        // converted from a preprocessing number to a constant of a type, parsed as an
2529        // expression, and folded to the number the array type carries.
2530        let text = tast("#define N 2\nint a[N];\n");
2531        assert!(text.starts_with("decl #0 a : int[2] object external static tentative"), "{text}");
2532    }
2533
2534    /// A pragma survives the preprocessor on purpose, since what one means is not its
2535    /// business, and nothing after it has a place for a `#` in the grammar. `pack` is the one
2536    /// the parser reads and every other line is walked past. Both spellings are here because
2537    /// they arrive by different routes and only one of them was ever on a line of its own in
2538    /// the source.
2539    #[test]
2540    fn a_pragma_is_not_a_declaration_and_the_parse_walks_past_the_ones_it_does_not_read() {
2541        let text = tast(concat!(
2542            "#pragma pack(4)\n",
2543            "struct s { int a; };\n",
2544            "#pragma pack()\n",
2545            "int b;\n",
2546            "_Pragma(\"GCC visibility push(default)\") int c;\n",
2547        ));
2548        assert!(text.contains("decl #0 b : int"), "{text}");
2549        assert!(text.contains("decl #1 c : int"), "{text}");
2550    }
2551
2552    /// The byte swaps and the bit counts of a constant are constants, which is how gcc has them, and
2553    /// every number here was read off gcc 16 on x86-64. `__builtin_clz(0)` and `__builtin_ctzll(0)`
2554    /// are undefined at run time and gcc folds them to the width.
2555    #[test]
2556    fn the_byte_swaps_and_the_bit_counts_of_a_constant_are_constants() {
2557        tast(concat!(
2558            "static const unsigned magic = __builtin_bswap32(0x11223344u);\n",
2559            "_Static_assert(__builtin_bswap16(0x1234) == 0x3412, \"16\");\n",
2560            "_Static_assert(__builtin_bswap32(0x11223344u) == 0x44332211u, \"32\");\n",
2561            "_Static_assert(__builtin_bswap64(0x0102030405060708ull) == 0x0807060504030201ull, \"64\");\n",
2562            "_Static_assert(__builtin_popcountll(-1ll) == 64 && __builtin_popcount(-1) == 32, \"ones\");\n",
2563            "_Static_assert(__builtin_parity(7) == 1 && __builtin_parity(3) == 0, \"parity\");\n",
2564            "_Static_assert(__builtin_ffs(0) == 0 && __builtin_ffs(8) == 4, \"ffs\");\n",
2565            "_Static_assert(__builtin_clrsb(0) == 31 && __builtin_clrsb(-1) == 31, \"clrsb\");\n",
2566            "_Static_assert(__builtin_clrsbl(1) == 62, \"clrsbl\");\n",
2567            "_Static_assert(__builtin_clz(1) == 31 && __builtin_clzl(1) == 63, \"clz\");\n",
2568            "_Static_assert(__builtin_ctzll(1ull << 40) == 40, \"ctz\");\n",
2569            "_Static_assert(__builtin_clz(0) == 32 && __builtin_ctzll(0) == 64, \"zero\");\n",
2570        ));
2571    }
2572
2573    /// Every number in these two tests was read off gcc 16 on x86-64 under `-std=gnu23`
2574    /// rather than reasoned about, which is why they are written as assertions the program
2575    /// makes about itself: a compilation with no messages is every one of them holding.
2576    ///
2577    /// This half is the attributes. `packed` takes the padding out, on the record or on one
2578    /// member, `aligned` raises and never lowers, and the two written together are the
2579    /// combination that packs and then aligns the whole thing.
2580    #[test]
2581    fn the_layout_attributes_move_the_members_and_the_record_the_way_gcc_lays_them_out() {
2582        tast(concat!(
2583            "struct A { char c; int i; } __attribute__((packed));\n",
2584            "_Static_assert(sizeof(struct A) == 5 && _Alignof(struct A) == 1, \"A\");\n",
2585            "_Static_assert(__builtin_offsetof(struct A, i) == 1, \"A.i\");\n",
2586            // `aligned` with nothing in the parentheses is the largest alignment the target
2587            // has, which gcc calls BIGGEST_ALIGNMENT and which is sixteen everywhere here.
2588            "struct B { char c; int i; } __attribute__((aligned));\n",
2589            "_Static_assert(sizeof(struct B) == 16 && _Alignof(struct B) == 16, \"B\");\n",
2590            "struct C { char c; int i __attribute__((packed)); };\n",
2591            "_Static_assert(sizeof(struct C) == 5 && _Alignof(struct C) == 1, \"C\");\n",
2592            "_Static_assert(__builtin_offsetof(struct C, i) == 1, \"C.i\");\n",
2593            "struct D { char c; int i; } __attribute__((packed, aligned(4)));\n",
2594            "_Static_assert(sizeof(struct D) == 8 && _Alignof(struct D) == 4, \"D\");\n",
2595            "_Static_assert(__builtin_offsetof(struct D, i) == 1, \"D.i\");\n",
2596            "struct E { char c; _Alignas(8) int i; };\n",
2597            "_Static_assert(sizeof(struct E) == 16 && _Alignof(struct E) == 8, \"E\");\n",
2598            "_Static_assert(__builtin_offsetof(struct E, i) == 8, \"E.i\");\n",
2599            "struct F { char c; int i __attribute__((aligned(8))); };\n",
2600            "_Static_assert(sizeof(struct F) == 16 && _Alignof(struct F) == 8, \"F\");\n",
2601            // Two the record already had, so the attribute asks for nothing new, and two
2602            // where four was already there, so the attribute is ignored rather than obeyed.
2603            "struct G { char c; short s; } __attribute__((aligned(2)));\n",
2604            "_Static_assert(sizeof(struct G) == 4 && _Alignof(struct G) == 2, \"G\");\n",
2605            "struct H { char c; int i; } __attribute__((aligned(2)));\n",
2606            "_Static_assert(sizeof(struct H) == 8 && _Alignof(struct H) == 4, \"H\");\n",
2607            // `packed` on a member takes the padding out in front of that member alone, so on
2608            // the first one it does nothing and on the second one it does all of it.
2609            "struct I { [[gnu::packed]] char c; int i; };\n",
2610            "_Static_assert(sizeof(struct I) == 8 && _Alignof(struct I) == 4, \"I\");\n",
2611            "struct J { char c; [[gnu::packed]] int i; };\n",
2612            "_Static_assert(sizeof(struct J) == 5 && _Alignof(struct J) == 1, \"J\");\n",
2613            "struct M { char c; int i : 5; int j : 20; } __attribute__((packed));\n",
2614            "_Static_assert(sizeof(struct M) == 5 && _Alignof(struct M) == 1, \"M\");\n",
2615            "struct N { char c; long long l; } __attribute__((aligned(32)));\n",
2616            "_Static_assert(sizeof(struct N) == 32 && _Alignof(struct N) == 32, \"N\");\n",
2617            "union L { char c; int i; } __attribute__((packed));\n",
2618            "_Static_assert(sizeof(union L) == 4 && _Alignof(union L) == 1, \"L\");\n",
2619            // The armoured spellings, which are the ones a system header writes, since a
2620            // program is entitled to a macro called `packed` and is not entitled to one called
2621            // `__packed__`. The two names are one attribute and the layout is the same one.
2622            "struct O { char c; int i; } __attribute__((__packed__));\n",
2623            "_Static_assert(sizeof(struct O) == 5 && _Alignof(struct O) == 1, \"O\");\n",
2624            "struct P { char c; int i; } __attribute__((__aligned__(8)));\n",
2625            "_Static_assert(sizeof(struct P) == 8 && _Alignof(struct P) == 8, \"P\");\n",
2626        ));
2627    }
2628
2629    /// The attribute that changes what a call means rather than what a record lays out.
2630    ///
2631    /// Both halves are here. A call hands a value to a parameter of the union type and the value
2632    /// goes into the member that takes it, which is a compound literal of the union and is the
2633    /// same object the GNU cast to a union builds. And a declaration written with a member's type
2634    /// declares the same function as one written with the union, which is what lets a pointer to
2635    /// either be assigned from the other, and is what gnulib's signature checks do.
2636    ///
2637    /// The `void *` member is last on purpose: the search takes a member whose type the value
2638    /// already has wherever it sits, and falls back to a pointer member that would take the value
2639    /// silently only when there is no such member, so `char *` reaches the catch-all past two
2640    /// members that are not it.
2641    #[test]
2642    fn a_transparent_union_takes_the_member_a_value_fits_and_is_declared_either_way() {
2643        let text = tast(concat!(
2644            "struct one { int x; };\n",
2645            "struct two { long y; };\n",
2646            "typedef union { struct one *a; struct two *b; void *any; }\n",
2647            "  __attribute__((__transparent_union__)) arg;\n",
2648            "int takes(arg v);\n",
2649            "int f(struct one *p, struct two *q, char *c) {\n",
2650            "  return takes(p) + takes(q) + takes(c) + takes(0);\n",
2651            "}\n",
2652            // The other half, which is about declarations and not about values.
2653            "int takes(struct one *p);\n",
2654            "int (*as_a_member)(struct one *) = takes;\n",
2655            "int (*as_the_union)(arg) = takes;\n",
2656        ));
2657        assert!(text.contains("compound-literal"), "{text}");
2658    }
2659
2660    /// The other place glibc writes it, which is the one that matters.
2661    ///
2662    /// `sys/socket.h` puts the attribute on the declarator of the typedef rather than after the
2663    /// closing brace, so a compiler that reads only the second position reads nothing at all of
2664    /// the eleven pointer union that `bind` and `connect` and five others take.
2665    #[test]
2666    fn the_attribute_on_the_declarator_of_a_typedef_is_the_one_glibc_writes() {
2667        let text = tast(concat!(
2668            "struct sockaddr { int family; };\n",
2669            "struct sockaddr_in { int family; int addr; };\n",
2670            "typedef union { struct sockaddr *plain; struct sockaddr_in *inet; }\n",
2671            "  addr_arg __attribute__((__transparent_union__));\n",
2672            "int bind_to(int fd, addr_arg where);\n",
2673            "int f(struct sockaddr_in *where) { return bind_to(0, where); }\n",
2674        ));
2675        assert!(text.contains("compound-literal"), "{text}");
2676    }
2677
2678    /// What the attribute promises has to be a promise this can keep, and is checked rather than
2679    /// believed.
2680    ///
2681    /// A union wider than its first member is not passed the way that member is, and a structure
2682    /// has no members that are alternatives to each other at all. gcc drops the attribute in both
2683    /// cases with a warning and compiles the program, because the type is still a perfectly good
2684    /// type and only the extra rule is gone.
2685    #[test]
2686    fn a_transparent_union_that_cannot_keep_the_promise_is_dropped_with_a_word_about_it() {
2687        let result = run(
2688            &options(),
2689            concat!(
2690                "union wider { int small; double large; } __attribute__((transparent_union));\n",
2691                "struct plain { int x; } __attribute__((transparent_union));\n",
2692            ),
2693        );
2694        assert_eq!(result.messages.len(), 2, "{:?}", result.messages);
2695        assert!(!result.failed(), "{:?}", result.messages);
2696        for message in &result.messages {
2697            assert!(message.contains("'transparent_union' attribute ignored"), "{message}");
2698        }
2699        assert!(result.messages[0].contains("first member"), "{:?}", result.messages);
2700        assert!(result.messages[1].contains("only a union"), "{:?}", result.messages);
2701    }
2702
2703    /// What an access to a packed member is allowed to assume about where it starts.
2704    ///
2705    /// C 6.2.8 gives an object of type `int` four byte alignment and `packed` takes it away: the
2706    /// member goes wherever the members in front of it ended, and an `int` one byte into a record
2707    /// is aligned to one. The number on the access has to say so, because it is what the back end
2708    /// picks instructions from and what judgement J1 of `spec/safe-memory/04-safety-model.md`
2709    /// tests at run time. Four on an address that is a multiple of one is the compiler refusing a
2710    /// program that is doing nothing wrong.
2711    #[test]
2712    fn an_access_to_a_packed_member_says_the_alignment_the_layout_left_it() {
2713        let packed = body(concat!(
2714            "struct P { char c; int v; } __attribute__((packed));\n",
2715            "int f(struct P *p) { return p->v; }\n",
2716        ));
2717        assert!(packed.contains("load.i32 %2, align 1,"), "{packed}");
2718        // The same record without the attribute, which is where the type's own answer is right.
2719        let plain = body(concat!(
2720            "struct P { char c; int v; };\n",
2721            "int f(struct P *p) { return p->v; }\n",
2722        ));
2723        assert!(plain.contains("load.i32 %2, align 4,"), "{plain}");
2724    }
2725
2726    /// The same, for the two ways of being further in than the member itself.
2727    ///
2728    /// An array member is stepped through rather than offset to, and a record member is offset to
2729    /// twice, and both have to carry the outer record's alignment with them. A step of a whole
2730    /// number of elements leaves what the element width and the address had in common, which for
2731    /// a one byte aligned base is one byte however wide the elements are.
2732    #[test]
2733    fn what_is_inside_a_packed_member_is_no_more_aligned_than_the_member_is() {
2734        let stepped = body(concat!(
2735            "struct P { char c; int v[4]; } __attribute__((packed));\n",
2736            "int f(struct P *p, int i) { return p->v[i]; }\n",
2737        ));
2738        assert!(stepped.contains(", align 1,"), "{stepped}");
2739        assert!(!stepped.contains(", align 4,"), "{stepped}");
2740        let nested = body(concat!(
2741            "struct Inner { int v; };\n",
2742            "struct P { char c; struct Inner in; } __attribute__((packed));\n",
2743            "int f(struct P *p) { return p->in.v; }\n",
2744        ));
2745        assert!(nested.contains(", align 1,"), "{nested}");
2746        assert!(!nested.contains(", align 4,"), "{nested}");
2747    }
2748
2749    /// The other way an access gets an alignment its type would not have given it, which is a
2750    /// typedef that lowered one.
2751    ///
2752    /// `aligned` raises on a declaration and replaces on a typedef, so `typedef aligned(1) U32
2753    /// unalign32` really is a four byte integer that may sit anywhere. Reading a word out of a
2754    /// buffer nothing aligned is what every compression library does and this is how they write
2755    /// it: zstd's `lib/common/mem.h` is four typedefs of exactly this shape and `MEM_read32` is
2756    /// `*(const unalign32 *)ptr`.
2757    ///
2758    /// What made this worth a test is where it went wrong. `__alignof__` was right the whole time,
2759    /// because that asks about the type and the type knew. The access was wrong, because the type
2760    /// of `*p` was worked out by resolving every typedef in `p`'s type rather than only the one on
2761    /// the pointer, so the thing being read came back as the `unsigned int` the typedef stands for
2762    /// and the alignment came off that. The number on the access is what judgement J1 tests, so
2763    /// the monitor refused fifty six of zstd's reads, all of them correct.
2764    #[test]
2765    fn an_access_through_a_typedef_that_lowered_its_alignment_says_the_one_the_typedef_asked_for() {
2766        let through = body(concat!(
2767            "typedef __attribute__((aligned(1))) unsigned int unalign32;\n",
2768            "unsigned int f(const void *p) { return *(const unalign32 *)p; }\n",
2769        ));
2770        assert!(through.contains("load.i32 %0, align 1,"), "{through}");
2771        // A subscript is `*(p + i)` and a member through an arrow is a dereference and then an
2772        // offset, so both read the pointee the same way and both have to come out the same.
2773        let stepped = body(concat!(
2774            "typedef __attribute__((aligned(1))) unsigned int unalign32;\n",
2775            "unsigned int f(unalign32 *p, int i) { return p[i]; }\n",
2776        ));
2777        assert!(stepped.contains(", align 1,"), "{stepped}");
2778        assert!(!stepped.contains(", align 4,"), "{stepped}");
2779        // And the same typedef without the attribute, which is where the type's own answer is the
2780        // right one and nothing above should have changed it.
2781        let plain = body(concat!(
2782            "typedef unsigned int word;\n",
2783            "unsigned int f(const void *p) { return *(const word *)p; }\n",
2784        ));
2785        assert!(plain.contains("load.i32 %0, align 4,"), "{plain}");
2786    }
2787
2788    /// The same thing where the object does not fit in a register, which is what `_mm_loadu_si128`
2789    /// is and is the reason the intrinsic header exists at all.
2790    ///
2791    /// `__m128i_u` is `__m128i` with `aligned(1)` on it and `_mm_loadu_si128` is one line,
2792    /// `return *(const __m128i_u *)__p;`. Two things had to be right for that to come out as the
2793    /// unaligned read it is. The dereference has to keep the typedef, which is what the test above
2794    /// covers, and then the return has to read the object as aligned as the object is rather than
2795    /// as aligned as the type it is being returned as: a vector comes back in registers on this
2796    /// ABI, so the sixteen bytes are read as two pieces of eight and the ABI's own alignment is
2797    /// what lays the two pieces out rather than what either read may claim.
2798    #[test]
2799    fn a_vector_read_through_a_typedef_that_lowered_its_alignment_comes_back_a_piece_at_a_time() {
2800        let prefix = concat!(
2801            "typedef long long v2di __attribute__((__vector_size__(16)));\n",
2802            "typedef long long v2di_u __attribute__((__vector_size__(16), __aligned__(1)));\n",
2803        );
2804        let loaded =
2805            body(&format!("{prefix}v2di f(const void *p) {{ return *(const v2di_u *)p; }}"));
2806        assert_eq!(loaded.matches("align 1\n").count(), 2, "{loaded}");
2807        assert!(!loaded.contains("align 16"), "{loaded}");
2808        // The store side, which travels as a copy into whatever the pointer names and so carries
2809        // one number for both ends of it.
2810        let stored = body(&format!("{prefix}void f(void *p, v2di b) {{ *(v2di_u *)p = b; }}"));
2811        assert!(stored.contains("memcpy %0, %3, size 16, align 1"), "{stored}");
2812        // And the aligned spelling of the same two, which is where sixteen is the right answer.
2813        let aligned =
2814            body(&format!("{prefix}v2di f(const void *p) {{ return *(const v2di *)p; }}"));
2815        assert!(aligned.contains("align 16"), "{aligned}");
2816    }
2817
2818    /// The same attribute on a declaration rather than on a type, which asks that this object or
2819    /// this function be at a multiple of that, and which is where a program that has to hand a
2820    /// buffer to hardware or keep two counters off one cache line writes it.
2821    ///
2822    /// A raise and never a lower, which is the one place it does not agree with `_Alignas`: below
2823    /// what the type already has, `_Alignas` is a constraint violation and this is ignored without
2824    /// a word. `__alignof__` of the object answers what the object got and not what its type has,
2825    /// because that is the question a program asking it is asking.
2826    #[test]
2827    fn the_aligned_attribute_on_a_declaration_raises_what_that_one_object_is_aligned_to() {
2828        tast(concat!(
2829            "int v __attribute__((aligned(64)));\n",
2830            "_Static_assert(__alignof__(v) == 64, \"v\");\n",
2831            // Written on the specifiers rather than after the declarator, which asks the same
2832            // thing and is the spelling a header is more likely to use.
2833            "__attribute__((aligned(32))) int w;\n",
2834            "_Static_assert(__alignof__(w) == 32, \"w\");\n",
2835            "[[gnu::aligned(16)]] int x;\n",
2836            "_Static_assert(__alignof__(x) == 16, \"x\");\n",
2837            // Two below the four an `int` already has, so nothing is asked for and nothing is
2838            // said, and the type still answers for the object.
2839            "int y __attribute__((aligned(2)));\n",
2840            "_Static_assert(__alignof__(y) == 4, \"y\");\n",
2841            // A local, which is the same question one scope down.
2842            "void f(void) { int a __attribute__((aligned(128)));\n",
2843            "_Static_assert(__alignof__(a) == 128, \"a\"); (void)a; }\n",
2844            // The type is untouched by any of it: `aligned` on a declaration says where that
2845            // declaration goes and says nothing about every other `int` in the program.
2846            "_Static_assert(__alignof__(int) == 4, \"int\");\n",
2847            // A function, which has no alignment of its own for this to be measured against and
2848            // takes whatever was asked for.
2849            "void g(void) __attribute__((aligned(256)));\n",
2850            "void g(void) {}\n",
2851            "_Static_assert(__alignof__(g) == 256, \"g\");\n",
2852        ));
2853    }
2854
2855    /// And what the object file says, which is the half that makes the answer above true. A
2856    /// function is at a fixed offset inside the text section, so it is at a multiple of two
2857    /// hundred and fifty six only if the section is at one too.
2858    #[test]
2859    fn what_a_declaration_asked_to_be_aligned_to_is_what_the_assembler_is_told() {
2860        let text = asm(concat!(
2861            "int v __attribute__((aligned(64)));\n",
2862            "void g(void) __attribute__((aligned(256)));\n",
2863            "void g(void) {}\n",
2864            "void plain(void) {}\n",
2865        ));
2866        assert!(text.contains("\t.p2align\t6\n\t.type\tv, @object\n"), "{text}");
2867        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "{text}");
2868        assert!(text.contains("\t.p2align\t4, 0x90\n\t.globl\tplain\n"), "{text}");
2869    }
2870
2871    /// The same question asked by the command line instead of by a declaration, which is
2872    /// `-falign-functions` and is what femtolisp's Makefile writes on every compile. The flag is a
2873    /// floor: a function that named a larger boundary itself keeps it, and one that named a
2874    /// smaller one is moved up, because the attribute is a requirement about one function and the
2875    /// flag is a preference about all of them.
2876    #[test]
2877    fn the_alignment_the_command_line_asked_of_every_function_is_a_floor_under_all_of_them() {
2878        let source = concat!(
2879            "void g(void) __attribute__((aligned(256)));\n",
2880            "void g(void) {}\n",
2881            "void small(void) __attribute__((aligned(4)));\n",
2882            "void small(void) {}\n",
2883            "void plain(void) {}\n",
2884        );
2885        let listing = |align: Option<u32>| {
2886            let mut opts = options();
2887            opts.emit = EmitKind::Asm;
2888            opts.align_functions = align;
2889            let result = run(&opts, source);
2890            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
2891            result.text().to_owned()
2892        };
2893
2894        let text = listing(Some(32));
2895        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "the larger one wins: {text}");
2896        assert!(text.contains("\t.p2align\t5, 0x90\n\t.globl\tsmall\n"), "{text}");
2897        assert!(text.contains("\t.p2align\t5, 0x90\n\t.globl\tplain\n"), "{text}");
2898
2899        // And the negative form, which asks for the smallest boundary the target has and is the
2900        // one spelling that takes a function below the sixteen bytes it would get anyway.
2901        let text = listing(Some(8));
2902        assert!(text.contains("\t.p2align\t3, 0x90\n\t.globl\tplain\n"), "{text}");
2903        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "{text}");
2904    }
2905
2906    /// And the one position where the attribute means something else. On a declaration it raises
2907    /// what that one object is aligned to, and on a typedef it says what the type is aligned to,
2908    /// which gcc lets it lower as well: `typedef int L __attribute__((aligned(2)))` really is an
2909    /// `int` at a multiple of two and a record with one in it really is smaller for it.
2910    ///
2911    /// The size is left alone, which is gcc's answer rather than an omission here. An aligned
2912    /// typedef whose alignment is larger than what it stands for keeps the size it stands for,
2913    /// and gcc refuses an array of one rather than padding the elements out to fit.
2914    #[test]
2915    fn an_aligned_typedef_says_what_an_object_of_it_is_aligned_to_and_may_lower_it() {
2916        tast(concat!(
2917            "typedef int L __attribute__((aligned(2)));\n",
2918            "_Static_assert(__alignof__(L) == 2, \"L\");\n",
2919            "_Static_assert(_Alignof(L) == 2, \"L alignof\");\n",
2920            // Below what an `int` has, which is the half a declaration cannot ask for.
2921            "_Static_assert(sizeof(L) == 4, \"L size\");\n",
2922            "struct T { char c; L x; };\n",
2923            "_Static_assert(sizeof(struct T) == 6, \"T\");\n",
2924            "_Static_assert(__builtin_offsetof(struct T, x) == 2, \"T.x\");\n",
2925            // And upwards, which is the ordinary direction and the one a header writes.
2926            "typedef int H __attribute__((aligned(16)));\n",
2927            "_Static_assert(__alignof__(H) == 16, \"H\");\n",
2928            "_Static_assert(sizeof(H) == 4, \"H size\");\n",
2929            "struct U { char c; H x; };\n",
2930            "_Static_assert(sizeof(struct U) == 32, \"U\");\n",
2931            "_Static_assert(__builtin_offsetof(struct U, x) == 16, \"U.x\");\n",
2932            // A typedef of a typedef, where the nearer one is the one the declaration was
2933            // written with and is the one that answers.
2934            "typedef L M __attribute__((aligned(8)));\n",
2935            "_Static_assert(__alignof__(M) == 8, \"M\");\n",
2936            // And one that asked for nothing, which still has whatever the one behind it asked
2937            // for because it is the same type spelled again.
2938            "typedef L N;\n",
2939            "_Static_assert(__alignof__(N) == 2, \"N\");\n",
2940            // The type it stands for is untouched by any of it.
2941            "_Static_assert(__alignof__(int) == 4, \"int\");\n",
2942        ));
2943        let text = asm(concat!(
2944            "typedef int L __attribute__((aligned(2)));\n",
2945            "typedef int H __attribute__((aligned(16)));\n",
2946            "L low;\n",
2947            "H high;\n",
2948        ));
2949        assert!(text.contains("\t.p2align\t1\n\t.type\tlow, @object\n"), "{text}");
2950        assert!(text.contains("\t.p2align\t4\n\t.type\thigh, @object\n"), "{text}");
2951    }
2952
2953    /// The attribute that builds a type rather than changing a layout. `vector_size(n)` says the
2954    /// declared type is `n` bytes of what was written, taken as lanes, and every operator over
2955    /// one is that operator over each lane.
2956    ///
2957    /// The size is in bytes and not in lanes, which is the part a reader gets backwards: sixteen
2958    /// of `int` is four lanes and sixteen of `char` is sixteen. A vector is aligned to its own
2959    /// size, which is what a machine that has the registers wants and what gcc gives one here.
2960    #[test]
2961    fn the_vector_size_attribute_builds_a_type_of_lanes_and_measures_it_in_bytes() {
2962        tast(concat!(
2963            "typedef int __attribute__((vector_size(16))) v4si;\n",
2964            "_Static_assert(sizeof(v4si) == 16 && _Alignof(v4si) == 16, \"v4si\");\n",
2965            "typedef char __attribute__((vector_size(16))) v16qi;\n",
2966            "_Static_assert(sizeof(v16qi) == 16, \"v16qi\");\n",
2967            // One lane, which is a power of two and is a vector rather than the type it was
2968            // written on: the operators it takes are the vector's and not the scalar's.
2969            "typedef int __attribute__((vector_size(4))) v1si;\n",
2970            "_Static_assert(sizeof(v1si) == 4, \"v1si\");\n",
2971            // The armoured spelling and the bracket one, which are the same attribute.
2972            "typedef float __attribute__((__vector_size__(8))) v2sf;\n",
2973            "_Static_assert(sizeof(v2sf) == 8, \"v2sf\");\n",
2974            "typedef short [[gnu::vector_size(8)]] v4hi;\n",
2975            "_Static_assert(sizeof(v4hi) == 8, \"v4hi\");\n",
2976            // A lane is what a subscript answers with, and a vector is not a pointer: there is
2977            // nothing to decay and the lane type is the one the arithmetic happens in.
2978            "v4si g;\n",
2979            "_Static_assert(sizeof(g[0]) == 4, \"lane\");\n",
2980            "_Static_assert(sizeof(g + g) == 16, \"whole\");\n",
2981            // A scalar beside a vector stands for itself in every lane, so the answer is still
2982            // the vector and not the wider of the two types.
2983            "_Static_assert(sizeof(g + 1) == 16, \"broadcast\");\n",
2984            // An array of them, which is the ordinary way a program holds several.
2985            "_Static_assert(sizeof(v4si[3]) == 48, \"array\");\n",
2986        ));
2987    }
2988
2989    /// A whole vector written into an array of them, and a vector named by a type name rather
2990    /// than by a typedef.
2991    ///
2992    /// Both are the same question asked twice. A vector is filled like an array of its lanes when
2993    /// a list is written into it, so a braced element that is itself a vector has to be taken
2994    /// whole rather than started as the first lane, and the type of what was written is the only
2995    /// thing that says which was meant. And a type name is where a compound literal and a cast
2996    /// spell the type out, which a macro taking a lane type and a lane count does, so the
2997    /// attribute has to be read there and not only on a declaration.
2998    #[test]
2999    fn a_vector_is_written_whole_into_an_array_of_them_and_named_by_a_type_name() {
3000        tast(concat!(
3001            "typedef int __attribute__((vector_size(8))) v2si;\n",
3002            "v2si table[] = { (v2si){ 1, 2 }, (v2si){ 3, 4 } };\n",
3003            "_Static_assert(sizeof(table) == 16, \"two of them and not eight lanes\");\n",
3004            // The size written out rather than named, which is the spelling a macro expands to.
3005            "v2si written = (int __attribute__((vector_size(8)))){ 5, 6 };\n",
3006            "_Static_assert(sizeof((int __attribute__((vector_size(16)))){ 0 }) == 16, \"named\");\n",
3007            // A lane is still a lane, so a list of them fills the vector the way it always did
3008            // and the rule above did not turn brace elision off.
3009            "v2si lanes[2] = { 1, 2, 3, 4 };\n",
3010            "_Static_assert(sizeof(lanes) == 16, \"still elided\");\n",
3011        ));
3012    }
3013
3014    /// A lane written rather than read, and a shift whose two vectors are not the same type.
3015    ///
3016    /// Both are places where a vector is not the aggregate it looks like. A subscript of one is
3017    /// an lvalue because the vector it came from is an object, so a lane can be assigned to and
3018    /// has an address, and a qualifier written on the vector reaches every lane the way it does
3019    /// on an array. And a shift is the one lanewise operator whose sides are not brought to a
3020    /// single type, since the right side counts rather than computes.
3021    #[test]
3022    fn a_lane_is_assignable_and_a_shift_takes_a_count_of_its_own_lane() {
3023        let result = run(
3024            &options(),
3025            concat!(
3026                "typedef int __attribute__((vector_size(16))) v4si;\n",
3027                "typedef unsigned __attribute__((vector_size(16))) v4ui;\n",
3028                "void write(v4si *out, v4ui a, v4si b, int n) {\n",
3029                "  v4si v = { 1, 2, 3, 4 };\n",
3030                "  v[0] = n;\n",
3031                "  v[1] += n;\n",
3032                "  v[2]++;\n",
3033                "  *&v[3] = n;\n",
3034                // The count is signed and the value is not, which no other operator allows.
3035                "  v4ui shifted = a >> b;\n",
3036                "  shifted <<= b;\n",
3037                // A scalar stands in every lane on either side of a shift, which is the half
3038                // that looks wrong: the shape of the answer comes off the count here.
3039                "  *out = v + (v4si)shifted + (1 << b);\n",
3040                "}\n",
3041                // A qualifier on the vector is a qualifier on the lane, so there is nothing here
3042                // to write to.
3043                "void refused(const v4si c) {\n",
3044                "  c[0] = 1;\n",
3045                "}\n",
3046            ),
3047        );
3048        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
3049        assert!(result.messages[0].contains("assignment of read-only"), "{:?}", result.messages);
3050    }
3051
3052    /// The third layout attribute, and the one that moves nothing. It says the scalars in the
3053    /// record are stored in the byte order it names, so on a target whose order is the other one
3054    /// every load through a member swaps its bytes and so does every store. The record is the size
3055    /// and the alignment it would be without it and every member is where it would be, which is
3056    /// what gcc 16.2.0 does and what was measured before any of this was written.
3057    ///
3058    /// All four spellings are here because a header writes the armoured one, the attribute may be
3059    /// written in front of the body as well as behind it, and the C23 spelling in gcc's namespace
3060    /// is the same attribute a fourth way. The order the target already has is the fifth case and
3061    /// asks for nothing, since a program saying what would have happened anyway is entitled to be
3062    /// compiled as though it had said nothing.
3063    #[test]
3064    fn a_record_that_asks_for_the_other_byte_order_swaps_every_scalar_it_holds() {
3065        let read = "int f(struct s *p) { return p->i; }\n";
3066        let big = "struct s { int i; } __attribute__((scalar_storage_order(\"big-endian\")));\n";
3067        assert!(body(&format!("{big}{read}")).contains("bswap"), "{big}");
3068
3069        let armoured =
3070            "struct s { int i; } __attribute__((__scalar_storage_order__(\"big-endian\")));\n";
3071        assert!(body(&format!("{armoured}{read}")).contains("bswap"), "{armoured}");
3072
3073        let front = "struct __attribute__((scalar_storage_order(\"big-endian\"))) s { int i; };\n";
3074        assert!(body(&format!("{front}{read}")).contains("bswap"), "{front}");
3075
3076        let standard = "struct s { int i; } [[gnu::scalar_storage_order(\"big-endian\")]];\n";
3077        assert!(body(&format!("{standard}{read}")).contains("bswap"), "{standard}");
3078
3079        let same =
3080            "struct s { int i; } __attribute__((scalar_storage_order(\"little-endian\")));\n";
3081        assert!(!body(&format!("{same}{read}")).contains("bswap"), "{same}");
3082
3083        // A member one byte wide has only one order, and neither has the record itself.
3084        let byte = "struct s { char c; } __attribute__((scalar_storage_order(\"big-endian\")));\n";
3085        let source = format!("{byte}int f(struct s *p) {{ return p->c; }}\n");
3086        assert!(!body(&source).contains("bswap"), "{byte}");
3087
3088        tast(concat!(
3089            "struct s { int i; short h; char c; }",
3090            " __attribute__((scalar_storage_order(\"big-endian\")));\n",
3091            "_Static_assert(sizeof(struct s) == 8 && _Alignof(struct s) == 4, \"s\");\n",
3092            "_Static_assert(__builtin_offsetof(struct s, h) == 4, \"s.h\");\n",
3093            "_Static_assert(__builtin_offsetof(struct s, c) == 6, \"s.c\");\n",
3094        ));
3095    }
3096
3097    /// A bit-field in one of these records lies in the same bytes and is counted from the top of
3098    /// them rather than from the bottom. `execute/20230630-2.c` is the program that says so:
3099    /// `short i : 12` in front of four one bit fields holds 341 in the two bytes `15 5f`, so the
3100    /// twelve bits are the top twelve and reading them is a shift right by four rather than a mask
3101    /// alone. The plain record shifts nothing, since there the field is already at the bottom.
3102    #[test]
3103    fn a_bit_field_in_one_of_those_records_is_counted_from_the_top_of_its_bytes() {
3104        let members = "short i : 12; char c1 : 1; char c2 : 1; char c3 : 1; char c4 : 1;";
3105        let read = "int f(struct s *p) { return p->i; }\n";
3106        let plain = format!("struct s {{ {members} }};\n{read}");
3107        let reversed = format!(
3108            "struct s {{ {members} }} __attribute__((scalar_storage_order(\"big-endian\")));\n\
3109             {read}"
3110        );
3111        assert!(body(&plain).contains("shl"), "{}", body(&plain));
3112        assert!(!body(&plain).contains("bswap"), "{}", body(&plain));
3113        // The two loaded bytes the other way round and then the top twelve bits of them, which
3114        // is the arithmetic shift right on its own with nothing to move the field up to the top.
3115        let built = body(&reversed);
3116        assert!(built.contains("bswap"), "{built}");
3117        assert!(!built.contains("shl"), "{built}");
3118        assert!(built.contains("ashr"), "{built}");
3119    }
3120
3121    /// The one thing a program may not do with a member of one of these records. The bytes are
3122    /// there and they are the other way round, so a pointer to them is a pointer to a value of
3123    /// that type which is not the value the member holds. gcc refuses it in these words, and it
3124    /// refuses only the scalars: the address of a nested record or of an array member is an
3125    /// address of the bytes as they lie, and an access through it asks its own type which order
3126    /// it is in.
3127    #[test]
3128    fn the_address_of_a_scalar_stored_the_other_way_round_is_refused() {
3129        let opts = options();
3130        let record = "struct s { int i; int a[2]; struct in { int n; } w; }\n\
3131                      __attribute__((scalar_storage_order(\"big-endian\")));\n";
3132        let taken = format!("{record}int *f(struct s *p) {{ return &p->i; }}\n");
3133        assert_eq!(
3134            run(&opts, &taken).messages,
3135            ["/main.c:3:30: error: cannot take address of scalar with reverse storage order \
3136              [E0712]"]
3137        );
3138        let element = format!("{record}int *f(struct s *p) {{ return &p->a[0]; }}\n");
3139        let messages = run(&opts, &element).messages;
3140        assert!(messages[0].contains("[E0712]"), "{messages:?}");
3141
3142        let whole = format!("{record}int *f(struct s *p) {{ return (int *) &p->w; }}\n");
3143        assert_eq!(run(&opts, &whole).messages, Vec::<String>::new(), "{whole}");
3144    }
3145
3146    /// An argument that names neither order, which gcc answers with the two words it does take.
3147    /// A program that writes one of these is reading a wire format and would rather be told the
3148    /// spelling it got wrong than be handed a record laid out in the order it did not ask for.
3149    #[test]
3150    fn a_storage_order_that_names_neither_end_is_refused_with_the_two_words_that_are_taken() {
3151        let opts = options();
3152        let wrong = "struct s { int i; } __attribute__((scalar_storage_order(\"middle\")));\n";
3153        assert_eq!(
3154            run(&opts, wrong).messages,
3155            ["/main.c:1:36: error: 'scalar_storage_order' argument must be one of \"big-endian\" \
3156              or \"little-endian\" [E0688]"]
3157        );
3158        let bare = "struct s { int i; } __attribute__((scalar_storage_order));\n";
3159        let messages = run(&opts, bare).messages;
3160        assert!(messages[0].contains("[E0688]"), "{messages:?}");
3161    }
3162
3163    /// Where a bit-field goes, which packing decides and which is the part of all this that
3164    /// is not what the names suggest. A bit-field goes at the next free bit unless that would
3165    /// make it span more storage than its own type occupies, and then it moves to the next
3166    /// boundary of its alignment. Any packing at all takes that rule out, and `#pragma pack`
3167    /// counts even where it lowers nothing, which is the fourth and seventh cases here.
3168    ///
3169    /// Nothing in the language can be asked where a bit-field is, since `offsetof` refuses one
3170    /// and every size below comes out the same either way, so what is asked is the byte a read
3171    /// of the field loads from.
3172    #[test]
3173    fn packing_is_what_decides_whether_a_bit_field_may_straddle_its_own_storage() {
3174        // A `char` field after twelve bits, which will not straddle unpacked and does packed.
3175        assert_eq!(bit_field_byte("struct s { int x : 12; char y : 6; };"), 2);
3176        assert_eq!(
3177            bit_field_byte("struct s { int x : 12; char y : 6; } __attribute__((packed));"),
3178            1
3179        );
3180        assert_eq!(
3181            bit_field_byte("struct s { int x : 12; __attribute__((packed)) char y : 6; };"),
3182            1
3183        );
3184        assert_eq!(bit_field_byte("#pragma pack(4)\nstruct s { int x : 12; char y : 6; };"), 1);
3185        // A thirty bit field after a byte, which is the case the rule was written for.
3186        assert_eq!(bit_field_byte("struct s { char x; int y : 30; };"), 4);
3187        assert_eq!(bit_field_byte("struct s { char x; int y : 30; } __attribute__((packed));"), 1);
3188        // Four is what an `int` asked for anyway, so this caps nothing and still counts.
3189        assert_eq!(bit_field_byte("#pragma pack(4)\nstruct s { char x; int y : 30; };"), 1);
3190        assert_eq!(bit_field_byte("#pragma pack(2)\nstruct s { char x; int y : 30; };"), 1);
3191    }
3192
3193    /// The byte a read of `s.y` loads from, which is where the bit-field was placed.
3194    fn bit_field_byte(record: &str) -> u64 {
3195        let source = format!("{record}\nint f(struct s *p) {{ return p->y; }}\n");
3196        let body = body(&source);
3197        let Some((before, _)) = body.split_once("ptr_add") else { return 0 };
3198        let (_, constant) = before.rsplit_once("iconst.i64 ").expect("an offset constant");
3199        constant.lines().next().expect("a line").trim().parse().expect("a byte offset")
3200    }
3201
3202    /// An attribute in the middle of a specifier list, which is where a member usually carries
3203    /// one and which was read and then thrown away. The `[[...]]` spelling and whatever was
3204    /// written in front of the declaration are collected as the list is walked and the
3205    /// `__attribute__` spelling is put straight on the specifiers, and the two were assigned
3206    /// over each other rather than joined.
3207    #[test]
3208    fn an_attribute_among_the_specifiers_is_kept_beside_the_ones_written_in_front() {
3209        tast(concat!(
3210            "struct a { char c; __attribute__((aligned(8))) int i; };\n",
3211            "_Static_assert(sizeof(struct a) == 16 && _Alignof(struct a) == 8, \"a\");\n",
3212            "_Static_assert(__builtin_offsetof(struct a, i) == 8, \"a.i\");\n",
3213            "struct b { char c; __attribute__((packed)) int i; };\n",
3214            "_Static_assert(sizeof(struct b) == 5 && _Alignof(struct b) == 1, \"b\");\n",
3215            "_Static_assert(__builtin_offsetof(struct b, i) == 1, \"b.i\");\n",
3216            "typedef struct { char c; int i; } __attribute__((packed)) c;\n",
3217            "_Static_assert(sizeof(c) == 5 && _Alignof(c) == 1, \"c\");\n",
3218        ));
3219    }
3220
3221    /// The other half, which is `#pragma pack`. It caps a member's alignment where `packed`
3222    /// drops it, so `pack(2)` leaves a `short` where it was and moves an `int`, and it caps a
3223    /// member the program asked to align as well, which is where the two differ. It is read
3224    /// at the closing brace of the body, so a line written in the middle of one settles the
3225    /// whole record rather than the members after it, and `push` and `pop` nest.
3226    #[test]
3227    fn pragma_pack_caps_every_member_and_is_read_where_the_body_closes() {
3228        tast(concat!(
3229            "#pragma pack(1)\n",
3230            "struct A { char c; int i; };\n",
3231            "_Static_assert(sizeof(struct A) == 5 && _Alignof(struct A) == 1, \"A\");\n",
3232            "_Static_assert(__builtin_offsetof(struct A, i) == 1, \"A.i\");\n",
3233            "#pragma pack()\n",
3234            "struct B { char c; int i; };\n",
3235            "_Static_assert(sizeof(struct B) == 8 && _Alignof(struct B) == 4, \"B\");\n",
3236            "#pragma pack(2)\n",
3237            "struct C { char c; int i; double d; };\n",
3238            "_Static_assert(sizeof(struct C) == 14 && _Alignof(struct C) == 2, \"C\");\n",
3239            "_Static_assert(__builtin_offsetof(struct C, d) == 6, \"C.d\");\n",
3240            // A member the program aligned, which `pack` caps and `packed` would not.
3241            "struct K { char c; int i __attribute__((aligned(8))); };\n",
3242            "_Static_assert(sizeof(struct K) == 6 && _Alignof(struct K) == 2, \"K\");\n",
3243            "_Static_assert(__builtin_offsetof(struct K, i) == 2, \"K.i\");\n",
3244            // The record's own `aligned` is not a member's, so it is not capped.
3245            "struct J { char c; int i; } __attribute__((aligned(8)));\n",
3246            "_Static_assert(sizeof(struct J) == 8 && _Alignof(struct J) == 8, \"J\");\n",
3247            "#pragma pack()\n",
3248            "#pragma pack(push, 1)\n",
3249            "struct D { char c; short s; };\n",
3250            "_Static_assert(sizeof(struct D) == 3 && _Alignof(struct D) == 1, \"D\");\n",
3251            "#pragma pack(pop)\n",
3252            "struct E { char c; short s; };\n",
3253            "_Static_assert(sizeof(struct E) == 4 && _Alignof(struct E) == 2, \"E\");\n",
3254            // Written in the middle of a body, and it still settles the whole record.
3255            "struct H { char c;\n",
3256            "#pragma pack(1)\n",
3257            "  int i; };\n",
3258            "_Static_assert(sizeof(struct H) == 5 && _Alignof(struct H) == 1, \"H\");\n",
3259            "#pragma pack(1)\n",
3260            "struct I { char c;\n",
3261            "#pragma pack()\n",
3262            "  int i; };\n",
3263            "_Static_assert(sizeof(struct I) == 8 && _Alignof(struct I) == 4, \"I\");\n",
3264            "#pragma pack()\n",
3265            // Nested pushes, each one giving back what the one under it had.
3266            "#pragma pack(push, 8)\n",
3267            "#pragma pack(push, 1)\n",
3268            "struct P { char c; int i; };\n",
3269            "_Static_assert(sizeof(struct P) == 5 && _Alignof(struct P) == 1, \"P\");\n",
3270            "#pragma pack(pop)\n",
3271            "struct Q { char c; int i; };\n",
3272            "_Static_assert(sizeof(struct Q) == 8 && _Alignof(struct Q) == 4, \"Q\");\n",
3273            "#pragma pack(pop)\n",
3274            // A cap above what every member already asks for changes nothing at all.
3275            "#pragma pack(16)\n",
3276            "struct R { char c; int i; };\n",
3277            "_Static_assert(sizeof(struct R) == 8 && _Alignof(struct R) == 4, \"R\");\n",
3278            "#pragma pack()\n",
3279            "#pragma pack(1)\n",
3280            "struct S { char c; int i : 5; int j : 20; };\n",
3281            "_Static_assert(sizeof(struct S) == 5 && _Alignof(struct S) == 1, \"S\");\n",
3282            "union T { char c; int i; };\n",
3283            "_Static_assert(sizeof(union T) == 4 && _Alignof(union T) == 1, \"T\");\n",
3284            "#pragma pack()\n",
3285        ));
3286    }
3287
3288    /// A line the reader cannot make sense of is a warning and the line is dropped, which is
3289    /// what GCC does with one, and these are its words for each of them. The last line is the
3290    /// one nothing else would reach, since it stands after every record in the file.
3291    #[test]
3292    fn a_pack_line_that_is_not_one_is_reported_in_the_words_gcc_uses() {
3293        let result = run(
3294            &options(),
3295            concat!(
3296                "#pragma pack 4\n",
3297                "#pragma pack(pop)\n",
3298                "#pragma pack(3)\n",
3299                "#pragma pack(1) junk\n",
3300                "#pragma pack(push, 1\n",
3301                "#pragma pack(x)\n",
3302                // These two are well formed and say nothing. Zero is how a line asks for the
3303                // target's own alignments back without writing empty parentheses.
3304                "#pragma pack(0)\n",
3305                "#pragma pack(push)\n",
3306                "struct s { char c; int i; };\n",
3307                "#pragma pack(pop)\n",
3308                "#pragma pack(pop, foo)\n",
3309            ),
3310        );
3311        let expected = [
3312            "missing `(` after `#pragma pack` - ignored",
3313            "`#pragma pack (pop)` encountered without matching `#pragma pack (push)`",
3314            "alignment must be a small power of two, not 3",
3315            "junk at end of `#pragma pack`",
3316            "malformed `#pragma pack(push[, id][, <n>])` - ignored",
3317            "unknown action `x` for `#pragma pack` - ignored",
3318            "`#pragma pack(pop, foo)` encountered without matching `#pragma pack(push, foo)`",
3319        ];
3320        assert_eq!(result.messages.len(), expected.len(), "{:?}", result.messages);
3321        for (message, want) in result.messages.iter().zip(expected) {
3322            assert!(message.contains(want), "expected {want:?} in {message:?}");
3323        }
3324    }
3325
3326    /// A pragma line ends where the next line starts, so a macro that comes to nothing and was
3327    /// written first on that next line has to hand the line on rather than take it away. This
3328    /// is SQLite through mingw-w64's headers: `<stdarg.h>` leaves a `#pragma pack(pop)` behind
3329    /// it and `sqlite3.h` writes every declaration with `SQLITE_API` in front, which is empty.
3330    /// Without it the pragma swallows the declaration, the program is left without it, and the
3331    /// only thing said about any of it is that there was junk on the pragma.
3332    #[test]
3333    fn a_declaration_behind_an_empty_macro_is_not_eaten_by_the_pragma_above_it() {
3334        let result = run(
3335            &options(),
3336            concat!(
3337                "#pragma pack(push, 1)\n",
3338                "#pragma pack(pop)\n",
3339                "#define API\n",
3340                "API const char version[] = \"3.53.4\";\n",
3341                "const char *get(void) { return version; }\n",
3342            ),
3343        );
3344        assert!(result.messages.is_empty(), "{:?}", result.messages);
3345    }
3346
3347    /// The two typedef spellings of the 128 bit types. gcc offers them as keywords rather
3348    /// than as typedefs in a header, which is the only way a program that includes nothing at
3349    /// all can still use them, and Apple's `<mach/arm/_structs.h>` is one such program.
3350    #[test]
3351    fn the_wide_integer_answers_to_all_three_of_its_names() {
3352        let text = tast("__uint128_t a; __int128_t b; unsigned __int128 c;\n");
3353        assert!(text.contains("decl #0 a : unsigned __int128"), "{text}");
3354        assert!(text.contains("decl #1 b : __int128"), "{text}");
3355        assert!(text.contains("decl #2 c : unsigned __int128"), "{text}");
3356    }
3357
3358    #[test]
3359    fn every_conversion_the_language_performs_is_a_node_in_the_output() {
3360        // The point of a typed tree. The source has one operator and the output has the
3361        // widening that operator asked for, spelled out, so that nothing downstream has to
3362        // work out the conversion rules a second time.
3363        let text = tast("long f(int a, long b) { return a + b; }\n");
3364        assert!(text.contains("convert arithmetic"), "{text}");
3365    }
3366
3367    #[test]
3368    fn a_mistake_in_each_phase_reaches_the_caller_and_writes_no_tree() {
3369        for source in [
3370            "#error stop\n",
3371            "int f(void) { return 1 + ; }\n",
3372            "int f(void) { return undeclared; }\n",
3373        ] {
3374            let result = run(&options(), source);
3375            assert!(result.failed(), "expected this to fail:\n{source}");
3376            assert!(
3377                result.text().is_empty(),
3378                "a file that did not compile wrote a tree:\n{source}"
3379            );
3380        }
3381    }
3382
3383    #[test]
3384    fn one_undeclared_name_is_one_message_and_not_one_per_use() {
3385        // The poisoning rule from `spec/06-lexer-and-parser.md` section 6.8, seen from the
3386        // outside. Three uses of a name that was never declared, and the operators over them
3387        // say nothing at all.
3388        let result = run(&options(), "int f(void) { return nope + nope * nope; }\n");
3389        assert_eq!(result.errors, 1, "{:?}", result.messages);
3390    }
3391
3392    #[test]
3393    fn a_declaration_the_parser_skipped_does_not_become_an_undeclared_name_as_well() {
3394        // The reason the checking is skipped after a failed parse. The parser gave up on the
3395        // first line and there is no `x` in the tree, so a checker run over it would report
3396        // every use of `x` below as undeclared, which is a second message about one mistake.
3397        let result = run(&options(), "int x = ;\nint f(void) { return x; }\n");
3398        assert_eq!(result.errors, 1, "{:?}", result.messages);
3399    }
3400
3401    #[test]
3402    fn werror_turns_a_warning_into_an_error_in_the_count_and_in_the_word() {
3403        let source = "int f(void) { char c = 300; return c; }\n";
3404        let plain = run(&options(), source);
3405        assert_eq!(plain.errors, 0, "{:?}", plain.messages);
3406        assert_eq!(plain.messages.len(), 1, "expected a warning about the narrowed constant");
3407        assert!(!plain.text().is_empty(), "a warning is not a reason to write nothing");
3408
3409        let mut opts = options();
3410        opts.warnings_are_errors = true;
3411        let strict = run(&opts, source);
3412        assert!(strict.failed());
3413        assert!(strict.text().is_empty(), "and under -Werror it is a reason to write nothing");
3414        for message in &strict.messages {
3415            assert!(!message.contains("warning:"), "{message}");
3416        }
3417    }
3418
3419    #[test]
3420    fn w_drops_the_warning_before_werror_can_promote_it() {
3421        let source = "int f(void) { char c = 300; return c; }\n";
3422        let mut opts = options();
3423        opts.warnings = false;
3424        let quiet = run(&opts, source);
3425        assert_eq!(quiet.messages, Vec::<String>::new());
3426        assert_eq!(quiet.errors, 0);
3427        assert!(!quiet.text().is_empty(), "and the file still compiles");
3428
3429        // A build that passes both means it wants neither, and the order it wrote them in is not
3430        // something to make it think about.
3431        opts.warnings_are_errors = true;
3432        let both = run(&opts, source);
3433        assert_eq!(both.messages, Vec::<String>::new());
3434        assert!(!both.failed(), "-w -Werror is not an error about a warning nobody saw");
3435    }
3436
3437    #[test]
3438    fn the_dialect_reaches_the_keywords_and_the_checking() {
3439        // `typeof` is C23's and GNU's, so the same source is a declaration under one dialect
3440        // and a mistake under the other, which is the keyword table being built per dialect.
3441        let source = "typeof(1) x;\n";
3442        let mut opts = options();
3443        opts.std = Std::C23;
3444        opts.gnu_extensions = false;
3445        assert!(!run(&opts, source).failed(), "{:?}", run(&opts, source).messages);
3446
3447        opts.std = Std::C17;
3448        assert!(run(&opts, source).failed());
3449    }
3450
3451    #[test]
3452    fn asking_for_a_kind_that_is_not_written_yet_runs_the_front_end_and_writes_nothing() {
3453        let mut opts = options();
3454        opts.emit = EmitKind::Object;
3455        let result = run(&opts, "int x = 1;\n");
3456        assert!(!result.failed(), "{:?}", result.messages);
3457        assert!(result.text().is_empty());
3458        // And it still finds what the checking finds, so a later kind on a broken file is not
3459        // a silent success.
3460        assert!(run(&opts, "int f(void) { return undeclared; }\n").failed());
3461    }
3462
3463    /// The machine code of `source`, insisting that it compiled cleanly.
3464    fn mir(source: &str) -> String {
3465        let mut opts = options();
3466        opts.emit = EmitKind::MirFinal;
3467        let result = run(&opts, source);
3468        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3469        result.text().to_owned()
3470    }
3471
3472    /// The whole compiler in one assertion, which is what this emit kind is for.
3473    ///
3474    /// C in, machine instructions out, every register a real one and every frame offset a
3475    /// number. Everything between the two is checked somewhere else, one pass at a time. What is
3476    /// checked here is that the passes are joined up and that the driver runs them.
3477    #[test]
3478    fn a_function_goes_from_c_to_instructions_with_real_registers_in_them() {
3479        let text = mir("int add(int a, int b) { return a + b; }\n");
3480        assert!(text.starts_with("mfunc @add {"), "{text}");
3481        assert!(text.contains("x64.add_rr_32"), "{text}");
3482        assert!(text.contains("x64.ret"), "{text}");
3483        // A virtual register is what the allocator was there to remove, so one left in the
3484        // output is the difference between code and something that looks like code.
3485        assert!(!text.contains('%'), "{text}");
3486    }
3487
3488    /// A declaration has no body, so there is nothing to generate for one and nothing is.
3489    #[test]
3490    fn a_function_with_no_body_produces_no_machine_function() {
3491        let text = mir("int g(int);\nint f(int a) { return g(a); }\n");
3492        assert_eq!(text.matches("mfunc @").count(), 1, "{text}");
3493        assert!(text.contains("mfunc @f {"), "{text}");
3494        assert!(text.contains("x64.call"), "{text}");
3495    }
3496
3497    /// Two functions come out in the order the module holds them, which is source order.
3498    #[test]
3499    fn every_definition_in_the_file_is_generated_and_they_keep_their_order() {
3500        let text = mir("int a(int x) { return x; }\nint b(int x) { return x; }\n");
3501        let first = text.find("mfunc @a").expect("the first function");
3502        let second = text.find("mfunc @b").expect("the second function");
3503        assert!(first < second, "{text}");
3504    }
3505
3506    /// The target reaches the back end, so the same C is different instructions on Windows.
3507    #[test]
3508    fn the_target_decides_which_convention_the_generated_code_follows() {
3509        let mut opts = options();
3510        opts.emit = EmitKind::MirFinal;
3511        let linux = run(&opts, "int f(int a) { return a; }\n").text().to_owned();
3512        assert!(linux.contains("$rdi"), "{linux}");
3513
3514        opts.target = "x86_64-pc-windows-msvc".parse::<Triple>().unwrap();
3515        let windows = run(&opts, "int f(int a) { return a; }\n").text().to_owned();
3516        assert!(windows.contains("$rcx"), "{windows}");
3517        assert!(!windows.contains("$rdi"), "{windows}");
3518    }
3519
3520    /// And it reaches the front end, where it decides what an anonymous member is.
3521    ///
3522    /// This is the shape `<objidl.h>` writes and the Windows headers are full of: the union inside
3523    /// `STGMEDIUM` closes with `} DUMMYUNIONNAME;`, and the macro expands to nothing unless the
3524    /// program defined `NONAMELESSUNION`, so what is left is a union with a tag and no name. On a
3525    /// Windows target that is an anonymous member, and reading it as a declaration of nothing
3526    /// drops it, which loses the names and the eight bytes the member takes up both.
3527    #[test]
3528    fn a_tagged_member_with_no_name_is_a_member_on_windows_and_nothing_on_linux() {
3529        let source = concat!(
3530            "struct S { union U { int i; void *p; }; unsigned long tymed; };\n",
3531            "int size(void) { return sizeof(struct S); }\n",
3532            "int f(struct S *s) { s->i = 1; return s->i; }\n",
3533        );
3534
3535        let mut opts = options();
3536        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
3537        let windows = run(&opts, source);
3538        assert!(windows.messages.is_empty(), "{:?}", windows.messages);
3539
3540        let linux = run(&options(), source);
3541        assert_eq!(linux.messages.len(), 3, "{:?}", linux.messages);
3542        assert!(linux.messages[0].contains("does not declare anything"), "{:?}", linux.messages);
3543
3544        // And the flag answers for either of them, so a program built for Linux against a header
3545        // written for Windows can be read the way the header meant it.
3546        let mut opts = options();
3547        opts.ms_extensions = Some(true);
3548        let asked = run(&opts, source);
3549        assert!(asked.messages.is_empty(), "{:?}", asked.messages);
3550    }
3551
3552    /// A target with no back end says so rather than generating something for another machine.
3553    #[test]
3554    fn a_target_this_has_no_back_end_for_is_reported_rather_than_generated() {
3555        let mut opts = options();
3556        opts.emit = EmitKind::MirFinal;
3557        opts.target = "riscv64-unknown-linux-gnu".parse::<Triple>().unwrap();
3558        let result = run(&opts, "int f(int a) { return a; }\n");
3559        assert!(result.failed());
3560        assert!(result.messages[0].contains("no back end for riscv64"), "{:?}", result.messages);
3561        assert!(result.text().is_empty());
3562    }
3563
3564    /// AArch64 is written as its own assembly, with a function that calls keeping its return
3565    /// address in the frame record.
3566    #[test]
3567    fn an_aarch64_target_is_written_as_aarch64_assembly() {
3568        let mut opts = options();
3569        opts.emit = EmitKind::Asm;
3570        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3571        let source = "int g(int);\nint f(int a, int b) { return g(a) + b; }\n";
3572        let result = run(&opts, source);
3573        assert!(!result.failed(), "{:?}", result.messages);
3574        let text = result.text();
3575        for line in ["stp x29, x30, [sp, #-16]!", "mov x29, sp", "bl g", "ldp x29, x30, [sp], #16"]
3576        {
3577            assert!(text.contains(line), "{line} is not in\n{text}");
3578        }
3579        assert!(!text.contains('%'), "{text}");
3580    }
3581
3582    /// An object for AArch64, which is the listing read back by the assembler. The same object
3583    /// with debug information is refused rather than written without its line table.
3584    #[test]
3585    fn an_aarch64_target_reaches_an_object_file() {
3586        let mut opts = options();
3587        opts.emit = EmitKind::Object;
3588        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3589        let source = concat!(
3590            "int g(int);\n",
3591            "int table[4] = {1, 2, 3, 4};\n",
3592            "int f(int a, int b) { return g(a) + table[b & 3]; }\n",
3593        );
3594        let result = run(&opts, source);
3595        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3596        let bytes = match result.artifact {
3597            Artifact::Object { bytes, defines } => {
3598                assert_eq!(defines, ["f", "table"]);
3599                bytes
3600            }
3601            other => panic!("expected an object, got {other:?}"),
3602        };
3603        assert_eq!(&bytes[..4], b"\x7fELF");
3604        assert_eq!(&bytes[18..20], &183u16.to_le_bytes(), "EM_AARCH64");
3605
3606        // And with debug information, which the listing path builds from a label in front of
3607        // every instruction rather than refusing.
3608        opts.debug_info = true;
3609        let result = run(&opts, source);
3610        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3611        let bytes = match result.artifact {
3612            Artifact::Object { bytes, .. } => bytes,
3613            other => panic!("expected an object, got {other:?}"),
3614        };
3615        let has = |name: &[u8]| bytes.windows(name.len()).any(|at| at == name);
3616        assert!(has(b".debug_line\0") && has(b".debug_info\0"));
3617        assert!(!has(b"rucc_row"), "a row label reached the symbol table");
3618    }
3619
3620    /// gcc's AArch64 vector type names are there before any header, which glibc's `<math.h>`
3621    /// needs, a declaration can still hide one, and on x86-64 they are ordinary identifiers.
3622    #[test]
3623    fn the_aarch64_vector_type_names_are_declared_on_that_target_and_nowhere_else() {
3624        let mut opts = options();
3625        opts.emit = EmitKind::Asm;
3626        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3627        let source = "typedef __Float32x4_t f4;\n__SVFloat32_t sv(__SVFloat32_t, __SVBool_t);\n\
3628                      int n = sizeof(f4) + sizeof(__Int8x8_t);\n\
3629                      int f(f4 v) { int __Uint8x16_t = 3; return v[1] + __Uint8x16_t; }\n";
3630        let result = run(&opts, source);
3631        assert!(!result.failed(), "{:?}", result.messages);
3632        assert!(result.text().contains(".long\t24"), "{}", result.text());
3633        opts.target = "x86_64-unknown-linux-gnu".parse::<Triple>().unwrap();
3634        let result = run(&opts, "typedef __Float32x4_t f4;\n");
3635        assert!(result.failed());
3636        let result = run(&opts, "int __Float32x4_t = 1;\n");
3637        assert!(!result.failed(), "{:?}", result.messages);
3638    }
3639
3640    /// A structure too big for registers comes back through the address in x8, which AAPCS64 keeps
3641    /// apart from the arguments, so the argument after it is still in x0.
3642    #[test]
3643    fn an_aarch64_result_in_memory_is_reached_through_x8() {
3644        let mut opts = options();
3645        opts.emit = EmitKind::Asm;
3646        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3647        let source = "struct big { long a, b, c; };\nstruct big make(long v);\n\
3648                      long f(long v) { return make(v).c; }\n\
3649                      struct big g(long v) { struct big b = { v, v, v }; return b; }\n";
3650        let result = run(&opts, source);
3651        assert!(!result.failed(), "{:?}", result.messages);
3652        let text = result.text();
3653        assert!(text.contains("x8"), "{text}");
3654        assert!(text.contains("bl make"), "{text}");
3655    }
3656
3657    /// A remainder is two instructions on AArch64, the division and then a multiply subtract that
3658    /// reads the quotient the division wrote.
3659    #[test]
3660    fn an_aarch64_remainder_is_a_division_and_a_multiply_subtract() {
3661        let mut opts = options();
3662        opts.emit = EmitKind::Asm;
3663        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3664        let source = "int s(int a, int b) { return a % b; }\n\
3665                      unsigned long u(unsigned long a, unsigned long b) { return a % b; }\n";
3666        let result = run(&opts, source);
3667        assert!(!result.failed(), "{:?}", result.messages);
3668        let text = result.text();
3669        let at = |what: &str| text.find(what).unwrap_or_else(|| panic!("{what} is not in\n{text}"));
3670        assert!(at("sdiv w") < at("msub w"), "{text}");
3671        assert!(at("udiv x") < at("msub x"), "{text}");
3672    }
3673
3674    /// A dense `switch` on AArch64 reads a cell of a table after the function with `adr` and
3675    /// `ldrsw`, and each cell is the distance from the table to an arm.
3676    #[test]
3677    fn an_aarch64_jump_table_is_reached_with_adr() {
3678        let mut opts = options();
3679        opts.emit = EmitKind::Asm;
3680        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3681        let source = "int f(int x) { switch (x) { case 0: return 10; case 1: return 21; \
3682                      case 2: return 32; case 3: return 43; case 4: return 54; case 5: return 65; \
3683                      case 6: return 76; case 7: return 87; case 8: return 98; case 9: return 9; \
3684                      case 10: return 19; case 11: return 29; default: return 0; } }\n";
3685        let result = run(&opts, source);
3686        assert!(!result.failed(), "{:?}", result.messages);
3687        let text = result.text();
3688        let at = |what: &str| text.find(what).unwrap_or_else(|| panic!("{what} is not in\n{text}"));
3689        assert!(at("adr x") < at("ldrsw x"), "{text}");
3690        assert!(at("ldrsw x") < at("br x"), "{text}");
3691        assert!(text.contains("_j0:"), "{text}");
3692        assert!(text.contains(".long"), "{text}");
3693    }
3694
3695    /// An AArch64 Linux `va_start` fills in the five fields AAPCS64 gives a list. The two offsets
3696    /// count up to nothing from minus the size of what is left of each half of the save area, so
3697    /// with one integer named they start at minus fifty six and minus one hundred and twenty eight.
3698    #[test]
3699    fn an_aarch64_va_start_writes_the_five_fields_of_its_list() {
3700        let mut opts = options();
3701        opts.emit = EmitKind::Asm;
3702        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3703        let source = "typedef __builtin_va_list va_list;\n\
3704                      int f(int n, ...) { va_list ap; __builtin_va_start(ap, n); \
3705                      int x = __builtin_va_arg(ap, int); double d = __builtin_va_arg(ap, double); \
3706                      __builtin_va_end(ap); return x + (int)d; }\n";
3707        let result = run(&opts, source);
3708        assert!(!result.failed(), "{:?}", result.messages);
3709        let text = result.text();
3710        assert!(text.contains("#-56"), "{text}");
3711        assert!(text.contains("#-128"), "{text}");
3712        assert!(text.contains("#24]"), "{text}");
3713        assert!(text.contains("#28]"), "{text}");
3714        assert!(text.contains("str q"), "{text}");
3715    }
3716
3717    /// A `long double` on AArch64 Linux is a quad, moved with `ldr q` and `str q` and added with a
3718    /// call to the same routine libgcc has.
3719    #[test]
3720    fn an_aarch64_long_double_is_a_quad_in_a_vector_register() {
3721        let mut opts = options();
3722        opts.emit = EmitKind::Asm;
3723        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3724        let source = "void f(long double *p, long double x) { *p = *p + x; }\n";
3725        let result = run(&opts, source);
3726        assert!(!result.failed(), "{:?}", result.messages);
3727        let text = result.text();
3728        assert!(text.contains("ldr q"), "{text}");
3729        assert!(text.contains("str q"), "{text}");
3730        assert!(text.contains("__addtf3"), "{text}");
3731    }
3732
3733    /// A thread-local variable on AArch64 Linux is initial exec: its offset comes out of the
3734    /// global offset table, the thread pointer out of `tpidr_el0`, and one `add` joins them.
3735    #[test]
3736    fn an_aarch64_thread_local_is_reached_through_tpidr_el0() {
3737        let mut opts = options();
3738        opts.emit = EmitKind::Asm;
3739        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3740        let source = "__thread int n;\nint *f(void) { return &n; }\n\
3741                      void *g(void) { return __builtin_thread_pointer(); }\n";
3742        let result = run(&opts, source);
3743        assert!(!result.failed(), "{:?}", result.messages);
3744        let text = result.text();
3745        assert!(text.contains(":gottprel:n"), "{text}");
3746        assert!(text.contains(":gottprel_lo12:n]"), "{text}");
3747        assert_eq!(text.matches("mrs x").count(), 2, "{text}");
3748        assert!(text.contains("tpidr_el0"), "{text}");
3749    }
3750
3751    /// Apple's platforms reach a thread-local variable by calling through its descriptor, which
3752    /// is what clang writes on both machines, and the variable is the image and the descriptor.
3753    #[test]
3754    fn a_darwin_thread_local_is_reached_through_its_descriptor() {
3755        let source = "__thread int n = 5;\nint *f(void) { return &n; }\n";
3756        for (triple, wanted) in [
3757            ("aarch64-apple-darwin", &["_n@TLVPPAGE\n", "_n@TLVPPAGEOFF]\n", "\tblr x"][..]),
3758            ("x86_64-apple-darwin", &["_n@TLVP(%rip), %rdi\n", "\tcall\t*%"][..]),
3759        ] {
3760            let mut opts = options();
3761            opts.emit = EmitKind::Asm;
3762            opts.target = triple.parse::<Triple>().unwrap();
3763            let result = run(&opts, source);
3764            assert!(!result.failed(), "{triple}: {:?}", result.messages);
3765            let text = result.text();
3766            for want in wanted {
3767                assert!(text.contains(want), "{triple} wanted {want:?}:\n{text}");
3768            }
3769            assert!(text.contains("\n_n:\n\t.quad\t__tlv_bootstrap\n"), "{text}");
3770            assert!(!text.contains("tpidr_el0") && !text.contains("%fs"), "{text}");
3771        }
3772    }
3773
3774    /// The thread pointer itself is somewhere else on Apple's platforms and is still refused.
3775    #[test]
3776    fn the_thread_pointer_is_refused_on_darwin() {
3777        let mut opts = options();
3778        opts.emit = EmitKind::Asm;
3779        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
3780        let result = run(&opts, "void *f(void) { return __builtin_thread_pointer(); }\n");
3781        assert!(result.failed());
3782        assert!(result.messages[0].contains("thread pointer"), "{:?}", result.messages);
3783    }
3784
3785    /// Darwin's list is a plain pointer and its variadic arguments are all on the stack, so a
3786    /// variadic definition saves no registers and its `va_start` stores one address.
3787    #[test]
3788    fn a_darwin_variadic_definition_saves_nothing_and_walks_the_stack() {
3789        let mut opts = options();
3790        opts.emit = EmitKind::Asm;
3791        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
3792        let source = "int f(int n, ...) { __builtin_va_list ap; __builtin_va_start(ap, n);\n\
3793                      int r = __builtin_va_arg(ap, int); __builtin_va_end(ap); return r; }\n";
3794        let result = run(&opts, source);
3795        assert!(!result.failed(), "{:?}", result.messages);
3796        let text = result.text();
3797        assert!(!text.contains("str q"), "{text}");
3798        assert!(!text.contains("x7"), "{text}");
3799    }
3800
3801    /// A call on Darwin puts every argument past the named ones in memory, even with registers
3802    /// left over, so the `double` here is stored rather than put in `d0`.
3803    #[test]
3804    fn a_darwin_call_puts_its_variadic_arguments_in_memory() {
3805        let mut opts = options();
3806        opts.emit = EmitKind::Asm;
3807        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
3808        let source = "int printf(const char *, ...);\n\
3809                      int g(double x) { return printf(\"%d %f\", 7, x); }\n";
3810        let result = run(&opts, source);
3811        assert!(!result.failed(), "{:?}", result.messages);
3812        let text = result.text();
3813        assert!(text.contains("str d0, [sp, #8]"), "{text}");
3814    }
3815
3816    /// Apple's assembler asks for part of an address after the name, a variable another image
3817    /// defines is read through the table because nothing copies it in, and the directive that
3818    /// makes a zeroed variable is also its definition, so its binding goes above it.
3819    #[test]
3820    fn a_darwin_listing_is_one_apples_assembler_reads() {
3821        let mut opts = options();
3822        opts.emit = EmitKind::Asm;
3823        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
3824        let source = "extern int ext;\n\
3825                      int g[4];\n\
3826                      int f(int i) { return g[i] + ext; }\n";
3827        let result = run(&opts, source);
3828        assert!(!result.failed(), "{:?}", result.messages);
3829        let text = result.text();
3830        assert!(text.contains(", _g@PAGE\n"), "{text}");
3831        assert!(text.contains(", _g@PAGEOFF\n"), "{text}");
3832        assert!(text.contains(", _ext@GOTPAGE\n"), "{text}");
3833        assert!(text.contains(", _ext@GOTPAGEOFF]\n"), "{text}");
3834        assert!(!text.contains(":lo12:"), "{text}");
3835        assert!(text.contains("\t.globl\t_g\n\t.zerofill\t__DATA,__bss,_g,16,2\n"), "{text}");
3836    }
3837
3838    /// A `signed char` read from memory and added to at 32 bits is widened with its sign first.
3839    ///
3840    /// The widening was being taken out as unneeded, because its source is written as a `w`
3841    /// register and was taken to have 32 bits in it, so `*p + 1` added one to the byte `ldrb` had
3842    /// loaded and -9 came out as 248. At every level, since the pass runs at `-O0` too.
3843    #[test]
3844    fn a_signed_char_on_aarch64_is_widened_with_its_sign_before_it_is_added_to() {
3845        for target in ["aarch64-linux-gnu", "aarch64-apple-darwin"] {
3846            let mut opts = options();
3847            opts.emit = EmitKind::Asm;
3848            opts.target = target.parse::<Triple>().unwrap();
3849            let source = "int f(signed char *p) { return *p + 1; }\n\
3850                          unsigned g(unsigned short *p) { return *p + 1u; }\n";
3851            let result = run(&opts, source);
3852            assert!(!result.failed(), "{:?}", result.messages);
3853            let text = result.text();
3854            let signed = text.contains("\tsxtb w") || text.contains("\tldrsb w");
3855            assert!(signed, "{target}: {text}");
3856        }
3857    }
3858
3859    /// A construct the rule set does not reach yet is named, along with the function it is in.
3860    ///
3861    /// The message is about this compiler being unfinished rather than about the program, which
3862    /// is valid C either way, so it carries the note that says where the work is tracked. Both
3863    /// functions are attempted, so a file that is ahead of the back end in three places says so
3864    /// three times rather than one recompilation at a time.
3865    ///
3866    /// The construct is a local of a fixed size wanting more alignment than a call leaves the
3867    /// stack pointer on, in a function whose frame also grows. The prologue would force the
3868    /// alignment and the array would move the stack pointer afterwards, and those are two frames
3869    /// that each want the one register the rest of the frame is counted from.
3870    #[test]
3871    fn a_construct_the_back_end_cannot_reach_yet_is_reported_against_its_function() {
3872        let mut opts = options();
3873        opts.emit = EmitKind::MirFinal;
3874        let source = "void a(int n) { int v[n]; struct __attribute__((aligned(32))) S { int x; } \
3875                      s; s.x = 1; v[0] = s.x; }\n\
3876                      void b(int n) { int v[n]; struct __attribute__((aligned(32))) S { int x; } \
3877                      s; s.x = 1; v[0] = s.x; }\n";
3878        let result = run(&opts, source);
3879        assert!(result.failed());
3880        assert_eq!(result.messages.len(), 2, "{:?}", result.messages);
3881        assert!(result.messages[0].contains("cannot generate code for 'a'"), "{:?}", result);
3882        assert!(result.messages[0].contains("wants more alignment"), "{:?}", result);
3883        assert!(result.messages[1].contains("cannot generate code for 'b'"), "{:?}", result);
3884        assert!(result.text().is_empty());
3885    }
3886
3887    /// A variable length array walks its pages under the flag that says every page is touched.
3888    ///
3889    /// The pages the prologue takes are touched by the prologue. The pages the array takes are
3890    /// however many the size worked out to, so touching them is a loop written around the
3891    /// declaration rather than anything a prologue can do. What says the loop is there is the
3892    /// ordered comparison it ends each step with, which nothing else in a function writes, and the
3893    /// touch behind it. Without the flag the declaration is still the one subtraction it always was.
3894    #[test]
3895    fn a_variable_length_array_walks_its_pages_where_every_page_of_the_frame_is_to_be_touched() {
3896        let mut opts = options();
3897        opts.emit = EmitKind::MirFinal;
3898        let source = "void a(int n) { int v[n]; v[0] = 1; }\n";
3899        let plain = run(&opts, source);
3900        assert!(!plain.failed(), "{:?}", plain.messages);
3901        assert!(!plain.text().contains("cmp_set_a_64"), "{}", plain.text());
3902
3903        opts.stack_clash = true;
3904        let result = run(&opts, source);
3905        assert!(!result.failed(), "{:?}", result.messages);
3906        assert!(result.text().contains("cmp_set_a_64"), "{}", result.text());
3907        assert!(result.text().contains("or_mi_8"), "{}", result.text());
3908    }
3909
3910    /// A function that keeps a frame pointer on Windows now has an unwind record and an object.
3911    ///
3912    /// The record that platform carries counts every slot in it from where the stack pointer ends
3913    /// the prologue, and it gets to that place by taking a constant off the frame pointer, so a
3914    /// register pushed after the pointer was established has no row the format can write. The order
3915    /// that does have one is the pushes, then the frame, and only then the pointer, which is what
3916    /// the back end writes there and only there. A variable length array and an `alloca` keep a
3917    /// pointer whatever the flags asked for, so before this they were the two shapes of C that
3918    /// could not be compiled for that target at all. See tamnd/rucc#1403.
3919    #[test]
3920    fn a_function_that_keeps_a_frame_pointer_on_windows_reaches_an_object_file() {
3921        let mut opts = options();
3922        opts.emit = EmitKind::Object;
3923        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
3924        let source = concat!(
3925            "void use(void *p);\n",
3926            "void array(int n) { int v[n]; v[0] = 1; use(v); }\n",
3927            "void taken(unsigned long n) { use(__builtin_alloca(n)); }\n",
3928        );
3929        let result = run(&opts, source);
3930        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3931        let bytes = match result.artifact {
3932            Artifact::Object { bytes, .. } => bytes,
3933            other => panic!("expected an object, got {other:?}"),
3934        };
3935        assert_eq!(&bytes[..2], b"\x64\x86", "an object that says which machine it is for");
3936
3937        // And the same two functions for Linux, so that what the test is measuring is the target
3938        // rather than the program being one this compiler cannot reach yet.
3939        let mut opts = options();
3940        opts.emit = EmitKind::Object;
3941        assert_eq!(run(&opts, source).messages, Vec::<String>::new());
3942    }
3943
3944    /// The address of a name this file only declares, on the format with no table to read it out
3945    /// of.
3946    ///
3947    /// Every such name went into the table on every target, and COFF has no table, so the object
3948    /// writer was handed a relocation it has no way to write and refused the whole file. What the
3949    /// name stands for on this format is an address in the image whichever way the link supplies
3950    /// it, so the instruction pointer reaches it and gcc writes the same. Three shapes here, since
3951    /// the one that found it was a callback stored in a table of its own: a function passed as an
3952    /// argument, one put in a variable that lives past the call, and one called outright, which
3953    /// never needed the table and is here so the test says which of the three changed.
3954    #[test]
3955    fn the_address_of_a_function_this_file_only_declares_reaches_a_windows_object() {
3956        let source = concat!(
3957            "void other(void *p);\n",
3958            "void takes(void (*f)(void *));\n",
3959            "void (*held)(void *);\n",
3960            "void pass(void) { takes(other); }\n",
3961            "void keep(void) { held = other; }\n",
3962            "void call(void) { other(0); }\n",
3963        );
3964        let mut opts = options();
3965        opts.emit = EmitKind::Object;
3966        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
3967        let result = run(&opts, source);
3968        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3969        let bytes = match result.artifact {
3970            Artifact::Object { bytes, .. } => bytes,
3971            other => panic!("expected an object, got {other:?}"),
3972        };
3973        assert_eq!(&bytes[..2], b"\x64\x86", "an object that says which machine it is for");
3974
3975        // And the same source for Linux, which does have a table and still uses it, so what this
3976        // measures is the format rather than the program.
3977        let mut opts = options();
3978        opts.emit = EmitKind::Object;
3979        assert_eq!(run(&opts, source).messages, Vec::<String>::new());
3980    }
3981
3982    /// An opcode the rule language has no word for is named anyway, and pointed at.
3983    ///
3984    /// The rule language's spelling is the better name when there is one, but an opcode it has
3985    /// no word for is exactly the opcode no rule lowers, so falling back to the opcode and the
3986    /// type is what makes the message say anything at all in the cases that happen. The span is
3987    /// the instruction's own, so the message lands on the line rather than on the file.
3988    ///
3989    /// The width of the float is what keeps the program refused. Everything else here is split into
3990    /// halves by `rucc_codegen::wide`, including the divisions and the conversions to a `float` and
3991    /// a `double`, which became calls into the compiler runtime. A `long double` is the eighty bit
3992    /// float on this target, the runtime has no conversion at that width because the back end has no
3993    /// register that holds one, which is tamnd/rucc#326, so a function converting to it is left with
3994    /// its wide values and reaches the selector the way every function of this width used to.
3995    #[test]
3996    fn an_opcode_with_no_name_in_the_rule_language_is_named_by_its_own_spelling() {
3997        let mut opts = options();
3998        opts.emit = EmitKind::MirFinal;
3999        let source =
4000            "long double f(int a) {\n  __int128 wide = a;\n  return (long double) wide;\n}\n";
4001        let result = run(&opts, source);
4002        assert!(result.failed());
4003        assert!(
4004            result.messages[0].contains("no rule lowers a `sext` producing a `i128`"),
4005            "{result:?}"
4006        );
4007        assert!(result.messages[0].contains(":2:"), "the line the widening is on: {result:?}");
4008        assert!(!result.messages[0].contains("this instruction"), "{result:?}");
4009    }
4010
4011    /// The note names the issue tracker, which is where a reader finds out whether it is known.
4012    #[test]
4013    fn the_note_on_unfinished_work_points_at_the_issues_rather_than_at_the_plan() {
4014        let mut opts = options();
4015        opts.emit = EmitKind::MirFinal;
4016        let source = "long double f(int a) { __int128 wide = a; return (long double) wide; }\n";
4017        let result = run(&opts, source);
4018        assert!(result.failed());
4019        let note = result.messages.iter().find(|line| line.contains("note:")).expect("a note");
4020        assert!(note.contains("https://github.com/tamnd/rucc/issues"), "{note}");
4021        assert!(!note.contains("spec/17-milestones.md"), "{note}");
4022    }
4023
4024    /// The two frame flags reach the frame, which is the only thing either of them does.
4025    #[test]
4026    fn the_frame_flags_on_the_command_line_reach_the_generated_frame() {
4027        let source = "int f(int a) { return a; }\n";
4028        assert!(!mir(source).contains("$rbp"), "a leaf needs no frame pointer when told so");
4029
4030        let mut opts = options();
4031        opts.emit = EmitKind::MirFinal;
4032        opts.frame_pointer = Some(true);
4033        let kept = run(&opts, source).text().to_owned();
4034        assert!(kept.contains("x64.push_64 $rbp"), "{kept}");
4035
4036        // Nothing said at -O0 is a frame pointer, which is what gcc keeps there.
4037        opts.frame_pointer = None;
4038        let kept = run(&opts, source).text().to_owned();
4039        assert!(kept.contains("x64.push_64 $rbp"), "{kept}");
4040    }
4041
4042    /// The assembly of `source`, insisting that it compiled cleanly.
4043    fn asm(source: &str) -> String {
4044        let mut opts = options();
4045        opts.emit = EmitKind::Asm;
4046        let result = run(&opts, source);
4047        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4048        result.text().to_owned()
4049    }
4050
4051    /// `-S`, which is the same compiler as the kind above it with a different last step.
4052    ///
4053    /// What the assembly says is checked in `rucc-asm`, one instruction at a time and against the
4054    /// target's own description of what an instruction is. What is checked here is that a C file
4055    /// goes all the way to a listing an assembler would take, which means the directives around
4056    /// the function as well as the instructions in it.
4057    #[test]
4058    fn a_function_goes_from_c_to_assembly_an_assembler_would_take() {
4059        let text = asm("int add(int a, int b) { return a + b; }\n");
4060        assert!(text.contains("\t.globl\tadd\n"), "{text}");
4061        assert!(text.contains("\t.type\tadd, @function\n"), "{text}");
4062        assert!(text.contains("\nadd:\n"), "{text}");
4063        assert!(text.contains("\taddl\t"), "{text}");
4064        assert!(text.contains("\tret\n"), "{text}");
4065        assert!(text.contains("\t.size\tadd, .-add\n"), "{text}");
4066        // Without this the stack the program runs on is executable, which is not a default
4067        // anybody chose and is not a thing a reader would notice missing.
4068        assert!(text.contains(".note.GNU-stack"), "{text}");
4069    }
4070
4071    /// A call through a function pointer, which is a different instruction from a call to a name.
4072    ///
4073    /// Both are in the one function on purpose. What is being read is that the two calls are told
4074    /// apart all the way down: one carries a name the linker resolves and one carries a register,
4075    /// and neither turns into the other on the way.
4076    #[test]
4077    fn a_call_through_a_function_pointer_goes_through_the_register_it_is_in() {
4078        let text = asm("int g(int);\nint f(int (*p)(int), int a) { return p(a) + g(a); }\n");
4079        assert!(text.contains("\tcall\t*%"), "{text}");
4080        assert!(text.contains("\tcall\tg\n"), "{text}");
4081        // The address arrived in the first argument register and the argument the call passes has
4082        // to end up there, so the two cannot be the same register and the compiler has to have
4083        // moved one of them.
4084        assert!(text.contains("%rdi"), "{text}");
4085    }
4086
4087    /// A name at file scope, which is the one address a function cannot compute for itself. The
4088    /// `lea` that computes it is folded into the load that reads through it, so what is left to
4089    /// read is the addressing mode, which is where the instruction pointer shows up.
4090    #[test]
4091    fn the_address_of_a_global_is_read_from_the_instruction_pointer() {
4092        let text = asm("extern int counter;\nint f(void) { return counter; }\n");
4093        assert!(text.contains("\tmovl\tcounter(%rip), %eax\n"), "{text}");
4094    }
4095
4096    /// Every comparison a branch can be on, which the machine jumps on without keeping a byte.
4097    ///
4098    /// Ten conditions, and each of them comes out as its opposite because the block falls into the
4099    /// arm the comparison is true for and jumps to the other one. That is the half of this most
4100    /// worth pinning: a jump on the condition rather than on its opposite compiles, encodes and
4101    /// runs, and gets every one of these ten functions backwards. The unsigned four and the signed
4102    /// four are separate for the same reason, since `jl` where `jb` was meant is a program that
4103    /// works until an address is above two gigabytes.
4104    #[test]
4105    fn a_branch_on_a_comparison_jumps_on_the_opposite_of_what_it_compared() {
4106        let arms = "return 1; return 2;";
4107        let signed = [("==", "jne"), ("!=", "je"), ("<", "jge"), ("<=", "jg"), (">", "jle")];
4108        for (operator, jump) in signed.into_iter().chain([(">=", "jl")]) {
4109            let text = asm(&format!("int f(int a, int b) {{ if (a {operator} b) {arms} }}\n"));
4110            assert!(
4111                text.contains(&format!("\tcmpl\t%esi, %edi\n\t{jump}\t")),
4112                "{operator}: {text}"
4113            );
4114            assert!(!text.contains("\tset"), "{operator}: {text}");
4115            assert!(!text.contains("\ttest"), "{operator}: {text}");
4116        }
4117        let unsigned = [("<", "jae"), ("<=", "ja"), (">", "jbe"), (">=", "jb")];
4118        for (operator, jump) in unsigned {
4119            let source =
4120                format!("int f(unsigned a, unsigned b) {{ if (a {operator} b) {arms} }}\n");
4121            let text = asm(&source);
4122            assert!(
4123                text.contains(&format!("\tcmpl\t%esi, %edi\n\t{jump}\t")),
4124                "{operator}: {text}"
4125            );
4126        }
4127
4128        // And against a constant, which is four comparisons in five and is where the saving
4129        // mostly is, since the byte that goes was the only reason the constant was in a register.
4130        let text = asm("int f(int a) { if (a < 7) return 1; return 2; }\n");
4131        assert!(text.contains("\tcmpl\t$7, %edi\n\tjge\t"), "{text}");
4132    }
4133
4134    /// The comparison whose answer is a value rather than a branch, which keeps its byte.
4135    ///
4136    /// The one that goes is the byte nothing but the branch reads. A comparison the program asked
4137    /// for the answer of is not that, and there is no branch behind it to fold into in any case,
4138    /// so this is here to say that what was taken out was taken out of one place and not two.
4139    #[test]
4140    fn a_comparison_whose_answer_the_program_wanted_still_writes_a_byte() {
4141        let text = asm("int f(int a, int b) { return a < b; }\n");
4142        assert!(text.contains("\tsetl\t"), "{text}");
4143    }
4144
4145    /// The same source at `-O2`, which is where the optimizer's passes are in the list.
4146    fn optimized(source: &str) -> String {
4147        let mut opts = options();
4148        opts.emit = EmitKind::Asm;
4149        opts.opt_level = rucc_session::OptLevel::O2;
4150        let result = run(&opts, source);
4151        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4152        result.text().to_owned()
4153    }
4154
4155    /// What each `switch` became is an `-fopt-info` remark, and `-Zswitch=` changes what it says.
4156    #[test]
4157    fn opt_info_says_what_each_switch_became_and_a_forced_shape_is_what_it_says() {
4158        let arms: String = (0..40)
4159            .map(|k| format!("case {}: return g({k});", k * 17))
4160            .collect::<Vec<_>>()
4161            .join(" ");
4162        let source = format!("int g(int);\nint f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n");
4163        let said = |shape: Option<&str>| {
4164            let mut opts = options();
4165            opts.emit = EmitKind::Asm;
4166            opts.opt_level = rucc_session::OptLevel::O2;
4167            opts.opt_info = vec![String::new()];
4168            opts.switch_shape = shape.map(str::to_owned);
4169            let result = run(&opts, &source);
4170            assert_eq!(result.messages, Vec::<String>::new());
4171            let lines: Vec<String> = result
4172                .remarks
4173                .lines()
4174                .filter(|line| line.contains("[switch-lowering]"))
4175                .map(str::to_owned)
4176                .collect();
4177            assert_eq!(lines.len(), 1, "{}", result.remarks);
4178            lines[0].clone()
4179        };
4180        assert!(said(None).contains(": f: optimized: switch of 40 cases lowered as a tree;"));
4181        assert!(said(Some("table")).contains("lowered as a table;"));
4182        assert!(said(Some("walk")).contains("lowered as a walk;"));
4183    }
4184
4185    /// A dense `switch` whose arms are a function of the label, which is arithmetic.
4186    ///
4187    /// Sixteen labels, and the arm for label `k` gives `k + 1`. What came out of this was a
4188    /// comparison and a jump for every one of them, which is tamnd/rucc#728. What comes out now is
4189    /// one comparison and one addition, and the count is the whole of the claim: it does not grow
4190    /// with the number of labels, so sixteen and a hundred and sixty compile to the same thing.
4191    ///
4192    /// The comparison is unsigned because the range check is the label minus the lowest one, which
4193    /// is a count and not a number the program wrote.
4194    #[test]
4195    fn a_switch_whose_arms_are_a_function_of_the_label_is_a_range_check_and_arithmetic() {
4196        let arms: String =
4197            (0..16).map(|k| format!("case {k}: return {};", k + 1)).collect::<Vec<_>>().join(" ");
4198        let text = optimized(&format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
4199        assert!(text.contains("\tcmpl\t$15, %edi\n\tja\t"), "{text}");
4200        assert!(text.contains("\taddl\t$1, %edi"), "{text}");
4201        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
4202    }
4203
4204    /// The same `switch` with one arm off the line, which is a table and not arithmetic.
4205    ///
4206    /// The answers being a line is what licenses the addition, since it answers for every label in
4207    /// the range at once. One label whose arm disagrees is a label it would answer wrongly, so this
4208    /// is here to say that the pass is reading the arms and not counting the labels. What it does
4209    /// instead is look the answer up: one comparison, no jump through a jump table, and the arm off
4210    /// the line is a cell of a constant array in `.rodata`, which is gcc's `CSWTCH` and its shape.
4211    #[test]
4212    fn a_dense_switch_whose_arms_are_not_a_line_is_a_load_from_a_table() {
4213        let arms: String = (0..16)
4214            .map(|k| format!("case {k}: return {};", if k == 9 { 100 } else { k + 1 }))
4215            .collect::<Vec<_>>()
4216            .join(" ");
4217        let text = optimized(&format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
4218        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
4219        assert!(!text.contains("\tjmp\t*"), "{text}");
4220        assert!(text.contains("leaq\tCSWTCH.0(%rip)"), "{text}");
4221        let table = &text[text.find("CSWTCH.0:").expect("the table is in the output")..];
4222        let section = text[..text.find("CSWTCH.0:").unwrap_or(0)].rfind("\t.section\t.rodata");
4223        assert!(section.is_some(), "{text}");
4224        assert_eq!(table.matches("\t.long\t").count(), 16, "{text}");
4225        assert!(table.contains("\t.long\t100\n"), "{text}");
4226    }
4227
4228    /// A `switch` whose arms give string literals is a table of how far each string is from it.
4229    ///
4230    /// gcc 16 keeps the compares here, because its table would hold addresses the loader has to
4231    /// write when the program starts, and that table would have to be in `.data.rel.ro`. This one
4232    /// holds four byte distances the linker writes once, so it stays in `.rodata` with the strings.
4233    #[test]
4234    fn a_switch_whose_arms_give_strings_is_a_table_of_how_far_away_they_are() {
4235        let text = optimized(
4236            "const char *f(int k) { switch (k) { case 0: return \"zero\"; \
4237             case 1: return \"one\"; case 2: return \"two\"; case 3: return \"three\"; } \
4238             return \"many\"; }\n",
4239        );
4240        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
4241        assert!(text.contains("leaq\tCSWTCH.0(%rip)"), "{text}");
4242        assert!(!text.contains(".data.rel.ro"), "{text}");
4243        let at = text.find("CSWTCH.0:").expect("the table is in the output");
4244        assert!(text[..at].rfind("\t.section\t.rodata").is_some(), "{text}");
4245        let table = &text[at..];
4246        assert_eq!(table.matches(" - .\n").count(), 4, "{text}");
4247        assert!(table.contains("\t.long\t.Lstr.1+4 - .\n"), "{text}");
4248    }
4249
4250    /// The same table at `-Os`, where a cell is a byte because every answer fits in one.
4251    ///
4252    /// gcc 16 narrows the cells at `-Os` and not at `-O2`, and so does rucc: sixteen answers under a
4253    /// hundred and twenty eight are sixteen bytes rather than sixty four, and the byte is widened
4254    /// back with its sign.
4255    #[test]
4256    fn a_table_at_os_has_cells_as_narrow_as_its_answers() {
4257        let arms: String = (0..16)
4258            .map(|k| format!("case {k}: return {};", if k == 9 { 100 } else { k + 1 }))
4259            .collect::<Vec<_>>()
4260            .join(" ");
4261        let mut opts = options();
4262        opts.emit = EmitKind::Asm;
4263        opts.opt_level = rucc_session::OptLevel::Os;
4264        let result = run(&opts, &format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
4265        assert_eq!(result.messages, Vec::<String>::new());
4266        let text = result.text();
4267        let table = &text[text.find("CSWTCH.0:").expect("the table is in the output")..];
4268        assert_eq!(table.matches("\t.byte\t").count(), 16, "{text}");
4269        assert!(text.contains("\tmovsbl\t"), "{text}");
4270    }
4271
4272    /// A table whose labels are every value the switched value can hold, which is the range check
4273    /// `rucc_opt::prune` takes out.
4274    ///
4275    /// The operand is `x & 3` and all four values are cases, so the `return -1` is dead. With the
4276    /// default out of the switch every case goes to the load, the switch is a jump, and what is
4277    /// left is the mask and the load with no compare in front of it.
4278    #[test]
4279    fn a_table_that_covers_its_operand_has_no_range_check() {
4280        let text = optimized(
4281            "int f(unsigned x) { switch (x & 3) { case 0: return 5; case 1: return 9; \
4282             case 2: return 2; case 3: return 7; } return -1; }\n",
4283        );
4284        assert!(text.contains("leaq\tCSWTCH.0(%rip)"), "{text}");
4285        assert!(!text.contains("\tcmp"), "{text}");
4286        assert!(!text.contains("$-1"), "{text}");
4287    }
4288
4289    /// A store one path makes to a local the loop has just read, which GCC also turns into a
4290    /// conditional move and an unconditional store. The branch was on data, so it was the one the
4291    /// machine gets wrong half the time. The move reads the flags of the comparison itself, so no
4292    /// byte is set and tested in between.
4293    #[test]
4294    fn a_store_to_a_local_the_loop_just_read_is_a_conditional_move() {
4295        let text = optimized(
4296            "int f(const int *v, int n, int k) { int best[8] = {0}; \
4297             for (int i = 0; i < n; i++) if (v[i] > best[i & 7]) best[i & 7] = v[i]; \
4298             return best[k & 7]; }\n",
4299        );
4300        assert!(text.contains("\tcmovgl"), "{text}");
4301        assert!(!text.contains("\tset"), "{text}");
4302        assert!(!text.contains("\ttestb"), "{text}");
4303    }
4304
4305    /// The same loop on a global keeps its branch, because another thread may own the slot.
4306    #[test]
4307    fn a_store_to_a_global_the_loop_just_read_keeps_its_branch() {
4308        let text = optimized(
4309            "int best[8]; void f(const int *v, int n) { \
4310             for (int i = 0; i < n; i++) if (v[i] > best[i & 7]) best[i & 7] = v[i]; }\n",
4311        );
4312        assert!(!text.contains("\tcmov"), "{text}");
4313    }
4314
4315    /// A conversion whose operand the optimizer turned into a constant, which is the whole of what
4316    /// `rucc_opt::fold` does with floating point.
4317    ///
4318    /// The cast is not a constant expression, so the front end leaves it alone and the pipeline is
4319    /// what has to see it. Load forwarding turns the local back into the constant that was stored
4320    /// into it, and the conversion then has an `fconst` in front of it. What came out before was
4321    /// the sixty four bit pattern moved into a register, moved into an `xmm`, and a `cvttsd2si`.
4322    #[test]
4323    fn a_conversion_from_a_constant_double_is_the_number_it_converts_to() {
4324        let text = optimized("int f(void) { double d = 2.75; return (int) d; }\n");
4325        assert!(text.contains("movl\t$2, %eax"), "{text}");
4326        assert!(!text.contains("cvttsd2si"), "{text}");
4327    }
4328
4329    /// A slot of a `const` table read at an index the optimizer works out, which is what
4330    /// `rucc_opt::image` is for.
4331    ///
4332    /// The subscript is not a constant expression and the front end does not fold it. What it
4333    /// writes is the index sign extended, multiplied by four and added to the address of the
4334    /// table, so the offset only exists once `fold` has run and the load only folds after that.
4335    /// What came out before was a `movl t+8(%rip), %eax`.
4336    #[test]
4337    fn a_slot_of_a_read_only_table_is_the_value_the_table_holds() {
4338        let text =
4339            optimized("static const int t[4] = {10, 20, 30, 40};\nint f(void) { return t[2]; }\n");
4340        assert!(text.contains("movl\t$30, %eax"), "{text}");
4341        assert!(!text.contains("t(%rip)"), "{text}");
4342    }
4343
4344    /// A byte of a string literal, which is the same fold reading literal bytes rather than the
4345    /// scalars an `int` array is written as.
4346    #[test]
4347    fn a_byte_of_a_read_only_string_is_the_byte_the_string_spells() {
4348        let text = optimized("static const char s[] = \"abc\";\nint f(void) { return s[1]; }\n");
4349        assert!(text.contains("movl\t$98, %eax"), "{text}");
4350    }
4351
4352    /// A global something can write to, which is the condition the fold turns on and therefore
4353    /// the one worth a test of its own. Nothing here is `const`, so the store in `g` could be the
4354    /// store that ran last and the load has to happen.
4355    #[test]
4356    fn a_table_that_is_not_read_only_keeps_its_load() {
4357        let text = optimized(
4358            "static int t[4] = {10, 20, 30, 40};\nvoid g(int x) { t[2] = x; }\nint f(void) { return t[2]; }\n",
4359        );
4360        assert!(!text.contains("movl\t$30, %eax"), "{text}");
4361    }
4362
4363    /// `gcc.c-torture/execute/20030216-1.c`, which is the program the whole of this is for.
4364    ///
4365    /// It calls a function nothing defines, guarded by a condition the optimizer is meant to prove
4366    /// false, so the program links exactly when the call has been folded away. Getting there is
4367    /// three folds standing on each other: the load of the `const double`, the conversion of it to
4368    /// an `int`, and the comparison against one.
4369    #[test]
4370    fn a_call_guarded_by_a_condition_a_read_only_object_settles_is_not_emitted() {
4371        let text = optimized(
4372            "void link_error(void);\nconst double one = 1.0;\nint main(void) { if ((int) one != 1) link_error(); return 0; }\n",
4373        );
4374        assert!(!text.contains("call\tlink_error"), "{text}");
4375    }
4376
4377    /// A cast between a pointer and an integer as wide as one, which is every one C writes here.
4378    #[test]
4379    fn a_cast_between_a_pointer_and_an_integer_leaves_the_value_where_it_is() {
4380        let text = asm("long f(void *p) { return (long)p; }\n");
4381        // Every instruction in the body is a full width move or the return. The copies are the
4382        // allocator taking no hints, and what matters here is what is not among them: nothing
4383        // narrows the value and nothing widens it again, which is what a cast that did something
4384        // would look like.
4385        for line in text.lines().filter(|line| line.starts_with('\t') && !line.contains('.')) {
4386            let mnemonic = line.split_whitespace().next().unwrap_or("");
4387            assert!(matches!(mnemonic, "movq" | "ret"), "{line} in\n{text}");
4388        }
4389    }
4390
4391    /// The arguments past the sixth arrive in the caller's memory rather than in a register, and
4392    /// where that memory is depends on what the prologue did, so this is checked at the end of the
4393    /// pipeline rather than in the middle of it.
4394    #[test]
4395    fn an_argument_past_the_last_register_is_read_out_of_the_caller_s_stack() {
4396        let six = "long a, long b, long c, long d, long e, long f";
4397        let text = asm(&format!("long f({six}, long g, long h) {{ return g + h; }}\n"));
4398
4399        // Nothing is pushed and no frame is taken, so the only thing between the stack pointer and
4400        // the caller's arguments is the return address the call pushed. Which is where gcc 16.2.0
4401        // reads them from too, at `-O0`, though it reads them in three instructions where this
4402        // reads them in two: the second read is the addition's own memory operand, which is
4403        // `rucc_codegen::combine`, and the offset in it is the one the frame layout wrote into the
4404        // load before the two were put together.
4405        assert!(text.contains("\tmovq\t8(%rsp), "), "{text}");
4406        assert!(text.contains("\taddq\t16(%rsp), "), "{text}");
4407
4408        // A narrower one is read at its own width, because the bits above it are bits the
4409        // convention says nothing about, and one in the other register file with the other file's
4410        // instruction.
4411        let narrow = asm(&format!("int f({six}, int g) {{ return g; }}\n"));
4412        assert!(narrow.contains("\tmovl\t8(%rsp), "), "{narrow}");
4413        let eight =
4414            "double a, double b, double c, double d, double e, double f, double g, double h";
4415        let float = asm(&format!("double f({eight}, double i) {{ return i; }}\n"));
4416        assert!(float.contains("\tmovsd\t8(%rsp), "), "{float}");
4417    }
4418
4419    /// The other end of the same thing. What the caller writes is at the stack pointer, because
4420    /// that is the bottom of its frame and the bottom of its frame is where the callee looks.
4421    #[test]
4422    fn a_call_writes_the_arguments_with_no_register_left_at_the_stack_pointer() {
4423        let six = "1, 2, 3, 4, 5, 6";
4424        let decl = "long g(long, long, long, long, long, long, long, long);\n";
4425        let text = asm(&format!("{decl}long f(void) {{ return g({six}, 7, 8); }}\n"));
4426
4427        assert!(text.contains("\tmovq\t%"), "{text}");
4428        assert!(text.contains(", (%rsp)\n"), "{text}");
4429        assert!(text.contains(", 8(%rsp)\n"), "{text}");
4430        // And it reserved the bytes it wrote into, so nothing else in the frame is on top of them.
4431        assert!(text.contains("\tsubq\t$"), "{text}");
4432
4433        // A narrower one is written at its own width, matching what the callee reads it back with.
4434        let narrow = "int g(int, int, int, int, int, int, int);\n";
4435        let text = asm(&format!("{narrow}int f(void) {{ return g({six}, 7); }}\n"));
4436        assert!(text.contains("\tmovl\t%"), "{text}");
4437        assert!(text.contains(", (%rsp)\n"), "{text}");
4438    }
4439
4440    /// The count a variadic callee on this convention reads is a count of vector registers, so a
4441    /// float that ran out of them and went to memory is not in it.
4442    #[test]
4443    fn a_variadic_call_counts_registers_and_not_arguments() {
4444        let nine = "1., 2., 3., 4., 5., 6., 7., 8., 9.";
4445        let decl = "int g(int, ...);\n";
4446        let text = asm(&format!("{decl}int f(void) {{ return g(0, {nine}); }}\n"));
4447
4448        assert!(text.contains("\tmovl\t$8, "), "eight registers, not nine: {text}");
4449        assert!(text.contains("\tmovsd\t%"), "{text}");
4450        assert!(text.contains(", (%rsp)\n"), "{text}");
4451    }
4452
4453    /// The callee's half of the same convention. Every argument register it was handed is written
4454    /// into its frame on the way in, because which of them hold anything is a thing only the caller
4455    /// knew, and the ones the signature does name are left out because `va_start` sets the offsets
4456    /// past them and nothing ever reads their slots.
4457    #[test]
4458    fn a_variadic_function_writes_the_argument_registers_it_was_handed_into_its_frame() {
4459        let body =
4460            "__builtin_va_list ap; __builtin_va_start(ap, n); __builtin_va_end(ap); return n;";
4461        let text = asm(&format!("int f(int n, ...) {{ {body} }}\n"));
4462
4463        // Five general purpose registers and eight vector ones, since the one parameter the
4464        // signature names took the first of the six.
4465        let stores = |mnemonic: &str| text.matches(&format!("\t{mnemonic}\t%")).count();
4466        assert!(text.contains(", 8(%r"), "the second slot, not the first: {text}");
4467        assert!(!text.contains(", 0(%r"), "{text}");
4468        // All sixteen bytes of each vector register, which is what gcc writes and what a `va_arg`
4469        // of a `_Float128` reads back, so the mnemonic is the one that moves a whole register.
4470        assert_eq!(stores("movaps"), 8, "every vector register: {text}");
4471        assert_eq!(stores("movsd"), 0, "and the whole of each one: {text}");
4472
4473        // And the area is one of the function's own stack objects, so the frame holds it.
4474        assert!(text.contains("\tsubq\t$"), "{text}");
4475    }
4476
4477    /// What `va_start` writes is the four fields of the list, and the two numbers among them are
4478    /// where the arguments the signature names left the walk over each file's registers.
4479    #[test]
4480    fn va_start_writes_the_four_fields_the_psabi_describes() {
4481        let start = "__builtin_va_list ap; __builtin_va_start(ap, d);";
4482        let params = "int a, int b, int c, double d";
4483        let text = asm(&format!("int f({params}, ...) {{ {start} return a; }}\n"));
4484
4485        // Three integers took three of the six general purpose registers, and one double took one
4486        // of the eight vector ones, so the walk starts at twenty four bytes into the first half and
4487        // sixteen bytes into the second, which begins at forty eight.
4488        assert!(text.contains("	movl	$24, "), "{text}");
4489        assert!(text.contains("	movl	$64, "), "{text}");
4490        // The other two fields are addresses rather than numbers, so each is stored as a word and
4491        // each is a `lea` away. One of them reaches above the frame, which is where the caller's
4492        // arguments are and is the only thing in this function that is not below the stack pointer.
4493        assert!(text.contains(", 8(%r"), "{text}");
4494        assert!(text.contains(", 16(%r"), "{text}");
4495        let frame: u32 = text
4496            .lines()
4497            .find_map(|line| line.trim().strip_prefix("subq	$")?.split(',').next()?.parse().ok())
4498            .expect("a variadic function takes a frame for the save area");
4499        let above = |line: &str| {
4500            let at: u32 = line.trim().strip_prefix("leaq	")?.split('(').next()?.parse().ok()?;
4501            Some(at > frame)
4502        };
4503        assert!(text.lines().filter_map(above).any(|it| it), "{frame}: {text}");
4504    }
4505
4506    /// A `va_arg` is a branch on whether the argument it wants is still in the save area, and which
4507    /// of the two halves it walks is the type's answer.
4508    #[test]
4509    fn va_arg_branches_on_whether_the_argument_is_still_in_the_save_area() {
4510        let read = "__builtin_va_list ap; __builtin_va_start(ap, n);";
4511        let ints = format!("int f(int n, ...) {{ {read} return __builtin_va_arg(ap, int); }}\n");
4512        let text = asm(&ints);
4513
4514        // The last general purpose slot begins at forty, so an offset above it is an argument the
4515        // caller left in its own memory instead.
4516        assert!(text.contains("$40, "), "{text}");
4517        assert!(text.contains("	cmpl	"), "{text}");
4518        // The jump is the unsigned one, since an offset is a count of bytes. It is the opposite
4519        // of the comparison the front end wrote, because the block falls into the half taken when
4520        // the argument is still in the save area and jumps to the other one.
4521        assert!(text.contains("	ja	"), "{text}");
4522
4523        let arg = "__builtin_va_arg(ap, double)";
4524        let text = asm(&format!("double f(int n, ...) {{ {read} return {arg}; }}\n"));
4525        assert!(text.contains("$160, "), "the last vector slot: {text}");
4526    }
4527
4528    /// A structure assigned is a copy of a known size, and a copy of a known size is a run of
4529    /// moves rather than a call to a library this compiler has no way to reach yet.
4530    #[test]
4531    fn a_structure_assignment_is_a_move_for_each_word_of_it() {
4532        let decl = "struct pair { long a, b; };\n";
4533        let body = "struct pair p = *q; return p.a + p.b;";
4534        let text = asm(&format!("{decl}long f(struct pair *q) {{ {body} }}\n"));
4535
4536        assert!(!text.contains("memcpy"), "nothing calls the library: {text}");
4537        assert!(!text.contains("\tcall"), "{text}");
4538        // Sixteen bytes aligned to eight is two words, and each is a load and a store.
4539        assert!(text.matches("\tmovq\t").count() >= 4, "two words each way: {text}");
4540    }
4541
4542    /// A word is as wide as the object is aligned to and no wider, so a character array is copied
4543    /// a byte at a time and a structure of longs eight bytes at a time.
4544    #[test]
4545    fn how_wide_a_word_of_a_copy_is_follows_the_alignment() {
4546        let decl = "struct bytes { char a[8]; };\n";
4547        let body = "struct bytes p = *q; return p.a[0];";
4548        let text = asm(&format!("{decl}int f(struct bytes *q) {{ {body} }}\n"));
4549
4550        // Eight bytes aligned to one is eight words, and each is a load and a store.
4551        assert!(text.matches("\tmovb\t").count() >= 16, "a byte at a time: {text}");
4552    }
4553
4554    /// What an initialiser does not name is zero, which the front end writes as a fill and this
4555    /// writes as the byte spread across each word.
4556    #[test]
4557    fn the_part_of_an_initialiser_that_names_nothing_is_stored_as_zero() {
4558        let decl = "struct wide { long a, b, c; };\n";
4559        let text = asm(&format!("{decl}long f(void) {{ struct wide w = {{ 7 }}; return w.c; }}\n"));
4560
4561        assert!(!text.contains("memset"), "nothing calls the library: {text}");
4562        // Either spelling of a zero in a register, the move of one or the exclusive or of the
4563        // register with itself that `rucc_codegen::shorten` writes instead where it is free. The
4564        // exclusive or is the thirty-two bit one whatever the width of the word, since the half of
4565        // the register it does not write is cleared rather than left alone.
4566        assert!(text.contains("\tmovq\t$0, ") || text.contains("\txorl\t"), "the zero: {text}");
4567    }
4568
4569    /// A copy too large to be worth unrolling is a call to the runtime, which is the C library on
4570    /// a hosted target and `rucc-builtins` on a freestanding one.
4571    #[test]
4572    fn a_copy_too_large_to_unroll_calls_the_runtime() {
4573        let decl = "struct huge { char a[4096]; };\n";
4574        let mut opts = options();
4575        opts.emit = EmitKind::Asm;
4576        let source = format!("{decl}void f(struct huge *p, struct huge *q) {{ *p = *q; }}\n");
4577        let result = run(&opts, &source);
4578        assert!(!result.failed(), "{:?}", result.messages);
4579        let text = result.text();
4580        assert!(text.contains("call") && text.contains("memcpy"), "{text}");
4581        // The size in the register the convention passes the third argument in, which is what
4582        // says the call was built from the convention and not from the shape of the IR.
4583        assert!(text.contains("4096"), "the size travels: {text}");
4584    }
4585
4586    /// And an object passed by value with more words in it than that is the same call again,
4587    /// written in front of the call the object is an argument of.
4588    ///
4589    /// The copy is one the caller owes the callee, since the callee is free to write to what it
4590    /// was handed, so it is not an optimization that the size decides but the only way the call
4591    /// can be made at all.
4592    #[test]
4593    fn a_structure_too_large_to_unroll_is_copied_into_the_argument_area_by_the_runtime() {
4594        let decl = "struct huge { char a[4096]; };\nint take(struct huge);\n";
4595        let text = asm(&format!("{decl}int f(struct huge *p) {{ return take(*p); }}\n"));
4596
4597        let copy = text.find("call\tmemcpy").expect("the copy");
4598        let call = text.find("call\ttake").expect("the call");
4599        assert!(copy < call, "the copy comes first: {text}");
4600        // Into the bottom of the outgoing area, which is where the stack pointer already is, and
4601        // with the size in the register the convention passes the third argument in. The address
4602        // of the bottom of the frame is the stack pointer itself, so what carries it is the move
4603        // rather than the address computation the selector wrote. See `rucc_codegen::shorten`.
4604        assert!(text.contains("movq\t%rsp, %rdi"), "the destination: {text}");
4605        assert!(text.contains("$4096, %edx"), "the size: {text}");
4606    }
4607
4608    /// A frame that had to force its own alignment cannot say how far away the caller's stack
4609    /// pointer was, so it reaches back through the frame pointer instead.
4610    #[test]
4611    fn a_realigned_frame_reads_them_through_the_frame_pointer() {
4612        let six = "long a, long b, long c, long d, long e, long f";
4613        let body = "_Alignas(32) long wide[4]; wide[0] = g; return wide[0];";
4614        let text = asm(&format!("long f({six}, long g) {{ {body} }}\n"));
4615
4616        // The frame pointer is saved and pointed at where it was saved before the alignment is
4617        // forced, so the caller's arguments stay a constant distance from it: one word for the
4618        // saved frame pointer and one for the return address.
4619        assert!(text.contains("\tandq\t$-32, %rsp"), "{text}");
4620        assert!(text.contains("\tmovq\t16(%rbp), "), "{text}");
4621        assert!(!text.contains("\tmovq\t16(%rsp), "), "{text}");
4622    }
4623
4624    /// The object format decides the directives, and the target decides the object format.
4625    #[test]
4626    fn the_target_decides_how_the_assembly_is_spelled() {
4627        let mut opts = options();
4628        opts.emit = EmitKind::Asm;
4629        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
4630        let text = run(&opts, "int f(void) { return 0; }\n").text().to_owned();
4631        assert!(text.contains("__TEXT,__text"), "{text}");
4632        assert!(text.contains("\n_f:\n"), "{text}");
4633        assert!(!text.contains(".note.GNU-stack"), "{text}");
4634    }
4635
4636    /// The object file of `source`, insisting that it compiled cleanly.
4637    fn obj(source: &str) -> Vec<u8> {
4638        let mut opts = options();
4639        opts.emit = EmitKind::Object;
4640        let result = run(&opts, source);
4641        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4642        match result.artifact {
4643            Artifact::Object { bytes, .. } => bytes,
4644            other => panic!("expected an object, got {other:?}"),
4645        }
4646    }
4647
4648    /// `-c`, which is the last step of the three the back end can end with.
4649    ///
4650    /// What is in the file is checked in `rucc-object`, a field at a time. What is checked here is
4651    /// that a C file goes all the way to one, which is the whole compiler in one line and the
4652    /// thing that stops working when a layer between them changes its mind about something.
4653    #[test]
4654    fn a_function_goes_from_c_to_an_object_a_linker_would_take() {
4655        let bytes = obj("int add(int a, int b) { return a + b; }\n");
4656        assert_eq!(&bytes[..4], b"\x7fELF", "an object file starts by saying it is one");
4657        let text = asm("int add(int a, int b) { return a + b; }\n");
4658        assert!(
4659            text.contains("\taddl\t"),
4660            "and the listing of it is the same instructions:\n{text}"
4661        );
4662    }
4663
4664    /// A variable this file defines, which is what a reference to one has to resolve against.
4665    #[test]
4666    fn a_variable_goes_from_c_to_the_section_it_belongs_in() {
4667        let text = asm("int counter = 42;\nstatic int hidden;\nconst int fixed = 7;\n");
4668        assert!(text.contains("\t.data\n\t.globl\tcounter\n"), "{text}");
4669        assert!(text.contains("\ncounter:\n\t.long\t42\n"), "{text}");
4670        assert!(text.contains("\t.size\tcounter, .-counter\n"), "{text}");
4671        // A zeroed variable carries its size and none of its bytes, and a `static` one is not
4672        // announced to the linker at all, which is the whole of what `static` means here.
4673        assert!(text.contains("\t.bss\n\t.p2align\t2\n"), "{text}");
4674        assert!(text.contains("\nhidden:\n\t.space\t4\n"), "{text}");
4675        assert!(!text.contains(".globl\thidden"), "{text}");
4676        // Nothing writes through it, so it goes in a page the loader can map read only and every
4677        // process running the program can share.
4678        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
4679    }
4680
4681    /// A bit-field with a value in it, which is written as the bytes the value lands in.
4682    ///
4683    /// The interesting one is the field whose lowest byte is zero. The bytes a bit-field
4684    /// initializer makes are put together first and then taken back out as the run they make,
4685    /// and taking them out starts at the byte the field starts at, so a zero byte at the front
4686    /// used to end the object up in `.bss` with the rest of its value thrown away.
4687    #[test]
4688    fn a_bit_field_initializer_writes_every_byte_of_the_value_and_not_only_the_ones_that_are_set() {
4689        let text = asm("struct s { unsigned f : 20; } x = { 0x12300 };\n");
4690        assert!(text.contains("\t.data\n"), "there is something to write: {text}");
4691        assert!(text.contains("\nx:\n\t.ascii\t\"\\000#\\001\"\n"), "and it is the value: {text}");
4692
4693        // Two fields, the first of them zero, which is the same thing said with the zero byte
4694        // inside the run rather than at the front of it.
4695        let text = asm("struct s { unsigned a : 8; unsigned b : 8; } x = { 0, 3 };\n");
4696        assert!(text.contains("\nx:\n\t.ascii\t\"\\000\\003\"\n"), "{text}");
4697
4698        // Wider than an `int`, which is the same code and is worth saying because the value no
4699        // longer fits in the thirty two bits a bit-field used to be read at.
4700        let text = asm("struct s { unsigned long long f : 40; } x = { 0x100000 };\n");
4701        assert!(text.contains("\nx:\n\t.ascii\t\"\\000\\000\\020\"\n\t.space\t5\n"), "{text}");
4702
4703        // Nothing in it, which still costs no bytes in the file.
4704        let text = asm("struct s { unsigned f : 20; } x = { 0 };\n");
4705        assert!(text.contains("\t.bss\n"), "an object of zeroes is zeroes: {text}");
4706        assert!(text.contains("\nx:\n\t.space\t4\n"), "{text}");
4707    }
4708
4709    /// A string literal, which is a variable the program never named.
4710    #[test]
4711    fn a_string_literal_is_a_variable_with_a_name_no_program_could_write() {
4712        let text = asm("const char *f(void) { return \"hi\"; }\n");
4713        assert!(text.contains("\t.ascii\t\"hi\\000\"\n"), "{text}");
4714        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
4715        let label = text
4716            .lines()
4717            .find(|line| line.starts_with(".Lstr"))
4718            .unwrap_or_else(|| panic!("a label for the literal in\n{text}"));
4719        assert!(!text.contains(&format!(".globl\t{}", label.trim_end_matches(':'))), "{text}");
4720    }
4721
4722    /// A variable holding the address of another one, which is the only hole an image has in it.
4723    #[test]
4724    fn an_address_in_an_initializer_is_left_to_the_linker() {
4725        let source = "int counter;\nint *p = &counter;\n";
4726        let text = asm(source);
4727        assert!(text.contains("\np:\n\t.quad\tcounter\n"), "{text}");
4728        // And in the object it is eight zero bytes and a relocation, which is what the two paths
4729        // being one description is for.
4730        let bytes = obj(source);
4731        assert!(bytes.windows(8).any(|w| w == b"counter\0"), "the object has to name it");
4732    }
4733
4734    /// A const table of function pointers, which is the shape that made SQLite link with a warning.
4735    ///
4736    /// The table is const so nothing in the program writes it, but the addresses in it are not
4737    /// numbers a link knows, so the loader writes it once at startup. Putting it in `.rodata`
4738    /// leaves a relocation in a section that is never writable, and what the linker does about
4739    /// that is set `DT_TEXTREL` on the whole image and say so. `.data.rel.ro` is writable for
4740    /// exactly as long as the loader is writing it and read only afterwards, which is what the
4741    /// program asked for in the first place.
4742    #[test]
4743    fn a_constant_holding_an_address_goes_in_the_section_the_loader_may_write_once() {
4744        // Both names are `static` and both are defined here, so nothing else can be the one that
4745        // defines them and the linker may lay the table out in the first pages of the segment.
4746        let text = asm("static void a(void) {}\nstatic void b(void) {}\n\
4747             struct m { void (*x)(void); void (*y)(void); };\n\
4748             const struct m t = { a, b };\n");
4749        assert!(text.contains("\t.section\t.data.rel.ro.local,\"aw\",@progbits\n"), "{text}");
4750        assert!(text.contains("\nt:\n\t.quad\ta\n\t.quad\tb\n"), "{text}");
4751
4752        // One name this file only declares is enough to lose the `.local` half, because a name the
4753        // link resolves from somewhere else is one another object may turn out to define.
4754        let text =
4755            asm("void a(void);\nstruct m { void (*x)(void); };\nconst struct m t = { a };\n");
4756        assert!(text.contains("\t.section\t.data.rel.ro,\"aw\",@progbits\n"), "{text}");
4757
4758        // And a constant with no address in it stays exactly where it was.
4759        let text = asm("const int fixed = 7;\n");
4760        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
4761    }
4762
4763    /// A thread-local variable, which is the whole of one: the storage and the way to reach it.
4764    ///
4765    /// The two halves are in one test on purpose. Either one alone is worse than neither: a
4766    /// definition with no way to reach it is a variable nothing can read, and a reference with no
4767    /// definition behind it is the bug this pair was written to prevent, where a thread-local is
4768    /// read as though it were an ordinary global and every thread quietly shares one copy.
4769    #[test]
4770    fn a_thread_local_variable_is_storage_a_thread_gets_a_copy_of_and_an_offset_into_it() {
4771        let text = asm("_Thread_local int x = 1;\nint read(void) { return x; }\n");
4772        // The storage: the section the loader makes a copy of for every thread, and the symbol
4773        // type that makes a linker refuse an ordinary relocation aimed at it.
4774        assert!(text.contains("\t.section\t.tdata,\"awT\",@progbits\n"), "{text}");
4775        assert!(text.contains("\t.type\tx, @tls_object\n"), "{text}");
4776        // The way to reach it: how far into a thread's block it sits, out of the table, plus where
4777        // this thread's block is, out of the segment register.
4778        assert!(text.contains("x@GOTTPOFF(%rip)"), "{text}");
4779        assert!(text.contains("%fs:0"), "{text}");
4780    }
4781
4782    /// The second half of that on its own, which is what a program asks for when the number it
4783    /// wants is the thread rather than anything in it.
4784    ///
4785    /// rpmalloc writes this to find its per thread cache, and it is the whole of what stood
4786    /// between that library and a build. gcc 16 writes the same one instruction.
4787    #[test]
4788    fn the_address_of_this_thread_s_own_storage_is_read_out_of_the_segment_register() {
4789        let text = asm("void *here(void) { return __builtin_thread_pointer(); }\n");
4790        assert!(text.contains("movq\t%fs:0, "), "{text}");
4791        // No table slot and no addition, because there is no variable to find inside the block.
4792        assert!(!text.contains("GOTTPOFF"), "{text}");
4793    }
4794
4795    /// The four hints and the one thing that decides between them, which is the locality.
4796    ///
4797    /// A prefetch promises nothing, so what is checked here is the instruction rather than any
4798    /// effect: the program runs the same whichever of the four it gets, and the whole point of
4799    /// writing one is which. The four spellings are what gcc 16.2.0 writes for the same four
4800    /// programs, measured on x86-64 rather than read off a manual.
4801    ///
4802    /// The write hint is not one of them. `prefetchw` is not in the base instruction set and gcc
4803    /// writes it only when the command line says the part has it, so a prefetch for a write is the
4804    /// same instruction as a prefetch for a read, which is the fourth line here.
4805    #[test]
4806    fn a_prefetch_is_one_of_four_instructions_and_the_locality_is_what_picks() {
4807        for (locality, wanted) in
4808            [(0, "prefetchnta"), (1, "prefetcht2"), (2, "prefetcht1"), (3, "prefetcht0")]
4809        {
4810            let source =
4811                format!("void warm(void *p) {{ __builtin_prefetch(p, 0, {locality}); }}\n");
4812            let text = asm(&source);
4813            assert!(text.contains(&format!("\t{wanted}\t")), "locality {locality}: {text}");
4814        }
4815        // The one argument form, which means a read that wants all of the data afterwards.
4816        let text = asm("void warm(void *p) { __builtin_prefetch(p); }\n");
4817        assert!(text.contains("\tprefetcht0\t"), "{text}");
4818        // A prefetch for a write, which on a part nobody said has `prefetchw` is the same
4819        // instruction as the read above.
4820        let text = asm("void warm(void *p) { __builtin_prefetch(p, 1); }\n");
4821        assert!(text.contains("\tprefetcht0\t"), "{text}");
4822        assert!(!text.contains("prefetchw"), "{text}");
4823    }
4824
4825    /// The same eight programs on AArch64, where the write hint is in the base instruction set and
4826    /// so is a different instruction, which is what gcc 16.2.0 writes for them.
4827    #[test]
4828    fn an_aarch64_prefetch_is_a_prfm_that_says_the_locality_and_whether_it_writes() {
4829        let mut opts = options();
4830        opts.emit = EmitKind::Asm;
4831        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
4832        for (write, kind) in [(0, "pld"), (1, "pst")] {
4833            for (locality, wanted) in [(0, "l1strm"), (1, "l3keep"), (2, "l2keep"), (3, "l1keep")] {
4834                let source = format!(
4835                    "void warm(void *p) {{ __builtin_prefetch(p, {write}, {locality}); }}\n"
4836                );
4837                let result = run(&opts, &source);
4838                assert_eq!(result.messages, Vec::<String>::new(), "{source}");
4839                let text = result.text();
4840                assert!(text.contains("prfm"), "{source}{text}");
4841                assert!(text.contains(&format!("{kind}{wanted}, [x0]")), "{source}{text}");
4842            }
4843        }
4844    }
4845
4846    /// The stop, which is the one instruction the machine is promised never to have a meaning for.
4847    ///
4848    /// What is checked is the instruction and not any effect, because the effect is a fault and a
4849    /// unit test has nowhere to take one. gcc 16.2.0 writes the same instruction for the same
4850    /// program, and it is not a call, which is the half that matters in a kernel and in a
4851    /// freestanding program: neither has an `abort` for a call to reach.
4852    ///
4853    /// The second half is the block going on after it. A statement written under a stop is
4854    /// compiled the way it would have been without one, so the addition is still there, and that
4855    /// is the front end declining to treat a stop as the end of a path.
4856    #[test]
4857    fn a_trap_is_the_instruction_the_machine_has_no_meaning_for() {
4858        let text = asm("void stop(void) { __builtin_trap(); }\n");
4859        assert!(text.contains("\tud2\n"), "{text}");
4860        assert!(!text.contains("\tcall"), "a stop is not a call to anything: {text}");
4861
4862        let text = asm("int stop(int a) { __builtin_trap(); return a + 1; }\n");
4863        assert!(text.contains("\tud2\n"), "{text}");
4864        assert!(text.contains("\taddl\t"), "the block goes on after a stop: {text}");
4865    }
4866
4867    /// `__builtin_cpu_init` is a call to libgcc's `__cpu_indicator_init` and nothing else, which
4868    /// is what gcc 16.2.0 writes for it. The name the program wrote does not reach the object
4869    /// file, because no library defines it.
4870    #[test]
4871    fn cpu_init_is_a_call_to_the_libgcc_function_that_fills_in_the_model() {
4872        let text = asm("void start(void) { __builtin_cpu_init(); }\n");
4873        assert!(text.contains("\tcall\t__cpu_indicator_init"), "{text}");
4874        assert!(!text.contains("__builtin_cpu_init"), "{text}");
4875    }
4876
4877    /// `__builtin_cpu_supports` is a load of the word the feature's bit is in and an `and` with
4878    /// the bit, and the answer is the bit where it stands, which is gcc 16.2.0's lowering.
4879    ///
4880    /// Three names, one from each place libgcc keeps the bits: sse4.2 is bit 8 of the last word of
4881    /// `__cpu_model`, vpclmulqdq is bit 1 of the first word of `__cpu_features2`, and xsave is bit
4882    /// 17 of its second word. The fourth is the top bit of a word, which gcc answers one for
4883    /// rather than the bit, so there is a compare after the `and`.
4884    #[test]
4885    fn cpu_supports_is_a_bit_of_the_words_libgcc_fills_in() {
4886        let text = asm("int f(void) { return __builtin_cpu_supports(\"sse4.2\"); }\n");
4887        assert!(text.contains("__cpu_model"), "{text}");
4888        assert!(text.contains("12(%"), "the fourth word of the model: {text}");
4889        assert!(text.contains("$256"), "{text}");
4890        assert!(!text.contains("\tcall"), "the answer is a read and not a call: {text}");
4891
4892        let text = asm("int f(void) { return __builtin_cpu_supports(\"vpclmulqdq\"); }\n");
4893        assert!(text.contains("__cpu_features2"), "{text}");
4894        assert!(text.contains("$2,"), "{text}");
4895
4896        let text = asm("int f(void) { return __builtin_cpu_supports(\"xsave\"); }\n");
4897        assert!(text.contains("__cpu_features2"), "{text}");
4898        assert!(text.contains("4(%"), "the second word of the second object: {text}");
4899        assert!(text.contains("$131072"), "{text}");
4900
4901        let text = asm("int f(void) { return __builtin_cpu_supports(\"avx512vbmi2\"); }\n");
4902        assert!(text.contains("set"), "the top bit is answered as a one: {text}");
4903    }
4904
4905    /// `__builtin_cpu_is` is a compare of one word of `__cpu_model` with a number: the vendor for
4906    /// `amd`, which is 2, and the subtype for `znver4`, which is 29.
4907    #[test]
4908    fn cpu_is_compares_one_word_of_the_model_with_a_number() {
4909        let text = asm("int f(void) { return __builtin_cpu_is(\"amd\"); }\n");
4910        assert!(text.contains("__cpu_model"), "{text}");
4911        assert!(text.contains("$2,"), "{text}");
4912
4913        let text = asm("int f(void) { return __builtin_cpu_is(\"znver4\"); }\n");
4914        assert!(text.contains("8(%"), "the subtype is the third word: {text}");
4915        assert!(text.contains("$29,"), "{text}");
4916    }
4917
4918    /// The name picks the word and the bit, so it has to be a string literal, and it has to be
4919    /// one gcc knows. Both are errors in gcc 16.2.0's words, and so is asking on a target other
4920    /// than x86-64, where nothing defines what these read.
4921    #[test]
4922    fn a_cpu_builtin_takes_a_name_it_knows_written_as_a_literal() {
4923        let mut opts = options();
4924        opts.emit = EmitKind::Ir;
4925        for (source, wanted) in [
4926            (
4927                "int f(const char *s) { return __builtin_cpu_supports(s); }\n",
4928                "parameter to builtin must be a string constant or literal",
4929            ),
4930            (
4931                "int f(void) { return __builtin_cpu_supports(\"sse5\"); }\n",
4932                "parameter to builtin not valid: sse5",
4933            ),
4934            (
4935                "int f(void) { return __builtin_cpu_is(\"sse\"); }\n",
4936                "parameter to builtin not valid: sse",
4937            ),
4938        ] {
4939            let result = run(&opts, source);
4940            assert!(
4941                result.messages.iter().any(|m| m.contains(wanted)),
4942                "{source}{:?}",
4943                result.messages
4944            );
4945        }
4946        // A cast in front of the literal is looked through, the way gcc looks through it.
4947        let text = asm("int f(void) { return __builtin_cpu_supports((const char *)\"avx2\"); }\n");
4948        assert!(text.contains("$1024"), "{text}");
4949
4950        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
4951        for source in [
4952            "void f(void) { __builtin_cpu_init(); }\n",
4953            "int f(void) { return __builtin_cpu_supports(\"sse4.2\"); }\n",
4954        ] {
4955            let result = run(&opts, source);
4956            assert!(
4957                result.messages.iter().any(|m| m.contains("only available on x86-64")),
4958                "{source}{:?}",
4959                result.messages
4960            );
4961        }
4962    }
4963
4964    /// The promise about the low bits of an address, whose value is the address.
4965    ///
4966    /// Nothing here reads an alignment fact about a value yet, so what the call leaves behind is
4967    /// its first argument and no instruction at all. The claim worth checking end to end is that
4968    /// the name is gone: a builtin nothing lowers reaches the assembler as a call to a name no
4969    /// object file defines, which is how this one used to fail to link out of glibc's string
4970    /// headers.
4971    ///
4972    /// The arguments behind the address are still evaluated, because gcc 16.2.0 evaluates them at
4973    /// every optimization level even though it has folded the call away. A constant has nothing to
4974    /// run and is dropped, and a call does, so the second half asks for the callee by name.
4975    #[test]
4976    fn assume_aligned_is_its_first_argument_and_keeps_the_rest() {
4977        let text = asm("void *aligned(char *p) { return __builtin_assume_aligned(p, 16); }\n");
4978        assert!(!text.contains("assume_aligned"), "{text}");
4979        assert!(!text.contains("\tcall"), "nothing is called for an alignment fact: {text}");
4980
4981        let source = "unsigned long width(void);\n\
4982                      void *aligned(char *p) { return __builtin_assume_aligned(p, width()); }\n";
4983        let text = asm(source);
4984        assert!(!text.contains("assume_aligned"), "{text}");
4985        assert!(text.contains("width"), "the argument that is not the answer still runs: {text}");
4986    }
4987
4988    /// Where a frame is, which on this machine is what the frame pointer holds.
4989    ///
4990    /// The first half is a function that would have kept no frame pointer at all, since it is a
4991    /// leaf with no locals, and keeps one because it asked where its frame is. The answer being
4992    /// `%rbp` rather than an offset off `%rsp` is the whole of the builtin at a depth of zero.
4993    ///
4994    /// The second half is the walk. Each link above zero is one load through the register the last
4995    /// one wrote, so a depth of two is two loads and a depth of three is three, which is what gcc
4996    /// 16.2.0 writes for the same programs at `-O2`.
4997    #[test]
4998    fn the_frame_address_is_the_frame_pointer_after_walking_that_many_links() {
4999        let text = asm("void *here(void) { return __builtin_frame_address(0); }\n");
5000        assert!(text.contains("pushq\t%rbp"), "a function that asks keeps a frame pointer: {text}");
5001        assert!(text.contains("movq\t%rbp, %rax"), "{text}");
5002        assert!(!text.contains("\tcall"), "a frame address is not a call to anything: {text}");
5003
5004        let walk = |depth: u32| {
5005            let source = format!("void *up(void) {{ return __builtin_frame_address({depth}); }}\n");
5006            asm(&source).matches("movq\t(%r").count()
5007        };
5008        assert_eq!(walk(1), 1, "one link is one load");
5009        assert_eq!(walk(3), 3, "three links are three loads");
5010    }
5011
5012    /// The address a frame returns to, which is one word above the frame the walk ended at.
5013    ///
5014    /// A word is eight bytes here and the `8(...)` is the whole claim: the call instruction pushed
5015    /// the return address and the prologue pushed the caller's frame pointer under it, so what the
5016    /// frame pointer points at is the link and what is above it is where control goes back to.
5017    /// gcc 16.2.0 writes `movq 8(%rbp), %rax` for the first of these, measured at `-O2`.
5018    ///
5019    /// The second half is the same walk the frame address does, with the load at the end of it
5020    /// reading one word further along rather than the register itself being the answer.
5021    #[test]
5022    fn the_return_address_is_one_word_above_the_frame_the_walk_ended_at() {
5023        let text = asm("void *back(void) { return __builtin_return_address(0); }\n");
5024        assert!(text.contains("pushq\t%rbp"), "a function that asks keeps a frame pointer: {text}");
5025        assert!(text.contains("movq\t8(%rbp), %rax"), "{text}");
5026        assert!(!text.contains("\tcall"), "a return address is not a call to anything: {text}");
5027
5028        let text = asm("void *back(void) { return __builtin_return_address(2); }\n");
5029        assert_eq!(text.matches("movq\t(%r").count(), 2, "two links are two loads: {text}");
5030        assert!(text.contains("movq\t8(%r"), "and the answer is above the last of them: {text}");
5031    }
5032
5033    /// A depth that is not a constant is refused, and so is one past the limit.
5034    ///
5035    /// The first is gcc's rule and not a convenience: what the call becomes is a walk that many
5036    /// links long, written out, so a number that is not known until the program runs has nothing
5037    /// to walk. gcc 16.2.0 says `invalid argument to '__builtin_return_address'` for the same
5038    /// program.
5039    ///
5040    /// The second is where this and gcc part company. gcc writes the walk however long it is, and
5041    /// this refuses a depth no program has a use for rather than filling an object file with loads
5042    /// that fault part way up.
5043    #[test]
5044    fn a_depth_that_is_not_a_small_constant_is_refused() {
5045        let mut opts = options();
5046        opts.emit = EmitKind::Ir;
5047        for source in [
5048            "void *up(int n) { return __builtin_return_address(n); }\n",
5049            "void *up(void) { return __builtin_frame_address(1000); }\n",
5050        ] {
5051            let messages = run(&opts, source).messages;
5052            let named = messages.iter().any(|m| m.contains("E0705"));
5053            assert!(named, "expected a refusal in {messages:?}");
5054        }
5055    }
5056
5057    /// Bytes off the frame, which is the stack pointer moving down and the answer being where it
5058    /// moved to.
5059    ///
5060    /// The rounding is the alignment: the size is taken up to the next sixteen before it is
5061    /// subtracted, so the pointer suits anything the program puts behind it. gcc 16.2.0 rounds the
5062    /// same way at `-O0` and spends a division doing it, which is the one place the two differ and
5063    /// is about how the rounding is written rather than about what it answers.
5064    ///
5065    /// There is no call anywhere in either program. An alloca that had reached the linker would
5066    /// have found the C library's, which is a real function with a real frame and is not what a
5067    /// program writing the builtin asked for.
5068    #[test]
5069    fn an_alloca_takes_the_bytes_off_the_stack_pointer_and_answers_where_they_are() {
5070        let text =
5071            asm("void use(void *p); void f(unsigned long n) { use(__builtin_alloca(n)); }\n");
5072        assert!(text.contains("andq\t$-16"), "the size is rounded up to sixteen: {text}");
5073        assert!(text.contains("subq\t%rdi, %rsp"), "and taken off the stack pointer: {text}");
5074        assert_eq!(text.matches("\tcall").count(), 1, "the only call is the one written: {text}");
5075
5076        // The plain name, which a program that declares it the way the C library does means the
5077        // same thing by. `gcc.c-torture/execute/20010122-1.c` is exactly this program.
5078        let plain = concat!(
5079            "extern void *alloca(__SIZE_TYPE__);\n",
5080            "void use(void *p);\n",
5081            "void f(unsigned long n) { use(alloca(n)); }\n",
5082        );
5083        let text = asm(plain);
5084        assert!(text.contains("subq\t%rdi, %rsp"), "the plain name is the same bytes: {text}");
5085        assert_eq!(text.matches("\tcall").count(), 1, "and is not a call either: {text}");
5086
5087        // And a program that means something of its own by the name keeps it, which is what the
5088        // declaration is looked at for.
5089        let own = concat!(
5090            "static void *alloca(unsigned long n) { return 0; }\n",
5091            "void *f(unsigned long n) { return alloca(n); }\n",
5092        );
5093        assert!(asm(own).contains("\tcall"), "a name the program took back is a call");
5094    }
5095
5096    /// A name nothing declared that the implementation knows the type of is declared with that
5097    /// type rather than with the `extern int f()` C89 6.3.2.2 writes down.
5098    ///
5099    /// That is gcc's rule and it is measurable: gcc 16.2.0 compiles an undeclared `alloca` with
5100    /// no call in it at all, and says `incompatible implicit declaration of built-in function`
5101    /// beside the implicit declaration warning. A C89 declaration would have made the call return
5102    /// an `int` and reach a function no C library defines, since every header that offers
5103    /// `alloca` offers it as a macro for the builtin. Four torture programs turn on it,
5104    /// `execute/20020314-1.c`, `20040223-1.c`, `941202-1.c` and `pr22061-1.c`, each of which
5105    /// calls `alloca` with nothing above it.
5106    ///
5107    /// The rule is the builtin table's rather than this one name's, so an undeclared `strlen` is
5108    /// the builtin too. What it is not is a declaration the program wrote that disagrees with the
5109    /// builtin's type, which gcc keeps and calls, and that was measured as well.
5110    #[test]
5111    fn a_builtin_the_program_never_declared_is_the_builtin_rather_than_the_one_c89_wrote_down() {
5112        // `-fpermissive`, because the implicit declaration itself is an error in every dialect
5113        // after C89 and the program would never get as far as a type without it. Each of the four
5114        // torture programs asks for either that or `-std=gnu89` on its own options line.
5115        let mut opts = options();
5116        opts.permissive = true;
5117        let undeclared = "void use(void *p);
5118void f(unsigned long n) { use(alloca(n)); }
5119";
5120        assert_eq!(
5121            run(&opts, undeclared).messages,
5122            [
5123                "/main.c:2:31: warning: implicit declaration of function 'alloca' [E0521]",
5124                "/main.c:2:31: warning: incompatible implicit declaration of built-in function \
5125                 'alloca' [E0713]",
5126            ]
5127        );
5128
5129        opts.emit = EmitKind::Asm;
5130        let text = run(&opts, undeclared).text().to_owned();
5131        assert!(text.contains("subq\t%rdi, %rsp"), "the bytes come off the stack: {text}");
5132        assert_eq!(text.matches("\tcall").count(), 1, "the only call is the one written: {text}");
5133
5134        // The table's rule and not this one name's, so a name whose whole answer is the library
5135        // function of the same name gets that function's type and still reaches it.
5136        let string = "unsigned long f(void) { return strlen(\"abc\"); }\n";
5137        let text = run(&opts, string).text().to_owned();
5138        assert!(text.contains("call\tstrlen"), "strlen is still a call: {text}");
5139
5140        // A declaration the program wrote is the program's, whatever the table says. gcc keeps
5141        // this one and writes the call, which is what makes the type worth looking at.
5142        let own = concat!(
5143            "static void *alloca(unsigned long n) { return 0; }\n",
5144            "void *f(unsigned long n) { return alloca(n); }\n",
5145        );
5146        assert!(asm(own).contains("\tcall"), "a name the program took back is a call");
5147    }
5148
5149    /// The bytes an alloca took live until the function returns and not until the end of the block
5150    /// the call was written in.
5151    ///
5152    /// That is what makes it different from a variable length array, and the way it is kept is that
5153    /// every scope open where the call was written stops giving the stack back. The second program
5154    /// is the mixed case: an array in the outer block and an alloca in the inner one, where the
5155    /// inner block gives nothing back either even though an array is in scope that ordinarily
5156    /// would. gcc 16.2.0 at `-O0` writes no restore at the end of either block, measured rather
5157    /// than read off the manual.
5158    #[test]
5159    fn the_bytes_an_alloca_took_are_still_there_at_the_end_of_the_block_that_took_them() {
5160        let inner = "{ use(__builtin_alloca(n)); }";
5161        for body in [inner.to_owned(), format!("int a[n]; {inner} use(a);")] {
5162            let source = format!("void use(void *p);\nvoid f(unsigned long n) {{ {body} }}\n");
5163            let text = asm(&source);
5164            // Every instruction that writes the stack pointer, which in a function that gives
5165            // nothing back is the alloca taking bytes and the epilogue putting the frame pointer
5166            // there. A restore would be a third kind, a move out of a register the save wrote.
5167            for line in text.lines().filter(|line| line.trim_end().ends_with(", %rsp")) {
5168                let taking = line.contains("subq");
5169                let leaving = line.contains("%rbp");
5170                assert!(taking || leaving, "nothing puts the stack back: {line} in {text}");
5171            }
5172        }
5173    }
5174
5175    /// Not a rewording of the check above: what the two paths agree about is the point.
5176    #[test]
5177    fn the_object_and_the_listing_are_two_spellings_of_one_compilation() {
5178        // A call, because it is the one thing whose spelling in the two differs completely: the
5179        // listing writes a name and the object writes four zero bytes and a relocation asking the
5180        // linker for the same name. If either path had lost the callee, one of these would fail.
5181        let source = "int callee(void); int g(void) { return callee(); }\n";
5182        let bytes = obj(source);
5183        assert!(
5184            bytes.windows(7).any(|w| w == b"callee\0"),
5185            "the object has to name the callee for the linker to find it"
5186        );
5187        let text = asm(source);
5188        assert!(text.contains("\tcall\tcallee\n"), "{text}");
5189    }
5190
5191    /// What a file of a link contributes is an object, and the default emit is a link.
5192    ///
5193    /// This is here because getting it wrong is silent in the worst way: an empty file is a valid
5194    /// empty linker script, so a link fed one gets as far as reporting every symbol of the file as
5195    /// undefined and says nothing about the compilation that produced nothing.
5196    #[test]
5197    fn compiling_for_an_executable_produces_an_object_and_not_a_dump() {
5198        let mut opts = options();
5199        // What a command line with no `-c` and no `-S` on it asks for.
5200        opts.emit = EmitKind::Executable;
5201        let result = run(&opts, "int main(void) { return 0; }\n");
5202        assert_eq!(result.messages, Vec::<String>::new());
5203        match result.artifact {
5204            Artifact::Object { bytes, .. } => assert_eq!(&bytes[..4], b"\x7fELF"),
5205            other => panic!("expected an object, got {other:?}"),
5206        }
5207    }
5208
5209    /// A target with a back end but no object writer says so rather than writing the wrong file.
5210    #[test]
5211    fn a_platform_with_no_object_writer_is_said_so_rather_than_written_as_elf() {
5212        let mut opts = options();
5213        opts.emit = EmitKind::Object;
5214        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
5215        let result = run(&opts, "int f(void) { return 0; }\n");
5216        assert!(result.failed(), "an object nobody can read is worse than a message");
5217        assert!(
5218            result.messages.iter().any(|m| m.contains("no object writer")),
5219            "{:?}",
5220            result.messages
5221        );
5222    }
5223
5224    /// The IR of `source`, insisting that it compiled cleanly.
5225    fn ir(source: &str) -> String {
5226        let mut opts = options();
5227        opts.emit = EmitKind::Ir;
5228        let result = run(&opts, source);
5229        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5230        result.text().to_owned()
5231    }
5232
5233    /// What was said about `source`, insisting that something was.
5234    fn errors(source: &str) -> Vec<String> {
5235        let mut opts = options();
5236        opts.emit = EmitKind::Ir;
5237        let result = run(&opts, source);
5238        assert!(result.failed(), "expected this to be refused:\n{source}");
5239        result.messages
5240    }
5241
5242    /// The body of the one function in `source`, which is what most of these are about.
5243    fn body(source: &str) -> String {
5244        let text = ir(source);
5245        let (_, rest) = text.split_once("{\n").expect("a function definition");
5246        let (body, _) = rest.rsplit_once("}\n").expect("a function definition");
5247        body.to_owned()
5248    }
5249
5250    /// What `-fgnu89-inline` is for, seen at the only place it shows: whether a body reached the
5251    /// module or only a declaration did.
5252    ///
5253    /// The C99 reading is the one an inline definition is written for and is not being changed
5254    /// here. What the flag is for is a program written before C99 swapped the two, which relies on
5255    /// `inline` alone leaving something behind for another unit to call, and there are twelve of
5256    /// those in the GCC torture suite alone.
5257    #[test]
5258    fn gnu89_inline_is_what_decides_whether_a_bare_inline_definition_reaches_the_module() {
5259        let source = "inline int f(int x) { return x + 1; }\n";
5260        let with = |flag: bool| {
5261            let mut opts = options();
5262            opts.emit = EmitKind::Ir;
5263            opts.gnu89_inline = flag;
5264            let result = run(&opts, source);
5265            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
5266            result.text().to_owned()
5267        };
5268
5269        // Under C's reading the module holds the declaration and the calls in this unit go to
5270        // whatever definition another unit has, which is C 6.7.4p7 and is what gcc does too.
5271        assert!(!with(false).contains("block0"), "no body: {}", with(false));
5272
5273        // Under GNU's it is an ordinary external definition, so the body is there and the symbol
5274        // is one the linker can resolve against.
5275        assert!(with(true).contains("block0"), "a body: {}", with(true));
5276    }
5277
5278    /// Every shape that reads or writes through a C type names that type.
5279    ///
5280    /// The tree itself is `rucc_lower::aliasing`'s and is tested there. What this is about is that
5281    /// the walk reaches it from every shape a program actually writes, since a node on the scalar
5282    /// load and nothing on the member load would be a layer that answers for a third of the
5283    /// accesses in a program and is not worth having.
5284    #[test]
5285    fn an_access_through_a_type_names_the_type_it_went_through() {
5286        let source = "\
5287struct s { int a; float b; };\n\
5288union u { int i; float f; };\n\
5289int scalar(int *p) { return *p; }\n\
5290float member(struct s *p) { p->a = 1; return p->b; }\n\
5291int element(int *a, long i) { return a[i]; }\n\
5292float through_a_union(union u *p) { p->i = 1; return p->f; }\n";
5293        let text = ir(source);
5294        assert!(text.contains(r#"!0 = tbaa "char""#), "the root: {text}");
5295        assert!(text.contains(r#"tbaa "int", parent !0"#), "int under it: {text}");
5296        assert!(text.contains(r#"tbaa "float", parent !0"#), "float under it: {text}");
5297        // One per access, and a function whose accesses all go through one type says so once per
5298        // access rather than once per function.
5299        let named = text.lines().filter(|line| line.contains(", tbaa !")).count();
5300        assert_eq!(named, 6, "six accesses: {text}");
5301    }
5302
5303    /// `-fno-strict-aliasing` is the front end leaving the name off.
5304    ///
5305    /// Nothing asks the alias analysis anything yet, so no program compiles differently for having
5306    /// passed this today. What this test is for is the day one does: the flag has to be the
5307    /// absence of the names rather than a condition somewhere downstream, since that is the only
5308    /// version of it that a pass added later cannot forget about.
5309    #[test]
5310    fn turning_strict_aliasing_off_leaves_the_type_off_every_access() {
5311        let source = "int punned(float *f, int *i) { *i = 1; *f = 2.0f; return *i; }\n";
5312        let mut opts = options();
5313        opts.emit = EmitKind::Ir;
5314        opts.strict_aliasing = false;
5315        let result = run(&opts, source);
5316        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
5317        let text = result.text().to_owned();
5318        assert!(!text.contains("tbaa"), "not even the root: {text}");
5319    }
5320
5321    /// `-finstrument-functions` puts one call to the entry hook in front of the body and one call
5322    /// to the exit hook in front of every return, each given the function's own address and the
5323    /// address it returns to. A function declared `no_instrument_function` gets neither, and the
5324    /// hooks are declared that way here as they are in `execute/eeprof-1.c`, since a hook that
5325    /// called itself would never get as far as its body.
5326    #[test]
5327    fn instrumenting_functions_calls_the_hooks_around_every_body_but_the_hooks() {
5328        let source = concat!(
5329            "#define NOCHK __attribute__((no_instrument_function))\n",
5330            "void __cyg_profile_func_enter(void *, void *) NOCHK;\n",
5331            "void __cyg_profile_func_exit(void *, void *) NOCHK;\n",
5332            "int calls;\n",
5333            "int pick(int x) { if (x) return 1; return 2; }\n",
5334            "void quiet(void) NOCHK;\n",
5335            "void quiet(void) { calls++; }\n",
5336            "void __cyg_profile_func_enter(void *fn, void *site) { calls++; }\n",
5337            "void __cyg_profile_func_exit(void *fn, void *site) { calls--; }\n",
5338        );
5339        let mut opts = options();
5340        opts.emit = EmitKind::Ir;
5341        opts.instrument_functions = true;
5342        let result = run(&opts, source);
5343        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
5344        let text = result.text().to_owned();
5345        let body = |name: &str| -> String {
5346            let open = format!("func @{name}(");
5347            let start = text.find(&open).unwrap_or_else(|| panic!("no {name}: {text}"));
5348            let rest = &text[start..];
5349            rest[..rest.find("\n}").unwrap_or(rest.len())].to_owned()
5350        };
5351        let pick = body("pick");
5352        assert_eq!(pick.matches("call @__cyg_profile_func_enter(").count(), 1, "{pick}");
5353        assert_eq!(pick.matches("call @__cyg_profile_func_exit(").count(), 2, "{pick}");
5354        assert!(pick.contains("return_address"), "{pick}");
5355        assert!(pick.contains("global_addr @pick"), "{pick}");
5356        for quiet in ["quiet", "__cyg_profile_func_enter", "__cyg_profile_func_exit"] {
5357            assert!(!body(quiet).contains("call "), "{quiet} is left alone: {text}");
5358        }
5359
5360        opts.instrument_functions = false;
5361        let result = run(&opts, source);
5362        assert!(!result.text().contains("call @__cyg_profile"), "off unless asked for");
5363    }
5364
5365    /// Calls whose open scopes owe the same handlers share one landing pad, as gcc's do, and a call
5366    /// after another object is declared, or once a scope has closed, gets the pad for what it owes
5367    /// then. Here that is two pads for six calls. A handler is a call like any other, so one that
5368    /// runs while an object further out still owes its own gets an edge to the pad for that.
5369    #[test]
5370    fn calls_that_owe_the_same_handlers_share_one_landing_pad() {
5371        let source = concat!(
5372            "void done(int *p);\n",
5373            "void work(int);\n",
5374            "void f(void) {\n",
5375            "  int a __attribute__((cleanup(done))) = 1;\n",
5376            "  work(1); work(2);\n",
5377            "  { int b __attribute__((cleanup(done))) = 2; work(3); work(4); }\n",
5378            "  work(5); work(6);\n",
5379            "}\n",
5380        );
5381        let mut opts = options();
5382        opts.emit = EmitKind::Ir;
5383        opts.exceptions = true;
5384        let result = run(&opts, source);
5385        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5386        let text = result.text();
5387        // Six for the calls to `work`, and one for the call to `done` that `b`'s scope makes on
5388        // the way out, which still owes `a`'s. The one `b`'s pad makes goes to `a`'s pad too.
5389        assert_eq!(text.matches("= unwound").count(), 8, "every call has its edge: {text}");
5390        assert_eq!(text.matches("= landing").count(), 2, "one pad for a, one for b and a: {text}");
5391    }
5392
5393    /// A `goto` out of two scopes runs their handlers in front of its branch, and under
5394    /// `-fexceptions` each one but the last is followed by an edge to the pad for the ones still
5395    /// owed, as a handler at the end of a scope is. The branch goes after them.
5396    #[test]
5397    fn a_goto_that_runs_handlers_gives_each_one_an_edge_to_what_is_still_owed() {
5398        let source = concat!(
5399            "void done(int *p);\n",
5400            "void f(int n) {\n",
5401            "  int a __attribute__((cleanup(done))) = 1;\n",
5402            "  { int b __attribute__((cleanup(done))) = 2;\n",
5403            "    { int c __attribute__((cleanup(done))) = 3; if (n) goto out; }\n",
5404            "  }\n",
5405            "out:\n",
5406            "  return;\n",
5407            "}\n",
5408        );
5409        let mut opts = options();
5410        opts.emit = EmitKind::Ir;
5411        opts.exceptions = true;
5412        let result = run(&opts, source);
5413        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5414        let text = result.text();
5415        // The goto's two for c and b, the two at the ends of the scopes of c and b, and the one
5416        // the pad for c makes after b's handler. The pad for b only runs a's, so it has none.
5417        assert_eq!(text.matches("= unwound").count(), 5, "{text}");
5418        assert_eq!(text.matches("= landing").count(), 2, "{text}");
5419    }
5420
5421    /// Under `-fexceptions` a `cleanup` handler is owed a call on an unwind as well. On x86-64 ELF
5422    /// a call inside a handler's scope gets a landing pad that runs the handler and resumes the
5423    /// unwind, a handler with no call in its scope needs none, and without the flag the same source
5424    /// compiles as it always did. Everywhere else the call is turned down by name, since no pad is
5425    /// built there.
5426    #[test]
5427    fn a_call_an_unwind_would_leave_a_cleanup_behind_gets_a_landing_pad_under_exceptions() {
5428        let source = concat!(
5429            "void done(int *p);\n",
5430            "void work(void);\n",
5431            "void calls(void) { int x __attribute__((cleanup(done))) = 1; work(); }\n",
5432            "int quiet(int y) { int x __attribute__((cleanup(done))) = y; return x + 1; }\n",
5433            "void after(void) { { int x __attribute__((cleanup(done))) = 1; } work(); }\n",
5434        );
5435        let mut opts = options();
5436        opts.emit = EmitKind::Ir;
5437        let result = run(&opts, source);
5438        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
5439        assert!(!result.text().contains("landing"), "{}", result.text());
5440
5441        opts.exceptions = true;
5442        let result = run(&opts, source);
5443        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5444        let text = result.text();
5445        assert_eq!(text.matches("= landing").count(), 1, "only the call in calls: {text}");
5446        assert!(text.contains("_Unwind_Resume"), "{text}");
5447
5448        opts.emit = EmitKind::Asm;
5449        let result = run(&opts, source);
5450        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5451        let text = result.text();
5452        assert!(text.contains(".cfi_personality 0x9b,DW.ref.__gcc_personality_v0"), "{text}");
5453        assert!(text.contains(".cfi_lsda 0x1b,.LLSDA_calls"), "{text}");
5454        assert!(text.contains(".gcc_except_table"), "{text}");
5455        assert_eq!(text.matches(".cfi_lsda").count(), 1, "{text}");
5456
5457        opts.emit = EmitKind::Object;
5458        let result = run(&opts, source);
5459        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5460        let bytes = result.artifact.bytes();
5461        let has = |what: &[u8]| bytes.windows(what.len()).any(|window| window == what);
5462        assert!(has(b".gcc_except_table\0"), "the call site table has a section");
5463        assert!(has(b"zPLR\0"), "a header naming the personality routine");
5464        assert!(has(b"zR\0"), "and the plain one for the functions with no pad");
5465        assert!(has(b"DW.ref.__gcc_personality_v0\0"), "the pointer the header reads through");
5466
5467        opts.emit = EmitKind::Ir;
5468        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
5469        let result = run(&opts, source);
5470        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
5471        assert!(result.messages[0].contains("landing pad"), "{:?}", result.messages);
5472        assert!(result.messages[0].contains(":3:"), "the call in calls: {:?}", result.messages);
5473    }
5474
5475    /// An `asm` at file scope with an instruction in it, which is how a unit writes a whole
5476    /// function in assembly. The template goes into the listing as it was written, between the
5477    /// markers gcc writes, and an object is assembled from that listing, so the function it
5478    /// defines is defined in the object and the C that calls it calls it there. tcc's
5479    /// `85_asm-outside-function.c` and `98_al_ax_extend.c` are this.
5480    #[test]
5481    fn an_asm_at_file_scope_with_an_instruction_in_it_is_assembled() {
5482        let source = concat!(
5483            "extern void vide(void);\n",
5484            "__asm__(\".text;.globl _us;_us:;movl $0x1234ABCD, %eax;ret\");\n",
5485            "__asm__(\"vide: ret\");\n",
5486            "unsigned short _us(void);\n",
5487            "int main(void) { vide(); return _us() == 0xABCD ? 0 : 1; }\n",
5488        );
5489        let mut opts = options();
5490        opts.emit = EmitKind::Ir;
5491        let result = run(&opts, source);
5492        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5493        assert_eq!(result.text().matches("module asm ").count(), 2, "{}", result.text());
5494
5495        opts.emit = EmitKind::Asm;
5496        let result = run(&opts, source);
5497        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5498        let text = result.text();
5499        assert!(text.contains("#APP\nvide: ret\n#NO_APP\n"), "{text}");
5500        let main = text.find("main:").expect("main");
5501        assert!(text.find("#NO_APP").expect("the markers") < main, "templates first: {text}");
5502
5503        opts.emit = EmitKind::Object;
5504        let result = run(&opts, source);
5505        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5506        let (bytes, defines) = match result.artifact {
5507            Artifact::Object { bytes, defines } => (bytes, defines),
5508            other => panic!("expected an object, got {other:?}"),
5509        };
5510        assert!(defines.iter().any(|name| name == "_us"), "{defines:?}");
5511        // `mov $0x1234abcd, %eax` and the `ret` after it, which only the assembler wrote.
5512        let us = [0xb8, 0xcd, 0xab, 0x34, 0x12, 0xc3];
5513        assert!(bytes.windows(us.len()).any(|window| window == us), "the template's bytes");
5514
5515        // Elsewhere there is no reader for the listing, so the template is still refused there.
5516        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
5517        opts.emit = EmitKind::Ir;
5518        let result = run(&opts, source);
5519        assert!(!result.messages.is_empty(), "refused on Mach-O");
5520        assert!(result.messages[0].contains("the instruction 'movl'"), "{:?}", result.messages);
5521    }
5522
5523    /// `return;` from a function that promised a value, which only C89 lets through and which
5524    /// therefore only reaches the IR builder under that dialect.
5525    ///
5526    /// Zero goes back. The alternatives are worse: an empty return list builds a `ret` the
5527    /// verifier refuses, which is what a torture case found, and `unreachable` would be a claim
5528    /// that the branch reaching this never runs, which is a claim about the program rather than
5529    /// about the value and lets the optimizer delete the path that led here.
5530    #[test]
5531    fn a_bare_return_from_a_function_that_promised_a_value_gives_back_a_zero() {
5532        let mut opts = options();
5533        opts.emit = EmitKind::Ir;
5534        opts.std = Std::C89;
5535        let compiled = |source: &str| {
5536            let result = run(&opts, source);
5537            assert_eq!(result.messages, Vec::<String>::new(), "C89 has nothing to say about this");
5538            result.text().to_owned()
5539        };
5540
5541        let text = compiled("int f(int x) { if (x) return; return 3; }\n");
5542        assert!(text.contains("iconst.i32 0\n    return"), "zero goes back: {text}");
5543        assert!(!text.contains("unreachable"), "the branch that reached it is kept: {text}");
5544
5545        // A floating point return needs the constant of its own kind rather than an integer one.
5546        let text = compiled("double f(int x) { if (x) return; return 1.0; }\n");
5547        assert!(text.contains("fconst.f64 0x0\n    return"), "a float zero goes back: {text}");
5548    }
5549
5550    /// What C89 6.3.2.2 declares for a call to a name nothing declared, seen in the IR rather than
5551    /// in what was said about it.
5552    ///
5553    /// `extern int f();`, so the call gives back an `int` and its arguments are promoted rather
5554    /// than converted to parameters there are none of. The declaration lasts for the file, which
5555    /// is what makes a second call to the same name ordinary and is why gcc says this once per
5556    /// file rather than once per call.
5557    #[test]
5558    fn a_call_to_a_name_nothing_declared_declares_it_as_c89_said_to() {
5559        let mut opts = options();
5560        opts.emit = EmitKind::Ir;
5561        opts.std = Std::C89;
5562        let compiled = |source: &str| {
5563            let result = run(&opts, source);
5564            assert_eq!(result.messages, Vec::<String>::new(), "C89 has nothing to say about this");
5565            result.text().to_owned()
5566        };
5567
5568        // An `int` back, which is the whole of what the implicit declaration says.
5569        let text = compiled("int f(void) { return g(); }\n");
5570        assert!(text.contains("call @g"), "the call is to the name that was written: {text}");
5571        assert!(text.contains("i32"), "and it gives back an int: {text}");
5572
5573        // No prototype, so a `char` argument arrives promoted to `int` the way an argument to a
5574        // function whose parameters are unspecified does.
5575        let text = compiled("int f(char c) { return g(c); }\n");
5576        assert!(text.contains("sext.i32"), "the argument is promoted: {text}");
5577
5578        // A name written as a value rather than called is still undeclared, since the rule is
5579        // about a call and nothing else.
5580        let mut opts = options();
5581        opts.std = Std::C89;
5582        let said = run(&opts, "int f(void) { return h; }\n").messages.join("\n");
5583        assert!(said.contains("'h' undeclared"), "not a call, so not declared: {said}");
5584    }
5585
5586    /// A file that calls a name above the definition of it, which is the shape the implicit
5587    /// declaration has to survive rather than swallow.
5588    ///
5589    /// The definition merges into the declaration the call already made rather than making a
5590    /// second one, so a declaration the tree does not carry at the top level takes the definition
5591    /// down with it: the body is attached to a node nothing walks and no function comes out.
5592    /// Nothing about the call itself looks wrong when that happens, and the program gets to the
5593    /// linker before anyone finds out, which is where `execute/cmpsi-1.c` in the torture suite
5594    /// found it, as an undefined reference to a name defined eleven lines further down.
5595    #[test]
5596    fn a_name_called_before_it_is_defined_still_gets_its_definition() {
5597        let mut opts = options();
5598        opts.emit = EmitKind::Ir;
5599        opts.std = Std::C89;
5600        let text = run(&opts, "int f(void) { return dummy(); }\ndummy () { return 7; }\n")
5601            .text()
5602            .to_owned();
5603        assert!(text.contains("func @f()"), "the caller is there: {text}");
5604        assert!(text.contains("func @dummy"), "and so is what it calls: {text}");
5605        assert!(text.contains("iconst.i32 7"), "with the body it was given: {text}");
5606    }
5607
5608    /// An old style definition whose parameter is narrower than what a call passes it.
5609    ///
5610    /// There is no prototype for a call to convert its argument to, so the argument is promoted
5611    /// and an `int` arrives for a parameter the body reads as an `unsigned char`. The entry block
5612    /// is where the two meet, and gcc writes the same pair of instructions there: store the low
5613    /// byte, read it back widened. `execute/950605-1.c` in the torture suite calls `f(-1)` and
5614    /// checks the parameter against `0xFF`, which is the difference between converting and not.
5615    #[test]
5616    fn an_old_style_parameter_is_converted_from_what_the_call_promoted_it_to() {
5617        let mut opts = options();
5618        opts.emit = EmitKind::Ir;
5619        opts.std = Std::C89;
5620        let compiled = |source: &str| run(&opts, source).text().to_owned();
5621
5622        let text = compiled("f (c) unsigned char c; { return c; }\n");
5623        assert!(text.contains("func @f(i32"), "an int arrives: {text}");
5624        assert!(text.contains("trunc.i8"), "and is cut down to what was declared: {text}");
5625        assert!(text.contains("zext.i32"), "then read back unsigned: {text}");
5626
5627        // A `short` is the same shape and signed, so it comes back the other way.
5628        let text = compiled("f (s) short s; { return s; }\n");
5629        assert!(text.contains("trunc.i16"), "cut down: {text}");
5630        assert!(text.contains("sext.i32"), "and read back signed: {text}");
5631
5632        // A `float` parameter is promoted to `double`, and without the conversion the multiply
5633        // below has one f64 operand and one f32, which the verifier refuses as invalid IR.
5634        let text = compiled("f (x) float x; { return x * 2; }\n");
5635        assert!(text.contains("func @f(f64"), "a double arrives: {text}");
5636        assert!(text.contains("fptrunc.f32"), "and is narrowed to the float: {text}");
5637
5638        // A parameter a prototype named arrives as itself and nothing is converted, which is the
5639        // case this must not have changed.
5640        let text = compiled("int f(unsigned char c) { return c; }\n");
5641        assert!(text.contains("func @f(i8)"), "the declared type arrives: {text}");
5642        assert!(!text.contains("trunc"), "so there is nothing to cut down: {text}");
5643    }
5644
5645    /// The six rules gcc 14 turned from a warning into an error, and the three answers each one
5646    /// gets depending on the dialect and on `-fpermissive`.
5647    ///
5648    /// The table is a measurement rather than a reading of the release notes. Six files, one per
5649    /// rule, put through gcc 16.2.0 on x86-64 Linux under each of the four command lines below
5650    /// with no `-W` flags on any of them, and what came back is what is written here. The three
5651    /// rules that say nothing under C89 are the three C89 did not have, and the three that warn
5652    /// there were constraint violations then as well.
5653    #[test]
5654    fn the_rules_gcc_promoted_are_decided_by_the_dialect_and_by_fpermissive() {
5655        // `-std=gnu89`, `-std=gnu17`, `-std=gnu17 -fpermissive`, and `-std=gnu23`.
5656        let modes = [(Std::C89, false), (Std::C17, false), (Std::C17, true), (Std::C23, false)];
5657        let cases = [
5658            ("static counted;\n", ["", "error", "warning", "error"]),
5659            ("int f(void) { return g(); }\n", ["", "error", "warning", "error"]),
5660            ("int f(x) { return x; }\n", ["", "error", "warning", "error"]),
5661            ("int *p;\nvoid h(void) { p = 1; }\n", ["warning", "error", "warning", "error"]),
5662            (
5663                "char *q;\nint *r;\nvoid k(void) { r = q; }\n",
5664                ["warning", "error", "warning", "error"],
5665            ),
5666            ("int f(void) { return; }\n", ["", "error", "warning", "error"]),
5667            ("void g(void) { return 1; }\n", ["warning", "error", "warning", "error"]),
5668        ];
5669
5670        for (source, wanted) in cases {
5671            for (&(std, permissive), wanted) in modes.iter().zip(wanted) {
5672                let mut opts = options();
5673                opts.std = std;
5674                opts.permissive = permissive;
5675                let said = run(&opts, source).messages.join("\n");
5676                let severity = if said.contains(": error: ") {
5677                    "error"
5678                } else if said.contains(": warning: ") {
5679                    "warning"
5680                } else {
5681                    ""
5682                };
5683                let how = if permissive { " -fpermissive" } else { "" };
5684                assert_eq!(
5685                    severity,
5686                    wanted,
5687                    "under -std={}{how}, {source} was answered with `{said}`",
5688                    std.as_str()
5689                );
5690                if wanted.is_empty() {
5691                    assert!(said.is_empty(), "nothing to say, but said `{said}`");
5692                }
5693            }
5694        }
5695    }
5696
5697    /// A first argument that is not a list, which the four variadic operators answer in two ways.
5698    ///
5699    /// gcc has `va_arg` as an operator, since it takes a type name and no function can, and the
5700    /// other three as builtin functions taking the address of a list. The difference is not a
5701    /// naming one: the operator's complaint is its own and is an error under every dialect, and
5702    /// the three functions go through the ordinary rule about an argument of the wrong type,
5703    /// which is one of the rules the table above is about. The same four command lines through
5704    /// gcc 16.2.0 on x86-64 Linux is where these came from.
5705    #[test]
5706    fn the_three_variadic_builtins_answer_a_bad_list_the_way_a_call_answers_a_bad_argument() {
5707        let modes = [(Std::C89, false), (Std::C17, false), (Std::C17, true), (Std::C23, false)];
5708        let cases = [
5709            (
5710                "int f(int n, ...) { char *p; return __builtin_va_arg(p, int); }\n",
5711                "first argument to 'va_arg' not of type 'va_list'",
5712                ["error", "error", "error", "error"],
5713            ),
5714            (
5715                "void f(int n, ...) { char *p; __builtin_va_start(p, n); }\n",
5716                "passing argument 1 of '__builtin_va_start' from incompatible pointer type",
5717                ["warning", "error", "warning", "error"],
5718            ),
5719            (
5720                "void f(int n, ...) { int x; __builtin_va_end(x); }\n",
5721                "passing argument 1 of '__builtin_va_end' makes pointer from integer without a \
5722                 cast",
5723                ["warning", "error", "warning", "error"],
5724            ),
5725            (
5726                "void f(int n, ...) { __builtin_va_list a; char *p; __builtin_va_copy(a, p); }\n",
5727                "passing argument 2 of '__builtin_va_copy' from incompatible pointer type",
5728                ["warning", "error", "warning", "error"],
5729            ),
5730        ];
5731
5732        for (source, message, wanted) in cases {
5733            for (&(std, permissive), wanted) in modes.iter().zip(wanted) {
5734                let mut opts = options();
5735                opts.std = std;
5736                opts.permissive = permissive;
5737                let said = run(&opts, source).messages.join("\n");
5738                let how = if permissive { " -fpermissive" } else { "" };
5739                assert!(
5740                    said.contains(&format!(": {wanted}: {message}")),
5741                    "under -std={}{how}, {source} was answered with `{said}`",
5742                    std.as_str()
5743                );
5744            }
5745        }
5746    }
5747
5748    /// The IR of `source` at one safety tier, insisting that it compiled cleanly.
5749    fn safe_ir(tier: rucc_session::Safety, source: &str) -> String {
5750        let mut opts = options();
5751        opts.emit = EmitKind::Ir;
5752        opts.safety = tier;
5753        let result = run(&opts, source);
5754        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5755        result.text().to_owned()
5756    }
5757
5758    const READS_THROUGH_A_POINTER: &str = "int read(int *p) { return p[1]; }\n";
5759
5760    /// The IR for a source built with a tier and a padding mode.
5761    fn padded_ir(padding: Padding, source: &str) -> String {
5762        let mut opts = options();
5763        opts.emit = EmitKind::Ir;
5764        opts.safety = rucc_session::Safety::Detect;
5765        opts.padding = padding;
5766        let result = run(&opts, source);
5767        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5768        result.text().to_owned()
5769    }
5770
5771    const FILLS_A_RECORD_A_MEMBER_AT_A_TIME: &str = "struct padded { char tag; int value; };\n\
5772         void fill(struct padded *p) { p->tag = 1; p->value = 2; }\n";
5773
5774    #[test]
5775    fn a_record_filled_a_member_at_a_time_comes_out_whole_when_padding_does_not_participate() {
5776        // Section 9.3 of document 09, and the reason the default is the one it gives library code.
5777        // Four bytes from the `char` and four from the `int` is the whole of an eight byte record,
5778        // so the `memcmp` or the hash or the `write` that reads it back is not refused.
5779        let text = padded_ir(Padding::Ignored, FILLS_A_RECORD_A_MEMBER_AT_A_TIME);
5780        assert_eq!(text.matches("owns 4").count(), 2, "{text}");
5781    }
5782
5783    #[test]
5784    fn a_store_says_only_what_it_wrote_when_padding_does_participate() {
5785        // The kernel profile's default, which is section 9.3's actual rule: the padding stays
5786        // unwritten and the read of the record that would leak it is the one that reports.
5787        let text = padded_ir(Padding::Tracked, FILLS_A_RECORD_A_MEMBER_AT_A_TIME);
5788        assert!(!text.contains("owns"), "{text}");
5789    }
5790
5791    #[test]
5792    fn a_member_of_a_union_owns_nothing_after_it() {
5793        // The bytes after a short member of a union belong to a longer member rather than to
5794        // padding, and saying a store through the short one wrote them would be saying the longer
5795        // one holds a value nobody put there.
5796        let text = padded_ir(
5797            Padding::Ignored,
5798            "union u { char tag; long wide; };\nvoid fill(union u *p) { p->tag = 1; }\n",
5799        );
5800        assert!(!text.contains("owns"), "{text}");
5801    }
5802
5803    #[test]
5804    fn an_inner_records_trailing_padding_reaches_the_outer_records() {
5805        // The composition. `in` owns four bytes of `outer` because `x` starts there, and `c` is
5806        // the last member of `in`, so what it owns is what `in` owns rather than its own one byte.
5807        // Without that the three bytes between them would stay unwritten and a read of the whole
5808        // thing would report.
5809        let text = padded_ir(
5810            Padding::Ignored,
5811            "struct inner { char c; };\n\
5812             struct outer { struct inner in; int x; };\n\
5813             void fill(struct outer *p) { p->in.c = 1; p->x = 2; }\n",
5814        );
5815        assert_eq!(text.matches("owns 4").count(), 2, "{text}");
5816    }
5817
5818    #[test]
5819    fn a_build_that_did_not_ask_for_the_monitor_is_compiled_the_way_it_always_was() {
5820        // This is the load bearing test of the whole flag. The monitor is being built in the open
5821        // and every build in the world is compiled by this compiler with the flag absent, so a
5822        // check that leaked into that path would be a regression for everybody.
5823        let text = ir(READS_THROUGH_A_POINTER);
5824        assert!(!text.contains("check_"), "{text}");
5825        assert!(!text.contains("cap_of"), "{text}");
5826    }
5827
5828    #[test]
5829    fn asking_for_a_tier_puts_the_checks_in_before_the_optimizer_sees_them() {
5830        let text = safe_ir(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
5831        assert!(text.contains("cap_of"), "{text}");
5832        assert!(text.contains("check_bounds"), "{text}");
5833        assert!(text.contains("check_live"), "{text}");
5834        // The subscript is address arithmetic, so J2 applies to it as well as J1.
5835        assert!(text.contains("check_deriv"), "{text}");
5836        // And the read names a type, so it asks the type plane about the bytes as well.
5837        assert!(text.contains("check_type"), "{text}");
5838    }
5839
5840    #[test]
5841    fn the_three_tiers_that_are_not_off_all_check_the_same_accesses_so_far() {
5842        // What separates them is the reporter and the boundary, which are milestones S2 and S3.
5843        // Pinning it here means the day they stop agreeing, this test says so rather than the
5844        // difference going unnoticed.
5845        let detect = safe_ir(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
5846        for tier in [rucc_session::Safety::Enforce, rucc_session::Safety::Kernel] {
5847            assert_eq!(safe_ir(tier, READS_THROUGH_A_POINTER), detect, "{tier}");
5848        }
5849    }
5850
5851    /// The safety summary of `source` at one tier, insisting that it compiled cleanly.
5852    fn summary(tier: rucc_session::Safety, source: &str) -> String {
5853        let mut opts = options();
5854        opts.emit = EmitKind::SafetySummary;
5855        opts.safety = tier;
5856        let result = run(&opts, source);
5857        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5858        result.text().to_owned()
5859    }
5860
5861    #[test]
5862    fn the_summary_counts_the_checks_that_went_in_and_the_ones_still_standing() {
5863        let text = summary(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
5864        assert!(text.contains("\"tier\": \"detect\""), "{text}");
5865        // One load, so one of each of the two access checks, and the subscript is a derivation.
5866        assert!(
5867            text.contains("\"bounds\": { \"emitted\": 1, \"remaining\": 1, \"discharged\": 0 }"),
5868            "{text}"
5869        );
5870        assert!(
5871            text.contains(
5872                "\"derivation\": { \"emitted\": 1, \"remaining\": 1, \"discharged\": 0 }"
5873            ),
5874            "{text}"
5875        );
5876    }
5877
5878    #[test]
5879    fn a_build_without_the_monitor_summarises_as_a_build_with_no_checks_in_it() {
5880        // Which is the honest summary rather than an error. A build system that emits a summary
5881        // for every unit should get one for the units nobody asked to instrument too, and the
5882        // zeroes are what say that the guarantee over that file is nothing at all.
5883        let text = summary(rucc_session::Safety::Off, READS_THROUGH_A_POINTER);
5884        assert!(text.contains("\"tier\": \"off\""), "{text}");
5885        assert!(
5886            text.contains("\"bounds\": { \"emitted\": 0, \"remaining\": 0, \"discharged\": 0 }"),
5887            "{text}"
5888        );
5889    }
5890
5891    #[test]
5892    fn a_call_the_boundary_models_is_counted_apart_from_one_it_does_not() {
5893        let text = summary(
5894            rucc_session::Safety::Detect,
5895            "void *memcpy(void *, const void *, unsigned long);\n\
5896             int puts(const char *);\n\
5897             void f(char *d, char *s) { memcpy(d, s, 4); puts(d); }\n",
5898        );
5899        assert!(text.contains("\"interposed\": 1"), "{text}");
5900        assert!(text.contains("\"puts\""), "{text}");
5901        // The wrapper it was pointed at is ours, so it is not on the list of things this build
5902        // failed to model. Counting it there would make instrumenting a file look worse than
5903        // leaving it alone.
5904        assert!(!text.contains("__rucc_wrap_memcpy\""), "{text}");
5905    }
5906
5907    #[test]
5908    fn an_address_taken_of_a_library_function_is_counted_the_way_a_call_to_one_is() {
5909        // The shape SQLite's syscall table has, cut down to two rows. `memcpy` has a wrapper so the
5910        // table holds the wrapper's address and the build modelled it; `puts` has none, so what the
5911        // table holds is the real function and the build did not, and section 10.1 says the one it
5912        // did not is named rather than passed over.
5913        let text = summary(
5914            rucc_session::Safety::Detect,
5915            "void *memcpy(void *, const void *, unsigned long);\n\
5916             int puts(const char *);\n\
5917             void *table[2] = { (void *)memcpy, (void *)puts };\n\
5918             void *f(int i) { return table[i]; }\n",
5919        );
5920        assert!(text.contains("\"interposed\": 1"), "{text}");
5921        assert!(text.contains("\"puts\""), "{text}");
5922        assert!(!text.contains("\"memcpy\""), "{text}");
5923    }
5924
5925    #[test]
5926    fn the_two_directions_a_pointer_crosses_the_boundary_are_counted_apart() {
5927        // `f` is a name the linker can bind to and takes a pointer, so a pointer arrives there.
5928        // `notes_open` is a library this build did not instrument, so a pointer comes back from
5929        // it. Both are crossings and neither is the other, which is why there are two numbers.
5930        let text = summary(
5931            rucc_session::Safety::Detect,
5932            "void *notes_open(void);\n\
5933             char *f(char *p) { char *q = notes_open(); return q ? q : p; }\n",
5934        );
5935        assert!(text.contains("\"crossings\": { \"entered\": 1, \"returned\": 1 }"), "{text}");
5936        assert!(text.contains("\"notes_open\""), "{text}");
5937    }
5938
5939    #[test]
5940    fn a_static_function_nobody_takes_the_address_of_is_not_a_crossing() {
5941        // Nothing outside the file can reach it, so a witness on its parameters would be counting
5942        // a crossing that does not happen.
5943        let text = summary(
5944            rucc_session::Safety::Detect,
5945            "static int len(const char *p) { return p ? 1 : 0; }\n\
5946             int f(void) { return len(\"x\"); }\n",
5947        );
5948        assert!(text.contains("\"crossings\": { \"entered\": 0, \"returned\": 0 }"), "{text}");
5949    }
5950
5951    /// The granule report for `source`, insisting that it compiled cleanly.
5952    fn granules(source: &str) -> String {
5953        let mut opts = options();
5954        opts.emit = EmitKind::TypeGranules;
5955        let result = run(&opts, source);
5956        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5957        result.text().to_owned()
5958    }
5959
5960    #[test]
5961    fn the_granule_report_names_every_record_and_both_keyings() {
5962        let text = granules(
5963            "struct hot { char *p; int a; int b; };\n\
5964             int f(struct hot *h) { return h->a; }\n",
5965        );
5966        assert!(text.contains("struct hot"), "{text}");
5967        // Both keyings are reported because which types count as one is a decision the design
5968        // has not made yet, and a report that picked one would be hiding the cost of the other.
5969        assert!(text.contains("every type distinct"), "{text}");
5970        assert!(text.contains("every pointer one type"), "{text}");
5971        assert!(text.contains("budget"), "{text}");
5972    }
5973
5974    #[test]
5975    fn a_record_nothing_uses_is_still_measured() {
5976        // The measurement is about what a program declares, not about what it runs, so a type
5977        // that is only ever declared still costs the plane whatever its layout costs.
5978        let text = granules("struct unused { long a; double b; };\nint f(void) { return 0; }\n");
5979        assert!(text.contains("struct unused"), "{text}");
5980    }
5981
5982    #[test]
5983    fn the_granule_report_stops_before_anything_is_lowered() {
5984        // A layout is settled at the closing brace, so lowering the function bodies would take
5985        // minutes on an amalgamation and answer nothing. The evidence that it stops is that a
5986        // body the back end has no way to compile still produces a report.
5987        let text = granules(
5988            "struct wide { long double d; };\n\
5989             long double f(long double x) { return x * x; }\n",
5990        );
5991        assert!(text.contains("struct wide"), "{text}");
5992    }
5993
5994    #[test]
5995    fn a_witness_reaches_the_assembler_as_a_call_to_the_runtime() {
5996        // The count only means anything if the call is really there, and a summary saying one is
5997        // there is not evidence that the back end emitted it.
5998        let text = safe_asm(rucc_session::Safety::Detect, "char *f(char *p) { return p; }\n");
5999        assert!(text.contains("\tcall\t__rucc_cap_witness\n"), "{text}");
6000    }
6001
6002    #[test]
6003    fn a_pointer_turned_into_an_integer_is_on_the_trust_set() {
6004        let text = summary(
6005            rucc_session::Safety::Detect,
6006            "unsigned long f(int *p) { return (unsigned long) p; }\n",
6007        );
6008        assert!(text.contains("\"exposed\": 1"), "{text}");
6009    }
6010
6011    /// The assembly of `source` at one safety tier, insisting that it compiled cleanly.
6012    fn safe_asm(tier: rucc_session::Safety, source: &str) -> String {
6013        let mut opts = options();
6014        opts.emit = EmitKind::Asm;
6015        opts.safety = tier;
6016        let result = run(&opts, source);
6017        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
6018        result.text().to_owned()
6019    }
6020
6021    #[test]
6022    fn a_check_reaches_the_assembler_as_a_call_to_the_runtime() {
6023        let text = safe_asm(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
6024        assert!(text.contains("\tcall\t__rucc_check_bounds\n"), "{text}");
6025        assert!(text.contains("\tcall\t__rucc_check_live\n"), "{text}");
6026        assert!(text.contains("\tcall\t__rucc_check_deriv\n"), "{text}");
6027        // The type check and the init check of one read reach the assembler as the one call that
6028        // asks both planes about it. `rucc_safety::lower::partner` is what recognises the pair.
6029        assert!(text.contains("\tcall\t__rucc_check_typed_init\n"), "{text}");
6030    }
6031
6032    #[test]
6033    fn every_check_that_reached_the_assembler_has_a_row_describing_it() {
6034        // Four calls and four descriptors, each in the section the runtime's reporter reads. The
6035        // width is `rucc_safety::lower::WIDTH` and the row is `rucc_safe_rt::fail::Descriptor`, and
6036        // the two agreeing is what makes the address a check is handed mean anything. Four rather
6037        // than five because the read's two plane questions are one call carrying one row, which the
6038        // two of them can share because a type check's row and an init check's row are identical.
6039        let text = safe_asm(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
6040        let section = format!("\t.section\t{},", rucc_safety::SECTION);
6041        assert_eq!(text.matches(&section).count(), 4, "{text}");
6042        for index in 0..4 {
6043            let name = format!("__rucc_safety_desc_{index}");
6044            // Defined once and referenced once, because a descriptor nothing points at describes
6045            // nothing and a reference with no definition does not link.
6046            assert!(text.contains(&format!("{name}:\n")), "{text}");
6047            assert!(text.contains(&format!("{name}(%rip)")), "{text}");
6048        }
6049        assert!(!text.contains("__rucc_safety_desc_4"), "{text}");
6050    }
6051
6052    /// `__builtin_constant_p` is answered in the front end and never reaches the IR.
6053    ///
6054    /// gcc folds it after optimization, so its answer for an argument that is not written as a
6055    /// constant can differ between `-O0` and `-O2`. What is checked here is the front end's
6056    /// answer, which is the same at every level, and the four cases where gcc gives the same
6057    /// answer at both levels are the ones measured on gcc 16: a literal is one, a variable is
6058    /// zero, a string literal is one and the address of an object is zero.
6059    #[test]
6060    fn builtin_constant_p_is_folded_where_it_is_written_rather_than_called() {
6061        let text = ir(concat!(
6062            "int g;\n",
6063            "int a = __builtin_constant_p(1);\n",
6064            "int b = __builtin_constant_p(g);\n",
6065            "int c = __builtin_constant_p(\"abc\");\n",
6066            "int d = __builtin_constant_p(&g);\n",
6067            "int e = __builtin_constant_p(1.5);\n",
6068            "int h = __builtin_choose_expr(__builtin_constant_p(3), 11, 22);\n",
6069        ));
6070        assert!(text.contains("global @a : i32 = 1,"), "{text}");
6071        assert!(text.contains("global @b : i32 = 0,"), "{text}");
6072        assert!(text.contains("global @c : i32 = 1,"), "{text}");
6073        assert!(text.contains("global @d : i32 = 0,"), "{text}");
6074        assert!(text.contains("global @e : i32 = 1,"), "{text}");
6075        assert!(text.contains("global @h : i32 = 11,"), "{text}");
6076        assert!(!text.contains("__builtin_constant_p"), "it is not a call to anything:\n{text}");
6077
6078        // The argument is not evaluated, which is what gcc does with it as well, so `i` is
6079        // still zero. The second constant is the answer, which nothing reads and which the
6080        // first pass that looks for dead code will take out.
6081        let text = body("int f(void) { int i = 0; __builtin_constant_p(i++); return i; }\n");
6082        assert_eq!(text, "block0:\n    %0 = iconst.i32 0\n    %1 = iconst.i32 0\n    return %0\n");
6083    }
6084
6085    /// A library builtin is the library function of the same name, and the call says so.
6086    ///
6087    /// A program writes `__builtin_strlen` rather than `strlen` to reach the function the C
6088    /// library promises where its own name has been taken by a macro, and to say that the usual
6089    /// meaning is the one intended. So the name in the program and the name in the object file
6090    /// are two different names and the call carries the second one. gcc folds several of these
6091    /// when the arguments allow it, which is an optimization on top of a call that is already
6092    /// right rather than instead of it, so nothing here depends on any folding happening.
6093    #[test]
6094    fn a_call_to_a_library_builtin_reaches_the_library_function() {
6095        let text = body("void f(void) { __builtin_abort(); }\n");
6096        assert_eq!(text, "block0:\n    call @abort() : ()\n    return\n");
6097
6098        // Nothing declared either of these and nothing had to: the prefix is what says the name
6099        // belongs to the implementation, and the type comes out of `features.toml`.
6100        let text = ir("int f(const char *s) { return __builtin_puts(s) + __builtin_strlen(s); }\n");
6101        assert!(text.contains("call @puts(%0) : (ptr) -> i32"), "{text}");
6102        assert!(text.contains("call @strlen(%0) : (ptr) -> i64"), "{text}");
6103        assert!(!text.contains("__builtin_"), "the prefix is not part of any name here:\n{text}");
6104    }
6105
6106    /// A `_chk` builtin reaches the checking function in the library with the object size still
6107    /// on the end of it.
6108    ///
6109    /// This is what a fortified `string.h` turns every copy into, so it is what a program built
6110    /// the way a distribution builds one is full of, and the whole of what makes the call right
6111    /// is that the size goes with it. The checking function takes `(size_t) -1` to mean nothing
6112    /// is known and does no check, which is what the header passes when the destination's object
6113    /// is not in sight, so the unconditional call means the same thing in both cases and costs a
6114    /// call gcc would have folded away in the second.
6115    ///
6116    /// The name is the one place this family reads like an exception and is not one:
6117    /// `__builtin___memcpy_chk` with `__builtin_` taken off is `__memcpy_chk`.
6118    #[test]
6119    fn a_chk_builtin_reaches_the_checking_function_and_keeps_the_size() {
6120        let text = ir(concat!(
6121            "char d[8];\n",
6122            "void f(const char *s, unsigned long n) {\n",
6123            "  __builtin___memcpy_chk(d, s, n, __builtin_object_size(d, 0));\n",
6124            "  __builtin___strcpy_chk(d, s, __builtin_object_size(d, 1));\n",
6125            "  __builtin___memset_chk(d, 0, n, 8);\n",
6126            "}\n",
6127        ));
6128        assert!(text.contains("call @__memcpy_chk("), "{text}");
6129        assert!(text.contains("call @__strcpy_chk("), "{text}");
6130        assert!(text.contains("call @__memset_chk("), "{text}");
6131        assert!(text.contains("iconst.i64 8"), "the object size reaches the call: {text}");
6132        assert!(!text.contains("__builtin_"), "the prefix is not part of any name here:\n{text}");
6133    }
6134
6135    /// A checking call whose object size says nothing is known is the plain library call.
6136    ///
6137    /// That is the whole of the folding half of the family. The checking function reads the all
6138    /// ones value as do not check, so the call it was going to make is the function it guards with
6139    /// an argument nobody reads on the end of it, and gcc drops the argument and calls the plain
6140    /// function at every level including `-O0`. Where the size is a real number the checking call
6141    /// stands, because the check is the point.
6142    #[test]
6143    fn a_checking_call_whose_size_says_nothing_is_known_is_the_plain_library_call() {
6144        let text = ir(concat!(
6145            "extern char *p;\n",
6146            "char d[8];\n",
6147            "void f(const char *s, unsigned long n) {\n",
6148            "  __builtin___memcpy_chk(d, s, n, __builtin_object_size(d, 0));\n",
6149            "  __builtin___memcpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
6150            "  __builtin___strcpy_chk(p, s, __builtin_object_size(p, 0));\n",
6151            "  __builtin___stpncpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
6152            "  __builtin___sprintf_chk(p, 1, __builtin_object_size(p, 0), s);\n",
6153            "}\n",
6154        ));
6155
6156        // The destination whose object is in sight keeps its check, size and all.
6157        assert!(
6158            text.contains("call @__memcpy_chk(%2, %0, %1, %3) : (ptr, ptr, i64, i64)"),
6159            "{text}"
6160        );
6161
6162        // The three whose object is not lose the argument and the name along with it. The type of
6163        // the call goes with them, which is what says the argument is gone rather than ignored.
6164        assert!(text.contains("call @memcpy(%6, %0, %1) : (ptr, ptr, i64) -> ptr"), "{text}");
6165        assert!(text.contains("call @strcpy(%10, %0) : (ptr, ptr) -> ptr"), "{text}");
6166        assert!(text.contains("call @stpncpy(%14, %0, %1) : (ptr, ptr, i64) -> ptr"), "{text}");
6167
6168        // The formatted one never folds, whatever the size says, because refusing a `%n` in a
6169        // writable format is the other half of what it was asked to do.
6170        assert!(text.contains("call @__sprintf_chk("), "{text}");
6171
6172        // Nothing is left behind in the instructions either. The size the folded calls no longer
6173        // take is a constant nobody reads, and no instruction is written for one.
6174        let asm = asm(concat!(
6175            "void f(char *p, const char *s, unsigned long n) {\n",
6176            "  __builtin___memcpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
6177            "}\n",
6178        ));
6179        assert!(asm.contains("call\tmemcpy"), "{asm}");
6180        assert!(!asm.contains("$-1"), "the size that went away leaves no instruction:\n{asm}");
6181    }
6182
6183    /// The `v` spellings take a `__builtin_va_list`, which is the first type in the table the
6184    /// target chooses the shape of rather than the width of.
6185    ///
6186    /// On x86-64 it is an array of one, so what the prototype has to say is the pointer that
6187    /// array decays to, which is the same adjustment C makes to any parameter written as an array
6188    /// and is what a `va_list` parameter already holds. A prototype that kept the array would be
6189    /// one no argument could ever match.
6190    #[test]
6191    fn the_v_spellings_of_the_chk_family_take_the_list_a_va_list_parameter_holds() {
6192        let text = ir(concat!(
6193            "char d[64];\n",
6194            "int f(const char *fmt, ...) {\n",
6195            "  __builtin_va_list ap;\n",
6196            "  __builtin_va_start(ap, fmt);\n",
6197            "  int n = __builtin___vsprintf_chk(d, 1, __builtin_object_size(d, 0), fmt, ap);\n",
6198            "  __builtin_va_end(ap);\n",
6199            "  return n;\n",
6200            "}\n",
6201        ));
6202        assert!(text.contains("call @__vsprintf_chk("), "{text}");
6203        assert!(text.contains("iconst.i64 64"), "the object size reaches the call: {text}");
6204    }
6205
6206    /// The absolute value family is four instructions and not a call, whoever declared the name.
6207    ///
6208    /// `abs`, `labs` and `llabs` are reserved to the implementation, so a program that writes one
6209    /// means the one the C library promises and the compiler is allowed to know what it does. The
6210    /// program in `gcc.c-torture/execute/20021127-1.c` is the one that insists: it defines `llabs`
6211    /// to abort and expects the call not to reach it. Measured against gcc 16.2.0, which writes a
6212    /// `neg` and a `cmovns` and never calls the definition either.
6213    ///
6214    /// The most negative value comes back as itself, which is what the arithmetic gives and what
6215    /// gcc's pair of instructions gives, and C says the answer is undefined there.
6216    #[test]
6217    fn the_absolute_value_family_is_the_magnitude_and_not_a_call() {
6218        let text = body(concat!(
6219            "long long llabs(long long);\n",
6220            "long long f(long long x) { return llabs(x); }\n",
6221        ));
6222        assert!(text.contains("%1 = iconst.i64 63"), "{text}");
6223        assert!(text.contains("%2 = ashr %0, %1"), "{text}");
6224        assert!(text.contains("%3 = xor %0, %2"), "{text}");
6225        assert!(text.contains("%4 = sub %3, %2"), "{text}");
6226        assert!(!text.contains("call"), "the call does not happen:\n{text}");
6227
6228        // The narrower two, whose width comes from the type the library gives the name and not
6229        // from anything at the call.
6230        let text = body("int abs(int);\nint f(int x) { return abs(x); }\n");
6231        assert!(text.contains("iconst.i32 31"), "{text}");
6232        let text = body("long labs(long);\nlong f(long x) { return labs(x); }\n");
6233        assert!(text.contains("iconst.i64 63"), "{text}");
6234
6235        // The prefixed spelling is the same node, and it is what a program writes to reach the
6236        // library's meaning where the plain name has been taken.
6237        let text = body("long long f(long long x) { return __builtin_llabs(x); }\n");
6238        assert!(!text.contains("call"), "{text}");
6239
6240        // A definition of the name in the same file changes nothing, which is the whole point.
6241        let text = ir(concat!(
6242            "long long llabs(long long b);\n",
6243            "long long g(long long x) { return llabs(x); }\n",
6244            "long long llabs(long long b) { return 7; }\n",
6245        ));
6246        assert!(!text.contains("call @llabs"), "{text}");
6247    }
6248
6249    /// A byte swap is one instruction and not a call, and nothing had to declare it.
6250    ///
6251    /// SQLite writes these for its page headers and glibc's `<endian.h>` defines `htobe32` and its
6252    /// neighbours as exactly these, so a program that reads a file format reaches one without ever
6253    /// naming it. There is no object file anywhere that defines `__builtin_bswap32`, so a call left
6254    /// standing here would not link.
6255    #[test]
6256    fn a_byte_swap_is_arithmetic_and_not_a_call() {
6257        let text = body("unsigned f(unsigned x) { return __builtin_bswap32(x); }\n");
6258        assert_eq!(text, "block0(%0: i32):\n    %1 = bswap %0\n    return %1\n");
6259
6260        // The argument is converted by the prototype the way any other call's would be, so the
6261        // swap happens at the width the name says and not at the width the program wrote.
6262        let text = body("unsigned f(unsigned char c) { return __builtin_bswap32(c); }\n");
6263        assert!(text.contains("zext.i32 %0"), "widened first: {text}");
6264        assert!(text.contains("bswap %1"), "and swapped at four bytes: {text}");
6265    }
6266
6267    /// Each of the three reverses in the width its name says, which is the type of the node.
6268    ///
6269    /// The width matters more here than it looks. `__builtin_bswap16` is the two bytes of a
6270    /// `uint16_t` exchanged, and if the node came out at the machine's width instead then the bits
6271    /// above the value would be dragged into the answer and the result would be zero.
6272    #[test]
6273    fn the_byte_swaps_reverse_at_the_width_their_name_says() {
6274        for (name, ty, width) in [
6275            ("__builtin_bswap16", "unsigned short", "i16"),
6276            ("__builtin_bswap32", "unsigned", "i32"),
6277            ("__builtin_bswap64", "unsigned long long", "i64"),
6278        ] {
6279            let source = format!("{ty} f({ty} x) {{ return {name}(x); }}\n");
6280            let text = body(&source);
6281            assert_eq!(
6282                text,
6283                format!("block0(%0: {width}):\n    %1 = bswap %0\n    return %1\n"),
6284                "{name}"
6285            );
6286        }
6287    }
6288
6289    /// The three bit counts the IR has an instruction for are that instruction and not a call.
6290    ///
6291    /// Eighteen rows of `features.toml` come out of six questions, and three of the six are one
6292    /// instruction each. The kernel's bitmap search is built on them, ffmpeg counts leading zeroes
6293    /// in its bitstream reader and SQLite uses one to size a page, so a call left standing here
6294    /// would not link against anything and would be slow if it did.
6295    #[test]
6296    fn the_bit_counts_are_instructions_and_not_calls() {
6297        let text = body("int f(unsigned x) { return __builtin_clz(x); }\n");
6298        assert_eq!(text, "block0(%0: i32):\n    %1 = ctlz %0\n    return %1\n");
6299
6300        let text = body("int f(unsigned x) { return __builtin_ctz(x); }\n");
6301        assert_eq!(text, "block0(%0: i32):\n    %1 = cttz %0\n    return %1\n");
6302
6303        let text = body("int f(unsigned x) { return __builtin_popcount(x); }\n");
6304        assert_eq!(text, "block0(%0: i32):\n    %1 = ctpop %0\n    return %1\n");
6305    }
6306
6307    /// The width counted is the operand's and the width answered is `int`, which are two different
6308    /// things at every spelling but the narrowest.
6309    ///
6310    /// This is the mistake the family invites. `__builtin_clz` of a value counts the leading zeroes
6311    /// of it narrowed to `unsigned int` and `__builtin_clzll` counts them at sixty four bits, and
6312    /// those are different numbers for the same value. What decides it is the prototype the row
6313    /// carries, so the count happens after the conversion and the narrowing back to `int` happens
6314    /// after the count.
6315    #[test]
6316    fn the_bit_counts_ask_about_the_width_their_name_says() {
6317        let text = body("int f(unsigned long long x) { return __builtin_clzll(x); }\n");
6318        assert!(text.starts_with("block0(%0: i64):"), "counted at eight bytes: {text}");
6319        assert!(text.contains("%1 = ctlz %0"), "{text}");
6320        assert!(text.contains("trunc.i32 %1"), "and answered in an int: {text}");
6321
6322        // The same value asked about at the narrower width, which converts first and so counts
6323        // something else.
6324        let text = body("int f(unsigned long long x) { return __builtin_clz(x); }\n");
6325        assert!(text.contains("trunc.i32 %0"), "narrowed to what was asked about: {text}");
6326        assert!(text.contains("ctlz %1"), "and counted there: {text}");
6327
6328        let text = body("int f(unsigned long x) { return __builtin_popcountl(x); }\n");
6329        assert!(text.contains("%1 = ctpop %0"), "{text}");
6330        assert!(!text.contains("call"), "{text}");
6331    }
6332
6333    /// A parity is whether the count of set bits is odd, which is that count and its low bit.
6334    ///
6335    /// Not the machine's parity flag, which on x86-64 is over the low byte of a result and so is a
6336    /// different question, and not the count itself, since C says the answer is zero or one.
6337    #[test]
6338    fn a_parity_is_the_low_bit_of_the_set_bit_count() {
6339        let text = body("int f(unsigned x) { return __builtin_parity(x); }\n");
6340        assert!(text.contains("%1 = ctpop %0"), "{text}");
6341        assert!(text.contains("iconst.i32 1"), "{text}");
6342        assert!(text.contains("and %1, %2"), "the low bit of it: {text}");
6343    }
6344
6345    /// `__builtin_ffs` is the trailing zero count and one, kept only when there was a bit to find.
6346    ///
6347    /// The one in the family defined at zero, where it answers zero. Written as a mask rather than
6348    /// as a branch: the count and the comparison do not depend on each other and both are cheap, so
6349    /// a branch would buy nothing and cost two blocks and a join.
6350    #[test]
6351    fn the_first_set_bit_is_one_based_and_zero_for_a_zero() {
6352        let text = body("int f(int x) { return __builtin_ffs(x); }\n");
6353        assert!(text.contains("%1 = cttz %0"), "{text}");
6354        assert!(text.contains("%4 = add %1, %2"), "one more than the count: {text}");
6355        assert!(text.contains("%5 = icmp ne %0, %3"), "whether there was a bit at all: {text}");
6356        assert!(text.contains("%7 = sub %3, %6"), "spread to a mask: {text}");
6357        assert!(text.contains("%8 = and %4, %7"), "and kept only then: {text}");
6358        assert!(!text.contains("br_if"), "no branch: {text}");
6359    }
6360
6361    /// `__builtin_clrsb` is how many bits below the sign bit repeat it, which is a leading zero
6362    /// count of the value folded onto its own sign.
6363    ///
6364    /// Exclusive or with the sign spread over every bit turns a negative value into its complement
6365    /// and leaves one that is not negative alone, so in both cases the top bit is clear and there
6366    /// is one zero above the highest bit that does not repeat the sign. The answer is one less
6367    /// than that count, and the shift left is what takes the one off, with the low bit set on the
6368    /// way so that zero and minus one have something to count: both of them fold to a word with no
6369    /// bits in it, which is the one input a leading zero count says nothing about.
6370    #[test]
6371    fn the_redundant_sign_bit_count_is_instructions_and_not_a_call() {
6372        let text = body("int f(int x) { return __builtin_clrsb(x); }\n");
6373        assert!(text.contains("%1 = iconst.i32 31"), "{text}");
6374        assert!(text.contains("%2 = ashr %0, %1"), "the sign over every bit: {text}");
6375        assert!(text.contains("%3 = xor %0, %2"), "folded onto it: {text}");
6376        assert!(text.contains("%5 = shl %3, %4"), "one less than the count: {text}");
6377        assert!(text.contains("%6 = or %5, %4"), "with something to count at zero: {text}");
6378        assert!(text.contains("%7 = ctlz %6"), "{text}");
6379        assert!(!text.contains("call"), "{text}");
6380        assert!(!text.contains("br_if"), "no branch: {text}");
6381    }
6382
6383    /// The unsigned four are the same four instructions answering in the unsigned type.
6384    ///
6385    /// Which on a two's complement machine is the same bits, so what this checks is that the type
6386    /// of the answer is the unsigned one. The reason the family exists is the most negative value,
6387    /// whose magnitude is not representable in the signed type and is representable in this one.
6388    #[test]
6389    fn the_unsigned_absolute_value_family_answers_in_the_unsigned_type() {
6390        let text = body("unsigned f(int x) { return __builtin_uabs(x); }\n");
6391        assert!(text.contains("%1 = iconst.i32 31"), "{text}");
6392        assert!(text.contains("%4 = sub %3, %2"), "{text}");
6393        assert!(!text.contains("call"), "nothing declares uabs, so a call would not link: {text}");
6394
6395        let text = body("unsigned long long f(long long x) { return __builtin_ullabs(x); }\n");
6396        assert!(text.contains("iconst.i64 63"), "at the width the name says: {text}");
6397
6398        // The answer is the unsigned type and not the signed one, which is what a comparison
6399        // against it is decided by.
6400        let text = body("int f(int x) { return __builtin_uabs(x) > 2147483647u; }\n");
6401        assert!(text.contains("icmp ugt"), "compared unsigned: {text}");
6402    }
6403
6404    /// `intmax_t` is not a fixed type, so the two widest spellings ask the target what it is.
6405    ///
6406    /// `long` where that is sixty four bits wide and `long long` where it is not, which is the rule
6407    /// `rucc_pp::predef` writes `__INTMAX_TYPE__` out of. The three targets here are all LP64, so
6408    /// the answer is `long` and the shift is sixty three, and the point of the test is that the
6409    /// signature was understood at all rather than refused for naming a type the table could not
6410    /// spell.
6411    #[test]
6412    fn the_widest_absolute_value_is_whichever_type_the_target_makes_intmax_t() {
6413        let text = body("long f(long x) { return __builtin_imaxabs(x); }\n");
6414        assert!(text.contains("iconst.i64 63"), "{text}");
6415        assert!(text.contains("%4 = sub %3, %2"), "{text}");
6416        assert!(!text.contains("call"), "{text}");
6417
6418        let text = body("unsigned long f(long x) { return __builtin_umaxabs(x); }\n");
6419        assert!(text.contains("iconst.i64 63"), "{text}");
6420        assert!(!text.contains("call"), "{text}");
6421    }
6422
6423    /// The `_p` spellings ask the same question, write nothing, and do not evaluate the third
6424    /// argument.
6425    ///
6426    /// gcc says the third argument is there for its type alone, so a call is two operands and a
6427    /// type by the time it reaches the IR. What the type decides is the same thing it decides for
6428    /// the three that write: whether the exact answer would have fit there, which is why the
6429    /// second call below is done at a wider width than the first.
6430    #[test]
6431    fn an_overflow_predicate_writes_nothing_and_answers_the_bit_the_check_would() {
6432        let text =
6433            body("int f(int a, int b) { return __builtin_add_overflow_p(a, b, (int) 0); }\n");
6434        assert!(text.contains("%2, %3 = sadd_overflow.(i32, i1) %0, %1"), "{text}");
6435        assert!(!text.contains("store"), "nothing is written: {text}");
6436        assert!(!text.contains("call"), "{text}");
6437
6438        // A wider destination is a wider arithmetic, and the narrowing test that goes with it is
6439        // what says whether the answer got there, exactly as for the spelling that stores.
6440        let text =
6441            body("int f(int a, int b) { return __builtin_mul_overflow_p(a, b, (long long) 0); }\n");
6442        assert!(text.contains("smul_overflow.(i64, i1)"), "{text}");
6443        assert!(!text.contains("store"), "{text}");
6444
6445        // The third argument is a value and not a pointer, and a side effect written in it does
6446        // not happen, because what the argument is there for is its type.
6447        let text = body(concat!(
6448            "int g(void);\n",
6449            "int f(int a, int b) { return __builtin_sub_overflow_p(a, b, g()); }\n",
6450        ));
6451        assert!(!text.contains("call @g"), "the third argument is not evaluated: {text}");
6452    }
6453
6454    /// The three overflow checks are arithmetic and a flag, and not a call to anything.
6455    ///
6456    /// gcc has emitted these since 5.0 and there is no object file that defines one, so a call left
6457    /// standing here would not link. SQLite reaches all three within twenty lines of each other, in
6458    /// `sqlite3AddInt64` and its two neighbours, which is the reason they were done now.
6459    ///
6460    /// The IR instruction answers two things at once, the wrapped value and whether it wrapped,
6461    /// which is a shape nothing else in the IR has. The store is the builtin writing the answer
6462    /// through the pointer it was handed.
6463    #[test]
6464    fn an_overflow_check_is_arithmetic_and_not_a_call() {
6465        let text =
6466            body("int f(int a, int b, int *r) { return __builtin_add_overflow(a, b, r); }\n");
6467        assert!(text.contains("%3, %4 = sadd_overflow.(i32, i1) %0, %1"), "{text}");
6468        assert!(text.contains("store %3 -> %2"), "{text}");
6469        assert!(!text.contains("call"), "{text}");
6470
6471        let text =
6472            body("int f(int a, int b, int *r) { return __builtin_sub_overflow(a, b, r); }\n");
6473        assert!(text.contains("ssub_overflow.(i32, i1) %0, %1"), "{text}");
6474
6475        let text =
6476            body("int f(int a, int b, int *r) { return __builtin_mul_overflow(a, b, r); }\n");
6477        assert!(text.contains("smul_overflow.(i32, i1) %0, %1"), "{text}");
6478
6479        // Unsigned operands get the unsigned form, which is a different question about the same
6480        // arithmetic: an unsigned sum wraps where a signed one of the same bits does not.
6481        let text = body(
6482            "int f(unsigned a, unsigned b, unsigned *r) { return __builtin_add_overflow(a, b, r); }\n",
6483        );
6484        assert!(text.contains("uadd_overflow.(i32, i1) %0, %1"), "{text}");
6485    }
6486
6487    /// The arithmetic happens at a type that holds every value all three written types can hold.
6488    ///
6489    /// That is what makes the check exact. `unsigned int` and `int` in one call need thirty three
6490    /// bits between them, so the add is done at sixty four with each operand extended the way its
6491    /// own signedness says: the unsigned one zero extended, the signed one sign extended. Sign
6492    /// extending the unsigned one would turn three billion into a negative number before the
6493    /// addition ever saw it.
6494    #[test]
6495    fn an_overflow_check_is_done_at_a_type_that_holds_every_operand() {
6496        let text = body(
6497            "int f(unsigned a, int b, long long *r) { return __builtin_add_overflow(a, b, r); }\n",
6498        );
6499        assert!(text.contains("%3 = zext.i64 %0"), "the unsigned operand keeps its value: {text}");
6500        assert!(text.contains("%4 = sext.i64 %1"), "and so does the signed one: {text}");
6501        assert!(text.contains("sadd_overflow.(i64, i1) %3, %4"), "{text}");
6502
6503        // Three types that agree need no extension at all, which is what nearly every real call
6504        // is written as.
6505        let text = body(
6506            "int f(long long a, long long b, long long *r) { return __builtin_mul_overflow(a, b, r); }\n",
6507        );
6508        assert!(text.contains("smul_overflow.(i64, i1) %0, %1"), "{text}");
6509        assert!(!text.contains("sext."), "{text}");
6510        // The one widening left is the answer, which is a bit becoming the `int` C says it is.
6511        assert!(!text.contains("zext.i64"), "{text}");
6512    }
6513
6514    /// The wrapped answer is written through the pointer whether or not it fit.
6515    ///
6516    /// That is gcc's rule and it is what makes the builtin usable as a wrapping add with a flag on
6517    /// the side. A destination narrower than the arithmetic is narrowed and widened back, and the
6518    /// answer being different is the second half of the test: the instruction says whether the
6519    /// arithmetic itself needed more room, and the round trip says whether what came out survived
6520    /// the trip down to where it was going.
6521    #[test]
6522    fn an_overflow_check_writes_the_wrapped_answer_whether_or_not_it_fit() {
6523        let text =
6524            body("int f(int a, int b, char *r) { return __builtin_sub_overflow(a, b, r); }\n");
6525        assert!(text.contains("%3, %4 = ssub_overflow.(i32, i1) %0, %1"), "{text}");
6526        assert!(text.contains("%5 = trunc.i8 %3"), "narrowed to where it goes: {text}");
6527        assert!(text.contains("%6 = sext.i32 %5"), "and back: {text}");
6528        assert!(text.contains("%7 = icmp ne %6, %3"), "which is whether it fit: {text}");
6529        assert!(text.contains("store %5 -> %2"), "the narrowed value is stored either way: {text}");
6530        assert!(text.contains("%8 = or %4, %7"), "and either bit is an overflow: {text}");
6531    }
6532
6533    /// A call needing more than the widest type there is compiles, by not asking for such a type.
6534    ///
6535    /// One way to reach it: an unsigned `__int128` mixed with a signed type, which needs a hundred
6536    /// and twenty nine bits to represent both and so has nowhere left to go. That used to be refused
6537    /// by name. It is done now by carrying the sign of each operand alongside its value rather than
6538    /// inside it, which is what gcc does, so all three of the family compile for that mix.
6539    #[test]
6540    fn a_call_needing_more_than_the_widest_type_still_compiles() {
6541        for name in ["add", "sub", "mul"] {
6542            let source = format!(
6543                "int f(unsigned __int128 a, long long b, __int128 *r) {{\n    \
6544                 return __builtin_{name}_overflow(a, b, r);\n}}\n"
6545            );
6546            let mut opts = options();
6547            opts.emit = EmitKind::MirFinal;
6548            assert!(!run(&opts, &source).failed(), "{name} was refused or stopped the back end");
6549        }
6550    }
6551
6552    /// An operand that is not an integer at all is the older message, from the type checking every
6553    /// type generic builtin shares.
6554    #[test]
6555    fn an_overflow_check_over_something_that_is_not_an_integer_says_so() {
6556        let messages =
6557            errors("int f(double a, int b, int *r) { return __builtin_add_overflow(a, b, r); }\n");
6558        assert!(messages.iter().any(|line| line.contains("E0671")), "{messages:?}");
6559
6560        let messages =
6561            errors("int f(int a, int b, double *r) { return __builtin_add_overflow(a, b, r); }\n");
6562        assert!(messages.iter().any(|line| line.contains("E0671")), "{messages:?}");
6563    }
6564
6565    /// An ordered access is an ordered access in the IR, with the ordering the program wrote.
6566    ///
6567    /// Which is the point of the node existing at all. An ordering is not an argument anything is
6568    /// passed, it is a thing the IR says about an access, so the number in the source is read once
6569    /// in the front end and after that the ordering travels on the instruction where every pass
6570    /// that moves code can see it.
6571    ///
6572    /// SQLite is why these are done: `AtomicLoad` and `AtomicStore` in `sqlite3.c` are
6573    /// `__atomic_load_n` and `__atomic_store_n` at the relaxed ordering, and there are thirty five
6574    /// calls to the pair.
6575    #[test]
6576    fn an_ordered_access_is_ordered_in_the_ir() {
6577        let text = body("int f(int *p) { return __atomic_load_n(p, 0); }\n");
6578        assert!(text.contains("atomic_load.i32 %0, align 4, relaxed"), "{text}");
6579
6580        let text = body("long f(long *p) { return __atomic_load_n(p, 2); }\n");
6581        assert!(text.contains("atomic_load.i64 %0, align 8, acquire"), "{text}");
6582
6583        let text = body("void f(int *p, int v) { __atomic_store_n(p, v, 3); }\n");
6584        assert!(text.contains("atomic_store %1 -> %0, align 4, release"), "{text}");
6585
6586        let text = body("void f(int *p, int v) { __atomic_store_n(p, v, 5); }\n");
6587        assert!(text.contains("atomic_store %1 -> %0, align 4, seq_cst"), "{text}");
6588
6589        // The value is converted to what the pointer points at before it is stored, which is what
6590        // the call would have done if it had a prototype to convert against.
6591        let text = body("void f(char *p, int v) { __atomic_store_n(p, v, 0); }\n");
6592        assert!(text.contains("trunc.i8 %1"), "{text}");
6593        assert!(text.contains("atomic_store %2 -> %0, align 1, relaxed"), "{text}");
6594    }
6595
6596    /// On this machine the ordered access is the plain instruction, except at the strongest
6597    /// ordering of a store.
6598    ///
6599    /// x86-64 is total store order: every load is already an acquire and every store is already a
6600    /// release, and an aligned access no wider than a word is indivisible whether or not anybody
6601    /// asked. So the whole family is `mov` and the one thing the machine does not give away is a
6602    /// store staying in front of a later load, which is `mfence` behind the store. Every line below
6603    /// is what gcc 16.2.0 writes for the same function.
6604    #[test]
6605    fn an_ordered_access_is_the_plain_instruction_on_this_machine() {
6606        let text = asm("int f(int *p) { return __atomic_load_n(p, 5); }\n");
6607        assert!(text.contains("movl\t(%rdi), %eax"), "{text}");
6608        assert!(!text.contains("mfence"), "a load needs no barrier here: {text}");
6609
6610        let text = asm("void f(int *p, int v) { __atomic_store_n(p, v, 3); }\n");
6611        assert!(text.contains("movl\t%esi, (%rdi)"), "{text}");
6612        assert!(!text.contains("mfence"), "a release store needs no barrier here: {text}");
6613
6614        let text = asm("void f(int *p, int v) { __atomic_store_n(p, v, 5); }\n");
6615        let (before, after) = text.split_once("mfence").expect("a barrier: {text}");
6616        assert!(before.contains("movl\t%esi, (%rdi)"), "the store comes first: {text}");
6617        assert!(!after.contains("movl"), "and nothing else is between them: {text}");
6618    }
6619
6620    /// A barrier is one instruction at the strongest ordering and no instruction below it.
6621    ///
6622    /// The same reasoning the other way round. An acquire, a release and an acquire release fence
6623    /// are already true of every program running on this machine, and what a program wanted from
6624    /// one is that the compiler not move accesses across it, which is already so by the time any
6625    /// instruction is picked. Sequential consistency is the one that costs something.
6626    ///
6627    /// `__sync_synchronize` is the older family's spelling of the strongest one and compiles to
6628    /// exactly the same instruction, which is what SQLite calls twice in `sqlite3.c`.
6629    #[test]
6630    fn a_barrier_is_one_instruction_at_the_strongest_ordering_and_none_below_it() {
6631        assert!(asm("void f(void) { __atomic_thread_fence(5); }\n").contains("mfence"));
6632        assert!(asm("void f(void) { __sync_synchronize(); }\n").contains("mfence"));
6633
6634        for weaker in ["1", "2", "3", "4"] {
6635            let source = format!("void f(void) {{ __atomic_thread_fence({weaker}); }}\n");
6636            assert!(!asm(&source).contains("mfence"), "{weaker} costs nothing here");
6637        }
6638    }
6639
6640    /// The three x86 fences under gcc's names are that same barrier at that same ordering.
6641    ///
6642    /// Exact for `mfence` and stronger than asked for the other two, which is a safe answer: a
6643    /// program that wanted its stores ordered gets that and more. Narrowing the two is worth doing
6644    /// once an instruction can be named from there, which is the note the shipped `xmmintrin.h`
6645    /// already carries at `_mm_sfence`.
6646    ///
6647    /// Each carries a signature, so an argument written on one is reported like an argument
6648    /// written on any other call, which is the whole reason they have one.
6649    #[test]
6650    fn the_three_x86_fences_are_the_barrier_the_strongest_ordering_gives() {
6651        for name in ["__builtin_ia32_sfence", "__builtin_ia32_lfence", "__builtin_ia32_mfence"] {
6652            let source = format!("void f(void) {{ {name}(); }}\n");
6653            assert!(asm(&source).contains("mfence"), "{name} is a barrier");
6654            let text = body(&source);
6655            assert!(text.contains("fence seq_cst"), "{name}: {text}");
6656        }
6657
6658        let result = run(&options(), "void f(void) { __builtin_ia32_sfence(1); }\n");
6659        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
6660        assert!(result.messages[0].contains("too many arguments"), "{:?}", result.messages);
6661    }
6662
6663    /// The four compare and exchange names are one IR instruction producing two values.
6664    ///
6665    /// Which of the two the expression answers is the difference between three of the four names,
6666    /// and the fourth difference is the C11 pair writing what they found back through the pointer
6667    /// they were handed, which is the branch after the instruction.
6668    #[test]
6669    fn a_compare_and_exchange_is_one_instruction_answering_two_things() {
6670        // The older family, whose two names are the same instruction read two ways. Neither has a
6671        // memory order argument and both are a full barrier, which is what `seq_cst` says.
6672        let text =
6673            body("int f(int *p, int e, int d) { return __sync_val_compare_and_swap(p, e, d); }\n");
6674        assert!(text.contains("%3, %4 = cmpxchg.(i32, i1) %0, %1, %2, align 4, seq_cst"), "{text}");
6675        assert!(text.contains("return %3"), "the value it found: {text}");
6676
6677        let text =
6678            body("int f(int *p, int e, int d) { return __sync_bool_compare_and_swap(p, e, d); }\n");
6679        assert!(text.contains("%3, %4 = cmpxchg.(i32, i1) %0, %1, %2, align 4, seq_cst"), "{text}");
6680        assert!(text.contains("zext.i32 %4"), "whether it happened: {text}");
6681
6682        // The C11 form, whose value expected arrives by pointer and is read before the exchange,
6683        // and whose answer is whether it happened. The write back is on the path where it did not.
6684        let text = body(
6685            "int f(int *p, int *e, int d) { return __atomic_compare_exchange_n(p, e, d, 0, 4, 2); }\n",
6686        );
6687        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
6688        assert!(text.contains("%4, %5 = cmpxchg.(i32, i1) %0, %3, %2, align 4, acq_rel"), "{text}");
6689        assert!(text.contains("br_if %5, block2, block1"), "{text}");
6690        assert!(text.contains("store %4 -> %1, align 4"), "{text}");
6691
6692        // And the form that takes the value to put there by pointer as well, which is one more
6693        // read and is otherwise the same node.
6694        let text = body(
6695            "int f(int *p, int *e, int *d) { return __atomic_compare_exchange(p, e, d, 0, 5, 5); }\n",
6696        );
6697        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
6698        assert!(text.contains("%4 = load.i32 %2, align 4"), "{text}");
6699        assert!(text.contains("%5, %6 = cmpxchg.(i32, i1) %0, %3, %4, align 4, seq_cst"), "{text}");
6700    }
6701
6702    /// On this machine it is `lock cmpxchg`, at the width of the object and at every ordering.
6703    ///
6704    /// The `lock` is what makes the whole of it one step as far as every other processor is
6705    /// concerned, and it is also what makes the instruction a full barrier, which is why the
6706    /// ordering the program wrote changes nothing in what is written here. Every line below is what
6707    /// gcc 16.2.0 writes for the same function.
6708    #[test]
6709    fn a_compare_and_exchange_is_a_locked_instruction_at_the_width_of_the_object() {
6710        let widths = [("char", "b", "%dl"), ("short", "w", "%dx"), ("int", "l", "%edx")];
6711        for (ty, suffix, reg) in widths {
6712            let source = format!(
6713                "int f({ty} *p, {ty} e, {ty} d) {{ return __sync_bool_compare_and_swap(p, e, d); }}\n"
6714            );
6715            let text = asm(&source);
6716            assert!(text.contains("\tlock\n"), "{ty}: {text}");
6717            assert!(text.contains(&format!("cmpxchg{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
6718            assert!(text.contains("sete\t"), "{ty}: {text}");
6719        }
6720        let source =
6721            "int f(long *p, long e, long d) { return __sync_bool_compare_and_swap(p, e, d); }\n";
6722        assert!(asm(source).contains("cmpxchgq\t%rdx, (%rdi)"), "{}", asm(source));
6723
6724        // The ordering the program asked for changes nothing, because a locked instruction on this
6725        // machine orders everything whatever it was asked for, so there is never a barrier beside
6726        // it either.
6727        for order in ["0", "2", "3", "4", "5"] {
6728            let call = format!("__atomic_compare_exchange_n(p, e, d, 0, {order}, 0)");
6729            let source = format!("int f(int *p, int *e, int d) {{ return {call}; }}\n");
6730            let text = asm(&source);
6731            assert!(text.contains("cmpxchgl\t"), "{order}: {text}");
6732            assert!(!text.contains("mfence"), "{order} needs no barrier here: {text}");
6733        }
6734    }
6735
6736    /// A read modify write is one IR instruction, and a name that asks for the value afterwards is
6737    /// that instruction and one more operation.
6738    ///
6739    /// The instruction answers what was there before, which is the convention every machine and
6740    /// every language in this area uses. Half the names in the family ask for the value afterwards
6741    /// instead, and that is the answer and the operand put together again, which is arithmetic on
6742    /// two values already in registers rather than a second flavour of the instruction.
6743    ///
6744    /// The two lock names are here too. They are not read modify writes in the same sense: one is
6745    /// an exchange and the other is a store of a zero, and what makes them a pair is the ordering,
6746    /// which is the one place in the older family that is not sequential consistency.
6747    #[test]
6748    fn a_read_modify_write_is_one_instruction_and_the_arithmetic_a_name_asks_for() {
6749        let text = body("int f(int *p, int v) { return __atomic_fetch_add(p, v, 5); }\n");
6750        assert!(text.contains("%2 = atomic_rmw.i32 add %0, %1, align 4, seq_cst"), "{text}");
6751        assert!(text.contains("return %2"), "the value that was there: {text}");
6752
6753        let text = body("int f(int *p, int v) { return __atomic_add_fetch(p, v, 5); }\n");
6754        assert!(text.contains("%2 = atomic_rmw.i32 add %0, %1, align 4, seq_cst"), "{text}");
6755        assert!(text.contains("%3 = add %2, %1"), "and the value afterwards: {text}");
6756
6757        let text = body("int f(int *p, int v) { return __atomic_sub_fetch(p, v, 5); }\n");
6758        assert!(text.contains("%2 = atomic_rmw.i32 sub %0, %1, align 4, seq_cst"), "{text}");
6759        assert!(text.contains("%3 = sub %2, %1"), "{text}");
6760
6761        // The older family, which passes no ordering and is a full barrier.
6762        let text = body("int f(int *p, int v) { return __sync_fetch_and_sub(p, v); }\n");
6763        assert!(text.contains("%2 = atomic_rmw.i32 sub %0, %1, align 4, seq_cst"), "{text}");
6764
6765        // The exchange, and the older family's spelling of it, which is taking a lock and so is an
6766        // acquire rather than the full barrier the rest of that family is.
6767        let text = body("int f(int *p, int v) { return __atomic_exchange_n(p, v, 5); }\n");
6768        assert!(text.contains("%2 = atomic_rmw.i32 xchg %0, %1, align 4, seq_cst"), "{text}");
6769
6770        let text = body("int f(int *p, int v) { return __sync_lock_test_and_set(p, v); }\n");
6771        assert!(text.contains("%2 = atomic_rmw.i32 xchg %0, %1, align 4, acquire"), "{text}");
6772
6773        // Giving the lock back, which is one of the two names in the family that is handed no value
6774        // to put there, because what it puts there is a zero.
6775        let text = body("void f(int *p) { __sync_lock_release(p); }\n");
6776        assert!(text.contains("release"), "{text}");
6777        assert!(text.contains("%1 = iconst.i32 0"), "{text}");
6778
6779        // And with something after the pointer, which is the list of variables the call promises to
6780        // protect rather than a value to write. Reading it as a value would store whatever the
6781        // caller happened to name there, which is the one thing giving a lock back must not do.
6782        let text = body("void f(int *p, int guard) { __sync_lock_release(p, guard); }\n");
6783        assert!(text.contains("%2 = iconst.i32 0"), "{text}");
6784        assert!(text.contains("atomic_store %2 -> %0, align 4, release"), "{text}");
6785
6786        // The bitwise four, which look no different here from the arithmetic ones: what the machine
6787        // has an instruction for is a question further down and this level does not ask it.
6788        let text = body("int f(int *p, int v) { return __atomic_fetch_and(p, v, 5); }\n");
6789        assert!(text.contains("%2 = atomic_rmw.i32 and %0, %1, align 4, seq_cst"), "{text}");
6790
6791        let text = body("int f(int *p, int v) { return __sync_or_and_fetch(p, v); }\n");
6792        assert!(text.contains("%2 = atomic_rmw.i32 or %0, %1, align 4, seq_cst"), "{text}");
6793        assert!(text.contains("%3 = or %2, %1"), "and the value afterwards: {text}");
6794
6795        // The nand, which is the one of the six that is two operations. The flip is an exclusive or
6796        // against every bit set because the IR has no not and that is what one is.
6797        let text = body("int f(int *p, int v) { return __atomic_nand_fetch(p, v, 5); }\n");
6798        assert!(text.contains("%2 = atomic_rmw.i32 nand %0, %1, align 4, seq_cst"), "{text}");
6799        assert!(text.contains("%3 = and %2, %1"), "{text}");
6800        assert!(text.contains("%4 = iconst.i32 -1"), "{text}");
6801        assert!(text.contains("%5 = xor %3, %4"), "{text}");
6802    }
6803
6804    /// The four operations with no instruction on this machine are a loop around `lock cmpxchg`.
6805    ///
6806    /// The shape is the one every architecture manual writes out by hand: read the word, work out
6807    /// what should be there instead, put it back if nothing else got in first, and go round again
6808    /// when something did. What is checked is that the loop is there at every width, that the
6809    /// operation is inside it, and that no `xchg` or `xadd` got used for something neither of them
6810    /// does.
6811    ///
6812    /// gcc 16.2.0 writes the same loop for the same functions, down to which register holds the
6813    /// value that was read.
6814    #[test]
6815    fn a_bitwise_read_modify_write_is_a_loop_around_the_compare_and_exchange() {
6816        let widths = [("char", "b", "%dl"), ("short", "w", "%dx"), ("int", "l", "%edx")];
6817        for (ty, suffix, reg) in widths {
6818            for (name, call, insn) in [
6819                ("and", "__atomic_fetch_and(p, v, 5)", "and"),
6820                ("or", "__sync_fetch_and_or(p, v)", "or"),
6821                ("xor", "__atomic_xor_fetch(p, v, 5)", "xor"),
6822            ] {
6823                let source = format!("{ty} f({ty} *p, {ty} v) {{ return {call}; }}\n");
6824                let text = asm(&source);
6825                assert!(text.contains("\tlock\n"), "{ty} {name}: {text}");
6826                assert!(
6827                    text.contains(&format!("cmpxchg{suffix}\t{reg}, (%rdi)")),
6828                    "{ty} {name}: {text}"
6829                );
6830                assert!(text.contains(&format!("{insn}{suffix}\t")), "{ty} {name}: {text}");
6831                // The tab matters on the second of these, since `cmpxchg` ends in the other name.
6832                assert!(!text.contains("\txadd"), "{ty} {name} is not an add: {text}");
6833                assert!(!text.contains("\txchg"), "{ty} {name} is not an exchange: {text}");
6834            }
6835        }
6836        let source = "long f(long *p, long v) { return __atomic_fetch_or(p, v, 5); }\n";
6837        assert!(asm(source).contains("cmpxchgq\t%rdx, (%rdi)"), "{}", asm(source));
6838
6839        // The nand, which puts two instructions inside the loop rather than one. The flip is an
6840        // exclusive or against every bit set in the IR and the folder turns that into the `not` the
6841        // machine has, which is what gcc writes here too.
6842        let text = asm("int f(int *p, int v) { return __sync_fetch_and_nand(p, v); }\n");
6843        assert!(text.contains("cmpxchgl\t"), "{text}");
6844        assert!(text.contains("andl\t"), "{text}");
6845        assert!(text.contains("notl\t"), "{text}");
6846    }
6847
6848    /// The three names that pass a value through a pointer are the same access and one plain one.
6849    ///
6850    /// They exist for an object too big to come back in a register, and the front end takes them at
6851    /// their word rather than folding them into the `_n` spellings, because the extra access is real:
6852    /// the caller handed over somewhere to read from or write into and that is where the value has
6853    /// to come from or go. Both of those accesses are plain. The object at the end of the caller's
6854    /// pointer is the caller's own and no other thread has its address, which is what the whole
6855    /// shape is for.
6856    #[test]
6857    fn an_access_through_a_second_pointer_is_the_same_access_and_one_more() {
6858        let text = body("void f(int *p, int *r) { __atomic_load(p, r, 5); }\n");
6859        assert!(text.contains("%2 = atomic_load.i32 %0, align 4, seq_cst"), "{text}");
6860        assert!(text.contains("store %2 -> %1, align 4"), "and out through the place: {text}");
6861
6862        let text = body("void f(int *p, int *v) { __atomic_store(p, v, 3); }\n");
6863        assert!(text.contains("%2 = load.i32 %1, align 4"), "in through the place: {text}");
6864        assert!(text.contains("atomic_store %2 -> %0, align 4, release"), "{text}");
6865
6866        // The exchange, which reads through one pointer and writes through another and is the same
6867        // instruction in between as the spelling that takes and answers values.
6868        let text = body("void f(int *p, int *v, int *r) { __atomic_exchange(p, v, r, 5); }\n");
6869        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
6870        assert!(text.contains("%4 = atomic_rmw.i32 xchg %0, %3, align 4, seq_cst"), "{text}");
6871        assert!(text.contains("store %4 -> %2, align 4"), "{text}");
6872    }
6873
6874    /// The flag pair is an exchange of one byte and a store of a zero over the same byte.
6875    ///
6876    /// One byte whatever the pointer was written as, which is the standard's reading rather than a
6877    /// liberty: the object is an `atomic_flag`, there is no other way to read or write one, so the
6878    /// type the pointer carries says nothing about the access and the width is the implementation's
6879    /// to fix. gcc 16.2.0 writes `xchgb` here through an `int *` too.
6880    ///
6881    /// The answer is a comparison against zero rather than the byte itself, because the type of the
6882    /// call is `_Bool` and a byte that is neither zero nor one is not one. gcc answers the raw byte,
6883    /// and the two agree wherever the flag is only ever touched through this pair.
6884    #[test]
6885    fn a_flag_is_an_exchange_of_one_byte_and_a_store_of_a_zero_over_the_same_byte() {
6886        for pointer in ["char", "int", "void"] {
6887            let source = format!("int f({pointer} *p) {{ return __atomic_test_and_set(p, 5); }}\n");
6888            let text = body(&source);
6889            assert!(text.contains("%1 = iconst.i8 1"), "{pointer}: {text}");
6890            assert!(
6891                text.contains("%2 = atomic_rmw.i8 xchg %0, %1, align 1, seq_cst"),
6892                "{pointer}: {text}"
6893            );
6894            assert!(text.contains("%4 = icmp ne %2, %3"), "{pointer}: {text}");
6895
6896            let source = format!("void f({pointer} *p) {{ __atomic_clear(p, 3); }}\n");
6897            let text = body(&source);
6898            assert!(text.contains("atomic_store %2 -> %0, align 1, release"), "{pointer}: {text}");
6899        }
6900
6901        // And on this machine, where the exchange carries no `lock` because one with memory locks
6902        // the bus whether it was asked to or not. Both lines are what gcc 16.2.0 writes.
6903        let text = asm("int f(int *p) { return __atomic_test_and_set(p, 5); }\n");
6904        assert!(text.contains("xchgb\t%al, (%rdi)"), "{text}");
6905        assert!(text.contains("setne\t"), "{text}");
6906    }
6907
6908    /// On this machine it is `xchg` where the machine has an exchange and `lock xadd` where it has
6909    /// an add, at the width of the object.
6910    ///
6911    /// The exchange carries no prefix and the add carries one, which is the machine rather than an
6912    /// oversight: an exchange with memory locks the bus whether it is asked to or not. Both are
6913    /// therefore full barriers whatever ordering the program wrote, so no ordering costs an
6914    /// `mfence` beside them. Every line below is what gcc 16.2.0 writes for the same function.
6915    #[test]
6916    fn a_read_modify_write_is_an_exchange_or_a_locked_add_at_the_width_of_the_object() {
6917        let widths = [("char", "b", "%sil"), ("short", "w", "%si"), ("int", "l", "%esi")];
6918        for (ty, suffix, reg) in widths {
6919            let source =
6920                format!("{ty} f({ty} *p, {ty} v) {{ return __atomic_fetch_add(p, v, 5); }}\n");
6921            let text = asm(&source);
6922            assert!(text.contains("\tlock\n"), "{ty}: {text}");
6923            assert!(text.contains(&format!("xadd{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
6924
6925            let source =
6926                format!("{ty} f({ty} *p, {ty} v) {{ return __atomic_exchange_n(p, v, 5); }}\n");
6927            let text = asm(&source);
6928            assert!(text.contains(&format!("xchg{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
6929            assert!(!text.contains("\tlock\n"), "an exchange is locked already: {ty}: {text}");
6930        }
6931        let source = "long f(long *p, long v) { return __atomic_fetch_add(p, v, 5); }\n";
6932        assert!(asm(source).contains("xaddq\t%rsi, (%rdi)"), "{}", asm(source));
6933
6934        // A subtraction is the same instruction over the negated operand, which is right at every
6935        // width because the machine's arithmetic wraps.
6936        let source = "int f(int *p, int v) { return __atomic_fetch_sub(p, v, 5); }\n";
6937        let text = asm(source);
6938        assert!(text.contains("negl\t"), "{text}");
6939        assert!(text.contains("xaddl\t"), "{text}");
6940
6941        // The ordering changes nothing, for the reason it changes nothing for a compare and
6942        // exchange: a locked instruction on this machine orders everything whatever it was asked.
6943        for order in ["0", "2", "3", "4", "5"] {
6944            let source =
6945                format!("int f(int *p, int v) {{ return __atomic_fetch_add(p, v, {order}); }}\n");
6946            let text = asm(&source);
6947            assert!(text.contains("xaddl\t"), "{order}: {text}");
6948            assert!(!text.contains("mfence"), "{order} needs no barrier here: {text}");
6949        }
6950
6951        // And the lock pair, which is the exchange and a store of a zero. Neither is a barrier
6952        // instruction: the exchange is one already and the store is a release, which this machine
6953        // gives away.
6954        let text = asm("int f(int *p, int v) { return __sync_lock_test_and_set(p, v); }\n");
6955        assert!(text.contains("xchgl\t%esi, (%rdi)"), "{text}");
6956        // The zero goes through a register on the way, which is where every constant this
6957        // compiler stores goes: gcc writes the one instruction because it has a store that takes an
6958        // immediate and no rule here does. That is a rule this rule set is missing rather than
6959        // anything about the builtin, and it is the same two instructions a plain `*p = 0` makes.
6960        // The register gets its zero from an exclusive or with itself rather than from a move of a
6961        // zero, which is `rucc_codegen::shorten` writing the shorter of the two spellings.
6962        let text = asm("void f(int *p) { __sync_lock_release(p); }\n");
6963        assert!(text.contains("xorl\t%eax, %eax"), "{text}");
6964        assert!(text.contains("movl\t%eax, (%rdi)"), "{text}");
6965        assert!(!text.contains("mfence"), "a release store needs no barrier here: {text}");
6966    }
6967
6968    /// The two lock free questions are numbers in the program rather than calls to anything.
6969    ///
6970    /// Both answer from the size, which has to be a power of two no wider than the widest access
6971    /// this compiler writes, and from what the pointer says about the alignment. Sixteen bytes is
6972    /// no here and is no in gcc without `-mcx16`, because `cmpxchg16b` is not in the baseline and
6973    /// nothing here writes it. Three bytes is no because there is no three byte access at all.
6974    ///
6975    /// The whole point of both names is that the answer is available before the program runs, so
6976    /// what is checked is that a `mov` of a constant is the whole function and that no call was
6977    /// left behind. A call would be to `__atomic_is_lock_free` in libatomic, which is not a library
6978    /// this links against.
6979    #[test]
6980    fn the_lock_free_questions_are_answered_as_constants() {
6981        for size in ["1", "2", "4", "8"] {
6982            let source =
6983                format!("int f(void) {{ return __atomic_always_lock_free({size}, 0); }}\n");
6984            let text = asm(&source);
6985            assert!(text.contains("movb\t$1, %al"), "{size} bytes is lock free: {text}");
6986            assert!(!text.contains("call"), "and is not a call: {text}");
6987        }
6988        for size in ["3", "16", "sizeof(long double)"] {
6989            let source = format!("int f(void) {{ return __atomic_is_lock_free({size}, 0); }}\n");
6990            let text = asm(&source);
6991            assert!(text.contains("movb\t$0, %al"), "{size} bytes is not: {text}");
6992            assert!(!text.contains("call"), "and is not a call either: {text}");
6993        }
6994
6995        // A size the compiler cannot work out, which is no rather than a refusal, and an object
6996        // whose type is aligned under the size asked about, which is the whole of what the second
6997        // argument is for.
6998        let text = asm("int f(int n) { return __atomic_is_lock_free(n, 0); }\n");
6999        assert!(text.contains("movb\t$0, %al"), "a size nobody knows is not lock free: {text}");
7000        let text = asm("int f(int *p) { return __atomic_always_lock_free(8, p); }\n");
7001        assert!(text.contains("movb\t$0, %al"), "eight bytes at four is not: {text}");
7002        let text = asm("int f(long *p) { return __atomic_always_lock_free(8, p); }\n");
7003        assert!(text.contains("movb\t$1, %al"), "and at eight it is: {text}");
7004    }
7005
7006    /// A memory order an operation cannot carry is read as the strongest one, and said so about.
7007    ///
7008    /// There are three ways the number is not one the operation can take: it is not a constant at
7009    /// all, it is not one of the six the headers define, or it is one of them and means nothing for
7010    /// this operation, which is a release load or an acquire store. All three become sequential
7011    /// consistency, which is stronger than anything the program could have meant, so a program that
7012    /// wrote nonsense gets a correct answer rather than a fast one. gcc does the same.
7013    ///
7014    /// The last two also warn, because the number was written down and is wrong. The first does
7015    /// not: gcc takes a computed order, and so does the C11 spelling, so a warning there would fire
7016    /// on correct programs.
7017    #[test]
7018    fn a_memory_order_an_operation_cannot_carry_is_read_as_the_strongest() {
7019        let mut opts = options();
7020        opts.emit = EmitKind::Ir;
7021
7022        let acquire_store = run(&opts, "void f(int *p, int v) { __atomic_store_n(p, v, 2); }\n");
7023        assert!(acquire_store.text().contains("seq_cst"), "{:?}", acquire_store.text());
7024        assert!(acquire_store.messages[0].contains("[W0333]"), "{:?}", acquire_store.messages);
7025
7026        let nonsense = run(&opts, "int f(int *p) { return __atomic_load_n(p, 99); }\n");
7027        assert!(nonsense.text().contains("seq_cst"), "{:?}", nonsense.text());
7028        assert!(nonsense.messages[0].contains("[W0333]"), "{:?}", nonsense.messages);
7029
7030        let computed = run(&opts, "int f(int *p, int n) { return __atomic_load_n(p, n); }\n");
7031        assert!(computed.text().contains("seq_cst"), "{:?}", computed.text());
7032        assert_eq!(computed.messages, Vec::<String>::new(), "a computed order is not a mistake");
7033    }
7034
7035    /// A conversion between a float and the widest unsigned integer, which the machine has not got.
7036    ///
7037    /// Every other conversion between a float and an integer is the signed one at some width with a
7038    /// widening in front or a narrowing behind. These two are not, because there is no signed width
7039    /// that holds every value of an unsigned sixty four bit integer, so each is the signed
7040    /// conversion with arithmetic around it that brings the value into range and puts it back.
7041    ///
7042    /// What is checked here is that the conversion happens at all and that it happens without a
7043    /// branch. gcc writes a branch for both; this writes the choice as a mask, because every rewrite
7044    /// in that pass stays inside the block it started in. The arithmetic itself is checked in
7045    /// `rucc-codegen`, where it can be run against the answer rather than read in the assembly.
7046    #[test]
7047    fn a_conversion_between_a_float_and_the_widest_unsigned_integer_is_written_without_a_branch() {
7048        let text = asm("double f(unsigned long long x) { return (double)x; }\n");
7049        assert!(text.contains("cvtsi2sdq"), "the signed conversion is what runs: {text}");
7050        assert!(text.contains("shrq"), "with the value halved first: {text}");
7051        assert!(text.contains("addsd"), "and doubled after: {text}");
7052        assert!(!text.contains("\tj"), "and no branch anywhere: {text}");
7053
7054        let text = asm("unsigned long long f(double d) { return (unsigned long long)d; }\n");
7055        assert!(text.contains("cvttsd2siq"), "the signed conversion is what runs: {text}");
7056        assert!(text.contains("subsd"), "with half the range taken off first: {text}");
7057        assert!(text.contains("shlq\t$63"), "and the top bit put back: {text}");
7058        assert!(!text.contains("\tj"), "and no branch anywhere: {text}");
7059    }
7060
7061    /// The plain names are the library's only where nothing else has taken them.
7062    ///
7063    /// Four ways a program says it means something else. A `static` definition is its own
7064    /// function and the name outside the file is somebody else's. A declaration of another type
7065    /// is another function. `-fno-builtin` and `-fno-builtin-<name>` say so outright, and
7066    /// `-ffreestanding` says there is no C library for the name to be the name of. Every one of
7067    /// these was measured against gcc 16.2.0, which calls the program's function in all of them.
7068    ///
7069    /// The `__builtin_` spelling goes on meaning the library's function through all of it, which
7070    /// is what the prefix is for and what lets a freestanding build reach one deliberately.
7071    #[test]
7072    fn a_plain_name_the_program_took_is_the_programs_own_function() {
7073        let taken = concat!(
7074            "static long long llabs(long long b) { return 7; }\n",
7075            "long long f(long long x) { return llabs(x); }\n",
7076        );
7077        assert!(ir(taken).contains("call @llabs"), "a static definition is the program's own");
7078
7079        let retyped = concat!("int llabs(int b);\n", "int f(int x) { return llabs(x); }\n",);
7080        assert!(ir(retyped).contains("call @llabs"), "another type is another function");
7081
7082        let plain = concat!(
7083            "long long llabs(long long b);\n",
7084            "long long f(long long x) { return llabs(x); }\n",
7085        );
7086        let mut opts = options();
7087        opts.emit = EmitKind::Ir;
7088        assert!(!run(&opts, plain).text().contains("call @llabs"), "the library's by default");
7089
7090        opts.builtins = false;
7091        assert!(run(&opts, plain).text().contains("call @llabs"), "-fno-builtin");
7092
7093        opts.builtins = true;
7094        opts.no_builtin = vec!["llabs".to_owned()];
7095        assert!(run(&opts, plain).text().contains("call @llabs"), "-fno-builtin-llabs");
7096        let one = "long labs(long b);\nlong f(long x) { return labs(x); }\n";
7097        assert!(!run(&opts, one).text().contains("call @labs"), "one name and not the family");
7098
7099        // `-ffreestanding` reaches the front end as the same answer, which is what the driver
7100        // does with it in `compile`, and the prefixed spelling is untouched by any of it.
7101        opts.no_builtin = Vec::new();
7102        opts.builtins = false;
7103        let prefixed = "long long f(long long x) { return __builtin_llabs(x); }\n";
7104        assert!(!run(&opts, prefixed).text().contains("call @llabs"), "the prefix is a promise");
7105    }
7106
7107    /// The hint builtins are their first argument, and nothing is left of the hint.
7108    ///
7109    /// Which way a branch is expected to go is the whole of what they say, and there is nothing
7110    /// here that reads a branch weight yet, so what reaches the IR is the value and the hint is
7111    /// gone. The one thing the prototype has to keep doing is converting: gcc gives both of them
7112    /// a `long` result, so `sizeof(__builtin_expect((char)1, 1))` is eight and a narrower argument
7113    /// widens before it is answered with.
7114    ///
7115    /// Whether a side effect in the hint happens depends on the first argument, which is gcc's
7116    /// answer rather than a rule anybody designed. A constant first argument folds the whole call
7117    /// where it is written and the hint goes with it, and a first argument that is not a constant
7118    /// leaves the hint standing. Both halves are below and both were measured on gcc 16.2.0.
7119    #[test]
7120    fn the_hint_builtins_are_their_first_argument_and_the_hint_leaves_no_trace() {
7121        let text = ir(concat!(
7122            "long a = __builtin_expect(7, 1);\n",
7123            "long b = __builtin_expect_with_probability(9, 1, 0.9);\n",
7124            "unsigned long c = sizeof(__builtin_expect((char)1, 1));\n",
7125        ));
7126        assert!(text.contains("global @a : i64 = 7,"), "{text}");
7127        assert!(text.contains("global @b : i64 = 9,"), "{text}");
7128        assert!(text.contains("global @c : i64 = 8,"), "{text}");
7129        assert!(!text.contains("__builtin_expect"), "it is not a call to anything:\n{text}");
7130
7131        // A narrower argument is widened by the prototype before it is handed back, and it is
7132        // widened with its sign, since the parameter is a signed `long`.
7133        let text = body("long f(char c) { return __builtin_expect(c, 1); }\n");
7134        assert!(text.contains("sext"), "{text}");
7135
7136        // The first argument is a constant, so the second is not evaluated and `i` is still zero,
7137        // and neither is the third. What is left of each statement is the first argument widened,
7138        // which nothing reads and which the first pass that looks for dead code will take out.
7139        let one = "block0:\n    %0 = iconst.i32 0\n    %1 = iconst.i32 1\n    %2 = sext.i64 %1\n    return %0\n";
7140        assert_eq!(body("int f(void) { int i = 0; __builtin_expect(1, i++); return i; }\n"), one);
7141        let source = "int g(void) { int i = 0; __builtin_expect_with_probability(1, i++, 0.5); return i; }\n";
7142        assert_eq!(body(source), one);
7143
7144        // The first argument is not a constant, so the hint runs and `i` comes back one. There is
7145        // an increment in the body and the value it returns is the load after it, which is what
7146        // gcc gives for the same program, and the whole of tamnd/rucc#584 is that this used to
7147        // come out the same as the pair above.
7148        let kept = body("int f(int n) { int i = 0; __builtin_expect(n, i++); return i; }\n");
7149        assert!(kept.contains("add.nsw"), "the hint still runs: {kept}");
7150        assert!(kept.ends_with("return %3\n"), "and the answer is what it left behind: {kept}");
7151        let both = "int g(int n) { int i = 0; __builtin_expect_with_probability(n, i++, 0.5); return i; }\n";
7152        assert!(body(both).contains("add.nsw"), "and so does the one with three arguments");
7153    }
7154
7155    /// A point control does not arrive at, in both of the ways the compiler has one.
7156    ///
7157    /// `__builtin_unreachable()` is the promise written down, and a function whose body can run
7158    /// off the bottom is the walk arriving at the same place on its own. Neither writes an
7159    /// instruction, which is what gcc 16.2.0 does at `-O0`: it emits the epilogue and the `ret`
7160    /// for both of the functions below and nothing else, and the two of them come out byte for
7161    /// byte the same there.
7162    ///
7163    /// The `ret` is the part worth holding on to. It is not there because anything runs it, it is
7164    /// there because a function whose last instruction is not a return is one that falls into
7165    /// whatever the assembler puts after it.
7166    #[test]
7167    fn a_promise_that_control_does_not_arrive_writes_no_instruction() {
7168        let promised = "int f(int x) { if (x) return 1; __builtin_unreachable(); }\n";
7169        let text = ir(promised);
7170        assert!(text.contains("    unreachable_hint\n"), "{text}");
7171        assert!(!text.contains("call"), "it is not a call to anything:\n{text}");
7172
7173        // The statement after it is still lowered. Continuing to translate a path the program
7174        // promised is dead is one of the things a compiler may do with undefined behaviour, and
7175        // it is the one that keeps a program built at `-O0` behaving the way it was watched to.
7176        let after = body("int g(int x) { __builtin_unreachable(); return x; }\n");
7177        assert!(after.contains("return"), "{after}");
7178
7179        // Both functions are the same instructions, because the hint writes none of them and the
7180        // terminator underneath it writes none either.
7181        let text = asm(promised);
7182        let mine = text.split_once("\nf:\n").expect("a definition").1;
7183        let mine = mine.split_once("\t.size").expect("a definition").0;
7184        let plain = asm("int f(int x) { if (x) return 1; }\n");
7185        let plain = plain.split_once("\nf:\n").expect("a definition").1;
7186        let plain = plain.split_once("\t.size").expect("a definition").0;
7187        assert_eq!(mine, plain);
7188        // The last instruction, rather than the last line, because the unwind record is closed
7189        // after it and a directive is not something the machine runs.
7190        let last = mine.lines().rfind(|line| !line.trim_start().starts_with('.'));
7191        assert_eq!(last.map(str::trim), Some("ret"), "{mine}");
7192        assert!(!mine.contains("ud2"), "{mine}");
7193    }
7194
7195    /// The two names stay apart, which is what having both of them is for.
7196    ///
7197    /// The one the program wrote is what the call is checked against and what a diagnostic about
7198    /// it says, and the one the library defines is what the call ends up carrying. A compiler
7199    /// that kept only the second would report this against `abort`, which is a function the
7200    /// program never mentions.
7201    #[test]
7202    fn a_library_builtin_is_diagnosed_under_the_name_the_program_wrote() {
7203        let mut opts = options();
7204        opts.emit = EmitKind::Ir;
7205        let messages = run(&opts, "void f(void) { __builtin_abort(1); }\n").messages;
7206        assert!(
7207            messages.iter().any(|m| m.contains("__builtin_abort")),
7208            "expected the written name in {messages:?}"
7209        );
7210    }
7211
7212    /// A builtin nothing lowers is refused where it is written, rather than at the link.
7213    ///
7214    /// One name is left, which is the last of the atomic family that is refused and is also the
7215    /// one whose prefix is not `__builtin_`; its older half has nothing left in it at all, and so
7216    /// does the half of the family that carries a prototype. What the message has to carry is the
7217    /// name, because the whole complaint about the link error this replaces is that the name in it
7218    /// was one the compiler chose.
7219    #[test]
7220    fn a_builtin_nothing_lowers_is_refused_by_name() {
7221        let mut opts = options();
7222        opts.emit = EmitKind::Ir;
7223        let builtin = "__atomic_signal_fence";
7224        let source = format!("int counter;\nint f(void) {{ return ({builtin}(5), 0); }}\n");
7225        let messages = run(&opts, &source).messages;
7226        let named = messages.iter().any(|m| m.contains(builtin) && m.contains("E0686"));
7227        assert!(named, "expected {builtin} to be refused by name in {messages:?}");
7228    }
7229
7230    /// The refusal is about a call and not about the name, so a program that defines the name
7231    /// itself gets the function it wrote.
7232    ///
7233    /// That is not the reason the refusal exists, but a definition in front of us is a definition
7234    /// and the call to it links. It works here because the name is one with no prototype and no
7235    /// meaning the front end knows, which is what is left once the rest of the family is
7236    /// implemented: a `__builtin_` name the front end does answer is answered whatever the program
7237    /// declares, the way gcc answers one.
7238    #[test]
7239    fn what_is_refused_is_the_call_and_not_the_name() {
7240        let text = ir(concat!(
7241            "void __atomic_signal_fence(int order) { (void)order; }\n",
7242            "void f(void) { __atomic_signal_fence(5); }\n",
7243        ));
7244        assert!(text.contains("call @__atomic_signal_fence"), "{text}");
7245    }
7246
7247    /// How many bytes are behind an address is read off the layout, for every shape the walk
7248    /// covers.
7249    ///
7250    /// This is what `_FORTIFY_SOURCE` runs on, so the numbers matter one at a time rather than in
7251    /// aggregate: a size too small turns a correct copy into an abort, and a size too large turns
7252    /// a checked copy back into an unchecked one. Every answer here was measured against gcc
7253    /// 16.2.0 first. They are written as initializers so that each one is a constant in the
7254    /// output and the test reads as the table it is.
7255    #[test]
7256    fn the_object_size_of_an_address_is_what_the_layout_leaves_in_front_of_it() {
7257        let text = ir(concat!(
7258            "struct S { char a[8]; int n; char b[12]; };\n",
7259            "char g[32];\n",
7260            "struct S gs;\n",
7261            "unsigned long whole = __builtin_object_size(g, 0);\n",
7262            "unsigned long moved = __builtin_object_size(g + 4, 0);\n",
7263            "unsigned long back = __builtin_object_size(g + 30 - 2, 0);\n",
7264            "unsigned long outer = __builtin_object_size(gs.a, 0);\n",
7265            "unsigned long inner = __builtin_object_size(gs.a, 1);\n",
7266            "unsigned long scalar = __builtin_object_size(&gs.n, 1);\n",
7267            "unsigned long after = __builtin_object_size(&gs.n, 0);\n",
7268            "unsigned long into = __builtin_object_size(&gs.b[2], 1);\n",
7269            "unsigned long text = __builtin_object_size(\"hello\", 0);\n",
7270            "unsigned long dyn = __builtin_dynamic_object_size(gs.b, 1);\n",
7271        ));
7272        for (name, size) in [
7273            ("whole", 32),
7274            ("moved", 28),
7275            ("back", 4),
7276            ("outer", 24),
7277            ("inner", 8),
7278            ("scalar", 4),
7279            ("after", 16),
7280            ("into", 10),
7281            ("text", 6),
7282            ("dyn", 12),
7283        ] {
7284            let said = format!("global @{name} : i64 = {size},");
7285            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
7286        }
7287    }
7288
7289    /// A local is as knowable as a global, which is the whole point of asking on the way into a
7290    /// copy.
7291    ///
7292    /// A fortified header expands around the destination the caller wrote, and the destination a
7293    /// program most wants checked is the buffer on its own stack. Nothing in the answer depends on
7294    /// storage duration, unlike in a constant expression, where the address of a local is exactly
7295    /// what is not allowed.
7296    #[test]
7297    fn the_object_behind_an_address_can_be_one_with_automatic_storage() {
7298        let text = body(concat!(
7299            "struct S { char a[8]; int n; char b[12]; };\n",
7300            "unsigned long f(void) {\n",
7301            "  char loc[20];\n",
7302            "  struct S ls;\n",
7303            "  return __builtin_object_size(loc + 3, 0) + __builtin_object_size(ls.b + 2, 1);\n",
7304            "}\n",
7305        ));
7306        assert!(text.contains("iconst.i64 17"), "twenty bytes with three used: {text}");
7307        assert!(text.contains("iconst.i64 10"), "twelve bytes with two used: {text}");
7308    }
7309
7310    /// An address whose object the walk cannot see answers at whichever end of the range the kind
7311    /// asks for.
7312    ///
7313    /// The two bits are a question and the answer has to fit it. A kind wanting the largest object
7314    /// the address could be in has to name a size nothing is bigger than, and a kind wanting the
7315    /// smallest has to name a size nothing is smaller than, so the unknown answers are all ones
7316    /// and zero. That pair is what a fortified header compares against to decide whether to check
7317    /// at all, and getting either of them the wrong way round turns every unknown copy into an
7318    /// abort.
7319    #[test]
7320    fn an_address_with_no_object_in_sight_answers_at_the_end_of_the_range_its_kind_asks_for() {
7321        let text = ir(concat!(
7322            "struct T { int n; char f[]; };\n",
7323            "extern char *p;\n",
7324            "extern struct T *t;\n",
7325            "unsigned long largest = __builtin_object_size(p, 0);\n",
7326            "unsigned long nearest = __builtin_object_size(p, 1);\n",
7327            "unsigned long least = __builtin_object_size(p, 2);\n",
7328            "unsigned long tight = __builtin_object_size(p, 3);\n",
7329            "unsigned long flex = __builtin_object_size(t->f, 1);\n",
7330            "int says = __builtin_object_size(p, 0) == (unsigned long)-1;\n",
7331        ));
7332        for name in ["largest", "nearest", "flex"] {
7333            // All ones, printed as the signed rendering of the sixty four bits it is held in.
7334            // `says` is what pins the pattern itself, since it is the comparison a fortified
7335            // header writes and it folds only if every bit is set.
7336            let said = format!("global @{name} : i64 = -1,");
7337            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
7338        }
7339        for name in ["least", "tight"] {
7340            let said = format!("global @{name} : i64 = 0,");
7341            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
7342        }
7343        assert!(text.contains("global @says : i32 = 1,"), "{text}");
7344    }
7345
7346    /// The address is not evaluated, which is the rule `sizeof` follows and for the same reason.
7347    ///
7348    /// What the builtin reads is the shape of the expression rather than the value it would
7349    /// produce, so there is nothing to run. It matters because a fortified header writes the
7350    /// destination twice, once into the copy and once into the size, and a program whose
7351    /// destination is `*next()` would advance twice if this evaluated.
7352    #[test]
7353    fn the_address_an_object_size_is_asked_about_is_not_evaluated() {
7354        let text = body(concat!(
7355            "extern char *side(void);\n",
7356            "unsigned long f(void) { return __builtin_object_size(side(), 0); }\n",
7357        ));
7358        assert!(!text.contains("call"), "nothing is called: {text}");
7359    }
7360
7361    /// The kind has to be a constant in range, because it says which of four questions was asked.
7362    ///
7363    /// A number that is not known until the program runs decides nothing, and one outside the two
7364    /// bits names no question at all. gcc refuses both in one sentence and so does this.
7365    #[test]
7366    fn a_kind_that_is_not_one_of_the_four_is_refused() {
7367        for source in [
7368            "extern char *p;\nextern int k;\nunsigned long f(void) ".to_owned()
7369                + "{ return __builtin_object_size(p, k); }\n",
7370            "extern char *p;\nunsigned long f(void) { return __builtin_object_size(p, 4); }\n"
7371                .to_owned(),
7372            "extern char *p;\nunsigned long f(void) ".to_owned()
7373                + "{ return __builtin_dynamic_object_size(p, -1); }\n",
7374        ] {
7375            let messages = errors(&source);
7376            let named = messages.iter().any(|m| m.contains("E0709") && m.contains("0 to 3"));
7377            assert!(named, "expected a complaint about the kind in {messages:?}");
7378        }
7379    }
7380
7381    /// The pair that saves a place in a function and comes back to it, which is not a call.
7382    ///
7383    /// What the IR has to show is one instruction each and no call to anything: there is no
7384    /// function of either name for a call to reach, and a program that got one would fail to link.
7385    /// The save answers an `int`, which is the value that says how control got there.
7386    #[test]
7387    fn the_pair_that_saves_a_place_lowers_to_the_two_markers() {
7388        let text = ir(concat!(
7389            "void *buf[5];\n",
7390            "int f(void) {\n",
7391            "  if (__builtin_setjmp(buf)) return 2;\n",
7392            "  return 1;\n",
7393            "}\n",
7394            "void g(void) { __builtin_longjmp(buf, 1); }\n",
7395        ));
7396        assert!(text.contains("= setjmp_marker.i32 %0\n"), "the save answers a value: {text}");
7397        assert!(text.contains("    longjmp_marker %0\n"), "the restore answers nothing: {text}");
7398        assert!(!text.contains("call @"), "neither of them is a call: {text}");
7399    }
7400
7401    /// Every local of a function that saves a place lives in the frame, and not in a value.
7402    ///
7403    /// The edge a restore travels is not an edge of the graph, so a local the SSA construction
7404    /// renamed would answer the write that reached the read along the edges there are rather than
7405    /// the write that last ran. The second function here is the same code without the save, where
7406    /// the local is a value and there is no slot at all, which is what makes the first one a rule
7407    /// about the save and not about the shape of the code.
7408    #[test]
7409    fn a_local_of_a_function_that_saves_a_place_gets_a_slot() {
7410        let text = ir(concat!(
7411            "void *buf[5];\n",
7412            "int f(int x) { int a = 0; if (__builtin_setjmp(buf)) return a; a = 1; return x; }\n",
7413            "int g(int x) { int a = 0; if (x) return a; a = 1; return x; }\n",
7414        ));
7415        let (saves, plain) = text.split_once("func @g").expect("both functions");
7416        assert_eq!(saves.matches("= alloca").count(), 2, "the parameter and the local: {text}");
7417        assert!(saves.contains("store %9 -> %2"), "the local is written through: {text}");
7418        assert!(!plain.contains("alloca"), "nothing in the plain one needs a slot: {text}");
7419    }
7420
7421    /// A value set before a library `sigsetjmp` and read after the `siglongjmp` keeps a spill slot
7422    /// of its own.
7423    ///
7424    /// The shape of Postgres's `PG_TRY`. Five values are live across the call, one more than the
7425    /// callee saved registers left over, so some go to the stack. They are dead on the arm that
7426    /// runs first, and before this that arm's own values were given the same slots, so the arm the
7427    /// jump lands in read them back. Every slot is written by one value, so no offset is stored to
7428    /// twice.
7429    #[test]
7430    fn a_value_live_across_sigsetjmp_keeps_its_spill_slot() {
7431        each_spill_slot_written_once(&across("int __sigsetjmp(sigjmp_buf, int);\n", "__sigsetjmp"));
7432    }
7433
7434    /// The same shape through a function with a name nobody knows, which only the attribute says
7435    /// comes back twice. tamnd/rucc#2012.
7436    #[test]
7437    fn a_value_live_across_a_returns_twice_call_keeps_its_spill_slot() {
7438        let declared = "int save_here(sigjmp_buf, int) __attribute__((__returns_twice__));\n";
7439        each_spill_slot_written_once(&across(declared, "save_here"));
7440    }
7441
7442    /// A value set before `setjmp` and read after the `longjmp` keeps its slot to itself, at `-O0`
7443    /// and at `-O2`.
7444    ///
7445    /// The reduction in tamnd/rucc#2035, which glibc's `<setjmp.h>` turns into a call to
7446    /// `_setjmp`. `v` is dead on the arm that runs first, so that arm's own values were given its
7447    /// slot and the handler printed `v + 1`. The handler reads `v` from a slot, and nothing between
7448    /// the `setjmp` and the call that jumps back writes that slot.
7449    #[test]
7450    fn a_value_live_across_setjmp_shares_its_slot_with_nothing_in_the_first_arm() {
7451        let source = concat!(
7452            "typedef long jmp_buf[25];\n",
7453            "int _setjmp(jmp_buf);\n",
7454            "void longjmp(jmp_buf, int) __attribute__((noreturn));\n",
7455            "int printf(const char *, ...);\n",
7456            "static jmp_buf *stack;\n",
7457            "static volatile long long sink;\n",
7458            "static int cells[64];\n",
7459            "static volatile int seed_in = 3;\n",
7460            "static void work(void) { longjmp(*stack, 1); }\n",
7461            "int main(void) {\n",
7462            "  int seed = seed_in;\n",
7463            "  int v = seed * 2;\n",
7464            "  jmp_buf buf;\n",
7465            "  if (_setjmp(buf) == 0) {\n",
7466            "    stack = &buf;\n",
7467            "    int *p = &cells[seed + 3];\n",
7468            "    int a = v + 8;\n",
7469            "    int b = seed * 2005;\n",
7470            "    int *q = &cells[v + 1];\n",
7471            "    work();\n",
7472            "    sink = *p + a + b + *q;\n",
7473            "  } else {\n",
7474            "    printf(\"%d\\n\", v);\n",
7475            "  }\n",
7476            "  return 0;\n",
7477            "}\n",
7478        );
7479        for level in [rucc_session::OptLevel::O0, rucc_session::OptLevel::O2] {
7480            let mut opts = options();
7481            opts.emit = EmitKind::Asm;
7482            opts.opt_level = level;
7483            let result = run(&opts, source);
7484            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
7485            let text = result.text();
7486            let body = text.split_once("\nmain:\n").expect("the function").1;
7487            let lines: Vec<&str> = body.lines().map(str::trim).collect();
7488            let save = lines.iter().position(|l| *l == "call\t_setjmp").expect("the save");
7489            let jump = lines[save..]
7490                .iter()
7491                .position(|l| *l == "call\twork" || *l == "call\tlongjmp")
7492                .map(|at| save + at)
7493                .unwrap_or_else(|| panic!("the call that jumps back at {level:?}:\n{text}"));
7494            let printf = lines.iter().position(|l| *l == "call\tprintf").expect("the handler");
7495            // The load that hands `v` to `printf` as its second argument.
7496            let slot = lines[jump..printf]
7497                .iter()
7498                .rev()
7499                .find_map(|l| l.strip_suffix(", %rsi").or_else(|| l.strip_suffix(", %esi")))
7500                .and_then(|l| l.split_once('\t'))
7501                .map(|(_, place)| place)
7502                .filter(|place| place.ends_with("(%rsp)") || place.ends_with("(%rbp)"))
7503                .unwrap_or_else(|| panic!("the handler reads v from a slot at {level:?}:\n{text}"));
7504            let writes = |l: &&str| {
7505                !l.starts_with("cmp") && !l.starts_with("test") && l.ends_with(&format!(", {slot}"))
7506            };
7507            assert!(
7508                lines[..save].iter().any(writes),
7509                "{slot} is written before the save at {level:?}:\n{text}"
7510            );
7511            assert!(
7512                !lines[save..jump].iter().any(writes),
7513                "{slot} is written again before the jump at {level:?}:\n{text}"
7514            );
7515        }
7516    }
7517
7518    /// Five values live across a call to `save`, declared by `declared`, and five more that die
7519    /// before the jump back, which is enough to spill on x86-64.
7520    fn across(declared: &str, save: &str) -> String {
7521        asm(&format!(
7522            "typedef long sigjmp_buf[25];\n{declared}int id(int);\nvoid thrower(int);\n\
7523             int work(int n) {{\n\
7524             \x20 int v0 = id(n), v1 = id(n + 1), v2 = id(n + 2), v3 = id(n + 3), v4 = id(n + 4);\n\
7525             \x20 sigjmp_buf b;\n\
7526             \x20 if ({save}(b, 0) == 0) {{\n\
7527             \x20   int w0 = id(v0 + v1), w1 = id(v1 + v2), w2 = id(v2 + v3);\n\
7528             \x20   int w3 = id(v3 + v4), w4 = id(v4 + v0);\n\
7529             \x20   thrower(n);\n\
7530             \x20   return w0 ^ w1 ^ w2 ^ w3 ^ w4;\n\
7531             \x20 }}\n\
7532             \x20 return v0 + v1 + v2 + v3 + v4;\n\
7533             }}\n"
7534        ))
7535    }
7536
7537    /// No two spills in the text go to the same slot, and there is at least one.
7538    fn each_spill_slot_written_once(text: &str) {
7539        let mut stored = Vec::new();
7540        for line in text.lines().map(str::trim) {
7541            let Some(operands) = line.strip_prefix("movq\t%") else { continue };
7542            if let Some((_, place)) = operands.split_once(", ") {
7543                if place.ends_with("(%rsp)") {
7544                    assert!(!stored.contains(&place), "{place} is written twice:\n{text}");
7545                    stored.push(place);
7546                }
7547            }
7548        }
7549        assert!(!stored.is_empty(), "something should have been spilled:\n{text}");
7550    }
7551
7552    /// What the save writes and where it leaves control, which is a new block.
7553    ///
7554    /// Four words: the frame pointer, the address to come back to, the stack pointer, and the
7555    /// address of the word the answer arrives in, which is this compiler's own and is why the
7556    /// block after the save opens with a load. The frame pointer is kept although the function
7557    /// asked for nothing and calls nothing, since the epilogue has to find the caller's frame
7558    /// after control has come back, and the frame is grown although there is one word in it,
7559    /// since a function control comes back into cannot use the red zone.
7560    #[test]
7561    fn the_save_writes_four_words_and_carries_on_in_a_new_block() {
7562        let text =
7563            asm(concat!("void *buf[5];\n", "int f(void) { return __builtin_setjmp(buf); }\n",));
7564        let body = text.split_once("\nf:\n").expect("the function").1;
7565        assert!(body.contains("\tmovq\t%rsp, %rbp\n"), "a frame pointer whatever: {text}");
7566        assert!(body.contains("\tsubq\t$8, %rsp\n"), "no red zone: {text}");
7567        assert!(body.contains("\tmovq\t%rbp, (%rax)\n"), "the frame pointer: {text}");
7568        assert!(body.contains("\tmovq\t%rsp, 16(%rax)\n"), "the stack pointer: {text}");
7569        assert!(body.contains("\tleaq\t.Lf_1(%rip), %rcx\n"), "where to come back to: {text}");
7570        assert!(body.contains("\tmovq\t%rcx, 8(%rax)\n"), "and that goes in the buffer: {text}");
7571        let back = body.split_once(".Lf_1:\n").expect("the block control comes back to").1;
7572        assert!(back.starts_with("\tmovq\t(%rsp), %rax\n"), "the answer is read back: {text}");
7573    }
7574
7575    /// Nothing stays in a register across the save, which is said with a write of every one of
7576    /// them and shows up as the callee-saved registers the function saves and restores.
7577    ///
7578    /// The restore puts back two registers and no others, so a function coming back through one
7579    /// finds every other register holding whatever the code between the two put there. The pushes
7580    /// are what makes the epilogue right on that path: the values popped are the caller's, off the
7581    /// stack the restore put back, rather than whatever is in the registers when control arrives.
7582    #[test]
7583    fn a_save_destroys_every_register_the_allocator_hands_out() {
7584        let text =
7585            asm(concat!("void *buf[5];\n", "int f(void) { return __builtin_setjmp(buf); }\n",));
7586        for reg in ["%rbx", "%r12", "%r13", "%r14", "%r15"] {
7587            assert!(text.contains(&format!("\tpushq\t{reg}\n")), "{reg} is saved: {text}");
7588            assert!(text.contains(&format!("\tpopq\t{reg}\n")), "{reg} is restored: {text}");
7589        }
7590    }
7591
7592    /// The restore puts both registers back before it goes, at every level.
7593    ///
7594    /// The jump reads the two of them as well as the address it goes through, which is what keeps
7595    /// it behind them. Without that the two instructions write registers nothing reads, and the
7596    /// scheduler at `-O2` puts the jump in front of both and the program comes back to a frame
7597    /// that is not there.
7598    #[test]
7599    fn the_restore_puts_the_frame_back_before_it_jumps() {
7600        for level in [rucc_session::OptLevel::O0, rucc_session::OptLevel::O2] {
7601            let mut opts = options();
7602            opts.emit = EmitKind::Asm;
7603            opts.opt_level = level;
7604            let source = "void *buf[5];\nvoid g(void) { __builtin_longjmp(buf, 1); }\n";
7605            let result = run(&opts, source);
7606            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
7607            let text = result.text().to_owned();
7608            let jump = text.find("\tjmp\t*%").unwrap_or_else(|| panic!("an indirect jump: {text}"));
7609            let stack = text.find(", %rsp\n").unwrap_or_else(|| panic!("the stack back: {text}"));
7610            let frame = text.find(", %rbp\n").unwrap_or_else(|| panic!("the frame back: {text}"));
7611            assert!(stack < jump, "the stack goes back first at {level:?}: {text}");
7612            assert!(frame < jump, "and so does the frame at {level:?}: {text}");
7613        }
7614    }
7615
7616    /// The second argument of the restore has one allowed value, which gcc 16.2.0 also insists on.
7617    ///
7618    /// This pair does not carry a value back the way the library's `longjmp` does, because what
7619    /// the matching save answers is decided by which way control reached it. So the argument is a
7620    /// place-holder, and a program that wrote anything else meant the library's function.
7621    #[test]
7622    fn a_longjmp_whose_second_argument_is_not_one_is_turned_down() {
7623        for source in [
7624            "void *buf[5];\nvoid f(void) { __builtin_longjmp(buf, 0); }\n",
7625            "void *buf[5];\nextern int v;\nvoid f(void) { __builtin_longjmp(buf, v); }\n",
7626        ] {
7627            let messages = errors(source);
7628            let named = messages.iter().any(|m| m.contains("E0710"));
7629            assert!(named, "expected a complaint about the value in {messages:?}");
7630        }
7631    }
7632
7633    /// A `static` function nothing refers to is not emitted, and one that is refered to is.
7634    ///
7635    /// The pair is written as one program so that the two answers come out of one walk. What
7636    /// makes the difference is the call in `main` and nothing else about either definition.
7637    #[test]
7638    fn a_static_function_nothing_refers_to_is_not_emitted() {
7639        let text = ir("static int dropped(void) { return 1; }\n\
7640                       static int kept(void) { return 2; }\n\
7641                       int main(void) { return kept(); }\n");
7642        assert!(text.contains("func @kept"), "{text}");
7643        assert!(!text.contains("dropped"), "{text}");
7644    }
7645
7646    /// The set is transitive, so two of them that only call each other are both dropped.
7647    ///
7648    /// Counting the references to a name would keep this pair, since each is named once, and
7649    /// that is the mistake this is here to catch: what decides it is whether a root reaches the
7650    /// definition, and a root is something the file has a reason to emit on its own.
7651    #[test]
7652    fn two_static_functions_that_only_call_each_other_are_both_dropped() {
7653        let text = ir("static int ping(void);\n\
7654                       static int pong(void) { return ping(); }\n\
7655                       static int ping(void) { return pong(); }\n\
7656                       int main(void) { return 0; }\n");
7657        assert!(!text.contains("ping"), "{text}");
7658        assert!(!text.contains("pong"), "{text}");
7659    }
7660
7661    /// Everything that names a function keeps it, whether or not the name is being called.
7662    ///
7663    /// An address taken in a body, an image that holds one, and a body that is only reached
7664    /// through another `static` function are three different ways for a definition to be needed
7665    /// and none of them is a call at the top level of a reachable function.
7666    #[test]
7667    fn naming_a_static_function_anywhere_keeps_it() {
7668        let text = ir("static int by_address(void) { return 1; }\n\
7669                       static int in_an_image(void) { return 2; }\n\
7670                       static int deeper(void) { return 3; }\n\
7671                       static int reaches_deeper(void) { return deeper(); }\n\
7672                       static int (*table[1])(void) = {in_an_image};\n\
7673                       int main(void) {\n\
7674                         int (*p)(void) = by_address;\n\
7675                         return p() + table[0]() + reaches_deeper();\n\
7676                       }\n");
7677        for kept in ["by_address", "in_an_image", "deeper", "reaches_deeper"] {
7678            assert!(text.contains(&format!("func @{kept}")), "expected {kept} in:\n{text}");
7679        }
7680    }
7681
7682    /// An attribute that says something outside the file reaches it keeps the definition.
7683    ///
7684    /// None of the five is implemented as anything else yet, and this is the part of each of
7685    /// them that a program notices first: a symbol a linker script names or a function the
7686    /// run-up to `main` calls is not written about anywhere a C file can see.
7687    #[test]
7688    fn an_attribute_keeps_a_static_function_nothing_refers_to() {
7689        for attribute in ["used", "retain", "constructor", "destructor", "__used__"] {
7690            let source = format!(
7691                "__attribute__(({attribute})) static int kept(void) {{ return 1; }}\n\
7692                 int main(void) {{ return 0; }}\n"
7693            );
7694            let text = ir(&source);
7695            assert!(text.contains("func @kept"), "for {attribute}:\n{text}");
7696        }
7697    }
7698
7699    /// `nonnull` is answered yes and taken with or without operands, and a check the program
7700    /// makes on a parameter it names stays, since nothing is assumed from the claim.
7701    #[test]
7702    fn nonnull_is_answered_yes_and_taken_with_or_without_operands() {
7703        let text = ir("#if !__has_attribute(nonnull) || !__has_attribute(__nonnull__)\n\
7704             #error nonnull\n\
7705             #endif\n\
7706             __attribute__((nonnull)) int first(char *p);\n\
7707             int both(char *a, int n, char *b) __attribute__((__nonnull__(1, 3)));\n\
7708             int both(char *a, int n, char *b) { return first(a) + n + (b != 0); }\n");
7709        assert!(text.contains("func @both"), "{text}");
7710    }
7711
7712    /// A function with external linkage is emitted whatever this file does with it, because
7713    /// another one may call it, and that is what external linkage is.
7714    #[test]
7715    fn a_function_anything_could_call_is_emitted_without_being_called() {
7716        let text =
7717            ir("int nobody_here_calls_it(void) { return 1; }\nint main(void) { return 0; }\n");
7718        assert!(text.contains("func @nobody_here_calls_it"), "{text}");
7719    }
7720
7721    /// Four of the classification builtins are operators C already has, and become those.
7722    ///
7723    /// What the standard's macro promises over the operator is that it does not raise the
7724    /// invalid operation exception on a quiet NaN. This compiler does not model floating point
7725    /// exceptions, so there is nothing left for a node of its own to carry and a second way of
7726    /// spelling a comparison would be a second thing every pass has to know about.
7727    #[test]
7728    fn a_classification_c_has_an_operator_for_is_that_operator() {
7729        for (builtin, operator) in [
7730            ("__builtin_isgreater", "binary >"),
7731            ("__builtin_isgreaterequal", "binary >="),
7732            ("__builtin_isless", "binary <"),
7733            ("__builtin_islessequal", "binary <="),
7734        ] {
7735            let source = format!("int f(double x, double y) {{ return {builtin}(x, y); }}\n");
7736            let text = tast(&source);
7737            assert!(text.contains(&format!("{operator} : int")), "for {builtin}:\n{text}");
7738        }
7739    }
7740
7741    /// The rest of the family are comparisons in the IR and never a call to anything.
7742    ///
7743    /// `math.h` defines the macro of each of these names as the builtin of the same name, so
7744    /// there is no function under any of them for a call to reach. `isunordered` and
7745    /// `islessgreater` are predicates the IR's comparison already has, `isnan` is the value that
7746    /// is unordered with itself, and the two that ask about a magnitude are written against the
7747    /// infinities. `signbit` is the one that is not a question about the value, since a negative
7748    /// zero compares equal to a positive one, so its answer comes from the bits.
7749    #[test]
7750    fn the_classification_builtins_are_comparisons_and_not_calls() {
7751        let text = body("int f(double x, double y) { return __builtin_isunordered(x, y); }\n");
7752        assert_eq!(
7753            text,
7754            "block0(%0: f64, %1: f64):\n    %2 = fcmp uno %0, %1\n    %3 = zext.i32 \
7755                          %2\n    return %3\n"
7756        );
7757
7758        // Not `x != y`, which is true when the two are unordered and so is true of a NaN.
7759        let text = body("int f(double x, double y) { return __builtin_islessgreater(x, y); }\n");
7760        assert!(text.contains("fcmp one %0, %1"), "{text}");
7761
7762        let text = body("int f(double x) { return __builtin_isnan(x); }\n");
7763        assert!(text.contains("fcmp uno %0, %0"), "{text}");
7764
7765        let text = body("int f(double x) { return __builtin_isinf(x); }\n");
7766        assert!(text.contains("fconst.f64 0x7ff0000000000000"), "{text}");
7767        assert!(text.contains("fconst.f64 0xfff0000000000000"), "{text}");
7768        assert!(text.contains("%3 = fcmp oeq %0, %1"), "{text}");
7769        assert!(text.contains("%4 = fcmp oeq %0, %2"), "{text}");
7770        assert!(text.contains("%5 = or %3, %4"), "{text}");
7771
7772        // Strictly between the two infinities, which a NaN is not, because an ordered comparison
7773        // against either of them is false. That is what makes this one test rather than two.
7774        let text = body("int f(double x) { return __builtin_isfinite(x); }\n");
7775        assert!(text.contains("%3 = fcmp olt %2, %0"), "{text}");
7776        assert!(text.contains("%4 = fcmp olt %0, %1"), "{text}");
7777        assert!(text.contains("%5 = and %3, %4"), "{text}");
7778
7779        let text = body("int f(double x) { return __builtin_signbit(x); }\n");
7780        assert!(text.contains("%1 = bitcast.i64 %0"), "{text}");
7781        assert!(text.contains("icmp slt %1, %2"), "{text}");
7782
7783        // The same question of a value in the target's widest format, where the bits are eighty
7784        // and the object they sit in is sixteen bytes. No integer is that wide, so the sign is
7785        // read from the word at the top of the value once it is in memory.
7786        let text = body("int f(long double x) { return __builtin_signbitl(x); }\n");
7787        assert!(text.contains("load.i16"), "{text}");
7788        assert!(text.contains("icmp slt"), "{text}");
7789        assert!(!text.contains("i80"), "{text}");
7790
7791        // The operand is evaluated once however many times it is compared, which is the whole
7792        // reason these are nodes rather than a rewriting into the operators.
7793        let text = body("double g(void);\nint f(void) { return __builtin_isnan(g()); }\n");
7794        assert_eq!(text.matches("call @g()").count(), 1, "{text}");
7795    }
7796
7797    /// A spelling that names a width converts its argument before it asks.
7798    ///
7799    /// gcc gives `__builtin_isinff` a `float` parameter and `__builtin_isinf` no parameter type
7800    /// at all, and the difference is visible rather than academic: `1e300` does not fit in a
7801    /// `float`, so converting it first is an infinity and not converting it is not. Both numbers
7802    /// here are what gcc 16 gives.
7803    #[test]
7804    fn a_classification_spelling_that_names_a_width_converts_before_it_asks() {
7805        let text = ir(concat!(
7806            "int a = __builtin_isinff(1e300);\n",
7807            "int b = __builtin_isinf(1e300);\n",
7808            // Folded here rather than compared at run time, because a question about a value has
7809            // an answer as soon as the value is a constant, and an initializer for an object
7810            // with static storage duration has to have one.
7811            "int c = __builtin_isnan(0.0);\n",
7812            "int d = __builtin_signbit(-0.0);\n",
7813            "int e = __builtin_islessgreater(1.0, 2.0);\n",
7814        ));
7815        assert!(text.contains("global @a : i32 = 1,"), "{text}");
7816        assert!(text.contains("global @b : i32 = 0,"), "{text}");
7817        assert!(text.contains("global @c : i32 = 0,"), "{text}");
7818        assert!(text.contains("global @d : i32 = 1,"), "{text}");
7819        assert!(text.contains("global @e : i32 = 1,"), "{text}");
7820    }
7821
7822    /// An argument that is not floating point is refused, in gcc's words.
7823    #[test]
7824    fn a_classification_builtin_refuses_an_argument_that_is_not_floating_point() {
7825        let mut opts = options();
7826        opts.emit = EmitKind::Ir;
7827        let source = concat!(
7828            "int a(int x) { return __builtin_isnan(x); }\n",
7829            "int b(int x, int y) { return __builtin_isunordered(x, y); }\n",
7830            "int c(double x) { return __builtin_isnan(x, x); }\n",
7831        );
7832        let messages = run(&opts, source).messages;
7833        assert_eq!(
7834            messages,
7835            [
7836                "/main.c:1:23: error: non-floating-point argument in call to function \
7837                 '__builtin_isnan' [E0685]",
7838                "/main.c:2:30: error: non-floating-point arguments in call to function \
7839                 '__builtin_isunordered' [E0685]",
7840                "/main.c:3:26: error: too many arguments to function '__builtin_isnan' [E0511]",
7841            ]
7842        );
7843    }
7844
7845    /// The three of the family that need a constant of the format other than an infinity.
7846    ///
7847    /// `isnormal` is the one that needs the smallest normal, and it is asked of the magnitude, so
7848    /// the sign comes off first and what is left is the same shape as `isfinite`. `isinf_sign` is
7849    /// the one whose answer is a number: the two comparisons `isinf` builds, subtracted rather
7850    /// than combined. `fpclassify` is four questions of one value and five answers to pick from,
7851    /// and the picking is a mask because all five are constants and neither of them can have an
7852    /// effect.
7853    #[test]
7854    fn the_last_three_classification_builtins_are_comparisons_and_not_calls() {
7855        let text = body("int f(double x) { return __builtin_isnormal(x); }\n");
7856        // The sign off, which is the magnitude, and then the range, asked of the bits rather than
7857        // of the number, since the encoding of a value whose sign bit is clear rises with the
7858        // value in every format this compiles for.
7859        assert!(text.contains("%1 = bitcast.i64 %0"), "{text}");
7860        assert!(text.contains("%2 = iconst.i64 9223372036854775807"), "{text}");
7861        assert!(text.contains("%3 = and %1, %2"), "{text}");
7862        assert!(text.contains("%4 = iconst.i64 4503599627370496"), "{text}");
7863        assert!(text.contains("%5 = iconst.i64 9218868437227405312"), "{text}");
7864        assert!(text.contains("%6 = icmp uge %3, %4"), "{text}");
7865        assert!(text.contains("%7 = icmp ult %3, %5"), "{text}");
7866        assert!(text.contains("%8 = and %6, %7"), "{text}");
7867
7868        // The same question in the target's widest format, where the smallest normal has the
7869        // leading significand bit stored rather than implied, so its encoding is two bits and not
7870        // one. There is no integer that wide to compare the bits in, so it is the magnitude that
7871        // is compared, as a value.
7872        let text = body("int f(long double x) { return __builtin_isnormal(x); }\n");
7873        assert!(text.contains("fconst.f80 0x18000000000000000"), "{text}");
7874        assert!(text.contains("fconst.f80 0x7fff8000000000000000"), "{text}");
7875        assert!(text.contains("fcmp oge"), "{text}");
7876        assert!(text.contains("fcmp olt"), "{text}");
7877
7878        let text = body("int f(double x) { return __builtin_isinf_sign(x); }\n");
7879        assert!(text.contains("%3 = fcmp oeq %0, %1"), "{text}");
7880        assert!(text.contains("%4 = fcmp oeq %0, %2"), "{text}");
7881        assert!(text.contains("%7 = sub %5, %6"), "{text}");
7882
7883        let text = body("int f(double x) { return __builtin_fpclassify(0, 1, 2, 3, 4, x); }\n");
7884        assert!(text.contains("fcmp uno %0, %0"), "{text}");
7885        assert!(text.contains("fcmp oeq %0, %6"), "{text}");
7886        // Four questions, each of them a bit widened into the type of the answer and then spread
7887        // into a mask that picks between the answer and whatever the questions after it settled
7888        // on. Nothing sign extends, because no rule lowers a sign extension out of one bit.
7889        assert_eq!(text.matches(" = zext.i32 ").count(), 4, "{text}");
7890        assert_eq!(text.matches(" = xor ").count(), 4, "{text}");
7891        assert!(!text.contains("call"), "{text}");
7892
7893        // The value is evaluated once however many questions are asked of it, which is the whole
7894        // reason `fpclassify` is a node rather than the chain of tests it turns into.
7895        let text = body(concat!(
7896            "double g(void);\n",
7897            "int f(void) { return __builtin_fpclassify(0, 1, 2, 3, 4, g()); }\n",
7898        ));
7899        assert_eq!(text.matches("call @g()").count(), 1, "{text}");
7900    }
7901
7902    /// Each of the three answers a constant where its operand is one.
7903    ///
7904    /// glibc's `fpclassify` macro is exactly this builtin, so a program that writes
7905    /// `fpclassify(0.0)` in a static initializer is writing this, and it has to have a value at
7906    /// translation time or the program is refused rather than merely compiled slowly. Every
7907    /// number here is what gcc 16 gives.
7908    #[test]
7909    fn the_last_three_classification_builtins_fold_where_their_operand_is_a_constant() {
7910        let text = ir(concat!(
7911            "int a = __builtin_isnormal(1.0);\n",
7912            "int b = __builtin_isnormal(0.0);\n",
7913            "int c = __builtin_isnormal(1.0 / 0.0);\n",
7914            "int d = __builtin_isinf_sign(-1.0 / 0.0);\n",
7915            "int e = __builtin_isinf_sign(1.0);\n",
7916            "int g = __builtin_fpclassify(0, 1, 2, 3, 4, 0.0);\n",
7917            "int h = __builtin_fpclassify(0, 1, 2, 3, 4, 1.0);\n",
7918            "int i = __builtin_fpclassify(0, 1, 2, 3, 4, 1.0 / 0.0);\n",
7919        ));
7920        assert!(text.contains("global @a : i32 = 1,"), "{text}");
7921        assert!(text.contains("global @b : i32 = 0,"), "{text}");
7922        assert!(text.contains("global @c : i32 = 0,"), "{text}");
7923        assert!(text.contains("global @d : i32 = -1,"), "{text}");
7924        assert!(text.contains("global @e : i32 = 0,"), "{text}");
7925        assert!(text.contains("global @g : i32 = 4,"), "{text}");
7926        assert!(text.contains("global @h : i32 = 2,"), "{text}");
7927        assert!(text.contains("global @i : i32 = 1,"), "{text}");
7928    }
7929
7930    /// `fpclassify` refuses what gcc refuses, in gcc's words.
7931    ///
7932    /// The five answers have to be integer constant expressions, because what the builtin does is
7933    /// pick one of them and a pick between values that are not known here would be a chain of
7934    /// conditionals over expressions the call has already evaluated.
7935    #[test]
7936    fn fpclassify_refuses_an_answer_that_is_not_an_integer_constant() {
7937        let mut opts = options();
7938        opts.emit = EmitKind::Ir;
7939        let source = concat!(
7940            "int a(double x, int n) { return __builtin_fpclassify(0, 1, n, 3, 4, x); }\n",
7941            "int b(double x) { return __builtin_fpclassify(0, 1, 2, 3, x); }\n",
7942            "int c(int x) { return __builtin_fpclassify(0, 1, 2, 3, 4, x); }\n",
7943        );
7944        let messages = run(&opts, source).messages;
7945        assert_eq!(
7946            messages,
7947            [
7948                "/main.c:1:60: error: non-const integer argument 3 in call to function \
7949                 '__builtin_fpclassify' [E0687]",
7950                "/main.c:2:26: error: too few arguments to function '__builtin_fpclassify' \
7951                 [E0511]",
7952                "/main.c:3:23: error: non-floating-point argument in call to function \
7953                 '__builtin_fpclassify' [E0685]",
7954            ]
7955        );
7956    }
7957
7958    /// A builtin whose answer is a constant is one, and is not a call to the library.
7959    ///
7960    /// This is the reason the family is answered in the front end at all. `double x =
7961    /// __builtin_inf();` at file scope initializes an object with static storage duration, so
7962    /// there is no point in the program at which a call could be made, and a compiler that
7963    /// lowered it to one would reject a program gcc accepts. Every number here is the encoding
7964    /// gcc 16 gives on x86-64.
7965    #[test]
7966    fn a_builtin_whose_answer_is_a_constant_is_one_and_not_a_call() {
7967        let text = ir(concat!(
7968            "double a = __builtin_inf();\n",
7969            "float b = __builtin_huge_valf();\n",
7970            "long double c = __builtin_infl();\n",
7971            "double d = __builtin_huge_val();\n",
7972        ));
7973        assert!(text.contains("global @a : f64 = 0x7ff0000000000000,"), "{text}");
7974        assert!(text.contains("global @b : f32 = 0x7f800000,"), "{text}");
7975        assert!(text.contains("f80 0x7fff8000000000000000"), "{text}");
7976        assert!(text.contains("global @d : f64 = 0x7ff0000000000000,"), "{text}");
7977        assert!(!text.contains("call"), "{text}");
7978    }
7979
7980    /// A nan is written with the payload the program asked for.
7981    ///
7982    /// The string is read the way `strtoull` reads a number, which is what the library function
7983    /// of the same name does with it, and a string that is not one at all leaves the call for the
7984    /// library to answer at run time. A quiet nan has the high fraction bit set and a signalling
7985    /// one does not, except that a signalling nan with nothing in it would be an infinity, so it
7986    /// gets the next bit down instead. Every encoding here was measured against gcc 16, the two
7987    /// `long double` ones on a machine with the x87 format.
7988    #[test]
7989    fn a_nan_is_written_with_the_payload_the_program_asked_for() {
7990        let text = ir(concat!(
7991            "double a = __builtin_nan(\"\");\n",
7992            "double b = __builtin_nan(\"0x1\");\n",
7993            // Octal, since there is a leading zero, so this is eight and not ten.
7994            "double c = __builtin_nan(\"010\");\n",
7995            "double d = __builtin_nans(\"\");\n",
7996            "double e = __builtin_nans(\"0x1\");\n",
7997            "float f = __builtin_nanf(\"0x1\");\n",
7998            "float g = __builtin_nansf(\"\");\n",
7999            "long double h = __builtin_nansl(\"\");\n",
8000        ));
8001        assert!(text.contains("global @a : f64 = 0x7ff8000000000000,"), "{text}");
8002        assert!(text.contains("global @b : f64 = 0x7ff8000000000001,"), "{text}");
8003        assert!(text.contains("global @c : f64 = 0x7ff8000000000008,"), "{text}");
8004        assert!(text.contains("global @d : f64 = 0x7ff4000000000000,"), "{text}");
8005        assert!(text.contains("global @e : f64 = 0x7ff0000000000001,"), "{text}");
8006        assert!(text.contains("global @f : f32 = 0x7fc00001,"), "{text}");
8007        assert!(text.contains("global @g : f32 = 0x7fa00000,"), "{text}");
8008        assert!(text.contains("f80 0x7fffa000000000000000"), "{text}");
8009
8010        // A payload that is not a number, and one that is not known until run time, are both
8011        // left to the library, which is the same thing gcc emits for either of them.
8012        let text = ir(concat!(
8013            "double f(const char *p) { return __builtin_nan(p); }\n",
8014            "double g(void) { return __builtin_nans(\"1x\"); }\n",
8015        ));
8016        assert_eq!(text.matches("call @nan(").count(), 1, "{text}");
8017        assert_eq!(text.matches("call @nans(").count(), 1, "{text}");
8018    }
8019
8020    /// The length and the order of a string literal are known here.
8021    ///
8022    /// A program that asks for either of them is asking about something the translation already
8023    /// has in front of it, and folding is not only an optimization: `execute/921007-1.c` in the
8024    /// torture suite calls `__builtin_strcmp` in a file that defines its own `strcmp` with a
8025    /// different signature, so leaving the call behind is a name collision that gcc does not
8026    /// have. The comparison is over `unsigned char`, which is why the second one is negative.
8027    #[test]
8028    fn the_length_and_the_order_of_a_string_literal_are_known_here() {
8029        let text = ir(concat!(
8030            "unsigned long a = __builtin_strlen(\"hello\");\n",
8031            "unsigned long b = __builtin_strlen(\"a\\0bc\");\n",
8032            "int c = __builtin_strcmp(\"X\", \"X\\376\") < 0;\n",
8033            "int d = __builtin_strcmp(\"abc\", \"abc\");\n",
8034            "int e = __builtin_strcmp(\"abc\", \"ab\") > 0;\n",
8035        ));
8036        assert!(text.contains("global @a : i64 = 5,"), "{text}");
8037        assert!(text.contains("global @b : i64 = 1,"), "{text}");
8038        assert!(text.contains("global @c : i32 = 1,"), "{text}");
8039        assert!(text.contains("global @d : i32 = 0,"), "{text}");
8040        assert!(text.contains("global @e : i32 = 1,"), "{text}");
8041        assert!(!text.contains("call"), "{text}");
8042
8043        // An argument that is not a literal is the library's to answer, as it has to be.
8044        let text = ir("unsigned long f(const char *p) { return __builtin_strlen(p); }\n");
8045        assert!(text.contains("call @strlen("), "{text}");
8046    }
8047
8048    /// A sign builtin is a mask over the bits, and is not a call.
8049    ///
8050    /// `fabs` and `copysign` are in the math library rather than the C one, so a program that
8051    /// only ever wrote the prefixed spelling never asked for `-lm` and a call left behind here
8052    /// would not link. Neither needs anything the library has: one clears the sign bit and the
8053    /// other takes it from the second operand, and every other bit goes through untouched.
8054    #[test]
8055    fn a_sign_builtin_is_a_mask_over_the_bits_and_not_a_call() {
8056        let text = body("double f(double x) { return __builtin_fabs(x); }\n");
8057        assert!(text.contains("bitcast.i64 %0"), "{text}");
8058        assert!(text.contains("iconst.i64 9223372036854775807"), "{text}");
8059        assert!(text.contains("and %1, %2"), "{text}");
8060        assert!(text.contains("bitcast.f64 %3"), "{text}");
8061        assert!(!text.contains("call"), "{text}");
8062
8063        let text = body("double f(double x, double y) { return __builtin_copysign(x, y); }\n");
8064        assert!(text.contains("iconst.i64 -9223372036854775808"), "{text}");
8065        assert!(text.contains("%8 = or %4, %7"), "{text}");
8066        assert!(!text.contains("call"), "{text}");
8067
8068        // The x87 format, whose value is eighty bits sitting in an object of sixteen. There is no
8069        // integer that wide, so the mask is on the word at the top of the value, in memory.
8070        let text = body("long double f(long double x) { return __builtin_fabsl(x); }\n");
8071        assert!(text.contains("iconst.i16 32767"), "{text}");
8072        assert!(text.contains("load.f80"), "{text}");
8073        assert!(!text.contains("call"), "{text}");
8074
8075        // The width a name does not spell out is `double`, so a `float` argument widens first and
8076        // the answer is a `double`, which is what gcc's declaration of it says.
8077        let text = body("double f(float x) { return __builtin_fabs(x); }\n");
8078        assert!(text.contains("fpext.f64 %0"), "{text}");
8079        assert!(text.contains("bitcast.i64 %1"), "{text}");
8080    }
8081
8082    /// A shuffle reads each lane of the answer out of a copy of its sources, at the index the mask
8083    /// lane gives with only its low bits kept, and is not a call.
8084    ///
8085    /// The copy is what makes `*v = __builtin_shuffle(*v, m)` right, since the answer is written
8086    /// over the vector it reads, and the mask is what `pr85331.c` checks: gcc keeps as many bits
8087    /// of an index as it takes to name a lane, so `10000000001` picks lane one of two.
8088    #[test]
8089    fn a_shuffle_picks_each_lane_by_the_low_bits_of_the_mask() {
8090        let text = body(concat!(
8091            "typedef int v2 __attribute__((vector_size(8)));\n",
8092            "void f(v2 *v, v2 m) { *v = __builtin_shuffle(*v, m); }\n",
8093        ));
8094        assert!(text.contains("memcpy"), "{text}");
8095        assert_eq!(text.matches("iconst.i32 1\n").count(), 2, "{text}");
8096        assert_eq!(text.matches(" = and ").count(), 2, "{text}");
8097        assert!(!text.contains("call"), "{text}");
8098
8099        // Two sources of four lanes are eight to pick from, so three bits of each index are
8100        // kept, and a mask of bytes is widened to a word before it is masked.
8101        let text = body(concat!(
8102            "typedef char v4 __attribute__((vector_size(4)));\n",
8103            "v4 f(v4 a, v4 b, v4 m) { return __builtin_shuffle(a, b, m); }\n",
8104        ));
8105        assert_eq!(text.matches("iconst.i32 7\n").count(), 4, "{text}");
8106        assert!(text.contains("zext.i32"), "{text}");
8107        assert!(!text.contains("call"), "{text}");
8108    }
8109
8110    /// A function holding `__builtin_apply_args` writes every argument register into its frame
8111    /// before anything else runs, the ones its parameters took as well as the ones they did not,
8112    /// and the answer is the address of where it wrote them.
8113    #[test]
8114    fn the_arguments_a_function_was_called_with_are_saved_on_the_way_in() {
8115        let text =
8116            mir("void *f(int a, double b) { (void)a; (void)b; return __builtin_apply_args(); }\n");
8117        // Six words and the address the arguments in memory start at, and eight vectors.
8118        assert!(text.matches("x64.mov_mr_64").count() >= 7, "{text}");
8119        assert!(text.matches("x64.movaps_mr").count() >= 8, "{text}");
8120        for reg in ["$rdi", "$rsi", "$rdx", "$rcx", "$r8", "$r9", "$xmm0", "$xmm7"] {
8121            assert!(text.contains(reg), "{reg} is not saved in\n{text}");
8122        }
8123
8124        // And a function without one saves nothing.
8125        let text = mir("int f(int a) { return a; }\n");
8126        assert!(!text.contains("movaps_mr"), "{text}");
8127    }
8128
8129    /// `__builtin_apply` loads every argument register out of the block it is given, copies the
8130    /// bytes of arguments in memory it was told about, and calls through the address, with eight
8131    /// in `%al` since every vector register may hold an argument.
8132    #[test]
8133    fn a_call_built_from_saved_arguments_loads_every_argument_register() {
8134        let text = mir(concat!(
8135            "void *g(void *args, void (*h)()) {\n",
8136            "  return __builtin_apply(h, args, 64);\n",
8137            "}\n",
8138        ));
8139        assert!(text.matches("x64.mov_rm_64").count() >= 7, "{text}");
8140        assert!(text.matches("x64.movaps_rm").count() >= 8, "{text}");
8141        assert!(text.contains("call"), "{text}");
8142        // What came back is written out, two words and two vectors.
8143        assert!(text.matches("x64.movaps_mr").count() >= 2, "{text}");
8144
8145        // The size is a number the frame can be laid out with, and nothing else is.
8146        let mut opts = options();
8147        opts.emit = EmitKind::Ir;
8148        let result = run(
8149            &opts,
8150            "void *g(void *a, void (*h)(), int n) { return __builtin_apply(h, a, n); }\n",
8151        );
8152        assert!(result.failed(), "{:?}", result.messages);
8153        assert!(
8154            result
8155                .messages
8156                .iter()
8157                .any(|m| m.contains("the size given to '__builtin_apply' is a constant")),
8158            "{:?}",
8159            result.messages
8160        );
8161    }
8162
8163    /// A shuffle whose operands gcc would refuse is refused, in gcc's words.
8164    #[test]
8165    fn a_shuffle_refuses_what_gcc_refuses() {
8166        let mut opts = options();
8167        opts.emit = EmitKind::Ir;
8168        let source = concat!(
8169            "typedef int v4 __attribute__((vector_size(16)));\n",
8170            "typedef float f4 __attribute__((vector_size(16)));\n",
8171            "typedef short s8 __attribute__((vector_size(16)));\n",
8172            "typedef long long l4 __attribute__((vector_size(32)));\n",
8173            "void a(v4 x, f4 m) { __builtin_shuffle(x, m); }\n",
8174            "void b(int x, v4 m) { __builtin_shuffle(x, m); }\n",
8175            "void c(v4 x, f4 y, v4 m) { __builtin_shuffle(x, y, m); }\n",
8176            "void d(v4 x, s8 m) { __builtin_shuffle(x, m); }\n",
8177            "void e(f4 x, l4 m) { __builtin_shuffle(x, m); }\n",
8178            "void g(v4 x) { __builtin_shuffle(x); }\n",
8179        );
8180        let messages = run(&opts, source).messages;
8181        let wanted = [
8182            "last argument must be an integer vector [E0715]",
8183            "arguments must be vectors [E0715]",
8184            "argument vectors must be of the same type [E0715]",
8185            "number of elements of the argument vector(s) and the mask vector should be the same \
8186             [E0715]",
8187            "argument vector(s) inner type must have the same size as inner type of the mask \
8188             [E0715]",
8189            "too few arguments to function '__builtin_shuffle' [E0511]",
8190        ];
8191        assert_eq!(messages.len(), wanted.len(), "{messages:?}");
8192        for (message, wanted) in messages.iter().zip(wanted) {
8193            assert!(message.ends_with(wanted), "{message}");
8194        }
8195    }
8196
8197    /// The plain math library names are the same mask, which is what makes a program link.
8198    ///
8199    /// `math.h` declares `fabs` and never spells `__builtin_fabs`, so the plain name is the one
8200    /// every program that includes the header reaches. Recognising only the prefixed spelling
8201    /// leaves a call to the math library behind, and the math library is not on the link line
8202    /// unless the program asked for `-lm`. parson is the project that shows it: its makefile has
8203    /// no `-lm`, it does not need one under gcc, and `undefined reference to 'fabs'` is where the
8204    /// build stopped. That is issue 630.
8205    #[test]
8206    fn the_plain_math_names_are_the_same_mask_and_not_a_call() {
8207        let text =
8208            body(concat!("double fabs(double x);\n", "double f(double x) { return fabs(x); }\n",));
8209        assert!(text.contains("iconst.i64 9223372036854775807"), "{text}");
8210        assert!(!text.contains("call"), "{text}");
8211
8212        let text =
8213            body(concat!("float fabsf(float x);\n", "float f(float x) { return fabsf(x); }\n",));
8214        assert!(text.contains("bitcast.i32 %0"), "{text}");
8215        assert!(!text.contains("call"), "{text}");
8216
8217        let text = body(concat!(
8218            "double copysign(double x, double y);\n",
8219            "double f(double x, double y) { return copysign(x, y); }\n",
8220        ));
8221        assert!(text.contains("iconst.i64 -9223372036854775808"), "{text}");
8222        assert!(!text.contains("call"), "{text}");
8223
8224        let text = body(concat!(
8225            "float copysignf(float x, float y);\n",
8226            "float f(float x, float y) { return copysignf(x, y); }\n",
8227        ));
8228        assert!(!text.contains("call"), "{text}");
8229
8230        // The `long double` pair is left alone on purpose. The prefixed spelling of both stops in
8231        // the back end with `no rule lowers a bitcast producing an i80`, so expanding the plain
8232        // name would trade a link error for a worse one. They go in with issue 540.
8233        let text = ir(concat!(
8234            "long double fabsl(long double x);\n",
8235            "long double f(long double x) { return fabsl(x); }\n",
8236        ));
8237        assert!(text.contains("call @fabsl"), "{text}");
8238    }
8239
8240    /// A plain math name the program took is the program's own function.
8241    ///
8242    /// The same four ways as the absolute value family next door, asked again here because these
8243    /// two go through a different path: the plain names of this family are taken after the call
8244    /// has been checked against the declaration, and the declaration is the whole reason the
8245    /// question can be answered at all. Measured against gcc 16.2.0, which calls the program's
8246    /// function in every one of them.
8247    #[test]
8248    fn a_plain_math_name_the_program_took_is_the_programs_own_function() {
8249        let taken = concat!(
8250            "static double fabs(double b) { return 7; }\n",
8251            "double f(double x) { return fabs(x); }\n",
8252        );
8253        assert!(ir(taken).contains("call @fabs"), "a static definition is the program's own");
8254
8255        let retyped = concat!("int fabs(int b);\n", "int f(int x) { return fabs(x); }\n");
8256        assert!(ir(retyped).contains("call @fabs"), "another type is another function");
8257
8258        let plain = concat!("double fabs(double b);\n", "double f(double x) { return fabs(x); }\n");
8259        let mut opts = options();
8260        opts.emit = EmitKind::Ir;
8261        assert!(!run(&opts, plain).text().contains("call @fabs"), "the library's by default");
8262
8263        opts.builtins = false;
8264        assert!(run(&opts, plain).text().contains("call @fabs"), "-fno-builtin");
8265
8266        opts.builtins = true;
8267        opts.no_builtin = vec!["fabs".to_owned()];
8268        assert!(run(&opts, plain).text().contains("call @fabs"), "-fno-builtin-fabs");
8269        let one = concat!(
8270            "double copysign(double a, double b);\n",
8271            "double f(double x) { return copysign(x, 1.0); }\n",
8272        );
8273        assert!(!run(&opts, one).text().contains("call @copysign"), "one name and not the family");
8274
8275        // The prefixed spelling is untouched by any of it, which is what the prefix is for.
8276        opts.no_builtin = Vec::new();
8277        opts.builtins = false;
8278        let prefixed = "double f(double x) { return __builtin_fabs(x); }\n";
8279        assert!(!run(&opts, prefixed).text().contains("call @fabs"), "the prefix is not a library");
8280    }
8281
8282    /// The sign builtins answer a zero and a nan the way the bits say.
8283    ///
8284    /// This is why they are described over the bits rather than written with comparisons and
8285    /// negation. A negative zero compares equal to a positive one and has a sign bit to clear,
8286    /// and a nan compares equal to nothing at all and keeps its payload through both operations.
8287    /// `execute/ieee/copysign1.c` in the torture suite is the test that notices, because it
8288    /// compares its answers with `memcmp`. Every number here is what gcc 16 gives, the two in the
8289    /// x87 format measured on a machine that has it.
8290    #[test]
8291    fn the_sign_builtins_answer_a_zero_and_a_nan_the_way_the_bits_say() {
8292        let text = ir(concat!(
8293            "double a = __builtin_fabs(-3.5);\n",
8294            "double b = __builtin_copysign(1.0, -0.0);\n",
8295            "double c = __builtin_copysign(0.0, -2.0);\n",
8296            // The payload survives both, and only the sign bit moves.
8297            "double d = __builtin_copysign(-__builtin_nan(\"\"), 1.0);\n",
8298            "double e = __builtin_fabs(-__builtin_nan(\"0x1\"));\n",
8299            "float g = __builtin_copysignf(-0.0f, 2.0f);\n",
8300            "long double h = __builtin_copysignl(1.0L, -1.0L);\n",
8301            "long double i = __builtin_fabsl(-__builtin_infl());\n",
8302        ));
8303        assert!(text.contains("global @a : f64 = 0x400c000000000000,"), "{text}");
8304        assert!(text.contains("global @b : f64 = 0xbff0000000000000,"), "{text}");
8305        assert!(text.contains("global @c : f64 = 0x8000000000000000,"), "{text}");
8306        assert!(text.contains("global @d : f64 = 0x7ff8000000000000,"), "{text}");
8307        assert!(text.contains("global @e : f64 = 0x7ff8000000000001,"), "{text}");
8308        assert!(text.contains("global @g : f32 = 0x0,"), "{text}");
8309        assert!(text.contains("f80 0xbfff8000000000000000"), "{text}");
8310        assert!(text.contains("f80 0x7fff8000000000000000"), "{text}");
8311    }
8312
8313    /// The sign of a `long double` is read and written in the word at the top of it.
8314    ///
8315    /// The other formats have their sign tested and set on an integer as wide as the value, and
8316    /// there is no eighty bit integer for the x87 one to go to: no rule lowers it, and
8317    /// `execute/20080502-1.c` and `execute/ieee/copysign1.c` in the torture suite stopped on that.
8318    /// The value goes through memory instead, and the word holding its sign is what is looked at.
8319    #[test]
8320    fn the_sign_of_a_long_double_is_in_the_word_at_the_top_of_it() {
8321        for source in [
8322            "int f(long double x) { return __builtin_signbit(x); }\n",
8323            "long double f(long double x) { return __builtin_fabsl(x); }\n",
8324            "long double f(long double x, long double y) { return __builtin_copysignl(x, y); }\n",
8325            "int f(long double x) { return __builtin_isnormal(x); }\n",
8326        ] {
8327            let text = body(source);
8328            assert!(!text.contains("i80"), "{text}");
8329            assert!(text.contains("i16"), "{text}");
8330        }
8331    }
8332
8333    /// The complex builtins are the halves of the value, and are not a call.
8334    ///
8335    /// `conj`, `creal` and `cimag` are `~`, `__real__` and `__imag__` under the names `complex.h`
8336    /// gives them, so there is nothing for the math library to do that the translation cannot do
8337    /// with the object in front of it. Leaving the call behind would not link either, since all
8338    /// three are in the math library and a program that wrote one never had a reason to ask for
8339    /// `-lm`. Measured against gcc 16.2.0, which emits no call for any of them even at `-O0`.
8340    #[test]
8341    fn the_complex_builtins_are_the_halves_of_the_value_and_not_a_call() {
8342        let text = body("double f(_Complex double z) { return __builtin_creal(z); }\n");
8343        assert!(!text.contains("call"), "{text}");
8344        let text = body("double f(_Complex double z) { return __builtin_cimag(z); }\n");
8345        assert!(!text.contains("call"), "{text}");
8346
8347        // The conjugate is the imaginary half negated and the real half as it stands, so there is
8348        // one negation in it. A complex negation is the one with two.
8349        let text = body("_Complex double f(_Complex double z) { return __builtin_conj(z); }\n");
8350        assert_eq!(text.matches("fneg").count(), 1, "{text}");
8351        assert!(!text.contains("call"), "{text}");
8352        let negated = body("_Complex double f(_Complex double z) { return -z; }\n");
8353        assert_eq!(negated.matches("fneg").count(), 2, "{negated}");
8354
8355        // `~` on a complex operand is the same operator, which is the spelling the language has
8356        // had all along and the one a program that never included the header writes.
8357        let written = body("_Complex double f(_Complex double z) { return ~z; }\n");
8358        assert_eq!(written, text, "the name and the operator are the same thing");
8359
8360        // The plain names, which are the ones the header declares and so the ones programs write.
8361        let text = body(concat!(
8362            "double creal(_Complex double z);\n",
8363            "double f(_Complex double z) { return creal(z); }\n",
8364        ));
8365        assert!(!text.contains("call"), "{text}");
8366        let text = body(concat!(
8367            "_Complex float conjf(_Complex float z);\n",
8368            "_Complex float f(_Complex float z) { return conjf(z); }\n",
8369        ));
8370        assert_eq!(text.matches("fneg").count(), 1, "{text}");
8371        assert!(!text.contains("call"), "{text}");
8372
8373        // A program that took the name means its own function, the same four ways the absolute
8374        // value family next door asks it.
8375        let taken = concat!(
8376            "static double creal(_Complex double z) { return 7; }\n",
8377            "double f(_Complex double z) { return creal(z); }\n",
8378        );
8379        assert!(ir(taken).contains("call @creal"), "a static definition is the program's own");
8380        let retyped = concat!("int cimag(int z);\n", "int f(int z) { return cimag(z); }\n");
8381        assert!(ir(retyped).contains("call @cimag"), "another type is another function");
8382        let plain = concat!(
8383            "double cimag(_Complex double z);\n",
8384            "double f(_Complex double z) { return cimag(z); }\n",
8385        );
8386        let mut opts = options();
8387        opts.emit = EmitKind::Ir;
8388        opts.builtins = false;
8389        assert!(run(&opts, plain).text().contains("call @cimag"), "-fno-builtin");
8390        opts.builtins = true;
8391        opts.no_builtin = vec!["cimag".to_owned()];
8392        assert!(run(&opts, plain).text().contains("call @cimag"), "-fno-builtin-cimag");
8393
8394        // A constant folds, which is what a static initializer written with one needs.
8395        let text = ir(concat!(
8396            "double a = __builtin_creal(1.5 + 2.5i);\n",
8397            "double b = __builtin_cimag(1.5 + 2.5i);\n",
8398            "_Complex double c = __builtin_conj(1.5 + 2.5i);\n",
8399        ));
8400        assert!(text.contains("global @a : f64 = 0x3ff8000000000000,"), "{text}");
8401        assert!(text.contains("global @b : f64 = 0x4004000000000000,"), "{text}");
8402        assert!(
8403            text.contains("{ f64 0x3ff8000000000000, f64 0xc004000000000000 }"),
8404            "the conjugate of a constant is the constant with the second half negated: {text}"
8405        );
8406        assert!(!text.contains("call"), "{text}");
8407    }
8408
8409    /// A math library builtin handed a constant is the answer, and is not a call.
8410    ///
8411    /// This is the reason the family is answered in the front end at all. `double x =
8412    /// __builtin_ceil(1.5);` at file scope initializes an object with static storage duration, so
8413    /// there is no point in the program at which a call could be made, and a compiler that lowered
8414    /// it to one would refuse a program gcc accepts. Every number here is the encoding gcc 16.2.0
8415    /// gives on x86-64, read out of the object file one initializer at a time.
8416    #[test]
8417    fn a_math_library_builtin_of_a_constant_is_the_answer_and_not_a_call() {
8418        let text = ir(concat!(
8419            "double a = __builtin_ceil(1.5);\n",
8420            "double b = __builtin_floor(1.5);\n",
8421            "double c = __builtin_trunc(-1.5);\n",
8422            // A half goes away from zero and not to even, which is where C and the default
8423            // rounding of IEEE 754 part company.
8424            "double d = __builtin_round(2.5);\n",
8425            // The sign survives a number that rounds away to nothing, so this is a negative zero.
8426            "double e = __builtin_ceil(-0.5);\n",
8427            "double f = __builtin_fmax(1.0, 2.0);\n",
8428            "double g = __builtin_fmin(1.0, 2.0);\n",
8429            "float h = __builtin_ceilf(1.25f);\n",
8430            // The plain name is the same answer, which is what a program that included `math.h`
8431            // and never wrote a prefix reaches.
8432            "double ceil(double x);\n",
8433            "double i = ceil(2.25);\n",
8434        ));
8435        assert!(text.contains("global @a : f64 = 0x4000000000000000,"), "{text}");
8436        assert!(text.contains("global @b : f64 = 0x3ff0000000000000,"), "{text}");
8437        assert!(text.contains("global @c : f64 = 0xbff0000000000000,"), "{text}");
8438        assert!(text.contains("global @d : f64 = 0x4008000000000000,"), "{text}");
8439        assert!(text.contains("global @e : f64 = 0x8000000000000000,"), "{text}");
8440        assert!(text.contains("global @f : f64 = 0x4000000000000000,"), "{text}");
8441        assert!(text.contains("global @g : f64 = 0x3ff0000000000000,"), "{text}");
8442        assert!(text.contains("global @h : f32 = 0x40000000,"), "{text}");
8443        assert!(text.contains("global @i : f64 = 0x4008000000000000,"), "{text}");
8444        assert!(!text.contains("call"), "{text}");
8445    }
8446
8447    /// A math library builtin handed anything else is a call to the library function it is.
8448    ///
8449    /// gcc emits `jmp ceil` for `__builtin_ceil` on x86-64 at the default architecture, measured
8450    /// on gcc 16.2.0, and reaches the `roundsd` instruction only under `-msse4.1`. So the call is
8451    /// what a program gets from gcc too, and the name on it is the plain one, which is the whole
8452    /// point of the prefixed spelling: a program writing it reaches the library's function even
8453    /// where a macro or a definition of its own has taken the short name.
8454    #[test]
8455    fn a_math_library_builtin_of_anything_else_is_a_call_to_the_library() {
8456        let text = ir(concat!(
8457            "double f(double x) { return __builtin_ceil(x); }\n",
8458            "float g(float x) { return __builtin_floorf(x); }\n",
8459            "double h(double x, double y) { return __builtin_fmax(x, y); }\n",
8460        ));
8461        assert!(text.contains("call @ceil("), "{text}");
8462        assert!(text.contains("call @floorf("), "{text}");
8463        assert!(text.contains("call @fmax("), "{text}");
8464
8465        // The two the rounding mode decides are calls even when the argument is a constant, since
8466        // what they answer is not known until the program runs. gcc refuses a static initializer
8467        // written with one for that reason, so there is nothing to fold here either.
8468        let text = ir(concat!(
8469            "double f(void) { return __builtin_rint(2.5); }\n",
8470            "double g(void) { return __builtin_nearbyint(2.5); }\n",
8471        ));
8472        assert!(text.contains("call @rint("), "{text}");
8473        assert!(text.contains("call @nearbyint("), "{text}");
8474
8475        // A nan operand is the library's rule rather than the machine's, 7.12.12.2 saying the
8476        // answer is the other operand, and gcc will not fold that one either.
8477        let text = ir("double f(void) { return __builtin_fmin(__builtin_nan(\"\"), 1.0); }\n");
8478        assert!(text.contains("call @fmin("), "{text}");
8479
8480        // `-fno-builtin-ceil` is a program saying it means its own `ceil`, and it leaves the
8481        // prefixed spelling alone, which is what writing the prefix is for.
8482        let plain = concat!("double ceil(double x);\n", "double f(void) { return ceil(2.25); }\n");
8483        let mut opts = options();
8484        opts.emit = EmitKind::Ir;
8485        opts.no_builtin = vec!["ceil".to_owned()];
8486        assert!(run(&opts, plain).text().contains("call @ceil("), "-fno-builtin-ceil");
8487    }
8488
8489    /// A `constexpr` object is a named constant, which is the whole reason the keyword exists.
8490    ///
8491    /// C23 6.6p8 puts two of them on the list an integer constant expression is built from: one
8492    /// of an arithmetic type, and a member of one of a structure or union type. A subscript of
8493    /// one is not on the list and is a variably modified type in gcc 16 as well, and every
8494    /// number here is what gcc 16 gives on x86-64.
8495    #[test]
8496    fn a_constexpr_object_is_a_constant_wherever_one_is_required() {
8497        let text = ir(concat!(
8498            "constexpr int side = 4;\n",
8499            "constexpr int wider = side + 1;\n",
8500            "constexpr double half = 1.5;\n",
8501            "struct point { int x; int y; };\n",
8502            "constexpr struct point origin = { 5, 6 };\n",
8503            "int square[side * side];\n",
8504            "int rectangle[wider];\n",
8505            "int rounded[(int)half * 2];\n",
8506            "int across[origin.y];\n",
8507            "enum named { four = side };\n",
8508            "int e = four;\n",
8509        ));
8510        assert!(text.contains("global @square : bytes 64 ="), "{text}");
8511        assert!(text.contains("global @rectangle : bytes 20 ="), "{text}");
8512        assert!(text.contains("global @rounded : bytes 8 ="), "{text}");
8513        assert!(text.contains("global @across : bytes 24 ="), "{text}");
8514        assert!(text.contains("global @e : i32 = 4,"), "{text}");
8515
8516        // A `const` object is not one of them, which is what makes `int a[n];` a variable
8517        // length array in C and is the distinction the keyword was added to draw.
8518        let mut opts = options();
8519        opts.emit = EmitKind::Ir;
8520        let konst = "const int n = 1;\nint a[n];\n";
8521        let message = "/main.c:2:5: error: variably modified 'a' at file scope [E0538]";
8522        assert_eq!(run(&opts, konst).messages, [message]);
8523
8524        // Nor is a subscript of one, which gcc 16 refuses in the same words.
8525        let subscript = "constexpr int t[3] = { 1, 2, 3 };\nint a[t[1]];\n";
8526        assert_eq!(run(&opts, subscript).messages, [message]);
8527
8528        // And `constexpr` implies `const`, so the address of one is an address of a `const`.
8529        let address = "constexpr int c = 3;\nint *p = &c;\n";
8530        let warning = "/main.c:2:6: warning: initialization discards 'const' qualifier from \
8531             pointer target type [E0514]";
8532        assert_eq!(run(&opts, address).messages, [warning]);
8533    }
8534
8535    /// A member whose size was refused is not a flexible array member, whatever it looks like.
8536    ///
8537    /// The refusal leaves the member with no size, which is also how `int a[]` is written, so
8538    /// without the count that tells the two apart the rules about where a flexible array member
8539    /// may sit read the wreckage of the first error as a second mistake. gcc 16.2.0 says one
8540    /// thing about each of these and so does this, which is what the program can act on: adding
8541    /// a named member to `struct D` makes the message about `k` no clearer, and moving `a` to
8542    /// the end of `struct E` does not either.
8543    #[test]
8544    fn a_member_whose_size_was_refused_is_not_a_flexible_array_member() {
8545        let mut opts = options();
8546        opts.emit = EmitKind::Ir;
8547
8548        let alone = "int k;\nextern struct D { int a[k]; } ed;\n";
8549        let message = "/main.c:2:23: error: variably modified 'a' at file scope [E0538]";
8550        assert_eq!(run(&opts, alone).messages, [message]);
8551
8552        // And not one in the wrong place either, which is the other half of the same rule.
8553        let first = "int k;\nextern struct E { int a[k]; int b; } ee;\n";
8554        assert_eq!(run(&opts, first).messages, [message]);
8555
8556        // A size that is refused for a reason of its own, to show the count is about the
8557        // refusal rather than about the one message that happens to have been found first.
8558        let negative = "struct F { int a[-1]; };\n";
8559        let refused = "/main.c:1:18: error: size of array 'a' is negative [E0536]";
8560        assert_eq!(run(&opts, negative).messages, [refused]);
8561
8562        // The member that was written with no size at all is still a flexible array member, and
8563        // a structure with nothing else in it still has no named member to hang one off.
8564        let flexible = "struct G { int a[]; };\n";
8565        let named = "/main.c:1:16: error: flexible array member in a struct with no named \
8566             members [E0554]";
8567        assert_eq!(run(&opts, flexible).messages, [named]);
8568    }
8569
8570    /// A pointer to an array, where the qualifiers are on the element and the comparison is not.
8571    ///
8572    /// 6.7.3p10 says the qualifiers in an array declaration belong to the element, so `const int
8573    /// [4]` is an unqualified array of `const int` and not a qualified array of `int`. Compatibility
8574    /// then reads the element types, finds one `const` and one not, and calls the two arrays
8575    /// incompatible, which makes `const int (*)[4] = p` an incompatible pointer rather than a
8576    /// pointer that gained a qualifier. That is what the wording said before C23 and it is not what
8577    /// any compiler does: gcc and clang take it, C23 wrote the rule the way they read it, and the
8578    /// two directions are told apart the way they are everywhere else, which is that adding a
8579    /// qualifier is silent and dropping one is worth a word.
8580    ///
8581    /// Found in libwebp, where `src/enc/vp8l_enc.c` takes the address of a `HistogramBuckets` out of
8582    /// a structure into a `const HistogramBuckets *const`, and a whole file of a real library did
8583    /// not compile for it.
8584    #[test]
8585    fn a_pointer_to_an_array_gains_a_qualifier_the_same_way_a_pointer_to_anything_else_does() {
8586        let mut opts = options();
8587        opts.emit = EmitKind::Ir;
8588        let prefix = "typedef unsigned int B[4];\nstruct H { B category[2]; };\n";
8589
8590        // Adding it, which is the direction the library writes and the one nothing is owed for.
8591        let adding = format!("{prefix}const B *f(struct H *h) {{ return &h->category[0]; }}\n");
8592        assert_eq!(run(&opts, &adding).messages, [] as [String; 0]);
8593
8594        // And the same thing written out rather than through the typedef, since the typedef is a
8595        // spelling and the rule is about the array.
8596        let plain = concat!(
8597            "const unsigned int (*f(unsigned int (*p)[4]))[4] { return p; }\n",
8598            "const unsigned int (*g(unsigned int (*p)[2][3]))[2][3] { return p; }\n",
8599        );
8600        assert_eq!(run(&opts, plain).messages, [] as [String; 0]);
8601
8602        // Dropping it, which is the direction that is worth a word, and the word is the one every
8603        // other pointer target gets rather than a complaint about the types not matching.
8604        let dropping = format!("{prefix}B *f(const B *p) {{ return p; }}\n");
8605        let warning = "/main.c:3:27: warning: return discards 'const' qualifier from pointer target type \
8606             [E0514]";
8607        assert_eq!(run(&opts, &dropping).messages, [warning]);
8608
8609        // A pointer to an array of something else is still an incompatible pointer, because
8610        // nothing here is about the element being a different type.
8611        let wrong = "const unsigned int (*f(unsigned short (*p)[4]))[4] { return p; }\n";
8612        let error = "/main.c:1:61: error: returning 'unsigned short (*)[4]' from a function with \
8613             incompatible return type 'const unsigned int (*)[4]' [E0512]";
8614        assert_eq!(run(&opts, wrong).messages, [error]);
8615    }
8616
8617    /// A definition that names its parameters and then declares them under the list.
8618    ///
8619    /// The declarations say what the types are, 6.9.1p6, and what the function takes is those
8620    /// types with the default argument promotions over them, which is what a caller of an
8621    /// unprototyped function hands over. A prototype already in scope overrules the promoted
8622    /// types, since a header saying `int narrow(char);` over a definition written this way is
8623    /// the pairing all the code written this way relies on and 6.7.6.3p15 is read that way by
8624    /// every compiler.
8625    #[test]
8626    fn an_old_style_definition_takes_its_types_from_the_declarations_under_its_list() {
8627        // C17, since the default dialect is the one that warns about the form and this is
8628        // about what it means rather than about the warning.
8629        let mut opts = options();
8630        opts.std = Std::C17;
8631        let source = concat!(
8632            "int add(a, b)\n",
8633            "int a;\n",
8634            "int b;\n",
8635            "{ return a + b; }\n",
8636            "int promoted(c)\n",
8637            "char c;\n",
8638            "{ return c; }\n",
8639            "int narrow(char);\n",
8640            "int narrow(c)\n",
8641            "char c;\n",
8642            "{ return c; }\n",
8643            "int first(a)\n",
8644            "int a[4];\n",
8645            "{ return a[0]; }\n",
8646        );
8647        let result = run(&opts, source);
8648        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
8649        let text = result.text();
8650        assert!(text.contains("add : int(int, int) function external defined"), "{text}");
8651        assert!(text.contains("promoted : int(int) function external defined"), "{text}");
8652        // The body still sees the `char` it was declared as, whatever the caller hands over.
8653        assert!(text.contains("c : char object automatic defined"), "{text}");
8654        assert!(text.contains("narrow : int(char) function external defined"), "{text}");
8655        // An array parameter is a pointer here as much as it is in a prototype.
8656        assert!(text.contains("first : int(int *) function external defined"), "{text}");
8657    }
8658
8659    /// What the two halves of an old-style parameter list can disagree about.
8660    ///
8661    /// Each of these is a sentence gcc 16 has, and every message below is the one it prints,
8662    /// read off it on x86-64 rather than reasoned about. The last two are the dialect: a name
8663    /// with no declaration is an `int` in C89 and a diagnostic from C99 on, and the whole form
8664    /// left the language in C23, where gcc still takes it and warns.
8665    #[test]
8666    fn the_two_halves_of_an_old_style_parameter_list_have_to_agree() {
8667        let mut opts = options();
8668        opts.std = Std::C17;
8669        for (source, message) in [
8670            ("int f(a, a)\nint a;\n{ return a; }\n", "1:10: error: multiple parameters named 'a'"),
8671            (
8672                "int f(a)\nint a;\nint b;\n{ return a; }\n",
8673                "3:5: error: declaration for parameter 'b' but no such parameter",
8674            ),
8675            ("int f(a)\nint a;\nint a;\n{ return a; }\n", "3:5: error: redefinition of parameter"),
8676            ("int f(a)\nint a = 1;\n{ return a; }\n", "2:5: error: parameter 'a' is initialized"),
8677            (
8678                "int f(a)\nstatic int a;\n{ return a; }\n",
8679                "2:12: error: storage class specified for parameter 'a'",
8680            ),
8681            (
8682                "int f(char);\nint f(a)\nshort a;\n{ return a; }\n",
8683                "2:7: error: argument 'a' doesn't match prototype",
8684            ),
8685        ] {
8686            let result = run(&opts, source);
8687            assert!(result.failed(), "expected this to fail:\n{source}");
8688            assert!(result.messages[0].contains(message), "{:?}", result.messages);
8689        }
8690
8691        // A name the declarations never mention. C89 gave it an `int` and gcc still takes it
8692        // in that dialect, and every dialect after it made the same line a diagnostic.
8693        let implicit = "int f(a, b)\nint a;\n{ return a + b; }\n";
8694        let mut older = options();
8695        older.std = Std::C89;
8696        assert!(!run(&older, implicit).failed(), "{:?}", run(&older, implicit).messages);
8697        let result = run(&opts, implicit);
8698        assert!(
8699            result.messages[0].contains("1:10: error: type of 'b' defaults to 'int'"),
8700            "{:?}",
8701            result.messages
8702        );
8703
8704        // C23 took the form out of the language and gcc kept accepting it with a warning, and
8705        // a warning is what this is, because the code written this way is not going to be
8706        // rewritten and refusing it would put the compiler out of reach of it.
8707        let mut newer = options();
8708        newer.std = Std::C23;
8709        let plain = "int f(a)\nint a;\n{ return a; }\n";
8710        let result = run(&newer, plain);
8711        assert!(!result.failed(), "{:?}", result.messages);
8712        assert_eq!(
8713            result.messages,
8714            ["/main.c:1:5: warning: old-style function definition [E0412]"]
8715        );
8716        assert!(run(&opts, plain).messages.is_empty(), "and nothing to say in the dialects before");
8717    }
8718
8719    /// The two obsolete designators, which are silent until `-pedantic` asks about them.
8720    ///
8721    /// `[3] 7` is what GCC had for an array before C99 settled on `[3] = 7`, and `x: 7` is the
8722    /// same era's spelling for a member. Both are still in code written against a compiler of
8723    /// that era, and gcc 16 takes both without a word unless it is asked to be pedantic, which
8724    /// is where the columns below come from as well.
8725    #[test]
8726    fn the_obsolete_designators_are_taken_and_are_pedantic_warnings() {
8727        let array = "int a[8] = { [3] 7 };\n";
8728        let member = "struct s { int x; } v = { x: 7 };\n";
8729        for source in [array, member] {
8730            let result = run(&options(), source);
8731            assert!(!result.failed(), "{:?}", result.messages);
8732            assert!(result.messages.is_empty(), "nothing to say: {:?}", result.messages);
8733        }
8734
8735        let mut asked = options();
8736        asked.pedantic = true;
8737        assert_eq!(
8738            run(&asked, array).messages,
8739            ["/main.c:1:18: warning: obsolete designator, write `[i] =` instead [E0415]"]
8740        );
8741        assert_eq!(
8742            run(&asked, member).messages,
8743            ["/main.c:1:27: warning: obsolete designator, write `.field =` instead [E0413]"]
8744        );
8745    }
8746
8747    /// A type nothing is ever an object of is a type `sizeof` still has to answer about, which
8748    /// is what `991014-1.c` in the gcc.c-torture execution suite asks.
8749    ///
8750    /// The limit is `PTRDIFF_MAX` and it is the same one for an array and for a record, so a
8751    /// record of every byte an object may have is laid out and one byte more is refused. All
8752    /// four numbers are what gcc 16 gives on x86-64.
8753    #[test]
8754    fn a_type_is_refused_when_it_passes_the_largest_object_and_not_before() {
8755        let text = ir(concat!(
8756            "struct huge_struct { short buf[(1L << 62) - 256]; int a, b, c, d; };\n",
8757            "struct brim { char buf[9223372036854775807L]; };\n",
8758            "struct bitty { char buf[9223372036854775800L]; int x : 1; };\n",
8759            "unsigned long h = sizeof(struct huge_struct);\n",
8760            "unsigned long b = sizeof(struct brim);\n",
8761            "unsigned long y = sizeof(struct bitty);\n",
8762        ));
8763        assert!(text.contains("global @h : i64 = 9223372036854775312,"), "{text}");
8764        assert!(text.contains("global @b : i64 = 9223372036854775807,"), "{text}");
8765        assert!(text.contains("global @y : i64 = 9223372036854775804,"), "{text}");
8766
8767        let mut opts = options();
8768        opts.emit = EmitKind::Ir;
8769        let over = "struct over { char buf[9223372036854775800L]; char x[8]; };\n";
8770        let message = "/main.c:1:1: error: type 'struct over' is too large [E0560]";
8771        assert_eq!(run(&opts, over).messages, [message]);
8772        let array = "struct wide { short buf[1L << 62]; };\n";
8773        let message = "/main.c:1:25: error: size of array 'buf' exceeds \
8774             maximum object size '9223372036854775807' [E0537]";
8775        assert_eq!(run(&opts, array).messages[0], message);
8776    }
8777
8778    /// A byte in the source that is not part of a character, which only a literal may hold.
8779    ///
8780    /// The source cannot be a `&str` here, which is the whole point: a file is bytes and only
8781    /// mostly text.
8782    fn compile_bytes(source: &[u8]) -> Compiled {
8783        let mut opts = options();
8784        opts.emit = EmitKind::Ir;
8785        let mut fs = MemoryFileSystem::new();
8786        fs.insert("/main.c", source.to_vec());
8787        compile(&opts, "/main.c", &fs)
8788    }
8789
8790    /// A raw byte inside a string literal is that byte, which gcc has always taken and which is
8791    /// the only place in a source file where a byte does not have to be part of a character.
8792    /// Replacing it would give the object three bytes rather than one, since the replacement
8793    /// character is three bytes of UTF-8, so the object would not be the one that was written
8794    /// even where the diagnostic is ignored. Anywhere else the byte is still a mistake, which
8795    /// is where gcc draws the same line.
8796    #[test]
8797    fn a_byte_that_is_not_a_character_is_kept_in_a_literal_and_refused_outside_one() {
8798        let mut source = b"char s[] = \"a".to_vec();
8799        source.push(0xff);
8800        source.extend_from_slice(b"b\";\nchar c = '");
8801        source.push(0xff);
8802        source.extend_from_slice(b"';\n");
8803        let result = compile_bytes(&source);
8804        assert_eq!(result.messages, Vec::<String>::new(), "a raw byte in a literal is that byte");
8805        assert!(result.text().contains(r#"bytes "a\ffb\00""#), "{}", result.text());
8806        // Plain `char` is signed on this target, so the constant is minus one rather than 255.
8807        assert!(result.text().contains("global @c : i8 = -1,"), "{}", result.text());
8808
8809        let mut stray = b"int a".to_vec();
8810        stray.push(0xff);
8811        stray.extend_from_slice(b" = 1;\n");
8812        let result = compile_bytes(&stray);
8813        assert!(
8814            result.messages.iter().any(|m| m.contains("source is not valid UTF-8 here")),
8815            "{:?}",
8816            result.messages
8817        );
8818    }
8819
8820    #[test]
8821    fn an_object_becomes_a_global_with_an_image_and_a_function_becomes_a_func() {
8822        let text = ir("int x = 7;\nint add(int a, int b) { return a + b; }\n");
8823        assert!(text.contains("global @x : i32 = 7, align 4, linkage(external)\n"), "{text}");
8824        let expected = "\
8825func @add(i32, i32) -> i32, linkage(external) {
8826block0(%0: i32, %1: i32):
8827    %2 = add.nsw %0, %1
8828    return %2
8829}
8830";
8831        assert!(text.contains(expected), "{text}");
8832    }
8833
8834    #[test]
8835    fn a_local_nothing_takes_the_address_of_is_a_value_and_never_a_stack_slot() {
8836        let text = body("int f(int n) { int a = n + 1; int b = a * 2; return a + b; }\n");
8837        assert!(!text.contains("alloca"), "{text}");
8838        assert!(!text.contains("load"), "{text}");
8839        assert!(!text.contains("store"), "{text}");
8840    }
8841
8842    #[test]
8843    fn a_local_whose_address_is_taken_gets_a_slot_in_the_entry_block() {
8844        let text = body("int g(int *);\nint f(void) { int a = 1; return g(&a); }\n");
8845        let expected = "\
8846block0:
8847    %0 = alloca, size 4, align 4
8848    %1 = iconst.i32 1
8849    store %1 -> %0, align 4, tbaa !1
8850    %2 = call @g(%0) : (ptr) -> i32
8851    return %2
8852";
8853        assert_eq!(text, expected);
8854    }
8855
8856    #[test]
8857    fn a_loop_carries_what_it_changes_as_block_parameters() {
8858        // The whole point of building SSA during the walk rather than after it: `i` and
8859        // `total` are values that arrive on an edge, and neither has ever been in memory.
8860        let text = body(
8861            "int f(int n) {\n  int total = 0;\n  for (int i = 0; i < n; i++) total += i;\n  \
8862             return total;\n}\n",
8863        );
8864        assert!(!text.contains("alloca"), "{text}");
8865        assert!(text.contains("block1(%3: i32, %4: i32):"), "{text}");
8866        assert!(text.contains("jump block1("), "{text}");
8867    }
8868
8869    #[test]
8870    fn a_comparison_used_as_a_condition_is_not_widened_and_narrowed_again() {
8871        let text = body("int f(int a, int b) { if (a < b) return 1; return 0; }\n");
8872        assert!(text.contains("icmp slt %0, %1"), "{text}");
8873        assert!(!text.contains("zext"), "{text}");
8874    }
8875
8876    #[test]
8877    fn the_right_side_of_a_short_circuit_is_in_a_block_of_its_own() {
8878        let text = body("int f(int a, int b) { return a && b; }\n");
8879        let expected = "\
8880block0(%0: i32, %1: i32):
8881    %2 = iconst.i32 0
8882    %3 = icmp ne %0, %2
8883    %4 = iconst.i1 0
8884    br_if %3, block1, block2(%4)
8885
8886block1:
8887    %5 = iconst.i32 0
8888    %6 = icmp ne %1, %5
8889    jump block2(%6)
8890
8891block2(%7: i1):
8892    %8 = zext.i32 %7
8893    return %8
8894";
8895        assert_eq!(text, expected);
8896    }
8897
8898    #[test]
8899    fn code_after_a_return_is_not_built_and_does_not_leave_an_empty_block_behind() {
8900        let text = body("int f(int a) { if (a) return 1; else return 2; return 3; }\n");
8901        // Three blocks, the test and the two arms. The join the `return 3` would need is
8902        // never created, because a block nothing branches to is not a block.
8903        assert!(!text.contains("block3"), "{text}");
8904        assert!(!text.contains("iconst.i32 3"), "{text}");
8905    }
8906
8907    #[test]
8908    fn falling_off_the_end_returns_zero_from_main_and_nothing_from_a_void_function() {
8909        assert!(body("int main(void) { }\n").contains("iconst.i32 0\n    return"));
8910        assert_eq!(body("void f(void) { }\n"), "block0:\n    return\n");
8911        assert!(body("int f(void) { }\n").contains("unreachable"));
8912    }
8913
8914    #[test]
8915    fn a_structure_is_copied_rather_than_held_in_a_value() {
8916        let text = body(
8917            "struct point { int x, y; };\n\
8918             int f(void) { struct point p = { 1, 2 }; struct point q = p; return q.x; }\n",
8919        );
8920        assert!(text.contains("memcpy"), "{text}");
8921    }
8922
8923    #[test]
8924    fn an_initializer_that_leaves_part_of_an_object_unwritten_zeroes_it_first() {
8925        let text = body("int f(void) { int a[4] = { 1 }; return a[3]; }\n");
8926        assert!(text.contains("memset"), "{text}");
8927    }
8928
8929    #[test]
8930    fn a_switch_is_one_branch_and_a_case_that_falls_through_carries_what_it_wrote() {
8931        let text = body(
8932            "int f(int x) { int r = 0; switch (x) { case 1: r = 1; case 2: r += 2; break; \
8933             default: r = 4; } return r; }\n",
8934        );
8935        let expected = "\
8936block0(%0: i32):
8937    %1 = iconst.i32 0
8938    switch %0, block1, [1 => block2, 2 => block3(%1)]
8939
8940block1:
8941    %2 = iconst.i32 4
8942    jump block4(%2)
8943
8944block2:
8945    %3 = iconst.i32 1
8946    jump block3(%3)
8947
8948block3(%4: i32):
8949    %5 = iconst.i32 2
8950    %6 = add.nsw %4, %5
8951    jump block4(%6)
8952
8953block4(%7: i32):
8954    return %7
8955";
8956        assert_eq!(text, expected);
8957    }
8958
8959    #[test]
8960    fn a_case_range_is_tested_for_rather_than_put_in_the_table() {
8961        // GNU's `case 1 ... 9`. Nine table entries would be nine here and four billion for the
8962        // range a program is allowed to write, so it is a subtraction and one unsigned compare.
8963        let text = body("int f(int x) { switch (x) { case 1 ... 9: return 1; } return 0; }\n");
8964        assert!(text.contains("%2 = sub %0, %1"), "{text}");
8965        assert!(text.contains("icmp ule"), "{text}");
8966        assert!(!text.contains("switch"), "{text}");
8967    }
8968
8969    #[test]
8970    fn break_leaves_the_switch_and_continue_leaves_the_loop_around_it() {
8971        let text = body(
8972            "int f(int n) { int t = 0; for (int i = 0; i < n; i++) { switch (i) { \
8973             case 0: continue; case 1: break; default: t += i; } t++; } return t; }\n",
8974        );
8975        // The `continue` goes to the step and the `break` goes to the `t++` after the switch,
8976        // which is also where the default falls out to.
8977        assert!(text.contains("switch %3, block4, [0 => block5, 1 => block6]"), "{text}");
8978        assert!(text.contains("block5:\n    jump block7("), "{text}");
8979        assert!(text.contains("block6:\n    jump block8("), "{text}");
8980    }
8981
8982    #[test]
8983    fn a_switch_with_nothing_to_branch_on_still_runs_what_comes_after_it() {
8984        assert_eq!(body("void f(int x) { switch (x) { } }\n"), "block0(%0: i32):\n    return\n");
8985    }
8986
8987    #[test]
8988    fn a_label_a_loop_is_only_entered_through_builds_the_loop_around_it() {
8989        // A branch into the middle of a loop that nothing else reaches, the Duff's device shape.
8990        // The `while` is not reached in order, so the walk starts a block nothing branches to and
8991        // builds it from there. What comes out is the loop with an edge straight into its body,
8992        // and the header that nothing arrives at is pruned.
8993        let text = body(
8994            "int f(int x, int n) { switch (x) { case 1: break; while (n) { case 2: n--; } } \
8995             return n; }\n",
8996        );
8997        // `case 2` lands on the body, `case 1` and the default land on the return, and the test
8998        // at the bottom of the loop comes back round to the body.
8999        assert!(text.contains("switch %0, block1(%1), [1 => block2, 2 => block3(%1)]"), "{text}");
9000        assert!(text.contains("block3(%3: i32):\n    %4 = iconst.i32 1"), "{text}");
9001        assert!(text.contains("block4:\n    jump block3("), "{text}");
9002    }
9003
9004    #[test]
9005    fn a_goto_into_a_loop_body_enters_it_without_the_test() {
9006        // The same thing through a `goto`. The first pass through the body runs whatever the
9007        // label is on, and only then does the loop reach its own test.
9008        let text = body("int f(int x, int n) { goto in; while (n) { in: n--; } return n; }\n");
9009        assert!(text.starts_with("block0(%0: i32, %1: i32):\n    jump block1(%1)"), "{text}");
9010        assert!(text.contains("block1(%2: i32):\n    %3 = iconst.i32 1"), "{text}");
9011        assert!(text.contains("br_if %6, block2, block3"), "{text}");
9012    }
9013
9014    #[test]
9015    fn a_goto_is_a_jump_to_the_block_the_label_starts() {
9016        let text = body("int f(int x) { int r = 0; if (x) goto out; r = 1; out: return r; }\n");
9017        // Both edges into `out` carry what `r` holds on the way, and neither is a stack slot. The
9018        // block the `goto` jumps out of is empty and hands its edge on, which is what moves `out`
9019        // up the block list to second place.
9020        assert!(!text.contains("alloca"), "{text}");
9021        assert!(text.contains("block2(%4: i32):\n    return %4"), "{text}");
9022        assert_eq!(text.matches("jump block2(").count(), 2, "{text}");
9023    }
9024
9025    #[test]
9026    fn a_backward_goto_is_a_loop_and_carries_what_it_changes() {
9027        let text =
9028            body("int f(int n) { int i = 0; again: if (i < n) { i++; goto again; } return i; }\n");
9029        assert!(!text.contains("alloca"), "{text}");
9030        assert!(text.contains("block1(%2: i32):"), "{text}");
9031        assert!(text.contains("jump block1(%5)"), "{text}");
9032    }
9033
9034    #[test]
9035    fn a_label_nothing_reaches_is_taken_out_rather_than_left_for_the_verifier() {
9036        // A block nothing branches to is not a legal function, and which labels are dead is not
9037        // known until the last statement has been walked, since the `goto` is allowed to be it.
9038        assert_eq!(
9039            body("int f(int x) { return x; spare: return 0; }\n"),
9040            "block0(%0: i32):\n    return %0\n"
9041        );
9042    }
9043
9044    #[test]
9045    fn a_bit_field_is_read_by_loading_the_bytes_it_lies_in_and_shifting() {
9046        let text = body(
9047            "struct s { unsigned a : 3; signed b : 5; };\nint f(struct s *p) { return p->b; }\n",
9048        );
9049        // One byte holds both fields, and the signed one needs no mask: shifting it down
9050        // arithmetically is what says its top bit is a sign.
9051        assert_eq!(
9052            text,
9053            "\
9054block0(%0: ptr):
9055    %1 = load.i8 %0, align 1
9056    %2 = iconst.i8 3
9057    %3 = ashr %1, %2
9058    %4 = sext.i32 %3
9059    return %4
9060"
9061        );
9062    }
9063
9064    #[test]
9065    fn a_store_to_a_bit_field_does_not_write_a_byte_it_has_no_bit_in() {
9066        // C11 says an ordinary member beside a bit-field is a memory location of its own, so
9067        // the four byte store this would take is a data race in a program that has none. The
9068        // three bytes of `a` go in as two and one, and `c` is not touched.
9069        let text =
9070            body("struct s { int a : 24; char c; };\nvoid f(struct s *p, int v) { p->a = v; }\n");
9071        assert_eq!(
9072            text,
9073            "\
9074block0(%0: ptr, %1: i32):
9075    %2 = iconst.i32 16777215
9076    %3 = and %1, %2
9077    %4 = trunc.i16 %3
9078    store %4 -> %0, align 2
9079    %5 = iconst.i32 16
9080    %6 = lshr %3, %5
9081    %7 = trunc.i8 %6
9082    %8 = iconst.i64 2
9083    %9 = ptr_add %0, %8
9084    store %7 -> %9, align 1
9085    return
9086"
9087        );
9088    }
9089
9090    #[test]
9091    fn what_an_assignment_to_a_bit_field_is_worth_is_what_fits_in_it() {
9092        let text =
9093            body("struct s { unsigned b : 5; };\nunsigned f(struct s *p) { return p->b = 33; }\n");
9094        // 33 does not fit in five bits, and 1 is both what goes in the field and what the
9095        // assignment is worth.
9096        assert!(text.contains("%3 = iconst.i8 31\n    %4 = and %2, %3"), "{text}");
9097        assert!(text.ends_with("%9 = zext.i32 %4\n    return %9\n"), "{text}");
9098    }
9099
9100    #[test]
9101    fn an_assignment_a_statement_throws_away_builds_none_of_what_it_is_worth() {
9102        // The value of an assignment to a bit-field takes a shift to build, and a statement
9103        // has no use for it. Nothing here reads back what was stored.
9104        let text = body("struct s { signed b : 5; };\nvoid f(struct s *p) { p->b = 3; }\n");
9105        assert_eq!(text.matches("ashr").count(), 0, "{text}");
9106        assert!(text.ends_with("store %8 -> %0, align 1\n    return\n"), "{text}");
9107    }
9108
9109    #[test]
9110    fn a_bit_field_in_an_initializer_goes_in_over_bytes_that_were_zeroed_first() {
9111        // A bit-field writes part of a byte and leaves the rest of it alone, so the object has
9112        // to be zero before it goes in or what the initializer did not name is whatever the
9113        // stack held.
9114        let text = body(
9115            "struct s { int a : 3; int b; };\nint f(void) { struct s v = { 1 }; return v.b; }\n",
9116        );
9117        assert!(text.contains("memset %0, %1, size 8, align 4"), "{text}");
9118    }
9119
9120    #[test]
9121    fn the_image_of_a_static_bit_field_is_the_bytes_the_fields_share() {
9122        // Two fields in one byte are not two entries in the image, because an image is written
9123        // in bytes: they are the byte they are both in.
9124        let text = ir("struct s { unsigned a : 3; unsigned b : 5; } g = { 1, 2 };\n");
9125        assert!(
9126            text.contains("global @g : bytes 4 = { bytes \"\\11\", zero 3 }, align 4"),
9127            "{text}"
9128        );
9129    }
9130
9131    #[test]
9132    fn an_initialized_flexible_array_member_makes_the_object_larger_than_its_type() {
9133        // `sizeof` answers without the array and the definition has to hold what was written, so
9134        // the object is the size of its image. gcc 16 gives these four, three and two bytes and
9135        // so does this. The image used to be written at the size the type had, which left the
9136        // verifier looking at twenty bytes going into four.
9137        let text = ir(concat!(
9138            "struct a { int i; int j[]; } x = { 1, { 2, 0, 2, 3 } };\n",
9139            "struct b { char c; char p[]; } y = { 'o', \"wx\" };\n",
9140            "struct c { char c; char p[]; } z = { '9', { 'e', 'b' } };\n",
9141            "char s[2] = \"hi\";\n",
9142        ));
9143        assert!(
9144            text.contains("global @x : bytes 20 = { i32 1, i32 2, i32 0, i32 2, i32 3 }"),
9145            "{text}"
9146        );
9147        assert!(text.contains("global @y : bytes 4 = { i8 111, bytes \"wx\\00\" }"), "{text}");
9148        assert!(text.contains("global @z : bytes 3 = { i8 57, i8 101, i8 98 }"), "{text}");
9149        // The array with a length of its own still cuts the literal down to it, which is the
9150        // one case in C where a string initializer drops its terminator.
9151        assert!(text.contains("global @s : bytes 2 = { bytes \"hi\" }"), "{text}");
9152    }
9153
9154    #[test]
9155    fn a_definition_takes_a_parameter_it_left_unnamed() {
9156        // The entry block's parameters are the definition's, and one the front end dropped for
9157        // having no name left the two lists different lengths, which the walk read as an
9158        // old-style definition and refused. gcc has taken these for far longer than C23 has.
9159        let text = ir("int f(int a, int) { return a; }\n");
9160        assert!(text.contains("func @f(i32, i32) -> i32"), "{text}");
9161        assert!(text.contains("block0(%0: i32, %1: i32):"), "{text}");
9162
9163        // The unnamed one first, so that the named one is the second parameter of the entry
9164        // block and not the first: the list says the order and not only how many there are.
9165        let text = ir("int g(int, int n) { return n; }\n");
9166        assert!(text.contains("block0(%0: i32, %1: i32):\n    return %1\n"), "{text}");
9167    }
9168
9169    #[test]
9170    fn an_assignment_of_a_structure_is_the_object_it_wrote() {
9171        // `d = e = c` used to be refused, because the middle assignment is a value of structure
9172        // type and the walk had nowhere to read one from. What an assignment is worth is the
9173        // value it stored, so the object it stored into is the answer and the chain is three
9174        // copies out of the one source with no temporary in it.
9175        let text = body(concat!(
9176            "struct s { int f; int g; };\n",
9177            "void h(struct s *a, struct s *c, struct s *d, struct s *e)\n",
9178            "{ *d = *e = a[0] = *c; }\n",
9179        ));
9180        assert_eq!(text.matches("memcpy").count(), 3, "{text}");
9181        assert!(text.contains("memcpy %8, %1, size 8, align 4\n"), "{text}");
9182        assert!(text.contains("memcpy %3, %8, size 8, align 4\n"), "{text}");
9183        assert!(text.contains("memcpy %2, %3, size 8, align 4\n"), "{text}");
9184    }
9185
9186    #[test]
9187    fn a_string_literal_stops_at_the_end_of_the_array_it_is_filling() {
9188        // The excess used to be laid into the object anyway, so the row after was written over
9189        // and the image refused the entry that came to it. C 6.7.10p14 says the terminator goes
9190        // in only if there is room for it, and gcc discards the rest of a literal that is longer
9191        // still, which is what the first of these is and why it warns.
9192        let mut opts = options();
9193        opts.emit = EmitKind::Ir;
9194        let result = run(
9195            &opts,
9196            concat!(
9197                "const char a[2][3] = { \"1234\", \"xyz\" };\n",
9198                "static const char b[3][5] = { \"12345\", \"678\", \"9\" };\n",
9199                "union u { struct { char x[4]; char y[4]; }; struct { char z[8]; }; };\n",
9200                "const union u c = { { \"1234\", \"567\" } };\n",
9201            ),
9202        );
9203        let text = result.text();
9204        assert_eq!(
9205            result.messages,
9206            ["/main.c:1:24: warning: initializer-string for array of 'const char' is too long \
9207              (5 chars into 3 available) [E0637]"]
9208        );
9209        assert!(text.contains("global @a : bytes 6 = { bytes \"123\", bytes \"xyz\" }"), "{text}");
9210        assert!(
9211            text.contains(
9212                "global @b : bytes 15 = { bytes \"12345\", bytes \"678\\00\", zero 1, \
9213                 bytes \"9\\00\", zero 3 }"
9214            ),
9215            "{text}"
9216        );
9217        // The eight bytes are four, three and a terminator, and then the byte the shorter
9218        // literal left for the string in the other member of the union to end at.
9219        assert!(
9220            text.contains("global @c : bytes 8 = { bytes \"1234\", bytes \"567\\00\" }"),
9221            "{text}"
9222        );
9223    }
9224
9225    #[test]
9226    fn a_cast_of_a_record_to_its_own_type_is_the_object_that_was_cast() {
9227        // gcc accepts one and does nothing with it, which sema already had. Lowering asked for
9228        // the object under it and had no arm for a cast, so `(struct s)x` in an initializer was
9229        // refused with E0519. It is one copy out of the object named, not two.
9230        let text = body(concat!(
9231            "struct s { int a, b; };\nstruct v { struct s s; int t; };\n",
9232            "void g(struct v *);\n",
9233            "void f(struct s *p) { struct v w = { (struct s)*p, 5 }; g(&w); }\n",
9234        ));
9235        assert_eq!(text.matches("memcpy").count(), 1, "{text}");
9236    }
9237
9238    #[test]
9239    fn a_compound_literal_read_in_a_static_initializer_lays_its_bytes_into_the_image() {
9240        // C 6.7.11p4 says a compound literal at file scope has static storage duration, which
9241        // makes it a constant element, and tcc and c-testsuite both write one. Sema used to call
9242        // it a non constant because reading it is a node of its own and the read was what it
9243        // looked at, and lowering had no way to put an object where it wanted a number.
9244        let text = ir(concat!(
9245            "struct s { int x; };\n",
9246            "struct t { struct s s; int o; } a = { (struct s){ 2 }, 3 };\n",
9247            "int n = (int){ 7 };\n",
9248            "struct u { struct s p; struct s q; } b = { (struct s){ 1 }, (struct s){ } };\n",
9249        ));
9250        assert!(text.contains("global @a : bytes 8 = { i32 2, i32 3 }"), "{text}");
9251        assert!(text.contains("global @n : i32 = 7,"), "{text}");
9252        // The second literal names nothing, so what it puts in is the zeros of its own size and
9253        // not the tail of the object it went in, which would have been the same bytes by luck.
9254        assert!(text.contains("global @b : bytes 8 = { i32 1, zero 4 }"), "{text}");
9255    }
9256
9257    #[test]
9258    fn the_address_of_a_compound_literal_asks_for_the_object_it_points_at() {
9259        // Nothing declares a compound literal, so the reference is the only thing that can ask
9260        // for it to be emitted. The image named `.Lanon.0` and the module defined no such
9261        // symbol, which the link would have been the first to find out.
9262        let text = ir("struct s { int x; };\nstruct s *q = &(struct s){ 9 };\n");
9263        assert!(text.contains("global @.Lanon.0 : i32 = 9, align 4, linkage(internal)"), "{text}");
9264        assert!(text.contains("global @q : bytes 8 = { addr.8 @.Lanon.0 }"), "{text}");
9265    }
9266
9267    #[test]
9268    fn an_object_of_no_size_at_all_has_an_image_with_nothing_in_it() {
9269        // A zero length array, which gcc allows and real code uses as the tail of a structure.
9270        // The image is there and holds nothing, which is not the global that has no image at
9271        // all, and the IR reader used to stop on the empty one.
9272        let text = ir("unsigned char foo[1][0];\n");
9273        assert!(text.contains("global @foo : bytes 0 = {}, align 1"), "{text}");
9274    }
9275
9276    #[test]
9277    fn a_null_pointer_in_an_image_is_the_bits_an_address_has_room_for() {
9278        // `NULL` in a static initializer, which every program has. The IR type is `ptr` and a
9279        // `ptr` has no width of its own, so the width the bits are cut to is the target's.
9280        let text = ir("void *p = 0;\nchar *q = (char *) 4096;\n");
9281        assert!(text.contains("global @p : i64 = 0, align 8"), "{text}");
9282        assert!(text.contains("global @q : i64 = 4096, align 8"), "{text}");
9283    }
9284
9285    #[test]
9286    fn an_object_another_module_defines_may_be_one_that_cannot_be_written_through() {
9287        // Which the verifier used to refuse, having read a declaration as a definition with
9288        // nothing in it. `extern const` is how a program names something in the library's read
9289        // only data, and glibc and Darwin both have one in a header a real program includes.
9290        let text = ir("extern const int limit;\nint f(void) { return limit; }\n");
9291        assert!(
9292            text.contains("global @limit : bytes 4, align 4, linkage(external), constant"),
9293            "{text}"
9294        );
9295    }
9296
9297    #[test]
9298    fn a_conditional_whose_value_is_an_object_answers_where_the_object_is() {
9299        // A structure is not a value in the IR, so the two arms cannot be joined as one. The
9300        // addresses can, and the answer is the address of whichever arm was taken rather than
9301        // a copy of it into a third place: both arms outlive the expression, so a copy would
9302        // be one nothing could observe. SQLite's parser writes one of these.
9303        let text = body(
9304            "\
9305struct s { int a, b; };
9306struct s pick(int c, struct s x, struct s y) { return c ? x : y; }
9307",
9308        );
9309        // The join takes an address, each arm hands it the one it has, and nothing is copied.
9310        assert!(text.contains("block3(%7: ptr)"), "{text}");
9311        assert!(text.contains("jump block3(%3)") && text.contains("jump block3(%4)"), "{text}");
9312        assert!(!text.contains("memcpy"), "the arms are joined rather than copied: {text}");
9313    }
9314
9315    /// GNU's `a ?: b` evaluates `a` once, and the arm answers the value that was tested.
9316    ///
9317    /// The checking keeps one node for `a` and converts it in two directions, to the bit the
9318    /// branch is taken on and to the type the whole expression has. Walking into the arm used to
9319    /// reach that node a second time and build a second copy of whatever it says, so `++i ?: 10`
9320    /// incremented twice and `f() ?: 10` called twice. Measured against gcc 16.2.0, which
9321    /// increments once.
9322    #[test]
9323    fn the_left_side_of_a_conditional_with_no_middle_is_evaluated_once() {
9324        let text = body("int f(int i) { return ++i ?: 10; }\n");
9325        assert!(text.contains("jump block3(%2)"), "the arm is the value that was tested: {text}");
9326        assert_eq!(text.matches("add.nsw").count(), 1, "incremented once: {text}");
9327
9328        // The arm still converts, since what the whole expression is worth is a `long` here and
9329        // the node under it is an `int`. What it converts is the value in hand.
9330        let text = body("long f(int i) { return ++i ?: 10L; }\n");
9331        assert!(text.contains("%5 = sext.i64 %2"), "the arm widens what was tested: {text}");
9332        assert_eq!(text.matches("add.nsw").count(), 1, "incremented once: {text}");
9333
9334        // A call, which is where evaluating twice is a wrong answer rather than a slow one.
9335        let text = body("int g(void);\nint f(void) { return g() ?: 10; }\n");
9336        assert_eq!(text.matches("call @g").count(), 1, "called once: {text}");
9337
9338        // Written out in full it is two reads of `i`, which is what C says it is, so the middle
9339        // operand being absent is the whole of the difference.
9340        let text = body("int f(int i) { return ++i ? ++i : 10; }\n");
9341        assert_eq!(text.matches("add.nsw").count(), 2, "incremented twice: {text}");
9342    }
9343
9344    #[test]
9345    fn a_structure_that_fits_in_registers_travels_as_the_registers_it_fits_in() {
9346        // `struct pair` is two eightbytes on SysV, one of them integer, so the signature says
9347        // one `i64` in each direction and the body takes the object apart and puts it back
9348        // together around the call.
9349        let text = ir("\
9350struct pair { int a, b; };
9351struct pair make(int a, int b);
9352struct pair twice(struct pair p) { return make(p.a, p.b); }
9353");
9354        assert!(text.contains("func @make(i32, i32) -> i64"), "{text}");
9355        assert!(text.contains("func @twice(i64) -> i64"), "{text}");
9356    }
9357
9358    #[test]
9359    fn a_structure_too_large_for_the_registers_travels_as_where_its_bytes_are() {
9360        // Over two eightbytes the caller passes the bytes in the argument area, which is
9361        // `byval`, and passes somewhere to write the return value, which is `sret`. Neither is
9362        // a parameter the program wrote and both are parameters the function has.
9363        let text = ir("\
9364struct big { double v[8]; };
9365struct big grow(struct big b);
9366struct big twice(struct big b) { return grow(grow(b)); }
9367");
9368        assert!(
9369            text.contains("func @grow(ptr sret(64, align 8), ptr byval(64, align 8))"),
9370            "{text}"
9371        );
9372        assert!(text.contains("block0(%0: ptr, %1: ptr):"), "{text}");
9373        // The inner call writes into a slot and the outer one reads the same slot, so the
9374        // object between the two calls is never copied anywhere.
9375        assert_eq!(text.matches("call @grow").count(), 2, "{text}");
9376    }
9377
9378    #[test]
9379    fn a_structure_passed_to_a_variadic_function_says_so_at_the_call() {
9380        // The bytes travel in the argument area the same way they would for a parameter, and
9381        // `printf` has no parameter there to say it on, so the call says it instead. The one
9382        // that fits in registers says nothing, because travelling as the registers it fits in
9383        // is what an argument does when nothing says otherwise.
9384        let text = ir("\
9385struct big { double v[8]; };
9386struct pair { int a, b; };
9387int p(const char *, ...);
9388int f(struct big b, struct pair q) { return p(\"\", 1, b, q); }
9389");
9390        assert!(
9391            text.contains("call @p(%4, %5, %2 byval(64, align 8), %6) : (ptr, ...) -> i32"),
9392            "{text}"
9393        );
9394    }
9395
9396    #[test]
9397    fn what_a_call_produced_is_somewhere_before_anything_is_read_out_of_it() {
9398        // `make(1, 2).b` has no object to read a member of until one is made, and what makes it
9399        // is a slot the returned registers are written to.
9400        let body = body(
9401            "\
9402struct pair { int a, b; };
9403struct pair make(int a, int b);
9404int second(void) { return make(1, 2).b; }
9405",
9406        );
9407        assert!(body.starts_with("block0:\n    %0 = alloca, size 8, align 4\n"), "{body}");
9408        assert!(body.contains("store %3 -> %0, align 4\n"), "{body}");
9409    }
9410
9411    #[test]
9412    fn a_structure_of_floats_travels_in_floating_point_registers_on_aarch64() {
9413        // The same declaration, classified by a different ABI: three `float` members are an
9414        // eightbyte of two of them and a half eightbyte of the third on SysV, and three vector
9415        // registers on AAPCS64.
9416        let source = "\
9417struct hfa { float x, y, z; };
9418int take(struct hfa h);
9419int give(struct hfa h) { return take(h); }
9420";
9421        assert!(ir(source).contains("func @take(f64, f32) -> i32"), "{}", ir(source));
9422        let mut opts = options();
9423        opts.emit = EmitKind::Ir;
9424        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
9425        let result = run(&opts, source);
9426        assert_eq!(result.messages, Vec::<String>::new());
9427        assert!(result.text().contains("func @take(f32, f32, f32) -> i32"), "{}", result.text());
9428    }
9429
9430    #[test]
9431    fn an_array_whose_length_is_not_a_constant_is_a_slot_made_where_its_declaration_is() {
9432        // The size is a multiplication rather than a number, the slot is taken from the stack
9433        // where the declaration is, and the scope it was declared in gives it back.
9434        let source = "\
9435int use(int *);
9436void f(int n) {
9437  {
9438    int a[n];
9439    use(a);
9440  }
9441  use(0);
9442}
9443";
9444        let body = body(source);
9445        assert!(body.contains("mul.nsw"), "{body}");
9446        assert!(body.contains("stacksave"), "{body}");
9447        assert!(body.contains("alloca %"), "{body}");
9448        assert!(body.contains("stackrestore"), "{body}");
9449    }
9450
9451    #[test]
9452    fn a_goto_out_of_the_scope_of_one_gives_its_stack_back_on_the_way() {
9453        // The label is outside the block the array is in, so arriving there means the array is
9454        // gone, and the restore that says so goes in front of the branch. The `goto` is written
9455        // before the walk knows where the label is, which is why the restore is put there at
9456        // the end rather than built where the branch was.
9457        let source = "\
9458int use(int *);
9459int f(int n) {
9460  {
9461    int a[n];
9462    if (use(a)) goto out;
9463    use(0);
9464  }
9465out:
9466  return 0;
9467}
9468";
9469        let body = body(source);
9470        // Two ways out of the block and a restore on each: the jump and the end of the block.
9471        assert_eq!(body.matches("stackrestore").count(), 2, "{body}");
9472        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
9473        assert!(after.starts_with(" %4\n    jump block"), "{body}");
9474    }
9475
9476    #[test]
9477    fn a_goto_to_a_label_the_array_is_still_alive_at_leaves_the_stack_alone() {
9478        // The label is after the declaration and in the same block, so control that arrives
9479        // there arrives somewhere the array exists. Giving it back would be giving back an
9480        // object the next statement reads.
9481        let source = "\
9482int use(int *);
9483int f(int n) {
9484  int a[n];
9485again:
9486  if (use(a)) goto again;
9487  return 0;
9488}
9489";
9490        let body = body(source);
9491        assert!(body.contains("stacksave"), "{body}");
9492        assert!(!body.contains("stackrestore"), "{body}");
9493    }
9494
9495    #[test]
9496    fn a_goto_back_to_a_label_in_front_of_one_gives_it_back_every_time_round() {
9497        // A loop written out of a `goto`, with the array made inside it. The label is in the
9498        // same block as the declaration and before it, which is a place where the array does
9499        // not exist yet, so the jump there leaves its scope and has to give the stack back. A
9500        // compiler that skips this restore grows the stack once per iteration.
9501        let source = "\
9502int use(int *);
9503int f(int n) {
9504again:
9505  {
9506    int a[n];
9507    if (use(a)) goto again;
9508  }
9509  return 0;
9510}
9511";
9512        let body = body(source);
9513        assert_eq!(body.matches("stacksave").count(), 1, "{body}");
9514        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
9515        assert!(after.starts_with(" %4\n    jump block1\n"), "{body}");
9516    }
9517
9518    #[test]
9519    fn the_head_of_a_for_loop_is_a_scope_that_closes_where_the_loop_is_left() {
9520        // The scope opened for `for (int a[n];;)` used to stay open, and a scope left open is
9521        // not one mark nobody reads. The marks are a stack, so the next close took this one
9522        // instead of its own, and the body of the loop gave back nothing while the block after
9523        // the loop restored a pointer saved inside it. The verifier refused that, which is how
9524        // it was found.
9525        let source = "\
9526int f(void);
9527void t(void) {
9528  int count = 10;
9529  for (; count--;) {
9530    int b[f()];
9531    int i;
9532    for (i = 0; i < f(); i++) {
9533      b[i] = count;
9534    }
9535  }
9536}
9537";
9538        let body = body(source);
9539        // One save, in the body, and one restore for it, also in the body: the block the
9540        // restore is in is the one the inner loop leaves through, and it goes back round the
9541        // outer loop rather than out of it.
9542        assert_eq!(body.matches("stacksave").count(), 1, "{body}");
9543        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
9544        // The rest of the block the restore is in, which is the last block here, so there is not
9545        // always another one after it to split on.
9546        let next = after.split("\n\n").next().expect("the block the restore is in");
9547        assert!(next.contains("jump block1("), "{body}");
9548    }
9549
9550    #[test]
9551    fn how_long_one_of_those_is_was_decided_where_it_was_declared_and_not_where_it_is_asked() {
9552        // What C says about the length being evaluated once: `sizeof a` after `n` changed is
9553        // still as long as the array is, which is what `n` was when the array came into being.
9554        let source = "\
9555unsigned long f(int n) {
9556  int a[n];
9557  n = 0;
9558  return sizeof a;
9559}
9560";
9561        let body = body(source);
9562        // One read of the parameter, at the declaration, and the answer is built out of it.
9563        assert_eq!(body.matches("sext.i64 %0").count(), 2, "{body}");
9564    }
9565
9566    #[test]
9567    fn a_block_in_the_middle_of_an_expression_is_walked_where_the_expression_is() {
9568        // GNU's statement expression: the statements happen where they are written and the last
9569        // one is the value, so the temporary in it never becomes a slot and never is copied.
9570        let source = "\
9571int use(int);
9572int f(int x) {
9573  return ({
9574    int t = use(x);
9575    t * t;
9576  });
9577}
9578";
9579        let expected = "\
9580block0(%0: i32):
9581    %1 = call @use(%0) : (i32) -> i32
9582    %2 = mul.nsw %1, %1
9583    return %2
9584";
9585        assert_eq!(body(source), expected);
9586    }
9587
9588    #[test]
9589    fn a_comma_whose_value_is_an_object_names_the_object_the_right_side_named() {
9590        // What janet writes, which is a call that does not return and then a value after it so
9591        // that the arm is worth something. The left side happens for what it did and the answer
9592        // is where the right side is, so there is nothing to copy and no temporary for a copy.
9593        let source = "\
9594struct pair { int a, b; };
9595void bail(void);
9596int f(struct pair p) {
9597  return (bail(), p).b;
9598}
9599";
9600        let expected = "\
9601block0(%0: i64):
9602    %1 = alloca, size 8, align 4
9603    store %0 -> %1, align 4
9604    call @bail() : ()
9605    %2 = iconst.i64 4
9606    %3 = ptr_add %1, %2
9607    %4 = load.i32 %3, align 4, tbaa !1
9608    return %4
9609";
9610        assert_eq!(body(source), expected);
9611    }
9612
9613    #[test]
9614    fn one_of_those_that_control_never_leaves_is_lowered_and_what_follows_it_is_dropped() {
9615        // A macro that always jumps, which is what this shape is in real code. The value is
9616        // never taken, and the block the rest of the expression would have been built in is
9617        // one nothing branches to, so it goes with the other unreachable blocks.
9618        let source = "int f(int x) { return ({ return x; 0; }); }\n";
9619        assert_eq!(body(source), "block0(%0: i32):\n    return %0\n");
9620    }
9621
9622    #[test]
9623    fn one_argument_off_a_variable_argument_list_stays_an_intrinsic() {
9624        // What it becomes is the target's answer, and this is not where the target's answers
9625        // are, so the walk writes down which list and which type and leaves it at that. Two of
9626        // them are two instructions, since each moves the list on.
9627        let source = "double f(__builtin_va_list ap) { return __builtin_va_arg(ap, double) + __builtin_va_arg(ap, double); }\n";
9628        let expected = "\
9629block0(%0: ptr):
9630    %1 = va_arg.f64 %0
9631    %2 = va_arg.f64 %0
9632    %3 = fadd %1, %2
9633    return %3
9634";
9635        assert_eq!(body(source), expected);
9636    }
9637
9638    #[test]
9639    fn one_that_reads_a_structure_answers_where_the_object_is() {
9640        // An aggregate is not a value, so there is nothing for the result of `va_arg` to be and
9641        // the object form is a second instruction. What it answers is an address, so it is a
9642        // place already and the walk copies nothing out of it: the copy here is the one the
9643        // initializer asks for, into the variable being declared. The size and the alignment
9644        // travel with it because they are what steps the list on and what a target that has to
9645        // put registers somewhere needs to know. So does the classification, which says the two
9646        // halves of this one arrived in general purpose registers: that is an answer about a C
9647        // type, and this is the last place that still has one.
9648        //
9649        // The slot is aligned to sixteen and the copy into it to eight, which is not a
9650        // disagreement. Sixteen is what a local aggregate of sixteen bytes gets whatever its
9651        // members ask for, and eight is what the type asks for and so what the copy may assume
9652        // about the object it is reading from.
9653        let source = "\
9654struct s { int a; long b; };
9655long f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.b; }
9656";
9657        let expected = "\
9658block0(%0: ptr):
9659    %1 = alloca, size 16, align 16
9660    %2 = va_object %0, size 16, align 8, in(int 8 at 0, int 8 at 8)
9661    memcpy %1, %2, size 16, align 8
9662    %3 = iconst.i64 8
9663    %4 = ptr_add %1, %3
9664    %5 = load.i64 %4, align 8, tbaa !1
9665    return %5
9666";
9667        assert_eq!(body(source), expected);
9668    }
9669
9670    /// Which register file each eightbyte arrived in is the whole of what the classification adds,
9671    /// and an object with no slots at all is one it sent to the caller's argument area, which is
9672    /// what everything over two eightbytes is whatever its members are.
9673    #[test]
9674    fn the_classification_says_which_registers_the_object_arrived_in() {
9675        let source = "\
9676struct s { double a; double b; };
9677double f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.a; }
9678";
9679        assert!(
9680            body(source)
9681                .contains("va_object %0, size 16, align 8, in(float f64 at 0, float f64 at 8)"),
9682            "{}",
9683            body(source)
9684        );
9685
9686        let big = "\
9687struct s { long a[4]; };
9688long f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.a[0]; }
9689";
9690        assert!(body(big).contains("va_object %0, size 32, align 8\n"), "{}", body(big));
9691    }
9692
9693    #[test]
9694    fn a_jump_to_an_address_branches_to_every_label_the_function_takes_the_address_of() {
9695        // GNU's computed goto. Which label the address holds is not known here, so all of them
9696        // are listed, and the values arriving at one are passed on every edge the same way they
9697        // are on an ordinary branch.
9698        let source = "\
9699int f(int c) {
9700  void *p = c ? &&one : &&two;
9701  goto *p;
9702one:
9703  return 1;
9704two:
9705  return 2;
9706}
9707";
9708        let expected = "\
9709block0(%0: i32):
9710    %1 = iconst.i32 0
9711    %2 = icmp ne %0, %1
9712    br_if %2, block1, block2
9713
9714block1:
9715    %3 = block_addr block3
9716    jump block4(%3)
9717
9718block2:
9719    %4 = block_addr block5
9720    jump block4(%4)
9721
9722block3:
9723    %5 = iconst.i32 1
9724    return %5
9725
9726block4(%6: ptr):
9727    indirect_br %6, block3, block5
9728
9729block5:
9730    %7 = iconst.i32 2
9731    return %7
9732";
9733        assert_eq!(body(source), expected);
9734    }
9735
9736    /// An interpreter, cut down to the shape that matters: a table of labels, a few values the
9737    /// loop keeps in hand, and a jump through the table at the end of every one of them.
9738    fn dispatch(labels: usize) -> String {
9739        let mask = labels - 1;
9740        let mut source = String::from("int spin(int n)\n{\n\tstatic void *table[] = {");
9741        for index in 0..labels {
9742            source.push_str(&format!(" &&a{index},"));
9743        }
9744        source.push_str(" };\n\tint w = n, x = n + 1, y = n + 2, z = n + 3;\n");
9745        source.push_str(&format!("\tif (n < 0) return 0;\n\tgoto *table[n & {mask}];\n"));
9746        for index in 0..labels {
9747            let step = match index % 4 {
9748                0 => "w += x;",
9749                1 => "x += y;",
9750                2 => "y += z;",
9751                _ => "z += w;",
9752            };
9753            source.push_str(&format!("a{index}:\n\t{step}\n"));
9754            source.push_str("\tif (--n <= 0) return w + x + y + z;\n");
9755            source.push_str(&format!("\tgoto *table[n & {mask}];\n"));
9756        }
9757        source.push_str("}\n");
9758        source
9759    }
9760
9761    /// How many moves are written in front of the first jump through a register.
9762    fn in_front_of_the_jump(text: &str) -> usize {
9763        let (before, _) = text.split_once("\tjmp\t*%").expect("a jump through a register");
9764        before.lines().rev().take_while(|line| line.starts_with("\tmov")).count()
9765    }
9766
9767    /// What a branch writes in front of its jump is what it carries, not what every label it can
9768    /// reach would like to be handed.
9769    ///
9770    /// A label an indirect branch reaches is given its values in registers the branch writes
9771    /// before it goes, because the moves cannot go after a jump and cannot go across the register
9772    /// the jump reads. Writing a register for each parameter of each label costs the table's
9773    /// length on every dispatch, which is a few moves in a program with two labels and five
9774    /// hundred in an interpreter with seventy. The values are the same values, so the registers
9775    /// are the same registers, and the cost stays where the number of values puts it.
9776    #[test]
9777    fn a_jump_through_a_register_writes_what_it_carries_and_not_the_whole_table() {
9778        let small = in_front_of_the_jump(&asm(&dispatch(4)));
9779        let large = in_front_of_the_jump(&asm(&dispatch(32)));
9780        assert_eq!(small, large, "eight times the labels and the same values in hand");
9781        assert!(large <= 8, "the values the loop keeps, and not a set of them per label: {large}");
9782    }
9783
9784    /// The same interpreter with more values in hand than there are registers, which is what makes
9785    /// the allocator send some of them to the stack at every label.
9786    fn crowded(labels: usize) -> String {
9787        const VALUES: usize = 24;
9788        let mask = labels - 1;
9789        let mut source = String::from("int spin(int n)\n{\n\tstatic void *table[] = {");
9790        for index in 0..labels {
9791            source.push_str(&format!(" &&a{index},"));
9792        }
9793        source.push_str(" };\n\t");
9794        for value in 0..VALUES {
9795            source.push_str(&format!("int v{value} = n + {value}; "));
9796        }
9797        let sum: Vec<String> = (0..VALUES).map(|value| format!("v{value}")).collect();
9798        source.push_str(&format!("\n\tif (n < 0) return 0;\n\tgoto *table[n & {mask}];\n"));
9799        for index in 0..labels {
9800            let (to, from) = (index % VALUES, (index + 1) % VALUES);
9801            source.push_str(&format!("a{index}:\n\tv{to} += v{from};\n"));
9802            source.push_str(&format!("\tif (--n <= 0) return {};\n", sum.join(" + ")));
9803            source.push_str(&format!("\tgoto *table[n & {mask}];\n"));
9804        }
9805        source.push_str("}\n");
9806        source
9807    }
9808
9809    /// How many bytes of frame the first function in a listing opens.
9810    fn the_frame(text: &str) -> u64 {
9811        text.lines()
9812            .find_map(|line| {
9813                let (size, _) = line.strip_prefix("\tsubq\t$")?.split_once(", %rsp")?;
9814                size.parse().ok()
9815            })
9816            .expect("a function that opens a frame")
9817    }
9818
9819    /// A frame holds what a function wants at once, and an interpreter does not want the whole
9820    /// table at once.
9821    ///
9822    /// Every label a dispatch table reaches is handed the values the loop keeps, and what the
9823    /// allocator has no register for goes on the stack. They are the same few values one label at
9824    /// a time, so they are the same bytes. A slot each put forty kilobytes on the frame of lua's
9825    /// interpreter and ran the C stack out at a depth lua's own limit was supposed to catch,
9826    /// which is tamnd/rucc#1630.
9827    #[test]
9828    fn a_frame_holds_what_is_wanted_at_once_and_not_a_slot_for_every_label() {
9829        let small = the_frame(&asm(&crowded(16)));
9830        let large = the_frame(&asm(&crowded(64)));
9831        assert_eq!(small, large, "four times the labels and the same values: {small}, {large}");
9832    }
9833
9834    /// A template that saves the callee-saved registers by name, which is micropython's non local
9835    /// return and is tamnd/rucc#1583.
9836    ///
9837    /// Every register in it is one the template named rather than one the statement handed over,
9838    /// because the buffer is defined as holding those registers and there is no constraint letter
9839    /// that means `%rsp`. The instructions come out naming what the program named, and the
9840    /// allocator, which was told about the writes rather than left to find out, saves the ones the
9841    /// calling convention says belong to whoever called.
9842    #[test]
9843    fn a_template_that_names_its_own_registers_gets_the_ones_it_named() {
9844        let source = "void save(void *nlr) {
9845    __asm volatile (
9846        \"movq   %%rsp, 32(%%rdi)   \\n\"
9847        \"movq   %%rbx, 40(%%rdi)   \\n\"
9848        \"movq   %%r12, 48(%%rdi)   \\n\"
9849        : : \"D\" (nlr) : \"memory\");
9850}
9851";
9852        let text = asm(source);
9853        assert!(text.contains("\tmovq\t%rsp, 32(%rdi)\n"), "{text}");
9854        assert!(text.contains("\tmovq\t%rbx, 40(%rdi)\n"), "{text}");
9855        assert!(text.contains("\tmovq\t%r12, 48(%rdi)\n"), "{text}");
9856    }
9857
9858    #[test]
9859    fn a_jump_to_an_address_no_label_in_the_function_has_arrives_nowhere() {
9860        // The address came from outside the function, and a jump to a label in another function
9861        // is undefined. The expression is still evaluated, since a call in it has to happen.
9862        let source = "void **next(void);
9863void f(void) { goto *next(); }
9864";
9865        let expected = "\
9866block0:
9867    %0 = call @next() : () -> ptr
9868    unreachable
9869";
9870        assert_eq!(body(source), expected);
9871    }
9872
9873    #[test]
9874    fn an_asm_with_no_operands_is_volatile_and_the_clobbers_are_the_whole_of_what_it_says() {
9875        // Nothing reads a result, so the only thing that keeps it is that it is volatile, which
9876        // a basic asm implies.
9877        let source = "void f(void) { __asm__(\"mfence\" ::: \"memory\"); }\n";
9878        let expected = "\
9879block0:
9880    inline_asm.volatile \"mfence\", \"\", \"memory\"()
9881    return
9882";
9883        assert_eq!(body(source), expected);
9884    }
9885
9886    #[test]
9887    fn the_constraints_are_one_list_in_the_order_the_template_counts_the_operands() {
9888        // The outputs first and then the inputs, which is the numbering `%0` and `%1` use. An
9889        // output in a register is a result, and one that is read as well is an argument too.
9890        let source = "\
9891int f(int x, int y) {
9892  int r;
9893  __asm__(\"addl %2, %0\" : \"=r\"(r), \"+r\"(y) : \"r\"(x));
9894  return r + y;
9895}
9896";
9897        let expected = "\
9898block0(%0: i32, %1: i32):
9899    %2, %3 = inline_asm.(i32, i32) \"addl %2, %0\", \"=r,+r,r\", \"\"(%1, %0)
9900    %4 = add.nsw %2, %3
9901    return %4
9902";
9903        assert_eq!(body(source), expected);
9904    }
9905
9906    #[test]
9907    fn a_memory_operand_travels_as_the_address_of_an_object_that_is_given_a_slot() {
9908        // The assembly is handed a pointer, so the object cannot live in a value, and the scan
9909        // that runs before the walk has to have known that or there would be nothing to point
9910        // at. A structure travels this way whatever else its constraint allows, since there is
9911        // no register that holds one.
9912        let source = "\
9913struct pair { int a, b; };
9914int f(int x) {
9915  int slot = x;
9916  struct pair p = { x, x };
9917  __asm__(\"incl %0\" : \"+m\"(slot), \"=m\"(p));
9918  return slot + p.a;
9919}
9920";
9921        let text = body(source);
9922        assert!(text.contains("inline_asm \"incl %0\", \"+m,=m\", \"\"(%1, %2)\n"), "{text}");
9923        assert!(text.contains("%1 = alloca, size 4, align 4\n"), "{text}");
9924        assert!(text.contains("%2 = alloca, size 8, align 4\n"), "{text}");
9925    }
9926
9927    #[test]
9928    fn an_asm_goto_falls_through_to_its_first_target_and_writes_its_outputs_there() {
9929        // The output is only in scope where the instruction dominates, which is the fall through
9930        // block, so the edge to the label carries the value the object had before the assembly
9931        // ran. That is what document 11 asks for and it is what putting the fall through first
9932        // buys.
9933        let source = "\
9934int f(int x) {
9935  int r = 7;
9936  __asm__ goto(\"cbnz %0, %l1\" : \"=r\"(r) : \"r\"(x) :: away);
9937  return r;
9938away:
9939  return r;
9940}
9941";
9942        let expected = "\
9943block0(%0: i32):
9944    %1 = iconst.i32 7
9945    %2 = inline_asm.volatile \"cbnz %0, %l1\", \"=r,r\", \"\"(%0), labels [block1, block2]
9946
9947block1:
9948    return %2
9949
9950block2:
9951    return %1
9952";
9953        assert_eq!(body(source), expected);
9954    }
9955
9956    #[test]
9957    fn an_asm_statement_that_is_not_well_formed_is_reported_in_the_words_gcc_uses() {
9958        // The operands are checked here rather than by the assembler, because by the time the
9959        // assembler sees the template the operands have become registers and it has nothing left
9960        // to say about the C that named them.
9961        let mut opts = options();
9962        opts.emit = EmitKind::Ir;
9963        for (source, expected) in [
9964            (
9965                "void f(int x) { __asm__(\"\" : \"r\"(x)); }\n",
9966                "output operand constraint lacks '='",
9967            ),
9968            (
9969                "void f(int x) { __asm__(\"\" : \"=r\"(x + 1)); }\n",
9970                "lvalue required in 'asm' statement",
9971            ),
9972            (
9973                "const int g = 1;\nvoid f(void) { __asm__(\"\" : \"=r\"(g)); }\n",
9974                "read-only variable 'g' used as 'asm' output",
9975            ),
9976            (
9977                "void f(int x) { __asm__(\"\" : : \"=r\"(x)); }\n",
9978                "input operand constraint contains '='",
9979            ),
9980            (
9981                "void f(void) { __asm__(\"\" : : \"m\"(1)); }\n",
9982                "memory input 0 is not directly addressable",
9983            ),
9984            ("void f(void) { __asm__(L\"\"); }\n", "wide string literal in 'asm'"),
9985            (
9986                "void f(int x, int y) { __asm__(\"\" : [a] \"=r\"(x) : [a] \"r\"(y)); }\n",
9987                "duplicate asm operand name 'a'",
9988            ),
9989            ("void f(int x) { __asm__(\"%[in]\" : \"=r\"(x)); }\n", "undefined named operand 'in'"),
9990        ] {
9991            let result = run(&opts, source);
9992            assert!(result.failed(), "expected this to be reported:\n{source}");
9993            assert!(
9994                result.messages.iter().any(|m| m.contains(expected)),
9995                "{expected}\n{:?}",
9996                result.messages
9997            );
9998        }
9999    }
10000
10001    /// An `asm` at file scope whose template is directives is the whole of what the incbin
10002    /// header, an alias table and a hand written jump table each write, and what it says is a
10003    /// section holding named bytes. So it becomes the globals it names, in the order it names
10004    /// them, which is what `spec/11-asm-objects-debug.md` section 11.2 asks for.
10005    #[test]
10006    fn an_asm_at_file_scope_that_is_directives_becomes_the_objects_it_defines() {
10007        let text = ir(concat!(
10008            "__asm__(\n",
10009            "  \".section .rodata\\n\"\n",
10010            "  \".globl first\\n\"\n",
10011            "  \".balign 8\\n\"\n",
10012            "  \"first:\\n\"\n",
10013            "  \".long 1\\n\"\n",
10014            "  \".long 2\\n\"\n",
10015            "  \".globl last\\n\"\n",
10016            "  \"last:\\n\"\n",
10017            "  \".quad last - first\\n\");\n",
10018            "extern const int first[];\n",
10019            "extern const long last;\n",
10020        ));
10021        assert!(text.contains("global @first : bytes 8 = { i32 1, i32 2 }, align 8"), "{text}");
10022        assert!(text.contains("global @last : i64 = 8"), "{text}");
10023    }
10024
10025    /// The distance between two labels is what the incbin header hands a program as the size of
10026    /// the data, so a declaration of one of the names has to find the definition the template
10027    /// made rather than turn it back into something the linker is asked for.
10028    #[test]
10029    fn a_name_an_asm_at_file_scope_defined_is_not_undone_by_a_declaration_of_it() {
10030        let text = ir(concat!(
10031            "__asm__(\".data\\n.globl counter\\ncounter:\\n.long 7\\n\");\n",
10032            "extern int counter;\n",
10033            "int read(void) { return counter; }\n",
10034        ));
10035        assert!(text.contains("global @counter : i32 = 7"), "{text}");
10036    }
10037
10038    /// Bytes written before any label are a global with a name minted for them, in front of the
10039    /// label written under them, which is what makes the first byte of the name the one written
10040    /// under it. The block is the one tcc's test file writes, without the line of it that measures
10041    /// from one section to another.
10042    #[test]
10043    fn bytes_under_no_label_at_file_scope_are_a_global_in_front_of_the_label() {
10044        let text = ir(concat!(
10045            "__asm__(\".data\\n.byte 41\\nstuff:\\n661:\\n.byte 42\\n662:\\n",
10046            ".pushsection .data.ignore\\n.byte 7\\n.popsection\\n.byte 662b - 661b\\n\");\n",
10047            "extern unsigned char stuff[];\n",
10048            "int read(void) { return stuff[0]; }\n",
10049        ));
10050        let under = text.find("global @.Lasm.0 : i8 = 41").expect(&text);
10051        let named = text.find("global @stuff : i8 = 42").expect(&text);
10052        assert!(under < named, "the bytes under no label come first: {text}");
10053        assert!(text.contains("global @.Lasm.1 : i8 = 7, align 1, linkage(internal), section"));
10054        // The byte after the pop is a run of its own, because coming back to a section finishes
10055        // what was being written to it the way a label does. It is the next global of that
10056        // section all the same, so the byte lands where the template put it, which is the one
10057        // after the byte under `stuff`.
10058        let after = text.find("global @.Lasm.2 : i8 = 1").expect(&text);
10059        assert!(named < after, "{text}");
10060    }
10061
10062    /// How far a place is from the bytes holding the answer, which is what tcc's test file writes
10063    /// last and what the alternative instruction tables in a kernel header are made of. It is the
10064    /// linker's answer rather than the compiler's, because the two sections are placed by the
10065    /// linker, so the image holds a hole and a name for it.
10066    #[test]
10067    fn a_distance_from_here_at_file_scope_is_a_hole_naming_the_global_it_measures_to() {
10068        let text = ir(concat!(
10069            "__asm__(\".data\\n.byte 41\\nstuff:\\n661:\\n.byte 42\\n",
10070            ".pushsection .data.ignore\\n.long 661b - .\\n.popsection\\n\");\n",
10071            "extern unsigned char stuff[];\n",
10072            "int read(void) { return stuff[0]; }\n",
10073        ));
10074        // The label the template measured to is a local one and no symbol, so what the hole names
10075        // is the global it stands inside, which is the byte under `stuff`, and nothing further on
10076        // since it is the first byte of it.
10077        assert!(text.contains("global @.Lasm.1 : bytes 4 = { away.4 @stuff }"), "{text}");
10078    }
10079
10080    /// A `.set` says one name stands for another, which is a second symbol at the first one's
10081    /// address and is an alias and nothing else. What the directives around it said about the
10082    /// name is what the name gets, and a name the file defines itself keeps its own definition,
10083    /// which is what gcc's symbol table shows for the block tcc's test file writes.
10084    #[test]
10085    fn a_set_at_file_scope_is_a_second_name_for_what_it_names() {
10086        let text = ir(concat!(
10087            "void base(void) {}\n",
10088            "__asm__(\".weak one\\n.set one, base\");\n",
10089            "__asm__(\".globl two\\n.set two, base\");\n",
10090            "__asm__(\".set three, base\");\n",
10091            "void three(void) {}\n",
10092        ));
10093        assert!(text.contains("alias @one = @base, linkage(weak)"), "{text}");
10094        assert!(text.contains("alias @two = @base"), "{text}");
10095        assert!(!text.contains("alias @three"), "a definition of the name wins: {text}");
10096        assert!(text.contains("func @three"), "{text}");
10097    }
10098
10099    /// The target has to be something this file defines, because an alias is a symbol at an
10100    /// address in this object and a name only declared here has none to be at. The same rule and
10101    /// the same words as for `__attribute__((alias))`, since it is the same thing written another
10102    /// way.
10103    #[test]
10104    fn a_set_of_a_name_this_file_does_not_define_says_so() {
10105        let messages = errors("__asm__(\".set here, elsewhere\");\n");
10106        assert!(
10107            messages
10108                .iter()
10109                .any(|m| m.contains("'here' is aliased to undefined symbol 'elsewhere'")
10110                    && m.contains("E0697")),
10111            "{messages:?}"
10112        );
10113    }
10114
10115    /// `.incbin` is the one directive that reads something, and what it reads comes through the
10116    /// same file system the sources did.
10117    #[test]
10118    fn an_incbin_at_file_scope_is_the_bytes_of_the_file_it_names() {
10119        let mut opts = options();
10120        opts.emit = EmitKind::Ir;
10121        let mut fs = MemoryFileSystem::new();
10122        fs.insert(
10123            "/main.c",
10124            b"__asm__(\".data\\n.globl blob\\nblob:\\n.incbin \\\"seed\\\"\\n\");\n".to_vec(),
10125        );
10126        fs.insert("seed", b"hi".to_vec());
10127        let result = compile(&opts, "/main.c", &fs);
10128        assert_eq!(result.messages, Vec::<String>::new());
10129        let text = result.text();
10130        assert!(text.contains("global @blob : bytes 2 = { bytes \"hi\" }"), "{text}");
10131    }
10132
10133    /// A file that is not there is the mistake a build makes when it runs the compiler from the
10134    /// wrong directory, and it is worth saying which file rather than saying the template failed.
10135    #[test]
10136    fn an_incbin_naming_a_file_that_is_not_there_says_which_file() {
10137        let messages = errors("__asm__(\".data\\nb:\\n.incbin \\\"nowhere\\\"\\n\");\n");
10138        assert!(
10139            messages
10140                .iter()
10141                .any(|m| m.contains("cannot open 'nowhere' for reading") && m.contains("E0702")),
10142            "{messages:?}"
10143        );
10144    }
10145
10146    /// A template of directives the reader does not take is refused by name rather than dropped.
10147    /// One with an instruction in it goes to the assembler instead, which
10148    /// `an_asm_at_file_scope_with_an_instruction_in_it_is_assembled` covers.
10149    #[test]
10150    fn a_directive_in_an_asm_at_file_scope_is_refused_rather_than_ignored() {
10151        let source = "__asm__(\".data\\n.set alias, 4\\n\");\n";
10152        let messages = errors(source);
10153        assert!(
10154            messages
10155                .iter()
10156                .any(|m| m.contains("not supported yet") && m.contains("in an `asm` at file scope")),
10157            "{source}\n{messages:?}"
10158        );
10159    }
10160
10161    /// micropython's `nlr_push`, which is the program that asks for all of this. The body is the
10162    /// whole of the function: the return address is read out of `(%rsp)` where the call left it,
10163    /// the registers the convention preserves are saved by hand, and the frame that was just built
10164    /// is handed to a function written in C that never comes back.
10165    ///
10166    /// What is checked is what gcc writes for the same file. No prologue in front of the saves,
10167    /// since a push would move the return address the first of them reads. No epilogue and no
10168    /// `ret`, since the jump is where the function ends. And a `ud2` behind the jump, which is
10169    /// where control arrives if the jump is ever not taken and is exactly what gcc puts there.
10170    #[test]
10171    fn a_naked_function_is_its_own_prologue_and_its_own_ending() {
10172        let text = asm(concat!(
10173            "unsigned nlr_push_tail(void *nlr);\n",
10174            "__attribute__((naked)) unsigned nlr_push(void *nlr) {\n",
10175            "  __asm volatile(\n",
10176            "    \"movq (%rsp), %rax\\n\"\n",
10177            "    \"movq %rax, 16(%rdi)\\n\"\n",
10178            "    \"movq %rbx, 40(%rdi)\\n\"\n",
10179            "    \"jmp nlr_push_tail\\n\");\n",
10180            "}\n",
10181        ));
10182        assert!(text.contains("\tmovq\t(%rsp), %rax\n"), "{text}");
10183        assert!(text.contains("\tjmp\tnlr_push_tail\n"), "{text}");
10184        assert!(text.contains("\tud2\n"), "{text}");
10185        assert!(!text.contains("\tpushq\t"), "nothing is saved in front of it: {text}");
10186        assert!(!text.contains("\tret\n"), "the jump is where it ends: {text}");
10187    }
10188
10189    /// The three things a naked function may not ask for, each of which is a frame nothing sets up
10190    /// or a jump over an epilogue there is one of.
10191    #[test]
10192    fn what_a_function_without_a_prologue_cannot_be_given_is_refused() {
10193        let mut opts = options();
10194        opts.emit = EmitKind::Asm;
10195        for (source, why) in [
10196            (
10197                "__attribute__((naked)) void f(void) { volatile long a[8]; a[0] = 1; }\n",
10198                "bytes of frame",
10199            ),
10200            (
10201                "__attribute__((naked)) void f(int n) { char a[n]; __asm(\"nop\" ::\"r\"(a)); }\n",
10202                "has no prologue to point a frame pointer at it with",
10203            ),
10204            ("void elsewhere(void); void f(void) { __asm(\"jmp elsewhere\"); }\n", "jumps out of"),
10205        ] {
10206            let result = run(&opts, source);
10207            assert!(result.failed(), "expected this to be refused:\n{source}");
10208            assert!(
10209                result.messages.iter().any(|message| message.contains(why)),
10210                "{:?}",
10211                result.messages
10212            );
10213        }
10214    }
10215
10216    #[test]
10217    fn what_the_walk_cannot_build_yet_is_reported_rather_than_mislowered() {
10218        let mut opts = options();
10219        opts.emit = EmitKind::Ir;
10220        for source in [
10221            "int f(int n) { void *p = &&out; if (n) goto *p; { int a[n]; out: return 1; } }\n",
10222            "int f(int n) { int a[n]; __asm__ goto(\"\" ::::out); out: return a[0]; }\n",
10223        ] {
10224            let result = run(&opts, source);
10225            assert!(result.failed(), "expected this to be reported:\n{source}");
10226            assert!(
10227                result.messages.iter().any(|m| m.contains("not supported yet")),
10228                "{:?}",
10229                result.messages
10230            );
10231        }
10232    }
10233
10234    /// Compiles `source` to IR, reads that back as an input, and gives back both texts.
10235    fn round_trip(source: &str) -> (String, String) {
10236        let printed = ir(source);
10237        let mut opts = options();
10238        opts.emit = EmitKind::Ir;
10239        let mut fs = MemoryFileSystem::new();
10240        fs.insert("/main.ir", printed.clone().into_bytes());
10241        let result = compile_ir(&opts, "/main.ir", &fs);
10242        assert_eq!(result.messages, Vec::<String>::new(), "expected this to read back:\n{printed}");
10243        (printed, result.text().to_owned())
10244    }
10245
10246    #[test]
10247    fn ir_that_arrives_as_an_input_is_read_back_and_written_out_the_same() {
10248        // The other half of the round trip test below, through the driver rather than through
10249        // the library, which is what makes the property something to run over a real program
10250        // rather than over the modules a test builds.
10251        let (printed, again) = round_trip(
10252            "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",
10253        );
10254        assert_eq!(printed, again);
10255    }
10256
10257    #[test]
10258    fn ir_that_is_not_ir_says_which_line_stopped_it() {
10259        let mut opts = options();
10260        opts.emit = EmitKind::Ir;
10261        let mut fs = MemoryFileSystem::new();
10262        let text = "\
10263; ModuleID = 'a.c'
10264; format 0
10265target triple = \"x86_64-unknown-linux-gnu\"
10266target datalayout = \"e-p:64:64-i64:64-S128\"
10267
10268func @f(), linkage(external) {
10269block0:
10270    frobnicate
10271}
10272";
10273        fs.insert("/main.ir", text.as_bytes().to_vec());
10274        let result = compile_ir(&opts, "/main.ir", &fs);
10275        assert!(result.failed());
10276        assert!(result.messages[0].contains("/main.ir:8"), "{:?}", result.messages);
10277    }
10278
10279    #[test]
10280    fn ir_that_reads_but_does_not_hold_together_is_reported_by_the_verifier() {
10281        // A module that a person edited has not been through the verifier, and the return of
10282        // an `i32` from a function that returns nothing is the kind of thing editing produces.
10283        let mut opts = options();
10284        opts.emit = EmitKind::Ir;
10285        let mut fs = MemoryFileSystem::new();
10286        let text = "\
10287; ModuleID = 'a.c'
10288; format 0
10289target triple = \"x86_64-unknown-linux-gnu\"
10290target datalayout = \"e-p:64:64-i64:64-S128\"
10291
10292func @f(), linkage(external) {
10293block0:
10294    %0 = iconst.i32 1
10295    return %0
10296}
10297";
10298        fs.insert("/main.ir", text.as_bytes().to_vec());
10299        let result = compile_ir(&opts, "/main.ir", &fs);
10300        assert!(result.failed());
10301        assert!(result.messages[0].contains("invalid IR"), "{:?}", result.messages);
10302    }
10303
10304    #[test]
10305    fn a_typed_tree_is_not_something_an_input_of_ir_can_produce() {
10306        // The C that became this is not here any more, so there is nothing to print a tree of.
10307        let mut fs = MemoryFileSystem::new();
10308        fs.insert("/main.ir", Vec::new());
10309        let result = compile_ir(&options(), "/main.ir", &fs);
10310        assert!(result.failed());
10311        assert!(result.messages[0].contains("can only be emitted as IR"), "{:?}", result.messages);
10312    }
10313
10314    #[test]
10315    fn the_printed_ir_reads_back_as_the_same_module() {
10316        // The M2 exit criterion: the text is the module and nothing about it is lost by
10317        // writing it down. Anything the printer invents or the parser drops shows up here.
10318        let text = ir("\
10319struct point { int x, y; };
10320static const char greeting[] = \"hi\";
10321int table[4] = { 1, 2, 3 };
10322int puts(const char *);
10323double half(double x) { return x / 2.0; }
10324int f(int n) {
10325  int total = 0;
10326  for (int i = 0; i < n; i++) {
10327    if (i == 3) continue;
10328    total += table[i];
10329  }
10330  switch (n) {
10331    case 0: total = 1;
10332    case 1: total++; break;
10333    default: total = -total;
10334  }
10335  struct point p = { total, 1 };
10336  int *q = &p.y;
10337  puts(greeting);
10338  return p.x + *q;
10339}
10340int dispatch(int c) {
10341  void *p = c ? &&one : &&two;
10342  goto *p;
10343one:
10344  return 1;
10345two:
10346  return 2;
10347}
10348int assembly(int x, int *p) {
10349  int r;
10350  __asm__ volatile(\"xadd %0, %2\" : \"=r\"(r), \"+m\"(*p) : \"0\"(x) : \"cc\");
10351  __asm__ goto(\"cbnz %0, %l1\" : : \"r\"(r) : : away);
10352  return r;
10353away:
10354  return 0;
10355}
10356");
10357        let mut names = Interner::new();
10358        let module = rucc_ir::parse(&text, &mut names).expect("the printer writes what it reads");
10359        assert_eq!(rucc_ir::print(&module, &names), text);
10360    }
10361
10362    #[test]
10363    fn what_save_temps_keeps_is_the_text_that_was_compiled_and_the_assembly_that_was_assembled() {
10364        // The point of the flag is that these two are the compilation rather than a description
10365        // of one, so both come out of the run that produced the object rather than out of a
10366        // second run under different flags.
10367        let mut opts = options();
10368        opts.emit = EmitKind::Object;
10369        opts.save_temps = rucc_session::SaveTemps::Object;
10370        let result = run(&opts, "#define N 2\nint a[N];\n");
10371        assert_eq!(result.messages, Vec::<String>::new());
10372        let text = result.temps.preprocessed.expect("the preprocessed text");
10373        assert!(text.contains("int a[2];"), "{text}");
10374        assert!(text.starts_with("# 1 \"/main.c\""), "{text}");
10375        let asm = result.temps.assembly.expect("the assembly");
10376        assert!(asm.contains("a:"), "{asm}");
10377        assert!(matches!(result.artifact, Artifact::Object { .. }), "{:?}", result.artifact);
10378    }
10379
10380    #[test]
10381    fn nothing_is_kept_unless_the_flag_asked_for_it() {
10382        // A compilation that was not asked to keep anything must not pay for printing text
10383        // nobody will read, and the empty value is what says so.
10384        let mut opts = options();
10385        opts.emit = EmitKind::Object;
10386        assert_eq!(run(&opts, "int a;\n").temps, Temps::default());
10387    }
10388
10389    #[test]
10390    fn a_compilation_that_stops_before_the_back_end_keeps_the_text_and_no_assembly() {
10391        // `--emit=ir` never produces any, and the text is worth keeping all the same: it is
10392        // what a report about the file being read wrongly has to have in it.
10393        let mut opts = options();
10394        opts.emit = EmitKind::Ir;
10395        opts.save_temps = rucc_session::SaveTemps::Cwd;
10396        let result = run(&opts, "int a;\n");
10397        assert!(result.temps.preprocessed.is_some());
10398        assert_eq!(result.temps.assembly, None);
10399    }
10400
10401    /// A stretch of a local's life, written short because these tests are about nothing else.
10402    fn span(from: u64, len: u64, held: rucc_debug::Held) -> rucc_debug::Span {
10403        rucc_debug::Span { from, len, held }
10404    }
10405
10406    #[test]
10407    fn two_stretches_that_meet_and_agree_come_out_as_one() {
10408        let one = span(0, 4, rucc_debug::Held::Reg(3));
10409        let two = span(4, 4, rucc_debug::Held::Reg(3));
10410        assert_eq!(settle(vec![two, one]), vec![span(0, 8, rucc_debug::Held::Reg(3))]);
10411    }
10412
10413    #[test]
10414    fn a_stretch_another_starts_inside_and_disagrees_with_ends_where_the_other_starts() {
10415        let one = span(0, 8, rucc_debug::Held::Reg(3));
10416        let two = span(4, 8, rucc_debug::Held::Reg(4));
10417        // The second starts where the declaration was given its value, so from there it is the
10418        // second and not the first.
10419        let settled = settle(vec![one, two]);
10420        assert_eq!(
10421            settled,
10422            vec![span(0, 4, rucc_debug::Held::Reg(3)), span(4, 8, rucc_debug::Held::Reg(4))]
10423        );
10424    }
10425
10426    #[test]
10427    fn a_stretch_cut_by_one_that_ends_first_does_not_come_back_after_it() {
10428        // The old value is still live after the new one is done with, because something else
10429        // reads it, but the declaration stopped holding it where the new one started.
10430        let one = span(0, 16, rucc_debug::Held::Reg(3));
10431        let two = span(4, 4, rucc_debug::Held::Reg(4));
10432        assert_eq!(
10433            settle(vec![one, two]),
10434            vec![span(0, 4, rucc_debug::Held::Reg(3)), span(4, 4, rucc_debug::Held::Reg(4))]
10435        );
10436    }
10437
10438    #[test]
10439    fn a_stretch_inside_another_that_agrees_with_it_cuts_nothing() {
10440        let one = span(0, 16, rucc_debug::Held::Reg(3));
10441        let two = span(4, 4, rucc_debug::Held::Reg(3));
10442        assert_eq!(settle(vec![one, two]), vec![span(0, 16, rucc_debug::Held::Reg(3))]);
10443    }
10444
10445    #[test]
10446    fn a_stretch_two_others_disagree_over_the_whole_of_says_nothing_at_all() {
10447        let one = span(0, 8, rucc_debug::Held::Reg(3));
10448        let two = span(0, 8, rucc_debug::Held::Frame(-16));
10449        assert_eq!(settle(vec![one, two]), Vec::new());
10450    }
10451
10452    #[test]
10453    fn stretches_with_a_gap_between_them_keep_the_gap() {
10454        let one = span(0, 4, rucc_debug::Held::Reg(3));
10455        let two = span(16, 4, rucc_debug::Held::Reg(3));
10456        assert_eq!(settle(vec![one, two]), vec![one, two]);
10457    }
10458
10459    /// A function of `len` bytes, since that is the only thing about one these tests look at.
10460    fn extent(len: usize) -> rucc_object::Extent {
10461        rucc_object::Extent {
10462            name: "f".to_owned(),
10463            start: 0,
10464            len,
10465            align: 1,
10466            binding: rucc_object::Binding::Global,
10467            visibility: rucc_object::Visibility::Default,
10468            patch: None,
10469            landings: Vec::new(),
10470        }
10471    }
10472
10473    /// A line table row at `at` built for the source bytes `lo` to `hi`.
10474    fn row(at: usize, lo: u32, hi: u32) -> rucc_asm::Row {
10475        let span = Span::new(lo, hi);
10476        rucc_asm::Row { at, span, inst: None }
10477    }
10478
10479    #[test]
10480    fn a_row_ends_where_the_next_address_begins() {
10481        let rows = [row(0, 0, 1), row(4, 1, 2), row(10, 2, 3)];
10482        assert_eq!(ends(&extent(16), &rows), vec![4, 10, 16]);
10483    }
10484
10485    #[test]
10486    fn rows_sharing_an_address_all_end_where_the_next_address_begins() {
10487        // Two instructions that encoded to nothing sit on the address of the one after them, and
10488        // none of the three ends in front of that one.
10489        let rows = [row(0, 0, 1), row(4, 1, 2), row(4, 2, 3), row(4, 3, 4)];
10490        assert_eq!(ends(&extent(12), &rows), vec![4, 12, 12, 12]);
10491    }
10492
10493    #[test]
10494    fn the_rows_of_a_scope_that_are_next_to_each_other_come_out_as_one_stretch() {
10495        let rows = [row(0, 0, 4), row(4, 10, 14), row(8, 14, 18), row(12, 40, 44)];
10496        let ends = ends(&extent(16), &rows);
10497        let scope = Span::new(8, 20);
10498        assert_eq!(spread(scope, &ends, &rows), vec![rucc_debug::Reach { from: 4, len: 8 }]);
10499    }
10500
10501    #[test]
10502    fn a_scope_the_back_end_split_in_two_comes_out_as_two_stretches() {
10503        let rows = [row(0, 10, 14), row(4, 40, 44), row(8, 14, 18)];
10504        let ends = ends(&extent(12), &rows);
10505        let scope = Span::new(8, 20);
10506        let over = spread(scope, &ends, &rows);
10507        assert_eq!(
10508            over,
10509            vec![rucc_debug::Reach { from: 0, len: 4 }, rucc_debug::Reach { from: 8, len: 4 }]
10510        );
10511    }
10512
10513    #[test]
10514    fn a_row_with_no_source_of_its_own_belongs_to_no_scope() {
10515        // The prologue is the one of these every function has, and it is not inside any block.
10516        let rows = [rucc_asm::Row { at: 0, span: Span::DUMMY, inst: None }, row(4, 10, 14)];
10517        let ends = ends(&extent(8), &rows);
10518        let scope = Span::new(0, 20);
10519        assert_eq!(spread(scope, &ends, &rows), vec![rucc_debug::Reach { from: 4, len: 4 }]);
10520    }
10521
10522    /// A scope of the unit, written short because these tests are about nothing else.
10523    fn scope(parent: Option<usize>, lo: u32, hi: u32) -> crate::shapes::Scope {
10524        let span = Span::new(lo, hi);
10525        crate::shapes::Scope { parent, span }
10526    }
10527
10528    #[test]
10529    fn a_function_gets_the_scopes_its_own_locals_are_in_and_nothing_else() {
10530        // Two functions' worth of scopes in one table, and this one is in the second pair.
10531        let scopes = [scope(None, 0, 10), scope(None, 20, 30), scope(Some(1), 22, 26)];
10532        let rows = [row(0, 22, 24), row(4, 26, 28)];
10533        let (out, at) = nests(&[Some(2)], &scopes, &extent(8), &rows);
10534        // The one the local is in and the one that is inside, numbered from zero for this
10535        // function, with the parent named by the entry it became rather than by where it was.
10536        assert_eq!(at.get(&1), Some(&0));
10537        assert_eq!(at.get(&2), Some(&1));
10538        assert_eq!(at.get(&0), None);
10539        assert_eq!(out.len(), 2);
10540        assert_eq!(out[0].parent, None);
10541        assert_eq!(out[1].parent, Some(0));
10542        assert_eq!(out[0].over, vec![rucc_debug::Reach { from: 0, len: 8 }]);
10543        assert_eq!(out[1].over, vec![rucc_debug::Reach { from: 0, len: 4 }]);
10544    }
10545
10546    #[test]
10547    fn a_local_written_straight_into_the_body_pulls_no_scope_in() {
10548        let scopes = [scope(None, 20, 30)];
10549        let rows = [row(0, 22, 24)];
10550        let (out, at) = nests(&[None], &scopes, &extent(4), &rows);
10551        assert_eq!(out, Vec::new());
10552        assert!(at.is_empty());
10553    }
10554
10555    #[test]
10556    fn a_scope_whose_code_all_went_away_is_still_one_of_the_functions_scopes() {
10557        // Nothing was built for the bytes it covers, so there is nowhere to say its names were
10558        // live. The entry is written anyway, since dropping it would move a local up into the
10559        // function and make it answer to a name it was not declared under.
10560        let scopes = [scope(None, 20, 30)];
10561        let rows = [row(0, 40, 44)];
10562        let (out, at) = nests(&[Some(0)], &scopes, &extent(4), &rows);
10563        assert_eq!(at.get(&0), Some(&0));
10564        assert_eq!(out.len(), 1);
10565        assert_eq!(out[0].over, Vec::new());
10566    }
10567}