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_cost::Goal;
23use rucc_diag::{Diagnostic, Severity, SourceMap, Span};
24use rucc_ir::{FpContract, Pic as IrPic, Visibility as IrVisibility};
25use rucc_lex::{Convert, Keywords, PpToken, convert};
26use rucc_lower::Protector as LowerProtector;
27use rucc_sema::{Checker, Context as CheckContext};
28use rucc_session::{
29    Contract, EmitKind, FileSystem, Options, Padding, Pic, Protector, Session, Visibility,
30};
31use rucc_target::TargetInfo;
32use rucc_tuple::{Arch, ObjectFormat};
33
34use crate::preprocess::render;
35
36/// What a compilation produced, which is text for most of the kinds and bytes for one of them.
37///
38/// Two variants rather than a string, because an object file is not text and a `Vec<u8>` holding
39/// UTF-8 for six kinds and a file format for the seventh would leave every reader guessing which
40/// it had. [`Artifact::Nothing`] is what a compilation that stopped early gives back, and it is
41/// not the same as an empty file: nothing is written for it at all.
42#[derive(Debug, Clone, PartialEq, Eq, Default)]
43pub enum Artifact {
44    /// The compilation stopped before it produced anything, or the kind asked for produces
45    /// nothing yet.
46    #[default]
47    Nothing,
48    /// Text, which is every kind up to and including assembly.
49    Text(String),
50    /// An object file, which is `-c`, and the names a linker can find in it.
51    ///
52    /// The names travel with the bytes rather than beside them because what wants them is the
53    /// archive step, and an index entry that does not match the member is worse than no archive:
54    /// the linker searches the index, pulls the member out, and still reports the name undefined.
55    /// One value holding both is one value the two cannot disagree in.
56    Object {
57        /// The file.
58        bytes: Vec<u8>,
59        /// Every name another object can reach, as the object writer wrote them. Empty is a real
60        /// answer: a translation unit of nothing but `static` functions is a member an archive
61        /// carries and nothing ever pulls out.
62        defines: Vec<String>,
63    },
64}
65
66impl Artifact {
67    /// The bytes to write, which is nothing at all for [`Artifact::Nothing`].
68    #[must_use]
69    pub fn bytes(&self) -> &[u8] {
70        match self {
71            Artifact::Nothing => &[],
72            Artifact::Text(text) => text.as_bytes(),
73            Artifact::Object { bytes, .. } => bytes,
74        }
75    }
76}
77
78/// What compiling one file produced.
79#[derive(Debug, Clone, PartialEq, Eq)]
80pub struct Compiled {
81    /// What to write, which is nothing when the compilation failed or produced nothing.
82    pub artifact: Artifact,
83    /// The diagnostics, already rendered, one per element, in the order they were reported.
84    pub messages: Vec<String>,
85    /// How many of them were errors.
86    pub errors: u32,
87    /// Which lowering rules this file fired, for `-Zrule-coverage`.
88    ///
89    /// Empty for a compilation that stopped before the back end, which every kind up to and
90    /// including `--emit=ir` does. That is not the same as a rule set nothing reaches and the
91    /// caller unions these rather than reading one, so a file that fired nothing adds nothing.
92    pub fired: Fired,
93    /// What the register allocator had to put on the stack, for `-Zregister-pressure`.
94    ///
95    /// Empty for the same compilations `fired` is empty for and for the same reason, since both
96    /// are written by the back end and neither is a fact a file that stopped before it has.
97    pub pressure: Pressure,
98    /// What the pre-selection lowering group did, for `-Zlowering`.
99    ///
100    /// Empty for the same compilations `fired` is empty for and for the same reason, since the
101    /// group runs in the back end and a file that stopped before it lowered nothing.
102    pub lowerings: Lowerings,
103    /// What `-fdump-ir=` asked to see, in the order the passes ran.
104    ///
105    /// The optimizer does not write files, because nothing below the driver in
106    /// `spec/18-package-layout.md` knows what a file is, so the text comes back here and the
107    /// caller decides where it goes.
108    pub dumps: Vec<rucc_opt::Dump>,
109    /// What `-fopt-info` asked to hear, already rendered, one remark per line.
110    ///
111    /// Empty when the flag was not given, and also empty when it was given and no pass had
112    /// anything of the kinds asked for to say. Those two are the same text and different facts,
113    /// which is why a misspelled keyword is an error rather than a quiet nothing.
114    pub remarks: String,
115    /// Every file an `#include` found, for the `-M` family.
116    ///
117    /// The same list `Preprocessed` carries and for the same reason. A `-MD` writes it beside
118    /// the object, so the compiling path needs it as much as the preprocessing one does.
119    pub deps: Vec<rucc_pp::Dependency>,
120    /// What `-save-temps` asked to be kept, which is nothing at all unless it was given.
121    ///
122    /// It comes back from here rather than being produced by a second run of the compiler under
123    /// different flags, because a second run is a second answer: the file a person reads has to
124    /// be the file that was compiled, and two runs of anything with a `__TIME__` in it are not
125    /// the same text.
126    pub temps: Temps,
127    /// Where the time went, phase by phase and pass by pass, for `-frucc-trace`.
128    pub timing: crate::trace::Timing,
129}
130
131/// The intermediate text a compilation went through, kept when `-save-temps` asked for it.
132///
133/// Both are `None` on a compilation that was not asked to keep anything, and the assembly is
134/// `None` on one that stopped before there was any. Holding the text rather than writing it is
135/// what keeps this function free of the file system, which is what lets it be tested against a
136/// map from path to bytes.
137#[derive(Debug, Clone, PartialEq, Eq, Default)]
138pub struct Temps {
139    /// Phase 4's output, the same text `-E` would have printed.
140    pub preprocessed: Option<String>,
141    /// The assembly the back end produced on the way to the object file.
142    pub assembly: Option<String>,
143}
144
145impl Compiled {
146    /// Whether anything went wrong badly enough that the output should not be used.
147    #[must_use]
148    pub fn failed(&self) -> bool {
149        self.errors > 0
150    }
151
152    /// The text that was produced, and the empty string for anything that is not text.
153    ///
154    /// A caller that asked for one of the text kinds knows which it asked for, so this saves it
155    /// matching on a variant it has already ruled out.
156    #[must_use]
157    pub fn text(&self) -> &str {
158        match &self.artifact {
159            Artifact::Text(text) => text,
160            _ => "",
161        }
162    }
163}
164
165/// Compiles one file as far as `opts.emit` asks for and renders the result.
166///
167/// `name` is the path as the user wrote it, which is the name every diagnostic about the file
168/// uses. Every kind but the executable produces something today, and that one runs the same front
169/// end and gives back nothing, so that a file with a mistake in it is reported the same way
170/// whichever kind was asked for, rather than compiling silently until the part that is written
171/// notices.
172///
173/// The checking is skipped when the parse reported an error. The two poisoning rules mean a
174/// diagnosed expression produces no further complaints, but a declaration the parser had to skip
175/// past leaves no declaration behind at all, and every later use of that name would be reported
176/// as undeclared. One mistake is worth one message.
177#[must_use]
178pub fn compile(opts: &Options, name: &str, fs: &dyn FileSystem) -> Compiled {
179    let mut clock = crate::trace::Clock::start();
180    let mut sess = Session::new(opts.clone());
181    // Before anything else interns a name. The keyword symbols have to be one unbroken run for
182    // a lookup to be a subtraction, and the preprocessor interns every identifier it reads, so
183    // building this after the expansion would mean building it after `char` had been seen.
184    let keywords = Keywords::new(&mut sess.interner, opts.std, opts.gnu_extensions);
185    let mut diagnostics: Vec<Diagnostic> = Vec::new();
186    // Filled in by the back end when there is one, and empty for every kind that stops before it.
187    let mut fired = Fired::new();
188    // The same, and the other thing the back end is asked to record about itself.
189    let mut pressure = Pressure::new();
190    let mut lowerings = Lowerings::asked(opts.lowering_dump.is_some());
191    // Filled in by the optimizer, and only when `-fdump-ir=` asked for something.
192    let mut dumps = Vec::new();
193    let mut remarks = String::new();
194    // How long each optimizer pass took, for `-frucc-trace`.
195    let mut passes = Vec::new();
196    // Filled in as the compilation goes past each of them, and only under `-save-temps`.
197    let mut temps = Temps::default();
198
199    let bytes = match fs.read(Path::new(name)) {
200        Ok(bytes) => bytes,
201        Err(e) => return failure(format!("{name}: {e}")),
202    };
203    let Ok(file) = sess.sources.add_shared(crate::phase::source_name(name), bytes, None) else {
204        return failure(format!("{name}: the source map has no room left for this file"));
205    };
206    clock.lap("read");
207
208    // Phases 1 to 4. The expanded stream is turned into pp-tokens straight away, because the
209    // include context borrows the source map that rendering a diagnostic reads and the borrow
210    // has to end before anything is rendered.
211    let mut pp = rucc_pp::Preprocessor::with_prefix_map(opts.prefix_map.macros.clone());
212    let predef = rucc_pp::Predef::for_options(opts);
213    let expanded: Vec<PpToken> = {
214        let mut tokens = Vec::new();
215        // The inner block is the borrow. The printer under `-save-temps` reads the source map
216        // that the include context is holding, so the context has to be gone before it runs, and
217        // nothing happens in between, which is what makes the text it prints the text that is
218        // compiled below rather than a second answer to the same question.
219        {
220            let mut cx =
221                rucc_pp::Context::new(&mut sess.interner, &mut sess.sources, fs, &opts.search);
222            cx.lex = rucc_lex::Options::for_dialect(opts.std, opts.gnu_extensions);
223            cx.pedantic = opts.pedantic;
224            if pp.predefine(&sess.target, &predef, &mut cx).is_err() {
225                return failure(format!(
226                    "{name}: the source map has no room for the built in macros"
227                ));
228            }
229            if pp.preinclude(&opts.preincludes, &mut tokens, &mut cx).is_err() {
230                return failure(format!("{name}: the source map has no room for the command line"));
231            }
232            tokens.append(&mut pp.run(file, &mut cx));
233        }
234        if opts.save_temps.wanted() {
235            temps.preprocessed = Some(rucc_pp::print(
236                file,
237                &tokens,
238                pp.line_directives(),
239                &sess.sources,
240                &sess.interner,
241                rucc_pp::PrintOptions { line_markers: opts.line_markers },
242            ));
243        }
244        tokens.iter().map(|token| token.to_pp()).collect()
245    };
246    diagnostics.extend(pp.take_diagnostics());
247    // Taken here rather than at the end, because the preprocessor is done with and everything
248    // after this is about the tree it produced.
249    let deps = pp.dependencies().to_vec();
250    clock.lap("preprocess");
251
252    // Phase 7, which is where a spelling becomes a keyword and a preprocessing number becomes
253    // a constant of a type.
254    let cx = Convert {
255        keywords: &keywords,
256        interner: &sess.interner,
257        target: &sess.target,
258        std: opts.std,
259        gnu: opts.gnu_extensions,
260        pedantic: opts.pedantic,
261    };
262    let (tokens, complaints) = convert(&expanded, &cx);
263    diagnostics.extend(complaints);
264    clock.lap("convert");
265
266    // Only the ones the file wrote, since a name nothing interned is one nothing can use.
267    let type_names: Vec<Symbol> =
268        sess.target.type_names().iter().filter_map(|&(name, _)| sess.interner.find(name)).collect();
269    let parsed = rucc_parse::parse(
270        &tokens,
271        rucc_parse::Context {
272            interner: &sess.interner,
273            std: opts.std,
274            gnu: opts.gnu_extensions,
275            pedantic: opts.pedantic,
276            error_limit: opts.error_limit as usize,
277            type_names: &type_names,
278        },
279    );
280    clock.lap("parse");
281    let parse_failed = parsed.diagnostics.iter().any(|d| d.severity.is_fatal());
282    diagnostics.extend(parsed.diagnostics);
283
284    let mut artifact = Artifact::Nothing;
285    // Zero when nothing instruments, which is the truthful summary of a file built without
286    // `-fsafety`: no checks went in, so none is standing, and every call it makes is unmodelled.
287    let mut instrumented = Instrumented::default();
288    if !parse_failed {
289        let mut checker = Checker::new(
290            &parsed.ast,
291            CheckContext {
292                names: &sess.interner,
293                target: &sess.target,
294                std: opts.std,
295                gnu: opts.gnu_extensions,
296                pedantic: opts.pedantic,
297                permissive: opts.permissive,
298                gnu89_inline: opts.gnu89_inline,
299                error_limit: opts.error_limit as usize,
300                // A freestanding program has no C library, so a name that is the library's
301                // everywhere else is the program's own here and means whatever it defined.
302                builtins: opts.builtins && opts.hosted,
303                no_builtin: &opts.no_builtin,
304                short_enums: opts.short_enums,
305                ms_extensions: sess.ms_extensions(),
306                trapping_math: opts.trapping_math,
307                isa: opts.isa,
308            },
309        );
310        checker.check_unit();
311        let checked = checker.finish();
312        clock.lap("check");
313        if !checked.failed() {
314            match opts.emit {
315                EmitKind::Tast => {
316                    artifact = Artifact::Text(rucc_sema::print(
317                        &checked.tast,
318                        &checked.types,
319                        &sess.interner,
320                    ));
321                }
322                // Nothing past the checker, because a granule is a fact about a layout and a
323                // layout is settled the moment the closing brace is seen. Lowering the
324                // function bodies would take minutes on an amalgamation and answer nothing.
325                EmitKind::TypeGranules => {
326                    artifact = Artifact::Text(rucc_types::granule_report(
327                        &checked.types,
328                        &sess.interner,
329                        &sess.target,
330                    ));
331                }
332                EmitKind::Ir
333                | EmitKind::MirFinal
334                | EmitKind::Asm
335                | EmitKind::Object
336                | EmitKind::Archive
337                | EmitKind::Executable
338                | EmitKind::SafetySummary => {
339                    // What a `.incbin` in an `asm` at file scope names is read through the same
340                    // file system the sources came through, and from where the compiler was run
341                    // rather than from beside the source, because that is where an assembler
342                    // looks for it.
343                    let mut read = |named: &str| {
344                        fs.read(Path::new(named))
345                            .map(|bytes| bytes.as_slice().to_vec())
346                            .map_err(|why| why.to_string())
347                    };
348                    // What the debug information will say about types and signatures, taken
349                    // here because this is the last place the checker's types are readable
350                    // without the back end's borrow of the interner in the way. Nothing at all
351                    // when the build asked for no debug information, since a translation unit
352                    // the size of an amalgamation has tens of thousands of types in it.
353                    let meaning = if opts.debug_info {
354                        crate::shapes::collect(
355                            &checked.tast,
356                            &checked.types,
357                            &sess.target,
358                            &sess.interner,
359                            &sess.sources,
360                        )
361                    } else {
362                        crate::shapes::Meaning::default()
363                    };
364                    let mut lowered = rucc_lower::lower(
365                        crate::phase::source_name(name),
366                        rucc_lower::Context {
367                            tast: &checked.tast,
368                            types: &checked.types,
369                            target: &sess.target,
370                            names: &mut sess.interner,
371                            visibility: match opts.visibility {
372                                Visibility::Default => IrVisibility::Default,
373                                Visibility::Hidden => IrVisibility::Hidden,
374                                Visibility::Protected => IrVisibility::Protected,
375                            },
376                            protector: match opts.protector {
377                                Protector::None => LowerProtector::None,
378                                Protector::Buffers => LowerProtector::Buffers,
379                                Protector::Strong => LowerProtector::Strong,
380                                Protector::All => LowerProtector::All,
381                            },
382                            wrapping: rucc_lower::Wrapping {
383                                signed: opts.wrapping.signed,
384                                pointer: opts.wrapping.pointer,
385                                trap: opts.wrapping.trap,
386                            },
387                            aliasing: opts.strict_aliasing,
388                            padding: opts.padding == Padding::Ignored,
389                            contract: match opts.fp_contract {
390                                Contract::Off => FpContract::Off,
391                                Contract::On => FpContract::On,
392                                Contract::Fast => FpContract::Fast,
393                            },
394                            align: opts.align_functions,
395                            instrument: opts.instrument_functions,
396                            exceptions: opts.exceptions,
397                            read: &mut read,
398                        },
399                    );
400                    // The walk reports what it cannot build, and what it did build is printed
401                    // anyway: a file with one construct missing from it is more use to read
402                    // than nothing at all, and the errors are what stop it being compiled.
403                    clock.lap("lower");
404                    let failed = lowered.diagnostics.iter().any(|d| d.severity.is_fatal());
405                    if !failed {
406                        // The verifier runs on everything the walk builds, always. It is the
407                        // one check that a bug in the walk cannot talk its way past, and a
408                        // wrong instruction found here costs a message rather than an hour
409                        // in front of a debugger over the assembly it turned into.
410                        if let Err(errors) = clock
411                            .time("verify", || rucc_ir::verify(&lowered.module, &sess.interner))
412                        {
413                            for error in errors {
414                                diagnostics.push(internal(&format!("invalid IR, {error}")));
415                            }
416                        } else if let Err(complaints) = clock
417                            .time("instrument", || {
418                                instrument(&mut lowered.module, &mut sess.interner, opts)
419                            })
420                            .map(|done| instrumented = done)
421                        {
422                            diagnostics.extend(complaints);
423                        } else if let Err(complaints) = clock
424                            .time("optimize", || {
425                                optimize(
426                                    &mut lowered.module,
427                                    &mut sess.interner,
428                                    &sess.target,
429                                    opts,
430                                    name,
431                                    &mut dumps,
432                                    &mut remarks,
433                                )
434                            })
435                            .map(|times| passes = times)
436                        {
437                            diagnostics.extend(complaints);
438                        } else if opts.emit == EmitKind::SafetySummary {
439                            // After the optimizer, because the number that matters is how many
440                            // checks are still standing and there is no way to know that before it
441                            // has run. Before the back end, because the back end turns a check into
442                            // a call and a summary of calls is not a summary of checks.
443                            artifact = Artifact::Text(
444                                rucc_safety::summarize(
445                                    &lowered.module,
446                                    &sess.interner,
447                                    name,
448                                    opts.safety.as_str(),
449                                    instrumented.checks,
450                                    instrumented.interposed,
451                                    instrumented.crossings,
452                                )
453                                .render(),
454                            );
455                        } else if opts.emit == EmitKind::Ir {
456                            // After the optimizer rather than before it, so that `--emit=ir -O2`
457                            // is the IR the back end will be given rather than the IR it would
458                            // have been given at `-O0`. There is no other way to see what a pass
459                            // did without reading the assembly it turned into.
460                            artifact =
461                                Artifact::Text(rucc_ir::print(&lowered.module, &sess.interner));
462                        } else {
463                            // The back end, which is every pass after the IR and which is
464                            // where a construct nothing has a rule for is finally noticed.
465                            let made = clock.time("generate", || {
466                                generate(
467                                    &mut lowered.module,
468                                    &mut sess.interner,
469                                    &sess.target,
470                                    opts,
471                                    &mut Recording {
472                                        fired: &mut fired,
473                                        pressure: &mut pressure,
474                                        lowerings: &mut lowerings,
475                                    },
476                                    &mut temps.assembly,
477                                    Origin { map: &sess.sources, name, meaning: &meaning },
478                                )
479                            });
480                            match made {
481                                Ok(made) => artifact = made,
482                                Err(complaints) => diagnostics.extend(complaints),
483                            }
484                        }
485                    }
486                    diagnostics.extend(lowered.diagnostics);
487                }
488                // The checker has said everything it has to say, and that is all that was asked.
489                EmitKind::SyntaxOnly => {}
490                _ => {}
491            }
492        }
493        diagnostics.extend(checked.diagnostics);
494    }
495    // The back end's remarks after the optimizer's, which is the order the work happened in. Only
496    // the `switch` lowering says anything yet, and what it says is a rewrite.
497    let mut wants = rucc_opt::Wants::none();
498    for spec in &opts.opt_info {
499        // Checked when the arguments were parsed, and again by the optimizer.
500        let _ = wants.add(spec);
501    }
502    if wants.wants(rucc_opt::stats::Kind::Optimized) {
503        remarks.push_str(&lowerings.remarks(name));
504    }
505
506    let mut messages = Vec::with_capacity(diagnostics.len());
507    let mut errors = 0;
508    for diag in &diagnostics {
509        // `-w` drops the warning here rather than at the several hundred places one is raised,
510        // and it drops it before the count, so `-w -Werror` compiles. A warning that was never
511        // raised is not a warning there is anything to promote. A warning about something in a
512        // header that came with the machine goes the same way for the same reason, unless
513        // `-Wsystem-headers` asked for it.
514        if rucc_diag::dropped(diag, &sess.sources, opts.warnings, opts.system_header_warnings) {
515            continue;
516        }
517        if diag.severity.is_fatal()
518            || (diag.severity == Severity::Warning && opts.warnings_are_errors)
519        {
520            errors += 1;
521        }
522        messages.push(render(diag, &sess.sources, opts.warnings_are_errors));
523    }
524    if errors > 0 {
525        // A tree built from a file that did not compile is not a tree anything should read.
526        artifact = Artifact::Nothing;
527    }
528    // Kept even when the compilation failed, because a rule that fired did fire and a report about
529    // which rules a corpus reaches should not lose the ones a file with a mistake in it reached.
530    clock.passes(passes);
531    let timing = clock.finish();
532    Compiled {
533        artifact,
534        messages,
535        errors,
536        fired,
537        pressure,
538        lowerings,
539        dumps,
540        remarks,
541        deps,
542        temps,
543        timing,
544    }
545}
546
547/// Reads one file of IR, checks it, and prints it back.
548///
549/// This is the compiler's own textual IR arriving as an input rather than leaving as an output,
550/// which is what makes the round trip in the M2 exit criterion something to run rather than
551/// something to believe: what the printer wrote is read back, verified, and written again, and
552/// the two files are either the same bytes or they are not.
553///
554/// The verifier runs here for the reason it runs after the walk. A module that was printed by
555/// this compiler has been through it once already, and one that a person edited has not.
556#[must_use]
557pub fn compile_ir(opts: &Options, name: &str, fs: &dyn FileSystem) -> Compiled {
558    let mut sess = Session::new(opts.clone());
559    if opts.emit != EmitKind::Ir {
560        return failure(format!(
561            "{name}: an input of IR can only be emitted as IR, and `--emit={}` asks for what \
562             the C in front of it became",
563            opts.emit.as_str()
564        ));
565    }
566    let bytes = match fs.read(Path::new(name)) {
567        Ok(bytes) => bytes,
568        Err(e) => return failure(format!("{name}: {e}")),
569    };
570    let Ok(text) = std::str::from_utf8(bytes.as_slice()) else {
571        return failure(format!("{name}: this is not text, so it is not IR"));
572    };
573
574    let module = match rucc_ir::parse(text, &mut sess.interner) {
575        Ok(module) => module,
576        Err(error) => {
577            return failure(format!("{name}:{}: {}", error.line, error.message));
578        }
579    };
580    let mut diagnostics: Vec<Diagnostic> = Vec::new();
581    if let Err(errors) = rucc_ir::verify(&module, &sess.interner) {
582        for error in errors {
583            diagnostics.push(invalid(&format!("invalid IR, {error}")));
584        }
585    }
586    let mut messages = Vec::with_capacity(diagnostics.len());
587    for diag in &diagnostics {
588        messages.push(render(diag, &sess.sources, opts.warnings_are_errors));
589    }
590    let errors = u32::try_from(messages.len()).unwrap_or(u32::MAX);
591    let artifact = if errors > 0 {
592        Artifact::Nothing
593    } else {
594        Artifact::Text(rucc_ir::print(&module, &sess.interner))
595    };
596    // Nothing here reaches the back end, so no rule fired and there is nothing to record.
597    Compiled {
598        artifact,
599        messages,
600        errors,
601        fired: Fired::new(),
602        pressure: Pressure::new(),
603        lowerings: Lowerings::new(),
604        dumps: Vec::new(),
605        remarks: String::new(),
606        deps: Vec::new(),
607        temps: Temps::default(),
608        timing: crate::trace::Timing::default(),
609    }
610}
611
612/// Puts the memory safety checks in and redirects the calls that cross the boundary, when
613/// `-fsafety=` asked for them.
614///
615/// Between the walk and the optimizer, which is where section 15.3 of
616/// `spec/safe-memory/15-integration.md` puts it and which is the whole design in one line: the
617/// checks go in while the addresses the program computes still exist, and the optimizer then
618/// discharges the ones it can prove. Every sanitizer that came before instruments after the
619/// optimizer so that its checks cannot be deleted, and pays for all of them forever.
620///
621/// The calls to the C library are redirected here too, and in the same window and for a related
622/// reason. `spec/safe-memory/10-boundaries.md` section 10.3 wants a `memcpy` modelled by a wrapper
623/// that performs the judgements, and `rucc_safety::wrap` is why that has to happen before the
624/// optimizer sees the call rather than after.
625///
626/// The verifier runs again afterwards, for the reason it runs after the walk. This pass rewrites
627/// every function in the module, and a pass that produced IR nothing else accepts should say so
628/// here rather than in the assembly it turned into.
629///
630/// # Errors
631///
632/// When the inserted checks left the module in a state the verifier refuses, which is a bug in
633/// this compiler and not in the program being compiled.
634fn instrument(
635    module: &mut rucc_ir::Module,
636    names: &mut Interner,
637    opts: &Options,
638) -> Result<Instrumented, Vec<Diagnostic>> {
639    if !opts.safety.instruments() {
640        return Ok(Instrumented::default());
641    }
642    let mut checks = rucc_safety::run(module, opts.subobject, opts.promise, opts.races);
643    // The one check that is about a call rather than about an access, so it is a walk of its own
644    // and it is here rather than in the walk above. `rucc_safety::ending` is why, and the short
645    // version is that deciding it means resolving a name, which takes the interner.
646    //
647    // Before the redirection for the same reason the redirection is before the optimizer: what this
648    // reads is the name the program wrote, and a pass that had already pointed the call somewhere
649    // else would leave it with a name this one has no row for.
650    checks.freed = rucc_safety::ending::checks(module, names);
651    // Before the optimizer rather than beside the check lowering, which is what
652    // `rucc_safety::wrap` argues out: `memcpy` is a name an optimizer knows things about, and a
653    // pass that turns a short copy into a pair of loads and stores would leave behind accesses the
654    // check insertion has already finished walking past.
655    let interposed = rucc_safety::redirect(module, names);
656    // After the redirection, so that a call this build models with a wrapper is not also counted
657    // as a crossing it did not model.
658    let crossings = rucc_safety::witness(module, names);
659    match rucc_ir::verify(module, names) {
660        Ok(()) => Ok(Instrumented { checks, interposed, crossings }),
661        Err(errors) => Err(errors
662            .iter()
663            .map(|e| internal(&format!("invalid IR after check insertion, {e}")))
664            .collect()),
665    }
666}
667
668/// What the instrumentation did, which nothing but the summary reads.
669///
670/// Carried out of [`instrument`] rather than recovered from the module afterwards because neither
671/// number survives the optimizer: a check that was discharged leaves nothing behind saying it was
672/// ever there, and a call that was pointed at a wrapper looks like a call that always named one.
673#[derive(Clone, Copy, Debug, Default)]
674struct Instrumented {
675    /// How many checks of each class went in.
676    checks: rucc_safety::Counts,
677    /// How many calls were pointed at an interposition wrapper.
678    interposed: usize,
679    /// How many places a pointer crosses to or from code this build did not instrument.
680    crossings: rucc_safety::Sites,
681}
682
683/// Runs the optimizer over the module, and collects whatever the dumps asked for.
684///
685/// The level chooses a pipeline, the `-f` flags edit it, and at `-O0` there is nothing in it, so
686/// this is a walk over an empty list rather than a branch on the level. See section 9.1 of
687/// `spec/09-optimizer.md` for why the pipelines are written out rather than assembled.
688///
689/// Gives back how long each pass took, for `-frucc-trace`.
690///
691/// # Errors
692///
693/// When a pass left the module in a state the verifier refuses, which is a bug in the pass and
694/// not in the program being compiled, so it is reported as an internal error the way a bad
695/// lowering is.
696fn optimize(
697    module: &mut rucc_ir::Module,
698    names: &mut Interner,
699    target: &TargetInfo,
700    opts: &Options,
701    file: &str,
702    dumps: &mut Vec<rucc_opt::Dump>,
703    remarks: &mut String,
704) -> Result<Vec<(&'static str, std::time::Duration)>, Vec<Diagnostic>> {
705    let mut settings = rucc_opt::Options::for_level(opts.opt_level);
706    // What the analyses that read a body may believe about it. The same question the back end asks
707    // about addresses, with one thing on top: `-fno-semantic-interposition` is the build promising
708    // that a name it exports is the one that will run, which is what every distribution builds a
709    // library with. It says nothing about how an address is reached, and gcc does not change that
710    // under the flag either, so the back end is not given this value.
711    settings.interposition = match opts.interposition {
712        true => replaceable(target, opts),
713        false => IrPic::Executable,
714    };
715    settings.toggles.clone_from(&opts.passes);
716    // The same pair the front end reads a call to a standard name with, which is section 20.1's
717    // three way split: `-ffreestanding` says the library is not there, `-fno-builtin` says it is
718    // there and is not to be assumed to do what the standard says, and a fold that leaves behind a
719    // call to `puts` needs both of those to be off.
720    settings.builtins = opts.builtins && opts.hosted;
721    settings.no_builtin.clone_from(&opts.no_builtin);
722    // What a function with no `target` attribute is built for, which the inliner compares a
723    // callee with one against.
724    settings.isa = opts.isa;
725    settings.fuel = opts.pass_fuel.iter().cloned().collect();
726    settings.global_fuel = opts.pass_fuel_global;
727    settings.verify |= opts.verify_each;
728    for (on, spec) in &opts.pass_gates {
729        // Same argument as the dumps below: every spelling in here was checked while the
730        // arguments were parsed, so a rejection now is this compiler disagreeing with itself.
731        if let Err(why) = settings.gates.add(*on, spec) {
732            return Err(vec![internal(&why)]);
733        }
734    }
735    for spec in &opts.dump_ir {
736        // Every spelling in here was checked while the arguments were parsed, so a rejection
737        // now is this compiler disagreeing with itself rather than the command line being wrong.
738        if let Err(why) = settings.dumps.add(spec) {
739            return Err(vec![internal(&why)]);
740        }
741    }
742    let mut wants = rucc_opt::Wants::none();
743    for spec in &opts.opt_info {
744        // Same argument as the dumps above: every spelling was checked while the arguments were
745        // parsed, so a rejection now is the compiler disagreeing with itself.
746        if let Err(why) = wants.add(spec) {
747            return Err(vec![internal(&why)]);
748        }
749    }
750    let report = rucc_opt::run(module, names, &settings);
751    remarks.push_str(&rucc_opt::optinfo::render(file, &report, names, wants));
752    dumps.extend(report.dumps);
753    match report.broke.is_empty() {
754        true => Ok(report.time),
755        false => Err(report.broke.iter().map(|why| internal(why)).collect()),
756    }
757}
758
759/// Runs the back end over every function in `module` and writes what came out.
760///
761/// One machine function per definition in the module, in the order the module holds them, every
762/// register physical and every frame offset a constant. A declaration has no body and is skipped,
763/// because there is nothing in it to compile.
764///
765/// What the last step is, is the only thing `--emit=mir-final`, `-S` and `-c` disagree about. The
766/// three read the same functions and differ in whether they are printed as machine IR, printed as
767/// assembly, or encoded and put in a file, which is the point of section 11.1 of
768/// `spec/11-asm-objects-debug.md`: a listing that disagrees with the object file beside it is
769/// worse than no listing, and the way to make that impossible is to have one description of an
770/// instruction and two ways of writing it down.
771///
772/// # Errors
773///
774/// One diagnostic per function the back end could not compile, or one about the target when no
775/// back end covers it at all. Every function is attempted rather than stopping at the first, so a
776/// file with three constructs missing from the rule set reports three rather than one at a time.
777///
778/// `assembly` is where `-save-temps` gets its listing from on the path that does not print one,
779/// which is the same functions written the other way rather than a second compilation of the same
780/// file. A listing that disagrees with the object beside it would be worse than none.
781/// Whether a name this file exports is one another object may define or replace.
782///
783/// The link that reads the object decides half of what is in it, and the command line is where that
784/// is said, which is why the flag reaches this far down. See #756.
785///
786/// ELF only, because it is a question about a format rather than about a machine and the other two
787/// answer it differently. Mach-O has a two level namespace, so a name a library defines is bound to
788/// that library and is not replaced by a definition loaded earlier, and it has no copy relocations,
789/// so a variable defined elsewhere needs the table whichever link is coming. COFF decides what
790/// leaves a DLL by an export table the linker is handed. Neither has an object writer here yet, so
791/// what this does is decline to say the ELF answer about them.
792fn replaceable(target: &TargetInfo, opts: &Options) -> IrPic {
793    match (target.tuple.os().object_format(), opts.pic) {
794        (Some(ObjectFormat::Elf), Pic::Library) => IrPic::Library,
795        _ => IrPic::Executable,
796    }
797}
798
799/// Where the file being generated came from, which is what the debug information is about.
800///
801/// The three together rather than separately because none of them is any use on its own here: a
802/// span without the map it points into is a pair of numbers, a name without the spans is a file
803/// nothing in the object refers to, and a signature without the name of the function it belongs to
804/// is an entry with nothing to attach it to.
805#[derive(Clone, Copy)]
806struct Origin<'a> {
807    /// Where every span in the module points.
808    map: &'a SourceMap,
809    /// What the command line called the file, which is what `DW_AT_name` says.
810    name: &'a str,
811    /// The types and the signatures, and empty where the build wanted no debug information.
812    meaning: &'a crate::shapes::Meaning,
813}
814
815fn generate(
816    module: &mut rucc_ir::Module,
817    names: &mut Interner,
818    target: &TargetInfo,
819    opts: &Options,
820    recording: &mut Recording<'_>,
821    assembly: &mut Option<String>,
822    origin: Origin<'_>,
823) -> Result<Artifact, Vec<Diagnostic>> {
824    let Some(machine) = Machine::for_target(target) else {
825        return Err(vec![unsupported(&format!(
826            "there is no back end for {} in this compiler yet, so there is nothing to generate",
827            target.tuple
828        ))]);
829    };
830    // Refused rather than dropped. A command line that asks for a stack protector on a target
831    // that has nowhere to keep the word one is compared against would otherwise get code with no
832    // protection in it and no indication that the flag did nothing, which is the one outcome worse
833    // than the error. Windows is the case: it has a protector and it is a different mechanism.
834    if opts.protector != Protector::None && machine.conv.guard.is_none() {
835        return Err(vec![unsupported(&format!(
836            "{} is not supported for {} yet, because the stack protector on that target is not \
837             the one this compiler writes",
838            opts.protector, target.tuple
839        ))]);
840    }
841    // The same answer for the same reason. What says a file was built to have its control flow
842    // checked is a note, the note is an ELF one, and a target whose objects are not ELF has nowhere
843    // to put it: the landing pads would go in and nothing would ever turn the check on. Windows has
844    // the same hardware and asks for it a different way, which is a bit in the image the linker is
845    // told to set rather than anything a compiler writes into an object.
846    if opts.control.any() && target.tuple.os().object_format() != Some(ObjectFormat::Elf) {
847        return Err(vec![unsupported(&format!(
848            "-fcf-protection={} is not supported for {} yet, because what says a file was built \
849             for it there is not the note this compiler writes",
850            opts.control, target.tuple
851        ))]);
852    }
853    // And once more. A profiled build is one whose functions call a routine the runtime provides,
854    // and a target whose runtime provides no such routine would get a call to a name nothing
855    // defines, which is a link error a long way from the flag that caused it. Windows profiles a
856    // build by calling something else, asked for a different way and taking its argument in a
857    // register, so it is not this hook spelled differently.
858    let profile = match machine.conv.trace {
859        Some(trace) => opts.profile.then(|| opts.hook.early(trace.fentry)),
860        None if opts.profile => {
861            return Err(vec![unsupported(&format!(
862                "-pg is not supported for {} yet, because the profiler's hook on that target is \
863                 not the one this compiler calls",
864                target.tuple
865            ))]);
866        }
867        None => None,
868    };
869    // And once more. The room a patcher was promised is only half the feature: the other half is a
870    // section listing where every function's room is, and both the section's shape and the way it
871    // points at the text it belongs to are ELF's. A format that has no such section would take the
872    // nops and quietly lose the list, which is a build that looks patchable and is not.
873    if opts.patchable.any() && target.tuple.os().object_format() != Some(ObjectFormat::Elf) {
874        return Err(vec![unsupported(&format!(
875            "-fpatchable-function-entry= is not supported for {} yet, because what records where \
876             the room is there is not the section this compiler writes",
877            target.tuple
878        ))]);
879    }
880    let flags = pipeline::Flags {
881        frame_pointer: opts.keeps_frame_pointer(),
882        red_zone: opts.red_zone,
883        stack_clash: opts.stack_clash,
884        landing: opts.control.branch(),
885        profile: match profile {
886            None => pipeline::Profile::No,
887            Some(true) => pipeline::Profile::Early,
888            Some(false) => pipeline::Profile::Late,
889        },
890        patch: pipeline::Room { after: opts.patchable.after(), before: opts.patchable.before },
891        // On at every level above `-O0`, which is where gcc turns `-freorder-blocks` on
892        // (`gcc/opts.cc:604`) and what `spec/optimizer/38-scheduling-and-layout.md` section 38.3
893        // reads off that: it is one of the earliest optimizations there is, it is nearly free,
894        // and it helps every target. `-O0` keeps the order the shape of the graph gives, so that
895        // the blocks come out in the order they were written and a person stepping through the
896        // code walks down the screen.
897        reorder: opts.reorder_blocks.unwrap_or_else(|| opts.opt_level.runs_optimizer()),
898        // On at every level above `-O0`, for the reason the line above is off at it. Sharing one
899        // run of bytes between two locals is a smaller frame and a worse debugger: a variable that
900        // is out of scope reads as whatever took its place, which is what `-O0` exists not to do.
901        // Above it the frame is the win, and `-fstack-reuse=` says either answer at any level.
902        reuse: opts.stack_reuse.unwrap_or_else(|| opts.opt_level.runs_optimizer()),
903        // On from `-O2`, which is where gcc turns `-fschedule-insns2` on and what
904        // `spec/optimizer/38-scheduling-and-layout.md` section 38.6 asks for. Not at `-O1`,
905        // because a schedule is a whole dependence graph per block and `-O1` is the level whose
906        // budget is roughly `-O0`'s. Not at `-O0` for the reason nothing else is.
907        schedule: opts.schedule_insns.unwrap_or_else(|| opts.opt_level.schedules()),
908        // Off unless asked for. gcc pads loops at `-O2` and `-O3`. gcc's padding here cost a third
909        // of a percent of the corpus's text and more than a percent of SQLite's for no speed
910        // anybody could measure, which is tamnd/rucc#1823. The padding this asks for now keeps a
911        // small loop inside one line, which is 18% on AMD EPYC and nothing on an Intel Core, so no
912        // level asks for it on every machine's behalf. See tamnd/rucc#1838.
913        align_loops: opts.align_loops.unwrap_or(false),
914        // Whatever the command line said, and the model's own answer when it said nothing.
915        accurate: opts.cycle_accurate_model,
916        // The same flag that turns the IR verifier on in a release build, since what it says is
917        // that this run should check itself and the back end has checks of its own.
918        verify: opts.verify_each,
919        // What the level asked for. The back end had no way to know until now, which is
920        // tamnd/rucc#741: `-Os` picked a shorter list of middle end passes and then compiled the
921        // result exactly as `-O2` would have. The level is asked whether it optimizes for size
922        // rather than matched against, so a level added later answers this without editing it.
923        goal: Goal::for_size(opts.opt_level.is_size()),
924        // Only when somebody is measuring, and checked when the arguments were parsed.
925        switch: opts.switch_shape.as_deref().and_then(rucc_codegen::switch::Force::named),
926        // On from `-O2` and at `-Os`, which is where gcc turns `-foptimize-sibling-calls` on.
927        sibling: opts.sibling_calls.unwrap_or_else(|| opts.opt_level.sibling_calls()),
928    };
929
930    // The checks become calls here rather than beside the insertion, because the id each one
931    // carries is an index into a table and a row for a check the optimizer deleted is a row nothing
932    // will ever name. Section 6.3.1 of `spec/safe-memory/06-instrumentation.md` is what this
933    // eventually becomes and `rucc_safety::lower` says why it is not that yet.
934    //
935    // It is inside the back end rather than beside the optimizer so that `--emit=ir` still shows
936    // the checks. The IR a person reads should say what the compiler decided, not how it spelled it
937    // for the machine.
938    if opts.safety.instruments() {
939        // Which calls hand back storage, which the lowering needs and `-O0` has not worked out.
940        // `rucc_opt::pipeline` runs this only when some pass in the run reads the summaries, since a
941        // flag nothing reads is noise in a dump, and at `-O0` nothing did. Something does now: the
942        // capability for a pointer an allocator just returned is the one capability that is exact
943        // and costs a load, and `rucc_safety::slot` finds those sites by the flag. The safety suite
944        // runs at `-O0`, so without this the cheap case would be the one case that never happens.
945        //
946        // Safe to run twice and safe to run late, because it only ever sets the flag and never
947        // clears one, so a build that had it already gets the same module back.
948        rucc_opt::heap::annotate(module, names);
949        // Which calls hand their capabilities to the callee and which say there are none. Here and
950        // not beside the insertion, because the rule is what each function still has left to check
951        // and the optimizer is what makes that small: running before it would give every callee a
952        // frame for checks that are about to be discharged. `rucc_safety::handover` is the rule and
953        // the pass both, and the census in `--emit=safety-summary` reads the same rule, so the
954        // buckets it prints describe the code that was actually built.
955        rucc_safety::handover::arrange(module);
956        rucc_safety::lower(module, names);
957        if let Err(errors) = rucc_ir::verify(module, names) {
958            return Err(errors
959                .iter()
960                .map(|e| internal(&format!("invalid IR after check lowering, {e}")))
961                .collect());
962        }
963    }
964
965    // Worked out before the loop and not inside it, because it reads the whole module and the loop
966    // is holding one function of it. It has to be after the check lowering above, since that adds
967    // calls to the runtime and so can add a name this file does not define.
968    //
969    // The link that reads the object decides half of what is in it, and the command line is where
970    // that is said, which is why the flag reaches this far down. See #756. The format decides the
971    // other half, since a table only exists on a format that has one to reach through.
972    //
973    // Only x86-64 copies a variable into the executable for a reference from the instruction
974    // pointer, so on the other machines a variable this file only declares is read from the table.
975    let copies = target.tuple.arch() == Arch::X86_64;
976    let elsewhere = Elsewhere::of(module, replaceable(target, opts), target.object_format, copies);
977
978    let mut funcs = Vec::new();
979    let mut complaints = Vec::new();
980    for id in module.funcs() {
981        if module[id].is_declaration() {
982            continue;
983        }
984        match pipeline::compile_recording(
985            &mut module[id],
986            names,
987            &machine,
988            &elsewhere,
989            flags,
990            recording,
991        ) {
992            Ok(func) => funcs.push(func),
993            Err(why) => {
994                let name = names.resolve(module[id].name).to_owned();
995                // The function knows where the instruction came from, so the message lands on
996                // the line somebody wrote rather than on the file as a whole.
997                let span = why.inst().map_or(Span::DUMMY, |inst| module[id].span(inst));
998                let said = format!("cannot generate code for '{name}': {why}");
999                complaints.push(unsupported_at(&said, span));
1000            }
1001        }
1002    }
1003    if !complaints.is_empty() {
1004        return Err(complaints);
1005    }
1006    // The variables the file defines, which go through the back end the way the functions did not:
1007    // there is nothing in a variable to select instructions for, so the module is what says what
1008    // one is right up to the point where it is written down.
1009    // The second names go the same way and for the same reason, and they are neither a function
1010    // nor a variable: an alias is an entry in the symbol table and no bytes of anything.
1011    let (globals, aliases) = match opts.emit {
1012        EmitKind::Asm | EmitKind::Object | EmitKind::Archive | EmitKind::Executable => (
1013            rucc_asm::globals(module, names, target.object_format).map_err(refused)?,
1014            rucc_asm::aliases(module, names).map_err(refused)?,
1015        ),
1016        _ => (rucc_asm::Globals::default(), Vec::new()),
1017    };
1018    // A failure in either of the last two is a bug here rather than a program this compiler is
1019    // behind on, because every instruction in a function that got this far came out of the same
1020    // description both of them read and every register in it has been allocated.
1021    let unwind = opts.unwinds();
1022    match opts.emit {
1023        EmitKind::Asm => {
1024            rucc_asm::print(&funcs, &globals, &aliases, names, target, unwind, output(opts, target))
1025                .map(Artifact::Text)
1026                .map_err(refused)
1027        }
1028        // An executable is an object as far as this gets: one is what each file of a link
1029        // contributes, and the linker is what turns them into the other. An archive is the same
1030        // again, with the archive writer in place of the linker.
1031        EmitKind::Object | EmitKind::Archive | EmitKind::Executable => {
1032            if opts.save_temps.wanted() {
1033                let listing = rucc_asm::print(
1034                    &funcs,
1035                    &globals,
1036                    &aliases,
1037                    names,
1038                    target,
1039                    unwind,
1040                    output(opts, target),
1041                );
1042                *assembly = Some(listing.map_err(refused)?);
1043            }
1044            // A template kept as text has no bytes until an assembler reads it. Most are read on
1045            // their own where they are, but one may jump to a label another statement's text
1046            // defines or switch section halfway through, and a unit with one of those in it is
1047            // assembled the way gcc assembles every unit: written out as a listing and read back.
1048            // A build that asked for debug information gets a label in front of every instruction,
1049            // and where the reader placed those is the row the encoder would have recorded.
1050            //
1051            // Every unit for AArch64 goes this way for now. The listing is already written from
1052            // the encoder's own tables, so reading it back is the encoder run over the same values,
1053            // and it is one path to get right rather than two.
1054            let aarch64 = target.tuple.arch() == Arch::Aarch64;
1055            if aarch64 || globals.kept() || rucc_asm::kept(&funcs, names, target) {
1056                // The reader keeps the frame rows of a listing but not the personality routine or
1057                // the call site tables, so a unit with a landing pad read back would unwind
1058                // straight past its cleanups. Saying so beats a program that skips them.
1059                if funcs.iter().any(|func| !func.landings.is_empty()) {
1060                    return Err(vec![unsupported(
1061                        "a cleanup that runs during an unwind, in a unit whose listing is read \
1062                         back by the assembler",
1063                    )]);
1064                }
1065                let print = if opts.debug_info { rucc_asm::print_marked } else { rucc_asm::print };
1066                let listing =
1067                    print(&funcs, &globals, &aliases, names, target, unwind, output(opts, target))
1068                        .map_err(refused)?;
1069                let arch = target.tuple.arch();
1070                let read =
1071                    rucc_asm::read_as(&listing, arch, target.object_format).map_err(|trouble| {
1072                        let what = if aarch64 {
1073                            "a unit for aarch64"
1074                        } else if globals.kept() {
1075                            "an `asm` at file scope"
1076                        } else {
1077                            "an `asm` template kept as text"
1078                        };
1079                        vec![unsupported(&format!(
1080                            "{what}, whose listing the assembler stopped at on line {}: {}",
1081                            trouble.line, trouble.why
1082                        ))]
1083                    })?;
1084                let info = if opts.debug_info {
1085                    let assembled =
1086                        placed(&read, &funcs, names, target).map_err(|why| vec![internal(&why)])?;
1087                    describe(&assembled, &globals.image(), &funcs, origin, opts, target)
1088                        .map_err(|why| vec![internal(&why)])?
1089                } else {
1090                    rucc_object::Info::default()
1091                };
1092                let defines = rucc_object::assembled_defines(&read);
1093                let bytes =
1094                    rucc_object::assembled_described(&read, target, &info).map_err(wrote)?;
1095                return Ok(Artifact::Object { bytes, defines });
1096            }
1097            let assembled = rucc_asm::assemble(&funcs, names, target, unwind, opts.debug_info)
1098                .map_err(refused)?;
1099            let data = globals.image();
1100            // The line table, from the spans the assembler kept beside the bytes. Empty when the
1101            // build asked for no debug information, which is the case the rows above are not even
1102            // recorded in.
1103            let info = if opts.debug_info {
1104                describe(&assembled, &data, &funcs, origin, opts, target)
1105                    .map_err(|why| vec![internal(&why)])?
1106            } else {
1107                rucc_object::Info::default()
1108            };
1109            let text = assembled.text;
1110            // A format with no writer is a target this compiler is behind on and anything else
1111            // the writer refused is a bug here, and the two are not the same news to get.
1112            let bytes =
1113                rucc_object::write(&text, &data, &aliases, target, output(opts, target), &info)
1114                    .map_err(wrote)?;
1115            // Asked of the writer rather than worked out from the same three values here, so that
1116            // what the archive's index says and what is in the member cannot come apart. It is
1117            // wanted only by `--emit=archive` and is cheap enough that the other two kinds are not
1118            // worth a second path.
1119            let defines = rucc_object::defines(&text, &data, &aliases, target).map_err(wrote)?;
1120            Ok(Artifact::Object { bytes, defines })
1121        }
1122        _ => Ok(Artifact::Text(rucc_mir::print(&funcs, names, target.regs))),
1123    }
1124}
1125
1126/// The rows a listing marked by [`rucc_asm::print_marked`] would have had from the encoder, read
1127/// off where the reader placed each label.
1128///
1129/// Each function is where its own symbol is and as long as its `.size` says, and each row is its
1130/// label's distance from the symbol. The row for the front of the function is the one the encoder
1131/// writes from `Func::declared`, and it is written here the same way.
1132///
1133/// # Errors
1134///
1135/// A function or a label the reader did not place, which is a listing this compiler wrote and got
1136/// wrong.
1137fn placed(
1138    read: &rucc_object::Assembled,
1139    funcs: &[rucc_mir::Func],
1140    names: &Interner,
1141    target: &TargetInfo,
1142) -> Result<rucc_asm::Assembled, String> {
1143    let at: HashMap<&str, &rucc_object::Name> =
1144        read.names.iter().map(|name| (name.name.as_str(), name)).collect();
1145    let offset = |name: &str| match at.get(name).map(|name| name.at) {
1146        Some(rucc_object::Held::In { part, offset }) => Some((part, offset)),
1147        _ => None,
1148    };
1149    let mut text = rucc_object::Text::default();
1150    let mut lines = Vec::with_capacity(funcs.len());
1151    for (which, func) in funcs.iter().enumerate() {
1152        let name = names.resolve(func.name);
1153        let Some((part, start)) = offset(name) else {
1154            return Err(format!("the listing has no label for the function '{name}'"));
1155        };
1156        let mut rows = Vec::with_capacity(func.inst_count() + 1);
1157        if !func.declared.is_dummy() {
1158            rows.push(rucc_asm::Row { at: 0, span: func.declared, inst: None });
1159        }
1160        for block in func.blocks() {
1161            for inst in func.insts(block) {
1162                let label = rucc_asm::mark(target, which, inst);
1163                let Some((held, here)) = offset(&label) else {
1164                    return Err(format!("the listing has no label '{label}'"));
1165                };
1166                if held != part || here < start {
1167                    return Err(format!("the label '{label}' is not inside '{name}'"));
1168                }
1169                let at = usize::try_from(here - start).map_err(|why| why.to_string())?;
1170                rows.push(rucc_asm::Row { at, span: func.span(inst), inst: Some(inst) });
1171            }
1172        }
1173        let len = at.get(name).map_or(0, |name| name.size);
1174        text.funcs.push(rucc_object::Extent {
1175            name: name.to_owned(),
1176            start: usize::try_from(start).map_err(|why| why.to_string())?,
1177            len: usize::try_from(len).map_err(|why| why.to_string())?,
1178            align: func.align.unwrap_or(rucc_object::FUNC_ALIGN),
1179            binding: rucc_object::Binding::Global,
1180            visibility: rucc_object::Visibility::Default,
1181            patch: None,
1182            landings: Vec::new(),
1183        });
1184        lines.push(rows);
1185    }
1186    Ok(rucc_asm::Assembled { text, lines, frames: None })
1187}
1188
1189/// The debug sections for what was just assembled, as bytes and relocations.
1190///
1191/// This is where a span becomes a file and a line, and it is here rather than anywhere further down
1192/// because the source map is the driver's and because the paths in it are still paths at this point.
1193/// [`rucc_session::PrefixMap::apply`] is run over every one of them, which is the whole of what
1194/// `-fdebug-prefix-map=` and `-ffile-prefix-map=` asked for: a build is only reproducible if all of
1195/// the paths in it are rewritten rather than most, so the file names, the name of the unit and the
1196/// directory it was compiled in all go through it.
1197///
1198/// A row whose span is [`Span::DUMMY`] is dropped rather than written at line zero. Those are the
1199/// instructions a pass invented, a prologue and a spill among them, and a debugger asking what a
1200/// program counter is in the middle of is better told the line before than told a line that is not
1201/// in the file. The row that follows covers those bytes, which is the same answer gcc gives.
1202///
1203/// # Errors
1204///
1205/// Whatever the DWARF writer refused, which is a bug here rather than a program this compiler is
1206/// behind on.
1207fn describe(
1208    assembled: &rucc_asm::Assembled,
1209    data: &rucc_object::Data,
1210    machine: &[rucc_mir::Func],
1211    origin: Origin<'_>,
1212    opts: &Options,
1213    target: &TargetInfo,
1214) -> Result<rucc_object::Info, String> {
1215    let rucc_asm::Assembled { text, lines, frames } = assembled;
1216    let rewrite = |path: &str| opts.prefix_map.debug.apply(path).into_owned();
1217    // The file table, built as the rows are walked rather than up front, because what belongs in it
1218    // is the files the code came from and not the files the preprocessor opened. A header that
1219    // contributed nothing but declarations is not one of them, and one that holds a definition is
1220    // in it twice over: once for the rows and once for the line the definition is declared on.
1221    let mut files: Vec<String> = Vec::new();
1222    let mut funcs = Vec::with_capacity(text.funcs.len());
1223    for ((extent, rows), built) in text.funcs.iter().zip(lines).zip(machine) {
1224        let mut out: Vec<rucc_debug::Row> = Vec::with_capacity(rows.len());
1225        for row in rows {
1226            if row.span.is_dummy() {
1227                continue;
1228            }
1229            let Some(at) = origin.map.presumed(row.span.lo) else {
1230                continue;
1231            };
1232            let which = interned(&mut files, rewrite(at.name));
1233            let place = rucc_debug::Row {
1234                at: row.at as u64,
1235                file: which,
1236                line: at.line,
1237                column: at.column,
1238            };
1239            // Two rows at one address is one row, and the first of the two wins. The only place it
1240            // happens is the front of a function, where the row the assembler writes for the
1241            // declaration and the row for the first instruction land on the same byte, which is
1242            // what a function this compiler built no prologue for looks like: two instructions
1243            // cannot start at one address, so nowhere else has the question. The declaration is the
1244            // better answer there because it is the answer gcc gives, which it gives because gcc
1245            // always builds a frame at -O0 and so always has a byte of prologue for the brace to be
1246            // about. A breakpoint on a function wants the line of the function rather than the line
1247            // of whatever its first statement happened to be.
1248            match out.last() {
1249                Some(last) if last.at == place.at => {}
1250                _ => out.push(place),
1251            }
1252        }
1253        // And the front of the function, for a function whose declaration had no span to give. The
1254        // assembler writes a row there from `Func::declared` and that is the usual way this is
1255        // covered, but a function that came from something other than a C source has no such span,
1256        // and the front of one is the one part of it no row would otherwise cover. A program
1257        // counter in there would get no answer at all rather than a slightly early one, and no
1258        // answer is the worse of the two for anybody reading a backtrace.
1259        if let Some(first) = out.first_mut() {
1260            first.at = 0;
1261        }
1262        // And what the function is, for the one this unit holds a definition of. A function the
1263        // walk above found and this did not is one whose name in the object is not the name the
1264        // declaration had, which `__asm__` on a declaration is the way to arrange, and one whose
1265        // signature could not be described. Both get rows and no entry, which leaves a debugger
1266        // where it is for every function today rather than anywhere worse.
1267        let known = origin.meaning.funcs.get(&extent.name);
1268        let decl = known.map(|known| rucc_debug::Place {
1269            file: interned(&mut files, rewrite(&known.file)),
1270            line: known.line,
1271        });
1272        // And where each of its locals is, for the ones the frame gave a slot. The back end hands
1273        // back the declaration each of them is and how far below the frame base it ended up, and
1274        // this is where a number turns back into a name, a type and a line, because this is the
1275        // last place the checker's declarations are still in hand.
1276        //
1277        // A parameter goes on the entry the signature already wrote for it rather than getting one
1278        // of its own, which is what the parameter numbers on the function are for. Two entries of
1279        // one name in one scope is a debugger's problem rather than a reader's.
1280        let mut sig = known.and_then(|known| known.sig.clone());
1281        let mut placed: Vec<(u32, i32)> = built.locals.clone();
1282        let mut spots = stretches(extent, rows, built, target);
1283        // And a local in the frame that shares its bytes and has no stretch at all, which still
1284        // gets its entry so that a debugger says it is not available rather than that there is no
1285        // such name. That is a function whose instructions were scheduled, where no stretch can be
1286        // given, and the whole of it is then somewhere the local may not be.
1287        for &decl in &built.sharing {
1288            if !spots.iter().any(|(at, _)| *at == decl) {
1289                spots.push((decl, Vec::new()));
1290            }
1291        }
1292        if let (Some(sig), Some(known)) = (sig.as_mut(), known) {
1293            for (param, decl) in sig.params.iter_mut().zip(&known.params) {
1294                let Some(decl) = *decl else { continue };
1295                if let Some(which) = placed.iter().position(|&(at, _)| at == decl) {
1296                    let at = rucc_debug::Held::Frame(i64::from(placed.remove(which).1));
1297                    param.spot = Some(rucc_debug::Spot::Always(at));
1298                    continue;
1299                }
1300                // Or the stretches, for a parameter the front end kept in a value rather than in
1301                // the frame, which is what a scalar parameter whose address is never taken is at
1302                // every optimization level including this one.
1303                let Some(which) = spots.iter().position(|(at, _)| *at == decl) else { continue };
1304                param.spot = Some(rucc_debug::Spot::Over(spots.remove(which).1));
1305            }
1306        }
1307        // Whatever is left, which is the locals that are not parameters, in the order the slots
1308        // were asked for. A number with nothing to look up is one whose declaration had no name,
1309        // which is a compound literal rather than anything the program can ask the value of.
1310        let mut locals = Vec::with_capacity(placed.len() + spots.len());
1311        // And which scope each of them was declared in, kept beside the list rather than on it,
1312        // because what goes on the entry is a place in this function's own table of scopes and that
1313        // table is not known until every local has been looked up.
1314        let mut wants: Vec<Option<usize>> = Vec::with_capacity(locals.capacity());
1315        for (decl, at) in placed {
1316            let Some(named) = origin.meaning.locals.get(&decl) else { continue };
1317            wants.push(named.scope);
1318            locals.push(rucc_debug::Local {
1319                name: named.name.clone(),
1320                ty: named.ty,
1321                decl: Some(rucc_debug::Place {
1322                    file: interned(&mut files, rewrite(&named.file)),
1323                    line: named.line,
1324                }),
1325                spot: rucc_debug::Spot::Always(rucc_debug::Held::Frame(i64::from(at))),
1326                scope: None,
1327            });
1328        }
1329        // And the ones with no slot at all, which are the locals the front end kept in a value.
1330        // Sorted by declaration, which is the order the program declared them in, so that what
1331        // comes out does not depend on the order the back end happened to hand registers out in.
1332        spots.sort_by_key(|(decl, _)| *decl);
1333        for (decl, spans) in spots {
1334            let Some(named) = origin.meaning.locals.get(&decl) else { continue };
1335            wants.push(named.scope);
1336            locals.push(rucc_debug::Local {
1337                name: named.name.clone(),
1338                ty: named.ty,
1339                decl: Some(rucc_debug::Place {
1340                    file: interned(&mut files, rewrite(&named.file)),
1341                    line: named.line,
1342                }),
1343                spot: rucc_debug::Spot::Over(spans),
1344                scope: None,
1345            });
1346        }
1347        // And the scopes the locals were declared in, which is where a name declared in an inner
1348        // block stops being one of the function's own. The numbers the walk over the tree handed out
1349        // are over the whole unit, and what goes on an entry is a place in this function's table, so
1350        // the two are joined here.
1351        let (scopes, at) = nests(&wants, &origin.meaning.scopes, extent, rows);
1352        for (local, want) in locals.iter_mut().zip(&wants) {
1353            local.scope = want.and_then(|want| at.get(&want).copied());
1354        }
1355        funcs.push(rucc_debug::Function {
1356            name: extent.name.clone(),
1357            len: extent.len as u64,
1358            rows: out,
1359            decl,
1360            sig,
1361            external: known.is_some_and(|known| known.external),
1362            locals,
1363            scopes,
1364        });
1365    }
1366    // And the file-scope variables, from the objects the back end laid out rather than from the
1367    // declarations, so that a name with an entry here is a name with a symbol to relocate against.
1368    // One the walk found and this did not is a `static` nothing read, and one this found and the
1369    // walk did not is a name the compiler made up rather than one the program wrote, a string
1370    // literal and a compound literal being the two: both are in the file and neither is a variable
1371    // anybody can ask the value of by name.
1372    let mut globals = Vec::new();
1373    for object in &data.objects {
1374        let Some(held) = origin.meaning.objects.get(&object.name) else { continue };
1375        globals.push(rucc_debug::Global {
1376            name: object.name.clone(),
1377            ty: held.ty,
1378            decl: Some(rucc_debug::Place {
1379                file: interned(&mut files, rewrite(&held.file)),
1380                line: held.line,
1381            }),
1382            external: held.external,
1383        });
1384    }
1385    let unit = rucc_debug::Unit {
1386        name: rewrite(origin.name),
1387        // A single dot when the process could not say where it was, which is a directory name every
1388        // debugger understands and which leaves a relative file name meaning what it already meant.
1389        dir: rewrite(opts.working_dir.as_deref().unwrap_or(".")),
1390        producer: format!("rucc {}", crate::VERSION),
1391        files,
1392        types: origin.meaning.types.clone(),
1393        funcs,
1394        globals,
1395        pointer: u8::try_from(target.pointer_width / 8).unwrap_or(8),
1396        // Whether a function can say where its frame base is, which it can when the build writes a
1397        // table that answers the question: the unwind table, or `.debug_frame` in its place. Read
1398        // off what was written rather than asked again, so the two cannot disagree about whether
1399        // the table a frame base is read through is there.
1400        frames: opts.unwinds() || frames.is_some(),
1401    };
1402    let mut info = rucc_debug::write(&unit).map_err(|why| why.to_string())?;
1403    info.chunks.extend(frames.clone());
1404    Ok(info)
1405}
1406
1407/// Where each local the back end kept in a register is, as stretches of the function's addresses.
1408///
1409/// The back end names a stretch by the instruction at either end of it, because a machine
1410/// instruction has no length until something encodes it. This is where it gets one: the assembler
1411/// writes a row per instruction for the line table and the row says how far into the function the
1412/// instruction begins, so the row after it is where it ends. The last instruction of a function
1413/// ends where the function does.
1414///
1415/// Grouped by declaration on the way out, since one local is in one place over one stretch and
1416/// somewhere else over the next, and that is the shape the debugging information wants.
1417fn stretches(
1418    extent: &rucc_object::Extent,
1419    rows: &[rucc_asm::Row],
1420    built: &rucc_mir::Func,
1421    target: &TargetInfo,
1422) -> Vec<(u32, Vec<rucc_debug::Span>)> {
1423    // A target nobody has written a calling convention down for has no DWARF numbering either, so
1424    // there is no way to name the register a local is in and nothing to say.
1425    let (false, Some(regs)) = (built.kept.is_empty(), target.call_regs) else {
1426        return Vec::new();
1427    };
1428    let ends = ends(extent, rows);
1429    let mut bounds = vec![None; built.inst_count()];
1430    for (which, row) in rows.iter().enumerate() {
1431        let Some(inst) = row.inst else { continue };
1432        bounds[inst.index()] = Some((row.at as u64, ends[which]));
1433    }
1434    let mut spots: Vec<(u32, Vec<rucc_debug::Span>)> = Vec::new();
1435    for kept in &built.kept {
1436        let (Some((from, _)), Some((_, to))) = (bounds[kept.from.index()], bounds[kept.to.index()])
1437        else {
1438            continue;
1439        };
1440        if to <= from {
1441            continue;
1442        }
1443        let held = match kept.at {
1444            // A register is named by the number this target's DWARF numbering gives it, which is a
1445            // fact about the class and the register together rather than about either alone.
1446            rucc_mir::Where::Reg { reg, class } => match regs.dwarf(class, reg) {
1447                Some(number) => rucc_debug::Held::Reg(number),
1448                None => continue,
1449            },
1450            rucc_mir::Where::Frame(at) => rucc_debug::Held::Frame(i64::from(at)),
1451        };
1452        let span = rucc_debug::Span { from, len: to - from, held };
1453        match spots.iter_mut().find(|(decl, _)| *decl == kept.decl) {
1454            Some((_, spans)) => spans.push(span),
1455            None => spots.push((kept.decl, vec![span])),
1456        }
1457    }
1458    for (_, spans) in &mut spots {
1459        *spans = settle(std::mem::take(spans));
1460    }
1461    spots.retain(|(_, spans)| !spans.is_empty());
1462    spots
1463}
1464
1465/// Where the instruction each of a function's line table rows was written for ends.
1466///
1467/// The row after it, which is where the next instruction begins, and the end of the function for the
1468/// last one. The row after it at a different address rather than simply the row after it, because an
1469/// instruction that encodes to nothing leaves two rows on one byte and the one in front of it is not
1470/// where anything ends.
1471///
1472/// Backwards, because that is one pass rather than a search from each row for the next address that
1473/// differs, and a function the size of `sqlite3VdbeExec` has tens of thousands of rows.
1474fn ends(extent: &rucc_object::Extent, rows: &[rucc_asm::Row]) -> Vec<u64> {
1475    let mut out = vec![extent.len as u64; rows.len()];
1476    let mut next = extent.len as u64;
1477    for which in (0..rows.len()).rev() {
1478        let at = rows[which].at as u64;
1479        // The answer the row behind got, for a row sharing an address with the one in front of it,
1480        // since the two end in the same place and the one in front has already been asked.
1481        out[which] = match next > at {
1482            true => next,
1483            false => out.get(which + 1).copied().unwrap_or(extent.len as u64),
1484        };
1485        next = next.min(at);
1486    }
1487    out
1488}
1489
1490/// The scopes one function's locals were declared in, as the debug writer wants them, and which of
1491/// its entries each of the unit's scopes became.
1492///
1493/// Only the ones a local of this function is in, and their ancestors. The unit's table holds every
1494/// scope in the translation unit, and a function reaches its own by walking up from the locals the
1495/// back end handed over, which is both the filter and the answer to which function a scope belongs
1496/// to. A scope no local of this function is in is not this function's business even if the numbers
1497/// happen to sit next to each other.
1498///
1499/// The addresses come from the source. A scope is a run of source bytes, every row of the line table
1500/// says which source bytes its instruction was built for, and the rows already say where each
1501/// instruction is, so the addresses of a scope are the addresses of the instructions whose bytes are
1502/// inside it. Nothing had to be carried down the compiler for this, and the nesting comes out right
1503/// on its own: a scope's bytes hold the bytes of every scope inside it, so its addresses hold
1504/// theirs.
1505fn nests(
1506    wants: &[Option<usize>],
1507    scopes: &[crate::shapes::Scope],
1508    extent: &rucc_object::Extent,
1509    rows: &[rucc_asm::Row],
1510) -> (Vec<rucc_debug::Scope>, HashMap<usize, usize>) {
1511    let mut needed: Vec<usize> = Vec::new();
1512    for &want in wants {
1513        let mut up = want;
1514        while let Some(which) = up {
1515            if needed.contains(&which) {
1516                break;
1517            }
1518            needed.push(which);
1519            up = scopes.get(which).and_then(|scope| scope.parent);
1520        }
1521    }
1522    // In the order the unit wrote them, which puts a scope after the one it is inside, because that
1523    // is the order the writer wants and is what lets a parent be named by an entry already made.
1524    needed.sort_unstable();
1525    let at: HashMap<usize, usize> =
1526        needed.iter().enumerate().map(|(which, &scope)| (scope, which)).collect();
1527    let ends = ends(extent, rows);
1528    let out = needed
1529        .iter()
1530        .map(|&which| {
1531            let scope = &scopes[which];
1532            rucc_debug::Scope {
1533                parent: scope.parent.and_then(|parent| at.get(&parent).copied()),
1534                over: spread(scope.span, &ends, rows),
1535            }
1536        })
1537        .collect();
1538    (out, at)
1539}
1540
1541/// Which of a function's addresses were built for a run of its source bytes.
1542///
1543/// A row whose own bytes are inside the run is code the run asked for, and the addresses of a scope
1544/// are the addresses of every such row joined up. Two rows that meet or overlap are one stretch,
1545/// which is what almost all of a scope is: the rows of a block are next to each other unless
1546/// something moved them, and a block the back end split into pieces is exactly the case a list is
1547/// for.
1548fn spread(span: Span, ends: &[u64], rows: &[rucc_asm::Row]) -> Vec<rucc_debug::Reach> {
1549    let mut out: Vec<rucc_debug::Reach> = Vec::new();
1550    for (which, row) in rows.iter().enumerate() {
1551        if row.span.is_dummy() || row.span.lo < span.lo || row.span.hi > span.hi {
1552            continue;
1553        }
1554        let (from, to) = (row.at as u64, ends[which]);
1555        if to <= from {
1556            continue;
1557        }
1558        match out.last_mut() {
1559            Some(last) if last.from + last.len >= from => {
1560                last.len = to.saturating_sub(last.from).max(last.len);
1561            }
1562            _ => out.push(rucc_debug::Reach { from, len: to - from }),
1563        }
1564    }
1565    out
1566}
1567
1568/// One declaration's stretches with the disagreements taken out and the neighbours joined up.
1569///
1570/// Two stretches of one declaration can cover the same address. That is what a program that assigns
1571/// to a local from something already live looks like: both values are live across the assignment,
1572/// the old one because something else still reads it. A stretch never runs past the end of its
1573/// block, so two that overlap are in one block, where the addresses go the way the instructions
1574/// run, and one that starts inside the other starts where the declaration was given its value:
1575/// where the value was computed, or where the assignment was for a value it took from another
1576/// declaration. From there the declaration holds the new value and not the old one, so the one
1577/// that started first ends there.
1578///
1579/// What is still left is two stretches that start at the same address, which is two values both
1580/// live into a block with nothing here to say which of them the declaration holds. Where the two
1581/// agree the answer is the same either way and they become one stretch, and where they disagree the
1582/// address is left out, so a debugger says the variable is unavailable there rather than printing
1583/// whichever register this walk reached first. A wrong answer is worse than none.
1584fn settle(mut spans: Vec<rucc_debug::Span>) -> Vec<rucc_debug::Span> {
1585    spans.sort_by_key(|span| (span.from, span.len));
1586    for which in 0..spans.len() {
1587        let (from, end, held) =
1588            (spans[which].from, spans[which].from + spans[which].len, spans[which].held);
1589        let later = spans[which + 1..]
1590            .iter()
1591            .take_while(|later| later.from < end)
1592            .find(|later| later.from > from && later.held != held);
1593        if let Some(later) = later {
1594            spans[which].len = later.from - from;
1595        }
1596    }
1597    // Every address a stretch begins or ends at, which cuts the function into pieces no stretch is
1598    // partly over: a piece is inside a stretch or outside it and never half of each.
1599    let mut edges: Vec<u64> =
1600        spans.iter().flat_map(|span| [span.from, span.from + span.len]).collect();
1601    edges.sort_unstable();
1602    edges.dedup();
1603    let mut out: Vec<rucc_debug::Span> = Vec::new();
1604    let mut first = 0;
1605    for pair in edges.windows(2) {
1606        let (from, to) = (pair[0], pair[1]);
1607        // Nothing before this can cover this piece or any piece after it, since the pieces only
1608        // ever move forward. The list is in the order the stretches start in, so the walk below
1609        // stops at the first one that starts too late as well.
1610        while spans.get(first).is_some_and(|span| span.from + span.len <= from) {
1611            first += 1;
1612        }
1613        let mut held = None;
1614        let mut agreed = true;
1615        for span in &spans[first..] {
1616            if span.from >= to {
1617                break;
1618            }
1619            if span.from > from || span.from + span.len < to {
1620                continue;
1621            }
1622            match held {
1623                None => held = Some(span.held),
1624                Some(seen) => agreed &= seen == span.held,
1625            }
1626        }
1627        let (Some(held), true) = (held, agreed) else { continue };
1628        match out.last_mut() {
1629            Some(last) if last.from + last.len == from && last.held == held => {
1630                last.len += to - from
1631            }
1632            _ => out.push(rucc_debug::Span { from, len: to - from, held }),
1633        }
1634    }
1635    out
1636}
1637
1638/// Where a file name is in the table, putting it there if it is not there yet.
1639///
1640/// A walk rather than a map because the table holds the files one object's code came from, which is
1641/// a handful even for an amalgamation: everything the preprocessor opened and nothing was generated
1642/// out of stays out of it.
1643fn interned(files: &mut Vec<String>, name: String) -> usize {
1644    match files.iter().position(|have| *have == name) {
1645        Some(which) => which,
1646        None => {
1647            files.push(name);
1648            files.len() - 1
1649        }
1650    }
1651}
1652
1653/// What the command line decided about the file being written, in the words the assembler and the
1654/// object writer use.
1655///
1656/// Two spellings of the same facts, because the flags are the command line's and the answer the two
1657/// writers want is the object format's. The conversion is here rather than in either of them so
1658/// that the two output paths are handed the same thing and cannot come to disagree about what is
1659/// in a file.
1660///
1661/// The feature word is empty on a machine whose bits these are not. It is the x86 one, and a target
1662/// that wanted its control flow checked would want a property of its own with a key of its own, so
1663/// writing this one there would be recording something untrue rather than recording nothing.
1664fn output(opts: &Options, target: &TargetInfo) -> rucc_object::Output {
1665    let mut features = 0;
1666    if target.tuple.arch() == Arch::X86_64 {
1667        if opts.control.branch() {
1668            features |= rucc_object::Property::IBT;
1669        }
1670        if opts.control.ret() {
1671            features |= rucc_object::Property::SHSTK;
1672        }
1673    }
1674    rucc_object::Output {
1675        sections: rucc_object::Sections {
1676            functions: opts.function_sections,
1677            data: opts.data_sections,
1678        },
1679        property: rucc_object::Property { features },
1680    }
1681}
1682
1683/// What the object writer said, as the kind of news it is.
1684///
1685/// A format with no writer is a target this compiler is behind on, which is a program nobody can
1686/// compile today and not a mistake in the one being compiled. Anything else it refused is a bug
1687/// here, because every value it was handed came out of this compiler.
1688fn wrote(why: rucc_object::Error) -> Vec<Diagnostic> {
1689    match why {
1690        rucc_object::Error::Format { .. } => vec![unsupported(&why.to_string())],
1691        rucc_object::Error::Refused { .. } => vec![internal(&why.to_string())],
1692    }
1693}
1694
1695/// What the assembler said, as the kind of news it is.
1696///
1697/// Three of these are about a program and the rest are about this compiler. A thread-local
1698/// variable, an ifunc and a prologue the target's unwind table cannot describe are all valid C that
1699/// the back end does not build yet, and everything else the assembler refuses is something that
1700/// should never have reached it.
1701fn refused(why: rucc_asm::Error) -> Vec<Diagnostic> {
1702    match why {
1703        rucc_asm::Error::Thread { .. }
1704        | rucc_asm::Error::IFunc { .. }
1705        | rucc_asm::Error::Frame { .. } => {
1706            vec![unsupported(&why.to_string())]
1707        }
1708        _ => vec![internal(&why.to_string())],
1709    }
1710}
1711
1712/// A diagnostic about a program this compiler is not finished enough to compile.
1713///
1714/// Not an internal error, because nothing here is wrong: the program is valid C and the part of
1715/// the back end that would handle it has not been written. The note says so, so that a report
1716/// about one of these is filed against the milestone rather than as a miscompilation.
1717fn unsupported(message: &str) -> Diagnostic {
1718    unsupported_at(message, Span::DUMMY)
1719}
1720
1721/// The same, about somewhere in the file rather than about the file.
1722///
1723/// The note names the issue tracker rather than `spec/17-milestones.md`, which is a document
1724/// about the plan: a reader who follows it wants to know whether the construct in front of them
1725/// is already written down as work, and the milestone list does not answer that.
1726fn unsupported_at(message: &str, span: Span) -> Diagnostic {
1727    Diagnostic::error(message.to_owned(), span)
1728        .with_code("E0653")
1729        .note("this construct is not lowered yet, see https://github.com/tamnd/rucc/issues", span)
1730}
1731
1732/// A diagnostic about IR that was handed to us rather than built by us.
1733fn invalid(message: &str) -> Diagnostic {
1734    Diagnostic::error(message.to_owned(), Span::DUMMY).with_code("E0661")
1735}
1736
1737/// A diagnostic about this compiler rather than about the program it was given.
1738fn internal(message: &str) -> Diagnostic {
1739    Diagnostic::error(format!("internal error: {message}"), Span::DUMMY)
1740        .with_code("E0652")
1741        .note("this is a bug in rucc rather than in the program, please report it", Span::DUMMY)
1742}
1743
1744/// A result that is nothing but one message, for the failures that happen before there is
1745/// anything to compile.
1746fn failure(message: String) -> Compiled {
1747    Compiled {
1748        artifact: Artifact::Nothing,
1749        messages: vec![format!("rucc: error: {message}")],
1750        errors: 1,
1751        fired: Fired::new(),
1752        pressure: Pressure::new(),
1753        lowerings: Lowerings::new(),
1754        dumps: Vec::new(),
1755        remarks: String::new(),
1756        deps: Vec::new(),
1757        temps: Temps::default(),
1758        timing: crate::trace::Timing::default(),
1759    }
1760}
1761
1762#[cfg(test)]
1763mod tests {
1764    use rucc_session::{MemoryFileSystem, Std};
1765    use rucc_target::Triple;
1766
1767    use super::*;
1768
1769    fn options() -> Options {
1770        let mut opts = Options::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
1771        opts.emit = EmitKind::Tast;
1772        // The tests here read the code a function turns into, and a frame pointer in every one
1773        // of them is noise that says nothing about what each test is about.
1774        opts.frame_pointer = Some(false);
1775        opts
1776    }
1777
1778    fn run(opts: &Options, source: &str) -> Compiled {
1779        let mut fs = MemoryFileSystem::new();
1780        fs.insert("/main.c", source.to_owned().into_bytes());
1781        compile(opts, "/main.c", &fs)
1782    }
1783
1784    /// Options with the compiler's own headers on the search path and nothing else, which is
1785    /// what a freestanding compilation is. There is no file system underneath these tests,
1786    /// so a header that reached for one would fail to resolve and say so.
1787    fn freestanding() -> Options {
1788        let mut opts = options();
1789        opts.hosted = false;
1790        opts.search.push_system(rucc_session::runtime::DIR);
1791        opts
1792    }
1793
1794    /// The typed tree of a freestanding `source`, insisting that it compiled cleanly.
1795    fn shipped(source: &str) -> String {
1796        let result = run(&freestanding(), source);
1797        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
1798        result.text().to_owned()
1799    }
1800
1801    /// The typed tree of `source`, insisting that it compiled cleanly.
1802    fn tast(source: &str) -> String {
1803        let result = run(&options(), source);
1804        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
1805        result.text().to_owned()
1806    }
1807
1808    #[test]
1809    fn the_shipped_stdarg_declares_a_list_and_the_four_operators() {
1810        let text = shipped(concat!(
1811            "#include <stdarg.h>\n",
1812            "int sum(int n, ...) {\n",
1813            "  va_list ap, copy;\n",
1814            "  va_start(ap, n);\n",
1815            "  va_copy(copy, ap);\n",
1816            "  int total = va_arg(ap, int) + va_arg(copy, int);\n",
1817            "  va_end(ap);\n",
1818            "  va_end(copy);\n",
1819            "  return total;\n",
1820            "}\n",
1821        ));
1822        assert!(text.contains("va-start"), "{text}");
1823        assert!(text.contains("va-copy"), "{text}");
1824        assert!(text.contains("va-arg"), "{text}");
1825        assert!(text.contains("va-end"), "{text}");
1826    }
1827
1828    /// glibc includes `<stdarg.h>` this way from every header that declares a `vprintf`, and
1829    /// what it wants is the type without the four macro names. Answering the whole header
1830    /// would put `va_start` in the way of a program that has its own.
1831    #[test]
1832    fn stdarg_hands_out_the_type_alone_when_that_is_all_that_was_asked_for() {
1833        let text = shipped(concat!(
1834            "#define __need___va_list\n",
1835            "#include <stdarg.h>\n",
1836            "int vprint(const char *f, __gnuc_va_list ap);\n",
1837            "#ifdef va_start\n",
1838            "#error va_start should not be defined\n",
1839            "#endif\n",
1840            "#ifdef _VA_LIST_DEFINED\n",
1841            "#error va_list should not have been made\n",
1842            "#endif\n",
1843        ));
1844        assert!(text.contains("vprint"), "{text}");
1845    }
1846
1847    /// The same protocol on `<stddef.h>`, which glibc uses far more heavily: `<stdio.h>` asks
1848    /// for `size_t` and `NULL` and would be wrong to receive `offsetof` as well.
1849    #[test]
1850    fn stddef_answers_one_piece_at_a_time_and_the_next_request_still_gets_through() {
1851        let text = shipped(concat!(
1852            "#define __need_size_t\n",
1853            "#include <stddef.h>\n",
1854            "#ifdef offsetof\n",
1855            "#error offsetof should not be defined yet\n",
1856            "#endif\n",
1857            "#define __need_ptrdiff_t\n",
1858            "#include <stddef.h>\n",
1859            "#include <stddef.h>\n",
1860            "size_t a;\n",
1861            "ptrdiff_t b;\n",
1862            "wchar_t c;\n",
1863            "max_align_t d;\n",
1864            "void *e = NULL;\n",
1865            "struct P { int x; long y; };\n",
1866            "size_t f = offsetof(struct P, y);\n",
1867        ));
1868        assert!(text.contains("decl #0 a : unsigned long"), "{text}");
1869        assert!(text.contains("decl #1 b : long"), "{text}");
1870    }
1871
1872    #[test]
1873    fn the_shipped_limits_and_float_are_the_targets_own_answers() {
1874        let text = shipped(concat!(
1875            "#include <limits.h>\n",
1876            "#include <float.h>\n",
1877            "int bits = CHAR_BIT;\n",
1878            "long big = LONG_MAX;\n",
1879            "int low = INT_MIN;\n",
1880            "int radix = FLT_RADIX;\n",
1881            "int digits = DBL_MANT_DIG;\n",
1882        ));
1883        assert!(text.contains("const 8 : int"), "{text}");
1884        assert!(text.contains("const 9223372036854775807 : long"), "{text}");
1885        assert!(text.contains("const 2 : int"), "{text}");
1886        assert!(text.contains("const 53 : int"), "{text}");
1887    }
1888
1889    /// Freestanding, so there is no library header to chain to and `<stdint.h>` writes the
1890    /// whole set out itself. The widths are the ones the target picked, which is the only
1891    /// reason this header is the compiler's.
1892    #[test]
1893    fn the_shipped_stdint_writes_the_whole_set_when_there_is_no_library_to_defer_to() {
1894        let text = shipped(concat!(
1895            "#include <stdint.h>\n",
1896            "int64_t a = INT64_C(1);\n",
1897            "uint_least16_t b;\n",
1898            "intptr_t c;\n",
1899            "uintmax_t d = UINTMAX_MAX;\n",
1900            "int wide = sizeof(int_fast64_t);\n",
1901        ));
1902        assert!(text.contains("decl #0 a : long"), "{text}");
1903        assert!(text.contains("decl #1 b : unsigned short"), "{text}");
1904        assert!(text.contains("decl #2 c : long"), "{text}");
1905    }
1906
1907    /// `<mmintrin.h>` is the base of the vector header chain and the first one whose contents
1908    /// are C rather than declarations, so what this checks is that the C in it compiles: a
1909    /// header that is nothing but definitions fails as a whole or not at all.
1910    ///
1911    /// What the intrinsics answer is not checked here and cannot be, because the answer is
1912    /// only interesting next to another compiler's. Every intrinsic in the header was built
1913    /// and run against GCC 16.2.0 on the same inputs, at `-O0`, `-O1`, `-O2` and `-Os`, and
1914    /// gave the same bytes in all four. Carrying that comparison rather than repeating it by
1915    /// hand needs a facet in `tamnd/rucc-corpus` that works out the expected bytes itself,
1916    /// which is a second implementation of MMX and is `tamnd/rucc#1150`.
1917    #[test]
1918    fn the_shipped_mmintrin_defines_the_mmx_type_and_the_operations_over_it() {
1919        let text = shipped(concat!(
1920            "#include <mmintrin.h>\n",
1921            "__m64 add(__m64 a, __m64 b) { return _mm_add_pi16(a, b); }\n",
1922            "__m64 pack(__m64 a, __m64 b) { return _m_packsswb(a, b); }\n",
1923            "__m64 shift(__m64 a) { return _mm_srai_pi32(a, 3); }\n",
1924            "int low(__m64 a) { return _mm_cvtsi64_si32(a); }\n",
1925            "void done(void) { _mm_empty(); }\n",
1926        ));
1927        assert!(text.contains("add"), "{text}");
1928        assert!(text.contains("pack"), "{text}");
1929        assert!(text.contains("shift"), "{text}");
1930    }
1931
1932    /// The allocator beside the vector headers, which is the one piece of the family that is
1933    /// not a vector operation. It reaches for `<stddef.h>` and for three names out of the
1934    /// library, and the point of the test is that the reach resolves with nothing on the
1935    /// search path but the compiler's own directory.
1936    #[test]
1937    fn the_shipped_mm_malloc_asks_for_aligned_memory_and_gives_it_back() {
1938        let text = shipped(concat!(
1939            "#include <mm_malloc.h>\n",
1940            "void *get(void) { return _mm_malloc(64, 16); }\n",
1941            "void put(void *p) { _mm_free(p); }\n",
1942        ));
1943        assert!(text.contains("get"), "{text}");
1944        assert!(text.contains("put"), "{text}");
1945    }
1946
1947    /// `<xmmintrin.h>` is the next rung of the chain and pulls the other two in behind it, so a
1948    /// program that includes this one alone has to get all three. What the intrinsics answer is
1949    /// checked the same way `<mmintrin.h>` next door is checked and for the same reason: a
1950    /// hundred and forty eight lines of answers over nans, infinities, both zeros and values
1951    /// that do not fit in the integer they convert to, identical to GCC 16.2.0 at `-O0`, `-O1`,
1952    /// `-O2` and `-Os`.
1953    ///
1954    /// `_mm_rcp_ps` is the one answer in that run that is not identical, and is not meant to be.
1955    /// The instruction approximates a reciprocal and this computes one exactly, so the bits
1956    /// differ while both sit inside the relative error Intel documents, which the same program
1957    /// checks directly rather than by comparing bits.
1958    #[test]
1959    fn the_shipped_xmmintrin_defines_the_sse_type_and_the_operations_over_it() {
1960        let text = shipped(concat!(
1961            "#include <xmmintrin.h>\n",
1962            "__m128 add(__m128 a, __m128 b) { return _mm_add_ps(a, b); }\n",
1963            "__m128 one(__m128 a, __m128 b) { return _mm_max_ss(a, b); }\n",
1964            "__m128 mask(__m128 a, __m128 b) { return _mm_cmpnle_ps(a, b); }\n",
1965            "__m128 pick(__m128 a, __m128 b) { return _mm_shuffle_ps(a, b, _MM_SHUFFLE(0,1,2,3)); }\n",
1966            "int bits(__m128 a) { return _mm_movemask_ps(a); }\n",
1967            "int near(__m128 a) { return _mm_cvtss_si32(a); }\n",
1968            "__m128 wide(__m64 a) { return _mm_cvtpi16_ps(a); }\n",
1969            "void *room(void) { return _mm_malloc(64, 16); }\n",
1970            "void hint(const float *p) { _mm_prefetch(p, _MM_HINT_T0); _mm_sfence(); }\n",
1971        ));
1972        assert!(text.contains("add"), "{text}");
1973        assert!(text.contains("mask"), "{text}");
1974        assert!(text.contains("pick"), "{text}");
1975        assert!(text.contains("wide"), "{text}");
1976    }
1977
1978    /// The six names of gcc's header this one leaves out, each of which is an instruction whose
1979    /// answer no plain C reproduces exactly. Leaving them out is what turns a program that wants
1980    /// one into a diagnostic naming the function it called, rather than into a wrong answer, and
1981    /// this is what notices if one is ever quietly defined to something close.
1982    ///
1983    /// `tamnd/rucc#1157` is the square root, which brings the first four back.
1984    #[test]
1985    fn the_shipped_xmmintrin_leaves_out_the_names_that_need_an_instruction() {
1986        let text = rucc_session::runtime::header("xmmintrin.h").expect("xmmintrin.h is shipped");
1987        for absent in [
1988            "_mm_sqrt_ps",
1989            "_mm_sqrt_ss",
1990            "_mm_rsqrt_ps",
1991            "_mm_rsqrt_ss",
1992            "_mm_getcsr",
1993            "_mm_setcsr",
1994        ] {
1995            let defined = text.contains(&format!("{absent}("));
1996            assert!(!defined, "{absent} is defined and the header says it is not");
1997            assert!(text.contains(absent), "{absent} is absent and unexplained");
1998        }
1999    }
2000
2001    #[test]
2002    fn the_shipped_emmintrin_defines_both_sse2_types_and_the_operations_over_them() {
2003        let text = shipped(concat!(
2004            "#include <emmintrin.h>\n",
2005            "__m128i add(__m128i a, __m128i b) { return _mm_add_epi64(a, b); }\n",
2006            "__m128i wide(__m128i a, __m128i b) { return _mm_mul_epu32(a, b); }\n",
2007            "__m128i pick(__m128i a) { return _mm_shuffle_epi32(a, _MM_SHUFFLE(0,1,2,3)); }\n",
2008            "__m128i up(__m128i a) { return _mm_slli_epi64(a, 13); }\n",
2009            "__m128i down(__m128i a) { return _mm_srli_si128(a, 3); }\n",
2010            "__m128i pack(__m128i a, __m128i b) { return _mm_packus_epi16(a, b); }\n",
2011            "int bits(__m128i a) { return _mm_movemask_epi8(a); }\n",
2012            "__m128d sum(__m128d a, __m128d b) { return _mm_add_sd(a, b); }\n",
2013            "__m128d mask(__m128d a, __m128d b) { return _mm_cmpunord_pd(a, b); }\n",
2014            "__m128i near(__m128d a) { return _mm_cvtpd_epi32(a); }\n",
2015            "__m128d over(__m128 a) { return _mm_cvtps_pd(a); }\n",
2016            "__m128i half(__m64 a) { return _mm_movpi64_epi64(a); }\n",
2017            "__m128i grab(void const *p) { return _mm_loadu_si128(p); }\n",
2018            "void wall(void) { _mm_lfence(); _mm_mfence(); }\n",
2019        ));
2020        assert!(text.contains("wide"), "{text}");
2021        assert!(text.contains("pack"), "{text}");
2022        assert!(text.contains("near"), "{text}");
2023        assert!(text.contains("half"), "{text}");
2024    }
2025
2026    /// The umbrella header reaches the three underneath it. This is brotli's use of it, from
2027    /// `c/enc/matching_tag_mask.h`, which is the whole of what `tamnd/rucc#1236` was about: four
2028    /// SSE2 names that were already shipped and no way to get at them by the name gcc uses.
2029    #[test]
2030    fn the_shipped_immintrin_reaches_the_names_the_headers_under_it_define() {
2031        let text = shipped(concat!(
2032            "#include <immintrin.h>\n",
2033            "unsigned long long matching(unsigned char tag, unsigned char const *bucket) {\n",
2034            "  __m128i const want = _mm_set1_epi8((char)tag);\n",
2035            "  __m128i const chunk = _mm_loadu_si128((__m128i const *)(void const *)bucket);\n",
2036            "  __m128i const same = _mm_cmpeq_epi8(chunk, want);\n",
2037            "  return (unsigned long long)_mm_movemask_epi8(same);\n",
2038            "}\n",
2039            "__m64 narrow(__m64 a, __m64 b) { return _mm_add_pi32(a, b); }\n",
2040            "__m128 single(__m128 a, __m128 b) { return _mm_add_ps(a, b); }\n",
2041        ));
2042        assert!(text.contains("matching"), "{text}");
2043        assert!(text.contains("narrow"), "the MMX header is not reached: {text}");
2044        assert!(text.contains("single"), "the SSE header is not reached: {text}");
2045    }
2046
2047    /// The wider umbrella reaches everything the narrower one does, and the fence family with it.
2048    /// This is what mingw-w64's `<winnt.h>` includes and what it then uses, so a Windows program
2049    /// that has never heard of an intrinsic gets here through `<windows.h>`.
2050    #[test]
2051    fn the_shipped_x86intrin_reaches_the_fences_windows_headers_ask_it_for() {
2052        let text = shipped(concat!(
2053            "#include <x86intrin.h>\n",
2054            "void barriers(void *p) {\n",
2055            "  _mm_lfence();\n",
2056            "  _mm_sfence();\n",
2057            "  _mm_mfence();\n",
2058            "  _mm_pause();\n",
2059            "  _mm_clflush(p);\n",
2060            "}\n",
2061            "__m128i wide(__m128i a, __m128i b) { return _mm_add_epi32(a, b); }\n",
2062        ));
2063        assert!(text.contains("barriers"), "{text}");
2064        assert!(text.contains("wide"), "the SSE2 header is not reached: {text}");
2065    }
2066
2067    /// Including it twice is the same as including it once, and so is including it beside the
2068    /// header it reaches. A program that includes both spellings is the usual case rather than an
2069    /// odd one, because one of its own headers includes the umbrella and another includes SSE2.
2070    #[test]
2071    fn the_umbrella_and_the_header_under_it_can_both_be_included() {
2072        let text = shipped(concat!(
2073            "#include <immintrin.h>\n",
2074            "#include <emmintrin.h>\n",
2075            "#include <immintrin.h>\n",
2076            "#include <x86intrin.h>\n",
2077            "__m128i twice(__m128i a, __m128i b) { return _mm_add_epi32(a, b); }\n",
2078        ));
2079        assert!(text.contains("twice"), "{text}");
2080    }
2081
2082    /// The AArch64 intrinsics, as xxhash uses them in `XXH3_accumulate_512_neon`: a load, a
2083    /// reinterpretation, the halves of a vector and a widening multiply added into a sum.
2084    #[test]
2085    fn the_shipped_arm_neon_has_what_xxhash_asks_it_for() {
2086        let mut opts = freestanding();
2087        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2088        let source = concat!(
2089            "#include <arm_neon.h>\n",
2090            "uint64x2_t acc(uint64x2_t sum, const void *in, const void *key) {\n",
2091            "  uint8x16_t data = vld1q_u8((const uint8_t *)in);\n",
2092            "  uint8x16_t k = vld1q_u8((const uint8_t *)key);\n",
2093            "  uint64x2_t mixed = vreinterpretq_u64_u8(veorq_u8(data, k));\n",
2094            "  uint32x2_t lo = vmovn_u64(mixed);\n",
2095            "  uint32x2_t hi = vshrn_n_u64(mixed, 32);\n",
2096            "  return vmlal_u32(sum, lo, hi);\n",
2097            "}\n",
2098            "uint32x4x2_t pair(uint32x4_t a, uint32x4_t b) { return vzipq_u32(a, b); }\n",
2099            "uint32_t total(uint32x4_t a) { return vaddvq_u32(a); }\n",
2100        );
2101        let result = run(&opts, source);
2102        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
2103        assert!(result.text().contains("pair"), "{}", result.text());
2104        assert!(result.text().contains("total"), "{}", result.text());
2105    }
2106
2107    /// Off AArch64 the header says so, rather than failing on a type the target does not have.
2108    #[test]
2109    fn the_shipped_arm_neon_refuses_another_target() {
2110        let result = run(&freestanding(), "#include <arm_neon.h>\n");
2111        let said = result.messages.join("\n");
2112        assert!(said.contains("arm_neon.h is for AArch64"), "{said}");
2113    }
2114
2115    /// The float header omits four square roots and SSE2 omits the matching two, for the reason
2116    /// both headers write down. A later change that quietly defines one as an approximation
2117    /// would be a wrong answer nobody sees, so the absence is held in place here.
2118    #[test]
2119    fn the_shipped_emmintrin_leaves_out_the_two_square_roots() {
2120        let text = rucc_session::runtime::header("emmintrin.h").expect("emmintrin.h is shipped");
2121        for absent in ["_mm_sqrt_pd", "_mm_sqrt_sd"] {
2122            let defined = text.contains(&format!("{absent}("));
2123            assert!(!defined, "{absent} is defined and the header says it is not");
2124            assert!(text.contains(absent), "{absent} is absent and unexplained");
2125        }
2126    }
2127
2128    /// The CRC32C steps and the population counts are each one instruction, and the point of
2129    /// naming them rather than writing the loop in C is that instruction, so what is checked is
2130    /// the assembly and not only that the names resolve. `-msse4.2` is what PostgreSQL's
2131    /// configure passes, and it has to bring popcnt and crc32 with it the way gcc's does.
2132    #[test]
2133    fn the_shipped_nmmintrin_is_one_instruction_per_step_under_sse4_2() {
2134        let mut opts = freestanding();
2135        opts.emit = EmitKind::Asm;
2136        let mut choices = rucc_target::Choices::new();
2137        choices.read("sse4.2").expect("gcc knows sse4.2");
2138        opts.isa = choices.over(opts.isa);
2139        let source = concat!(
2140            "#include <nmmintrin.h>\n",
2141            "unsigned b(unsigned c, unsigned char v) { return _mm_crc32_u8(c, v); }\n",
2142            "unsigned w(unsigned c, unsigned short v) { return _mm_crc32_u16(c, v); }\n",
2143            "unsigned l(unsigned c, unsigned v) { return _mm_crc32_u32(c, v); }\n",
2144            "unsigned long long q(unsigned long long c, unsigned long long v) {\n",
2145            "  return _mm_crc32_u64(c, v);\n",
2146            "}\n",
2147            "int n(unsigned v) { return _mm_popcnt_u32(v); }\n",
2148            "long long m(unsigned long long v) { return _mm_popcnt_u64(v); }\n",
2149        );
2150        let result = run(&opts, source);
2151        assert_eq!(result.messages, Vec::<String>::new());
2152        let text = result.text();
2153        for step in ["crc32b", "crc32w", "crc32l", "crc32q", "popcntl", "popcntq"] {
2154            assert!(text.contains(step), "no {step} in:\n{text}");
2155        }
2156    }
2157
2158    /// Without the flag a function not built for the instruction cannot call it, which is gcc's
2159    /// refusal in gcc's words and the answer a configure probe reads.
2160    #[test]
2161    fn the_shipped_smmintrin_refuses_a_caller_not_built_for_the_checksum() {
2162        let result = run(
2163            &freestanding(),
2164            "#include <immintrin.h>\nunsigned f(unsigned c) { return _mm_crc32_u32(c, 1); }\n",
2165        );
2166        let said = result.messages.join("\n");
2167        let refusal = "inlining failed in call to 'always_inline' '_mm_crc32_u32': target \
2168                       specific option mismatch";
2169        assert!(said.contains(refusal), "{said}");
2170    }
2171
2172    /// A function carrying the attribute is built for the instruction whatever the unit is, which
2173    /// is how PostgreSQL writes its checksum: no flag, the attribute on the one function, and the
2174    /// step inlined into it as one instruction. PostgreSQL's probe writes the attribute only when
2175    /// `__has_attribute` says it is there, so that has to say so as well.
2176    #[test]
2177    fn a_function_built_for_sse4_2_calls_the_steps_without_a_flag() {
2178        let mut opts = freestanding();
2179        opts.emit = EmitKind::Asm;
2180        let source = concat!(
2181            "#include <nmmintrin.h>\n",
2182            "#if defined(__has_attribute) && __has_attribute (target)\n",
2183            "__attribute__((target(\"sse4.2\")))\n",
2184            "#endif\n",
2185            "unsigned l(unsigned c, unsigned v) { return _mm_crc32_u32(c, v); }\n",
2186            "__attribute__((target(\"popcnt\")))\n",
2187            "int n(unsigned v) { return _mm_popcnt_u32(v); }\n",
2188        );
2189        let result = run(&opts, source);
2190        assert_eq!(result.messages, Vec::<String>::new());
2191        let text = result.text();
2192        assert!(text.contains("crc32l") && text.contains("popcntl"), "{text}");
2193        let l = &text[text.find("\nl:").expect("l is defined")..];
2194        let l = &l[..l.find("ret").expect("l returns")];
2195        assert!(l.contains("crc32l") && !l.contains("call"), "{l}");
2196    }
2197
2198    /// PostgreSQL's two AVX-512 configure probes, as its `config/c-compiler.m4` writes them, with
2199    /// the functions made external so that each one is written out. Each compiles without a flag
2200    /// and every intrinsic in it is inlined into the one function, since a call left behind would
2201    /// be a call to a function built for an extension the caller may not have. Both were also run
2202    /// under Intel SDE as a Sapphire Rapids, with PostgreSQL's own files, and gave what gcc 16's
2203    /// build gives at `-O0` and `-O2`.
2204    #[test]
2205    fn the_shipped_avx512_headers_pass_postgres_probes() {
2206        let popcount = concat!(
2207            "#include <immintrin.h>\n",
2208            "#include <stdint.h>\n",
2209            "char buf[sizeof(__m512i)];\n",
2210            "#if defined(__has_attribute) && __has_attribute (target)\n",
2211            "__attribute__((target(\"avx512vpopcntdq,avx512bw\")))\n",
2212            "#endif\n",
2213            "int popcount_test(void)\n",
2214            "{\n",
2215            "  int64_t popcnt = 0;\n",
2216            "  __m512i accum = _mm512_setzero_si512();\n",
2217            "  __m512i val = _mm512_maskz_loadu_epi8((__mmask64) 0xf0f0f0f0f0f0f0f0, (const __m512i *) buf);\n",
2218            "  __m512i cnt = _mm512_popcnt_epi64(val);\n",
2219            "  accum = _mm512_add_epi64(accum, cnt);\n",
2220            "  popcnt = _mm512_reduce_add_epi64(accum);\n",
2221            "  return (int) popcnt;\n",
2222            "}\n",
2223        );
2224        let pclmul = concat!(
2225            "#include <immintrin.h>\n",
2226            "__m512i x;\n",
2227            "__m512i y;\n",
2228            "#if defined(__has_attribute) && __has_attribute (target)\n",
2229            "__attribute__((target(\"vpclmulqdq,avx512vl\")))\n",
2230            "#endif\n",
2231            "int avx512_pclmul_test(void)\n",
2232            "{\n",
2233            "  __m128i z;\n",
2234            "  x = _mm512_xor_si512(_mm512_zextsi128_si512(_mm_cvtsi32_si128(0)), x);\n",
2235            "  y = _mm512_clmulepi64_epi128(x, y, 0);\n",
2236            "  z = _mm_ternarylogic_epi64(\n",
2237            "            _mm512_castsi512_si128(y),\n",
2238            "            _mm512_extracti32x4_epi32(y, 1),\n",
2239            "            _mm512_extracti32x4_epi32(y, 2),\n",
2240            "            0x96);\n",
2241            "  return _mm_crc32_u64(0, _mm_extract_epi64(z, 0));\n",
2242            "}\n",
2243        );
2244        let checks: [(&str, &str, &[&str]); 2] = [
2245            (popcount, "popcount_test", &["kmovq", "vmovdqu8", "vpopcntq", "vpaddq", "vshufi64x2"]),
2246            (pclmul, "avx512_pclmul_test", &["vpxorq", "vpclmulqdq", "vpternlogq", "crc32q"]),
2247        ];
2248        for (source, name, wanted) in checks {
2249            for level in [rucc_session::OptLevel::O0, rucc_session::OptLevel::O2] {
2250                let mut opts = freestanding();
2251                opts.emit = EmitKind::Asm;
2252                opts.opt_level = level;
2253                let result = run(&opts, source);
2254                assert_eq!(result.messages, Vec::<String>::new(), "{name} at {level:?}");
2255                let text = result.text();
2256                let start = text.find(&format!("\n{name}:")).expect("the probe is written out");
2257                let body = &text[start..];
2258                let body = &body[..body.find(".size").unwrap_or(body.len())];
2259                for instruction in wanted {
2260                    assert!(
2261                        body.contains(instruction),
2262                        "no {instruction} at {level:?} in:\n{body}"
2263                    );
2264                }
2265                assert!(!body.contains("call"), "a call left behind at {level:?} in:\n{body}");
2266            }
2267        }
2268    }
2269
2270    /// A function not built for the extension cannot call one of its intrinsics, which is the
2271    /// refusal gcc gives in gcc's words, and what tells a probe without the attribute no.
2272    #[test]
2273    fn the_shipped_avx512_headers_refuse_a_caller_not_built_for_them() {
2274        let result = run(
2275            &freestanding(),
2276            "#include <immintrin.h>\n__m512i f(__m512i a) { return _mm512_popcnt_epi64(a); }\n",
2277        );
2278        let said = result.messages.join("\n");
2279        let refusal = "inlining failed in call to 'always_inline' '_mm512_popcnt_epi64': target \
2280                       specific option mismatch";
2281        assert!(said.contains(refusal), "{said}");
2282    }
2283
2284    /// A string gcc does not know is refused in gcc's words, and AArch64's own strings are
2285    /// something x86-64 does not know either.
2286    #[test]
2287    fn a_target_string_gcc_does_not_know_is_refused() {
2288        for (string, name) in [("sse5", "sse5"), ("+crc", "+crc"), ("sse4.2,foo", "foo")] {
2289            let source =
2290                format!("__attribute__((target(\"{string}\"))) int f(void) {{ return 0; }}\n");
2291            let said = run(&freestanding(), &source).messages.join("\n");
2292            let wanted = format!("attribute 'target' argument '{name}' is unknown");
2293            assert!(said.contains(&wanted), "{string}: {said}");
2294        }
2295    }
2296
2297    /// AArch64 has strings of its own, which the x86-64 reading does not look at, so the
2298    /// checksum PostgreSQL builds there with `target("+crc")` still compiles.
2299    #[test]
2300    fn an_aarch64_target_string_is_still_accepted() {
2301        let mut opts = freestanding();
2302        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2303        let source = "__attribute__((target(\"+crc\"))) int f(void) { return 0; }\n";
2304        let result = run(&opts, source);
2305        assert_eq!(result.messages, Vec::<String>::new());
2306    }
2307
2308    #[test]
2309    fn the_three_formality_headers_still_have_to_work() {
2310        let text = shipped(concat!(
2311            "#include <stdbool.h>\n",
2312            "#include <stdalign.h>\n",
2313            "#include <iso646.h>\n",
2314            "#include <stdnoreturn.h>\n",
2315            "int t = true and not false;\n",
2316            "_Alignas(16) char buf[16];\n",
2317            "int a = alignof(long);\n",
2318        ));
2319        assert!(text.contains("decl #0 t : int"), "{text}");
2320        assert!(text.contains("const 8 : unsigned long"), "{text}");
2321    }
2322
2323    /// Including everything twice has to change nothing, because that is what happens in any
2324    /// program large enough to matter and a guard that is wrong shows up nowhere else.
2325    ///
2326    /// Stated as the two trees being the same rather than as a fact about what is in either
2327    /// one. A header that carries definitions puts them in the tree and moves everything
2328    /// after them along, so an assertion about where the program's own declaration landed is
2329    /// an assertion about how much `<mmintrin.h>` defines, which is not what is being asked.
2330    #[test]
2331    fn every_shipped_header_can_be_included_twice() {
2332        // This is x86-64, and `<arm_neon.h>` is for AArch64 only, so it is held to the same
2333        // thing by the AArch64 test below.
2334        let once: String = rucc_session::runtime::names()
2335            .iter()
2336            .filter(|name| **name != "arm_neon.h")
2337            .map(|name| format!("#include <{name}>\n"))
2338            .collect();
2339        let twice = once.repeat(2);
2340        assert_eq!(shipped(&format!("{once}int x;\n")), shipped(&format!("{twice}int x;\n")));
2341
2342        let mut opts = freestanding();
2343        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2344        let tree = |source: &str| {
2345            let result = run(&opts, source);
2346            assert_eq!(
2347                result.messages,
2348                Vec::<String>::new(),
2349                "expected this to compile:\n{source}"
2350            );
2351            result.text().to_owned()
2352        };
2353        let neon = "#include <arm_neon.h>\n";
2354        assert_eq!(tree(&format!("{neon}int x;\n")), tree(&format!("{neon}{neon}int x;\n")));
2355    }
2356
2357    #[test]
2358    fn a_file_that_is_not_there_says_so_and_produces_nothing() {
2359        let fs = MemoryFileSystem::new();
2360        let result = compile(&options(), "/nope.c", &fs);
2361        assert!(result.failed());
2362        assert!(result.messages[0].contains("/nope.c"), "{:?}", result.messages);
2363        assert!(result.text().is_empty());
2364    }
2365
2366    #[test]
2367    fn an_object_comes_out_with_its_type_its_linkage_and_how_much_of_a_definition_it_is() {
2368        let text = tast("int x = 1;\n");
2369        let expected = "\
2370decl #0 x : int object external static defined
2371  init
2372    +0
2373      const 1 : int
2374";
2375        assert_eq!(text, expected);
2376    }
2377
2378    #[test]
2379    fn the_macros_are_expanded_before_anything_is_parsed() {
2380        // The whole pipeline in one line. The bound came out of a macro, so it was expanded,
2381        // converted from a preprocessing number to a constant of a type, parsed as an
2382        // expression, and folded to the number the array type carries.
2383        let text = tast("#define N 2\nint a[N];\n");
2384        assert!(text.starts_with("decl #0 a : int[2] object external static tentative"), "{text}");
2385    }
2386
2387    /// A pragma survives the preprocessor on purpose, since what one means is not its
2388    /// business, and nothing after it has a place for a `#` in the grammar. `pack` is the one
2389    /// the parser reads and every other line is walked past. Both spellings are here because
2390    /// they arrive by different routes and only one of them was ever on a line of its own in
2391    /// the source.
2392    #[test]
2393    fn a_pragma_is_not_a_declaration_and_the_parse_walks_past_the_ones_it_does_not_read() {
2394        let text = tast(concat!(
2395            "#pragma pack(4)\n",
2396            "struct s { int a; };\n",
2397            "#pragma pack()\n",
2398            "int b;\n",
2399            "_Pragma(\"GCC visibility push(default)\") int c;\n",
2400        ));
2401        assert!(text.contains("decl #0 b : int"), "{text}");
2402        assert!(text.contains("decl #1 c : int"), "{text}");
2403    }
2404
2405    /// The byte swaps and the bit counts of a constant are constants, which is how gcc has them, and
2406    /// every number here was read off gcc 16 on x86-64. `__builtin_clz(0)` and `__builtin_ctzll(0)`
2407    /// are undefined at run time and gcc folds them to the width.
2408    #[test]
2409    fn the_byte_swaps_and_the_bit_counts_of_a_constant_are_constants() {
2410        tast(concat!(
2411            "static const unsigned magic = __builtin_bswap32(0x11223344u);\n",
2412            "_Static_assert(__builtin_bswap16(0x1234) == 0x3412, \"16\");\n",
2413            "_Static_assert(__builtin_bswap32(0x11223344u) == 0x44332211u, \"32\");\n",
2414            "_Static_assert(__builtin_bswap64(0x0102030405060708ull) == 0x0807060504030201ull, \"64\");\n",
2415            "_Static_assert(__builtin_popcountll(-1ll) == 64 && __builtin_popcount(-1) == 32, \"ones\");\n",
2416            "_Static_assert(__builtin_parity(7) == 1 && __builtin_parity(3) == 0, \"parity\");\n",
2417            "_Static_assert(__builtin_ffs(0) == 0 && __builtin_ffs(8) == 4, \"ffs\");\n",
2418            "_Static_assert(__builtin_clrsb(0) == 31 && __builtin_clrsb(-1) == 31, \"clrsb\");\n",
2419            "_Static_assert(__builtin_clrsbl(1) == 62, \"clrsbl\");\n",
2420            "_Static_assert(__builtin_clz(1) == 31 && __builtin_clzl(1) == 63, \"clz\");\n",
2421            "_Static_assert(__builtin_ctzll(1ull << 40) == 40, \"ctz\");\n",
2422            "_Static_assert(__builtin_clz(0) == 32 && __builtin_ctzll(0) == 64, \"zero\");\n",
2423        ));
2424    }
2425
2426    /// Every number in these two tests was read off gcc 16 on x86-64 under `-std=gnu23`
2427    /// rather than reasoned about, which is why they are written as assertions the program
2428    /// makes about itself: a compilation with no messages is every one of them holding.
2429    ///
2430    /// This half is the attributes. `packed` takes the padding out, on the record or on one
2431    /// member, `aligned` raises and never lowers, and the two written together are the
2432    /// combination that packs and then aligns the whole thing.
2433    #[test]
2434    fn the_layout_attributes_move_the_members_and_the_record_the_way_gcc_lays_them_out() {
2435        tast(concat!(
2436            "struct A { char c; int i; } __attribute__((packed));\n",
2437            "_Static_assert(sizeof(struct A) == 5 && _Alignof(struct A) == 1, \"A\");\n",
2438            "_Static_assert(__builtin_offsetof(struct A, i) == 1, \"A.i\");\n",
2439            // `aligned` with nothing in the parentheses is the largest alignment the target
2440            // has, which gcc calls BIGGEST_ALIGNMENT and which is sixteen everywhere here.
2441            "struct B { char c; int i; } __attribute__((aligned));\n",
2442            "_Static_assert(sizeof(struct B) == 16 && _Alignof(struct B) == 16, \"B\");\n",
2443            "struct C { char c; int i __attribute__((packed)); };\n",
2444            "_Static_assert(sizeof(struct C) == 5 && _Alignof(struct C) == 1, \"C\");\n",
2445            "_Static_assert(__builtin_offsetof(struct C, i) == 1, \"C.i\");\n",
2446            "struct D { char c; int i; } __attribute__((packed, aligned(4)));\n",
2447            "_Static_assert(sizeof(struct D) == 8 && _Alignof(struct D) == 4, \"D\");\n",
2448            "_Static_assert(__builtin_offsetof(struct D, i) == 1, \"D.i\");\n",
2449            "struct E { char c; _Alignas(8) int i; };\n",
2450            "_Static_assert(sizeof(struct E) == 16 && _Alignof(struct E) == 8, \"E\");\n",
2451            "_Static_assert(__builtin_offsetof(struct E, i) == 8, \"E.i\");\n",
2452            "struct F { char c; int i __attribute__((aligned(8))); };\n",
2453            "_Static_assert(sizeof(struct F) == 16 && _Alignof(struct F) == 8, \"F\");\n",
2454            // Two the record already had, so the attribute asks for nothing new, and two
2455            // where four was already there, so the attribute is ignored rather than obeyed.
2456            "struct G { char c; short s; } __attribute__((aligned(2)));\n",
2457            "_Static_assert(sizeof(struct G) == 4 && _Alignof(struct G) == 2, \"G\");\n",
2458            "struct H { char c; int i; } __attribute__((aligned(2)));\n",
2459            "_Static_assert(sizeof(struct H) == 8 && _Alignof(struct H) == 4, \"H\");\n",
2460            // `packed` on a member takes the padding out in front of that member alone, so on
2461            // the first one it does nothing and on the second one it does all of it.
2462            "struct I { [[gnu::packed]] char c; int i; };\n",
2463            "_Static_assert(sizeof(struct I) == 8 && _Alignof(struct I) == 4, \"I\");\n",
2464            "struct J { char c; [[gnu::packed]] int i; };\n",
2465            "_Static_assert(sizeof(struct J) == 5 && _Alignof(struct J) == 1, \"J\");\n",
2466            "struct M { char c; int i : 5; int j : 20; } __attribute__((packed));\n",
2467            "_Static_assert(sizeof(struct M) == 5 && _Alignof(struct M) == 1, \"M\");\n",
2468            "struct N { char c; long long l; } __attribute__((aligned(32)));\n",
2469            "_Static_assert(sizeof(struct N) == 32 && _Alignof(struct N) == 32, \"N\");\n",
2470            "union L { char c; int i; } __attribute__((packed));\n",
2471            "_Static_assert(sizeof(union L) == 4 && _Alignof(union L) == 1, \"L\");\n",
2472            // The armoured spellings, which are the ones a system header writes, since a
2473            // program is entitled to a macro called `packed` and is not entitled to one called
2474            // `__packed__`. The two names are one attribute and the layout is the same one.
2475            "struct O { char c; int i; } __attribute__((__packed__));\n",
2476            "_Static_assert(sizeof(struct O) == 5 && _Alignof(struct O) == 1, \"O\");\n",
2477            "struct P { char c; int i; } __attribute__((__aligned__(8)));\n",
2478            "_Static_assert(sizeof(struct P) == 8 && _Alignof(struct P) == 8, \"P\");\n",
2479        ));
2480    }
2481
2482    /// The attribute that changes what a call means rather than what a record lays out.
2483    ///
2484    /// Both halves are here. A call hands a value to a parameter of the union type and the value
2485    /// goes into the member that takes it, which is a compound literal of the union and is the
2486    /// same object the GNU cast to a union builds. And a declaration written with a member's type
2487    /// declares the same function as one written with the union, which is what lets a pointer to
2488    /// either be assigned from the other, and is what gnulib's signature checks do.
2489    ///
2490    /// The `void *` member is last on purpose: the search takes a member whose type the value
2491    /// already has wherever it sits, and falls back to a pointer member that would take the value
2492    /// silently only when there is no such member, so `char *` reaches the catch-all past two
2493    /// members that are not it.
2494    #[test]
2495    fn a_transparent_union_takes_the_member_a_value_fits_and_is_declared_either_way() {
2496        let text = tast(concat!(
2497            "struct one { int x; };\n",
2498            "struct two { long y; };\n",
2499            "typedef union { struct one *a; struct two *b; void *any; }\n",
2500            "  __attribute__((__transparent_union__)) arg;\n",
2501            "int takes(arg v);\n",
2502            "int f(struct one *p, struct two *q, char *c) {\n",
2503            "  return takes(p) + takes(q) + takes(c) + takes(0);\n",
2504            "}\n",
2505            // The other half, which is about declarations and not about values.
2506            "int takes(struct one *p);\n",
2507            "int (*as_a_member)(struct one *) = takes;\n",
2508            "int (*as_the_union)(arg) = takes;\n",
2509        ));
2510        assert!(text.contains("compound-literal"), "{text}");
2511    }
2512
2513    /// The other place glibc writes it, which is the one that matters.
2514    ///
2515    /// `sys/socket.h` puts the attribute on the declarator of the typedef rather than after the
2516    /// closing brace, so a compiler that reads only the second position reads nothing at all of
2517    /// the eleven pointer union that `bind` and `connect` and five others take.
2518    #[test]
2519    fn the_attribute_on_the_declarator_of_a_typedef_is_the_one_glibc_writes() {
2520        let text = tast(concat!(
2521            "struct sockaddr { int family; };\n",
2522            "struct sockaddr_in { int family; int addr; };\n",
2523            "typedef union { struct sockaddr *plain; struct sockaddr_in *inet; }\n",
2524            "  addr_arg __attribute__((__transparent_union__));\n",
2525            "int bind_to(int fd, addr_arg where);\n",
2526            "int f(struct sockaddr_in *where) { return bind_to(0, where); }\n",
2527        ));
2528        assert!(text.contains("compound-literal"), "{text}");
2529    }
2530
2531    /// What the attribute promises has to be a promise this can keep, and is checked rather than
2532    /// believed.
2533    ///
2534    /// A union wider than its first member is not passed the way that member is, and a structure
2535    /// has no members that are alternatives to each other at all. gcc drops the attribute in both
2536    /// cases with a warning and compiles the program, because the type is still a perfectly good
2537    /// type and only the extra rule is gone.
2538    #[test]
2539    fn a_transparent_union_that_cannot_keep_the_promise_is_dropped_with_a_word_about_it() {
2540        let result = run(
2541            &options(),
2542            concat!(
2543                "union wider { int small; double large; } __attribute__((transparent_union));\n",
2544                "struct plain { int x; } __attribute__((transparent_union));\n",
2545            ),
2546        );
2547        assert_eq!(result.messages.len(), 2, "{:?}", result.messages);
2548        assert!(!result.failed(), "{:?}", result.messages);
2549        for message in &result.messages {
2550            assert!(message.contains("'transparent_union' attribute ignored"), "{message}");
2551        }
2552        assert!(result.messages[0].contains("first member"), "{:?}", result.messages);
2553        assert!(result.messages[1].contains("only a union"), "{:?}", result.messages);
2554    }
2555
2556    /// What an access to a packed member is allowed to assume about where it starts.
2557    ///
2558    /// C 6.2.8 gives an object of type `int` four byte alignment and `packed` takes it away: the
2559    /// member goes wherever the members in front of it ended, and an `int` one byte into a record
2560    /// is aligned to one. The number on the access has to say so, because it is what the back end
2561    /// picks instructions from and what judgement J1 of `spec/safe-memory/04-safety-model.md`
2562    /// tests at run time. Four on an address that is a multiple of one is the compiler refusing a
2563    /// program that is doing nothing wrong.
2564    #[test]
2565    fn an_access_to_a_packed_member_says_the_alignment_the_layout_left_it() {
2566        let packed = body(concat!(
2567            "struct P { char c; int v; } __attribute__((packed));\n",
2568            "int f(struct P *p) { return p->v; }\n",
2569        ));
2570        assert!(packed.contains("load.i32 %2, align 1,"), "{packed}");
2571        // The same record without the attribute, which is where the type's own answer is right.
2572        let plain = body(concat!(
2573            "struct P { char c; int v; };\n",
2574            "int f(struct P *p) { return p->v; }\n",
2575        ));
2576        assert!(plain.contains("load.i32 %2, align 4,"), "{plain}");
2577    }
2578
2579    /// The same, for the two ways of being further in than the member itself.
2580    ///
2581    /// An array member is stepped through rather than offset to, and a record member is offset to
2582    /// twice, and both have to carry the outer record's alignment with them. A step of a whole
2583    /// number of elements leaves what the element width and the address had in common, which for
2584    /// a one byte aligned base is one byte however wide the elements are.
2585    #[test]
2586    fn what_is_inside_a_packed_member_is_no_more_aligned_than_the_member_is() {
2587        let stepped = body(concat!(
2588            "struct P { char c; int v[4]; } __attribute__((packed));\n",
2589            "int f(struct P *p, int i) { return p->v[i]; }\n",
2590        ));
2591        assert!(stepped.contains(", align 1,"), "{stepped}");
2592        assert!(!stepped.contains(", align 4,"), "{stepped}");
2593        let nested = body(concat!(
2594            "struct Inner { int v; };\n",
2595            "struct P { char c; struct Inner in; } __attribute__((packed));\n",
2596            "int f(struct P *p) { return p->in.v; }\n",
2597        ));
2598        assert!(nested.contains(", align 1,"), "{nested}");
2599        assert!(!nested.contains(", align 4,"), "{nested}");
2600    }
2601
2602    /// The other way an access gets an alignment its type would not have given it, which is a
2603    /// typedef that lowered one.
2604    ///
2605    /// `aligned` raises on a declaration and replaces on a typedef, so `typedef aligned(1) U32
2606    /// unalign32` really is a four byte integer that may sit anywhere. Reading a word out of a
2607    /// buffer nothing aligned is what every compression library does and this is how they write
2608    /// it: zstd's `lib/common/mem.h` is four typedefs of exactly this shape and `MEM_read32` is
2609    /// `*(const unalign32 *)ptr`.
2610    ///
2611    /// What made this worth a test is where it went wrong. `__alignof__` was right the whole time,
2612    /// because that asks about the type and the type knew. The access was wrong, because the type
2613    /// of `*p` was worked out by resolving every typedef in `p`'s type rather than only the one on
2614    /// the pointer, so the thing being read came back as the `unsigned int` the typedef stands for
2615    /// and the alignment came off that. The number on the access is what judgement J1 tests, so
2616    /// the monitor refused fifty six of zstd's reads, all of them correct.
2617    #[test]
2618    fn an_access_through_a_typedef_that_lowered_its_alignment_says_the_one_the_typedef_asked_for() {
2619        let through = body(concat!(
2620            "typedef __attribute__((aligned(1))) unsigned int unalign32;\n",
2621            "unsigned int f(const void *p) { return *(const unalign32 *)p; }\n",
2622        ));
2623        assert!(through.contains("load.i32 %0, align 1,"), "{through}");
2624        // A subscript is `*(p + i)` and a member through an arrow is a dereference and then an
2625        // offset, so both read the pointee the same way and both have to come out the same.
2626        let stepped = body(concat!(
2627            "typedef __attribute__((aligned(1))) unsigned int unalign32;\n",
2628            "unsigned int f(unalign32 *p, int i) { return p[i]; }\n",
2629        ));
2630        assert!(stepped.contains(", align 1,"), "{stepped}");
2631        assert!(!stepped.contains(", align 4,"), "{stepped}");
2632        // And the same typedef without the attribute, which is where the type's own answer is the
2633        // right one and nothing above should have changed it.
2634        let plain = body(concat!(
2635            "typedef unsigned int word;\n",
2636            "unsigned int f(const void *p) { return *(const word *)p; }\n",
2637        ));
2638        assert!(plain.contains("load.i32 %0, align 4,"), "{plain}");
2639    }
2640
2641    /// The same thing where the object does not fit in a register, which is what `_mm_loadu_si128`
2642    /// is and is the reason the intrinsic header exists at all.
2643    ///
2644    /// `__m128i_u` is `__m128i` with `aligned(1)` on it and `_mm_loadu_si128` is one line,
2645    /// `return *(const __m128i_u *)__p;`. Two things had to be right for that to come out as the
2646    /// unaligned read it is. The dereference has to keep the typedef, which is what the test above
2647    /// covers, and then the return has to read the object as aligned as the object is rather than
2648    /// as aligned as the type it is being returned as: a vector comes back in registers on this
2649    /// ABI, so the sixteen bytes are read as two pieces of eight and the ABI's own alignment is
2650    /// what lays the two pieces out rather than what either read may claim.
2651    #[test]
2652    fn a_vector_read_through_a_typedef_that_lowered_its_alignment_comes_back_a_piece_at_a_time() {
2653        let prefix = concat!(
2654            "typedef long long v2di __attribute__((__vector_size__(16)));\n",
2655            "typedef long long v2di_u __attribute__((__vector_size__(16), __aligned__(1)));\n",
2656        );
2657        let loaded =
2658            body(&format!("{prefix}v2di f(const void *p) {{ return *(const v2di_u *)p; }}"));
2659        assert_eq!(loaded.matches("align 1\n").count(), 2, "{loaded}");
2660        assert!(!loaded.contains("align 16"), "{loaded}");
2661        // The store side, which travels as a copy into whatever the pointer names and so carries
2662        // one number for both ends of it.
2663        let stored = body(&format!("{prefix}void f(void *p, v2di b) {{ *(v2di_u *)p = b; }}"));
2664        assert!(stored.contains("memcpy %0, %3, size 16, align 1"), "{stored}");
2665        // And the aligned spelling of the same two, which is where sixteen is the right answer.
2666        let aligned =
2667            body(&format!("{prefix}v2di f(const void *p) {{ return *(const v2di *)p; }}"));
2668        assert!(aligned.contains("align 16"), "{aligned}");
2669    }
2670
2671    /// The same attribute on a declaration rather than on a type, which asks that this object or
2672    /// this function be at a multiple of that, and which is where a program that has to hand a
2673    /// buffer to hardware or keep two counters off one cache line writes it.
2674    ///
2675    /// A raise and never a lower, which is the one place it does not agree with `_Alignas`: below
2676    /// what the type already has, `_Alignas` is a constraint violation and this is ignored without
2677    /// a word. `__alignof__` of the object answers what the object got and not what its type has,
2678    /// because that is the question a program asking it is asking.
2679    #[test]
2680    fn the_aligned_attribute_on_a_declaration_raises_what_that_one_object_is_aligned_to() {
2681        tast(concat!(
2682            "int v __attribute__((aligned(64)));\n",
2683            "_Static_assert(__alignof__(v) == 64, \"v\");\n",
2684            // Written on the specifiers rather than after the declarator, which asks the same
2685            // thing and is the spelling a header is more likely to use.
2686            "__attribute__((aligned(32))) int w;\n",
2687            "_Static_assert(__alignof__(w) == 32, \"w\");\n",
2688            "[[gnu::aligned(16)]] int x;\n",
2689            "_Static_assert(__alignof__(x) == 16, \"x\");\n",
2690            // Two below the four an `int` already has, so nothing is asked for and nothing is
2691            // said, and the type still answers for the object.
2692            "int y __attribute__((aligned(2)));\n",
2693            "_Static_assert(__alignof__(y) == 4, \"y\");\n",
2694            // A local, which is the same question one scope down.
2695            "void f(void) { int a __attribute__((aligned(128)));\n",
2696            "_Static_assert(__alignof__(a) == 128, \"a\"); (void)a; }\n",
2697            // The type is untouched by any of it: `aligned` on a declaration says where that
2698            // declaration goes and says nothing about every other `int` in the program.
2699            "_Static_assert(__alignof__(int) == 4, \"int\");\n",
2700            // A function, which has no alignment of its own for this to be measured against and
2701            // takes whatever was asked for.
2702            "void g(void) __attribute__((aligned(256)));\n",
2703            "void g(void) {}\n",
2704            "_Static_assert(__alignof__(g) == 256, \"g\");\n",
2705        ));
2706    }
2707
2708    /// And what the object file says, which is the half that makes the answer above true. A
2709    /// function is at a fixed offset inside the text section, so it is at a multiple of two
2710    /// hundred and fifty six only if the section is at one too.
2711    #[test]
2712    fn what_a_declaration_asked_to_be_aligned_to_is_what_the_assembler_is_told() {
2713        let text = asm(concat!(
2714            "int v __attribute__((aligned(64)));\n",
2715            "void g(void) __attribute__((aligned(256)));\n",
2716            "void g(void) {}\n",
2717            "void plain(void) {}\n",
2718        ));
2719        assert!(text.contains("\t.p2align\t6\n\t.type\tv, @object\n"), "{text}");
2720        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "{text}");
2721        assert!(text.contains("\t.p2align\t4, 0x90\n\t.globl\tplain\n"), "{text}");
2722    }
2723
2724    /// The same question asked by the command line instead of by a declaration, which is
2725    /// `-falign-functions` and is what femtolisp's Makefile writes on every compile. The flag is a
2726    /// floor: a function that named a larger boundary itself keeps it, and one that named a
2727    /// smaller one is moved up, because the attribute is a requirement about one function and the
2728    /// flag is a preference about all of them.
2729    #[test]
2730    fn the_alignment_the_command_line_asked_of_every_function_is_a_floor_under_all_of_them() {
2731        let source = concat!(
2732            "void g(void) __attribute__((aligned(256)));\n",
2733            "void g(void) {}\n",
2734            "void small(void) __attribute__((aligned(4)));\n",
2735            "void small(void) {}\n",
2736            "void plain(void) {}\n",
2737        );
2738        let listing = |align: Option<u32>| {
2739            let mut opts = options();
2740            opts.emit = EmitKind::Asm;
2741            opts.align_functions = align;
2742            let result = run(&opts, source);
2743            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
2744            result.text().to_owned()
2745        };
2746
2747        let text = listing(Some(32));
2748        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "the larger one wins: {text}");
2749        assert!(text.contains("\t.p2align\t5, 0x90\n\t.globl\tsmall\n"), "{text}");
2750        assert!(text.contains("\t.p2align\t5, 0x90\n\t.globl\tplain\n"), "{text}");
2751
2752        // And the negative form, which asks for the smallest boundary the target has and is the
2753        // one spelling that takes a function below the sixteen bytes it would get anyway.
2754        let text = listing(Some(8));
2755        assert!(text.contains("\t.p2align\t3, 0x90\n\t.globl\tplain\n"), "{text}");
2756        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "{text}");
2757    }
2758
2759    /// And the one position where the attribute means something else. On a declaration it raises
2760    /// what that one object is aligned to, and on a typedef it says what the type is aligned to,
2761    /// which gcc lets it lower as well: `typedef int L __attribute__((aligned(2)))` really is an
2762    /// `int` at a multiple of two and a record with one in it really is smaller for it.
2763    ///
2764    /// The size is left alone, which is gcc's answer rather than an omission here. An aligned
2765    /// typedef whose alignment is larger than what it stands for keeps the size it stands for,
2766    /// and gcc refuses an array of one rather than padding the elements out to fit.
2767    #[test]
2768    fn an_aligned_typedef_says_what_an_object_of_it_is_aligned_to_and_may_lower_it() {
2769        tast(concat!(
2770            "typedef int L __attribute__((aligned(2)));\n",
2771            "_Static_assert(__alignof__(L) == 2, \"L\");\n",
2772            "_Static_assert(_Alignof(L) == 2, \"L alignof\");\n",
2773            // Below what an `int` has, which is the half a declaration cannot ask for.
2774            "_Static_assert(sizeof(L) == 4, \"L size\");\n",
2775            "struct T { char c; L x; };\n",
2776            "_Static_assert(sizeof(struct T) == 6, \"T\");\n",
2777            "_Static_assert(__builtin_offsetof(struct T, x) == 2, \"T.x\");\n",
2778            // And upwards, which is the ordinary direction and the one a header writes.
2779            "typedef int H __attribute__((aligned(16)));\n",
2780            "_Static_assert(__alignof__(H) == 16, \"H\");\n",
2781            "_Static_assert(sizeof(H) == 4, \"H size\");\n",
2782            "struct U { char c; H x; };\n",
2783            "_Static_assert(sizeof(struct U) == 32, \"U\");\n",
2784            "_Static_assert(__builtin_offsetof(struct U, x) == 16, \"U.x\");\n",
2785            // A typedef of a typedef, where the nearer one is the one the declaration was
2786            // written with and is the one that answers.
2787            "typedef L M __attribute__((aligned(8)));\n",
2788            "_Static_assert(__alignof__(M) == 8, \"M\");\n",
2789            // And one that asked for nothing, which still has whatever the one behind it asked
2790            // for because it is the same type spelled again.
2791            "typedef L N;\n",
2792            "_Static_assert(__alignof__(N) == 2, \"N\");\n",
2793            // The type it stands for is untouched by any of it.
2794            "_Static_assert(__alignof__(int) == 4, \"int\");\n",
2795        ));
2796        let text = asm(concat!(
2797            "typedef int L __attribute__((aligned(2)));\n",
2798            "typedef int H __attribute__((aligned(16)));\n",
2799            "L low;\n",
2800            "H high;\n",
2801        ));
2802        assert!(text.contains("\t.p2align\t1\n\t.type\tlow, @object\n"), "{text}");
2803        assert!(text.contains("\t.p2align\t4\n\t.type\thigh, @object\n"), "{text}");
2804    }
2805
2806    /// The attribute that builds a type rather than changing a layout. `vector_size(n)` says the
2807    /// declared type is `n` bytes of what was written, taken as lanes, and every operator over
2808    /// one is that operator over each lane.
2809    ///
2810    /// The size is in bytes and not in lanes, which is the part a reader gets backwards: sixteen
2811    /// of `int` is four lanes and sixteen of `char` is sixteen. A vector is aligned to its own
2812    /// size, which is what a machine that has the registers wants and what gcc gives one here.
2813    #[test]
2814    fn the_vector_size_attribute_builds_a_type_of_lanes_and_measures_it_in_bytes() {
2815        tast(concat!(
2816            "typedef int __attribute__((vector_size(16))) v4si;\n",
2817            "_Static_assert(sizeof(v4si) == 16 && _Alignof(v4si) == 16, \"v4si\");\n",
2818            "typedef char __attribute__((vector_size(16))) v16qi;\n",
2819            "_Static_assert(sizeof(v16qi) == 16, \"v16qi\");\n",
2820            // One lane, which is a power of two and is a vector rather than the type it was
2821            // written on: the operators it takes are the vector's and not the scalar's.
2822            "typedef int __attribute__((vector_size(4))) v1si;\n",
2823            "_Static_assert(sizeof(v1si) == 4, \"v1si\");\n",
2824            // The armoured spelling and the bracket one, which are the same attribute.
2825            "typedef float __attribute__((__vector_size__(8))) v2sf;\n",
2826            "_Static_assert(sizeof(v2sf) == 8, \"v2sf\");\n",
2827            "typedef short [[gnu::vector_size(8)]] v4hi;\n",
2828            "_Static_assert(sizeof(v4hi) == 8, \"v4hi\");\n",
2829            // A lane is what a subscript answers with, and a vector is not a pointer: there is
2830            // nothing to decay and the lane type is the one the arithmetic happens in.
2831            "v4si g;\n",
2832            "_Static_assert(sizeof(g[0]) == 4, \"lane\");\n",
2833            "_Static_assert(sizeof(g + g) == 16, \"whole\");\n",
2834            // A scalar beside a vector stands for itself in every lane, so the answer is still
2835            // the vector and not the wider of the two types.
2836            "_Static_assert(sizeof(g + 1) == 16, \"broadcast\");\n",
2837            // An array of them, which is the ordinary way a program holds several.
2838            "_Static_assert(sizeof(v4si[3]) == 48, \"array\");\n",
2839        ));
2840    }
2841
2842    /// A whole vector written into an array of them, and a vector named by a type name rather
2843    /// than by a typedef.
2844    ///
2845    /// Both are the same question asked twice. A vector is filled like an array of its lanes when
2846    /// a list is written into it, so a braced element that is itself a vector has to be taken
2847    /// whole rather than started as the first lane, and the type of what was written is the only
2848    /// thing that says which was meant. And a type name is where a compound literal and a cast
2849    /// spell the type out, which a macro taking a lane type and a lane count does, so the
2850    /// attribute has to be read there and not only on a declaration.
2851    #[test]
2852    fn a_vector_is_written_whole_into_an_array_of_them_and_named_by_a_type_name() {
2853        tast(concat!(
2854            "typedef int __attribute__((vector_size(8))) v2si;\n",
2855            "v2si table[] = { (v2si){ 1, 2 }, (v2si){ 3, 4 } };\n",
2856            "_Static_assert(sizeof(table) == 16, \"two of them and not eight lanes\");\n",
2857            // The size written out rather than named, which is the spelling a macro expands to.
2858            "v2si written = (int __attribute__((vector_size(8)))){ 5, 6 };\n",
2859            "_Static_assert(sizeof((int __attribute__((vector_size(16)))){ 0 }) == 16, \"named\");\n",
2860            // A lane is still a lane, so a list of them fills the vector the way it always did
2861            // and the rule above did not turn brace elision off.
2862            "v2si lanes[2] = { 1, 2, 3, 4 };\n",
2863            "_Static_assert(sizeof(lanes) == 16, \"still elided\");\n",
2864        ));
2865    }
2866
2867    /// A lane written rather than read, and a shift whose two vectors are not the same type.
2868    ///
2869    /// Both are places where a vector is not the aggregate it looks like. A subscript of one is
2870    /// an lvalue because the vector it came from is an object, so a lane can be assigned to and
2871    /// has an address, and a qualifier written on the vector reaches every lane the way it does
2872    /// on an array. And a shift is the one lanewise operator whose sides are not brought to a
2873    /// single type, since the right side counts rather than computes.
2874    #[test]
2875    fn a_lane_is_assignable_and_a_shift_takes_a_count_of_its_own_lane() {
2876        let result = run(
2877            &options(),
2878            concat!(
2879                "typedef int __attribute__((vector_size(16))) v4si;\n",
2880                "typedef unsigned __attribute__((vector_size(16))) v4ui;\n",
2881                "void write(v4si *out, v4ui a, v4si b, int n) {\n",
2882                "  v4si v = { 1, 2, 3, 4 };\n",
2883                "  v[0] = n;\n",
2884                "  v[1] += n;\n",
2885                "  v[2]++;\n",
2886                "  *&v[3] = n;\n",
2887                // The count is signed and the value is not, which no other operator allows.
2888                "  v4ui shifted = a >> b;\n",
2889                "  shifted <<= b;\n",
2890                // A scalar stands in every lane on either side of a shift, which is the half
2891                // that looks wrong: the shape of the answer comes off the count here.
2892                "  *out = v + (v4si)shifted + (1 << b);\n",
2893                "}\n",
2894                // A qualifier on the vector is a qualifier on the lane, so there is nothing here
2895                // to write to.
2896                "void refused(const v4si c) {\n",
2897                "  c[0] = 1;\n",
2898                "}\n",
2899            ),
2900        );
2901        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
2902        assert!(result.messages[0].contains("assignment of read-only"), "{:?}", result.messages);
2903    }
2904
2905    /// The third layout attribute, and the one that moves nothing. It says the scalars in the
2906    /// record are stored in the byte order it names, so on a target whose order is the other one
2907    /// every load through a member swaps its bytes and so does every store. The record is the size
2908    /// and the alignment it would be without it and every member is where it would be, which is
2909    /// what gcc 16.2.0 does and what was measured before any of this was written.
2910    ///
2911    /// All four spellings are here because a header writes the armoured one, the attribute may be
2912    /// written in front of the body as well as behind it, and the C23 spelling in gcc's namespace
2913    /// is the same attribute a fourth way. The order the target already has is the fifth case and
2914    /// asks for nothing, since a program saying what would have happened anyway is entitled to be
2915    /// compiled as though it had said nothing.
2916    #[test]
2917    fn a_record_that_asks_for_the_other_byte_order_swaps_every_scalar_it_holds() {
2918        let read = "int f(struct s *p) { return p->i; }\n";
2919        let big = "struct s { int i; } __attribute__((scalar_storage_order(\"big-endian\")));\n";
2920        assert!(body(&format!("{big}{read}")).contains("bswap"), "{big}");
2921
2922        let armoured =
2923            "struct s { int i; } __attribute__((__scalar_storage_order__(\"big-endian\")));\n";
2924        assert!(body(&format!("{armoured}{read}")).contains("bswap"), "{armoured}");
2925
2926        let front = "struct __attribute__((scalar_storage_order(\"big-endian\"))) s { int i; };\n";
2927        assert!(body(&format!("{front}{read}")).contains("bswap"), "{front}");
2928
2929        let standard = "struct s { int i; } [[gnu::scalar_storage_order(\"big-endian\")]];\n";
2930        assert!(body(&format!("{standard}{read}")).contains("bswap"), "{standard}");
2931
2932        let same =
2933            "struct s { int i; } __attribute__((scalar_storage_order(\"little-endian\")));\n";
2934        assert!(!body(&format!("{same}{read}")).contains("bswap"), "{same}");
2935
2936        // A member one byte wide has only one order, and neither has the record itself.
2937        let byte = "struct s { char c; } __attribute__((scalar_storage_order(\"big-endian\")));\n";
2938        let source = format!("{byte}int f(struct s *p) {{ return p->c; }}\n");
2939        assert!(!body(&source).contains("bswap"), "{byte}");
2940
2941        tast(concat!(
2942            "struct s { int i; short h; char c; }",
2943            " __attribute__((scalar_storage_order(\"big-endian\")));\n",
2944            "_Static_assert(sizeof(struct s) == 8 && _Alignof(struct s) == 4, \"s\");\n",
2945            "_Static_assert(__builtin_offsetof(struct s, h) == 4, \"s.h\");\n",
2946            "_Static_assert(__builtin_offsetof(struct s, c) == 6, \"s.c\");\n",
2947        ));
2948    }
2949
2950    /// A bit-field in one of these records lies in the same bytes and is counted from the top of
2951    /// them rather than from the bottom. `execute/20230630-2.c` is the program that says so:
2952    /// `short i : 12` in front of four one bit fields holds 341 in the two bytes `15 5f`, so the
2953    /// twelve bits are the top twelve and reading them is a shift right by four rather than a mask
2954    /// alone. The plain record shifts nothing, since there the field is already at the bottom.
2955    #[test]
2956    fn a_bit_field_in_one_of_those_records_is_counted_from_the_top_of_its_bytes() {
2957        let members = "short i : 12; char c1 : 1; char c2 : 1; char c3 : 1; char c4 : 1;";
2958        let read = "int f(struct s *p) { return p->i; }\n";
2959        let plain = format!("struct s {{ {members} }};\n{read}");
2960        let reversed = format!(
2961            "struct s {{ {members} }} __attribute__((scalar_storage_order(\"big-endian\")));\n\
2962             {read}"
2963        );
2964        assert!(body(&plain).contains("shl"), "{}", body(&plain));
2965        assert!(!body(&plain).contains("bswap"), "{}", body(&plain));
2966        // The two loaded bytes the other way round and then the top twelve bits of them, which
2967        // is the arithmetic shift right on its own with nothing to move the field up to the top.
2968        let built = body(&reversed);
2969        assert!(built.contains("bswap"), "{built}");
2970        assert!(!built.contains("shl"), "{built}");
2971        assert!(built.contains("ashr"), "{built}");
2972    }
2973
2974    /// The one thing a program may not do with a member of one of these records. The bytes are
2975    /// there and they are the other way round, so a pointer to them is a pointer to a value of
2976    /// that type which is not the value the member holds. gcc refuses it in these words, and it
2977    /// refuses only the scalars: the address of a nested record or of an array member is an
2978    /// address of the bytes as they lie, and an access through it asks its own type which order
2979    /// it is in.
2980    #[test]
2981    fn the_address_of_a_scalar_stored_the_other_way_round_is_refused() {
2982        let opts = options();
2983        let record = "struct s { int i; int a[2]; struct in { int n; } w; }\n\
2984                      __attribute__((scalar_storage_order(\"big-endian\")));\n";
2985        let taken = format!("{record}int *f(struct s *p) {{ return &p->i; }}\n");
2986        assert_eq!(
2987            run(&opts, &taken).messages,
2988            ["/main.c:3:30: error: cannot take address of scalar with reverse storage order \
2989              [E0712]"]
2990        );
2991        let element = format!("{record}int *f(struct s *p) {{ return &p->a[0]; }}\n");
2992        let messages = run(&opts, &element).messages;
2993        assert!(messages[0].contains("[E0712]"), "{messages:?}");
2994
2995        let whole = format!("{record}int *f(struct s *p) {{ return (int *) &p->w; }}\n");
2996        assert_eq!(run(&opts, &whole).messages, Vec::<String>::new(), "{whole}");
2997    }
2998
2999    /// An argument that names neither order, which gcc answers with the two words it does take.
3000    /// A program that writes one of these is reading a wire format and would rather be told the
3001    /// spelling it got wrong than be handed a record laid out in the order it did not ask for.
3002    #[test]
3003    fn a_storage_order_that_names_neither_end_is_refused_with_the_two_words_that_are_taken() {
3004        let opts = options();
3005        let wrong = "struct s { int i; } __attribute__((scalar_storage_order(\"middle\")));\n";
3006        assert_eq!(
3007            run(&opts, wrong).messages,
3008            ["/main.c:1:36: error: 'scalar_storage_order' argument must be one of \"big-endian\" \
3009              or \"little-endian\" [E0688]"]
3010        );
3011        let bare = "struct s { int i; } __attribute__((scalar_storage_order));\n";
3012        let messages = run(&opts, bare).messages;
3013        assert!(messages[0].contains("[E0688]"), "{messages:?}");
3014    }
3015
3016    /// Where a bit-field goes, which packing decides and which is the part of all this that
3017    /// is not what the names suggest. A bit-field goes at the next free bit unless that would
3018    /// make it span more storage than its own type occupies, and then it moves to the next
3019    /// boundary of its alignment. Any packing at all takes that rule out, and `#pragma pack`
3020    /// counts even where it lowers nothing, which is the fourth and seventh cases here.
3021    ///
3022    /// Nothing in the language can be asked where a bit-field is, since `offsetof` refuses one
3023    /// and every size below comes out the same either way, so what is asked is the byte a read
3024    /// of the field loads from.
3025    #[test]
3026    fn packing_is_what_decides_whether_a_bit_field_may_straddle_its_own_storage() {
3027        // A `char` field after twelve bits, which will not straddle unpacked and does packed.
3028        assert_eq!(bit_field_byte("struct s { int x : 12; char y : 6; };"), 2);
3029        assert_eq!(
3030            bit_field_byte("struct s { int x : 12; char y : 6; } __attribute__((packed));"),
3031            1
3032        );
3033        assert_eq!(
3034            bit_field_byte("struct s { int x : 12; __attribute__((packed)) char y : 6; };"),
3035            1
3036        );
3037        assert_eq!(bit_field_byte("#pragma pack(4)\nstruct s { int x : 12; char y : 6; };"), 1);
3038        // A thirty bit field after a byte, which is the case the rule was written for.
3039        assert_eq!(bit_field_byte("struct s { char x; int y : 30; };"), 4);
3040        assert_eq!(bit_field_byte("struct s { char x; int y : 30; } __attribute__((packed));"), 1);
3041        // Four is what an `int` asked for anyway, so this caps nothing and still counts.
3042        assert_eq!(bit_field_byte("#pragma pack(4)\nstruct s { char x; int y : 30; };"), 1);
3043        assert_eq!(bit_field_byte("#pragma pack(2)\nstruct s { char x; int y : 30; };"), 1);
3044    }
3045
3046    /// The byte a read of `s.y` loads from, which is where the bit-field was placed.
3047    fn bit_field_byte(record: &str) -> u64 {
3048        let source = format!("{record}\nint f(struct s *p) {{ return p->y; }}\n");
3049        let body = body(&source);
3050        let Some((before, _)) = body.split_once("ptr_add") else { return 0 };
3051        let (_, constant) = before.rsplit_once("iconst.i64 ").expect("an offset constant");
3052        constant.lines().next().expect("a line").trim().parse().expect("a byte offset")
3053    }
3054
3055    /// An attribute in the middle of a specifier list, which is where a member usually carries
3056    /// one and which was read and then thrown away. The `[[...]]` spelling and whatever was
3057    /// written in front of the declaration are collected as the list is walked and the
3058    /// `__attribute__` spelling is put straight on the specifiers, and the two were assigned
3059    /// over each other rather than joined.
3060    #[test]
3061    fn an_attribute_among_the_specifiers_is_kept_beside_the_ones_written_in_front() {
3062        tast(concat!(
3063            "struct a { char c; __attribute__((aligned(8))) int i; };\n",
3064            "_Static_assert(sizeof(struct a) == 16 && _Alignof(struct a) == 8, \"a\");\n",
3065            "_Static_assert(__builtin_offsetof(struct a, i) == 8, \"a.i\");\n",
3066            "struct b { char c; __attribute__((packed)) int i; };\n",
3067            "_Static_assert(sizeof(struct b) == 5 && _Alignof(struct b) == 1, \"b\");\n",
3068            "_Static_assert(__builtin_offsetof(struct b, i) == 1, \"b.i\");\n",
3069            "typedef struct { char c; int i; } __attribute__((packed)) c;\n",
3070            "_Static_assert(sizeof(c) == 5 && _Alignof(c) == 1, \"c\");\n",
3071        ));
3072    }
3073
3074    /// The other half, which is `#pragma pack`. It caps a member's alignment where `packed`
3075    /// drops it, so `pack(2)` leaves a `short` where it was and moves an `int`, and it caps a
3076    /// member the program asked to align as well, which is where the two differ. It is read
3077    /// at the closing brace of the body, so a line written in the middle of one settles the
3078    /// whole record rather than the members after it, and `push` and `pop` nest.
3079    #[test]
3080    fn pragma_pack_caps_every_member_and_is_read_where_the_body_closes() {
3081        tast(concat!(
3082            "#pragma pack(1)\n",
3083            "struct A { char c; int i; };\n",
3084            "_Static_assert(sizeof(struct A) == 5 && _Alignof(struct A) == 1, \"A\");\n",
3085            "_Static_assert(__builtin_offsetof(struct A, i) == 1, \"A.i\");\n",
3086            "#pragma pack()\n",
3087            "struct B { char c; int i; };\n",
3088            "_Static_assert(sizeof(struct B) == 8 && _Alignof(struct B) == 4, \"B\");\n",
3089            "#pragma pack(2)\n",
3090            "struct C { char c; int i; double d; };\n",
3091            "_Static_assert(sizeof(struct C) == 14 && _Alignof(struct C) == 2, \"C\");\n",
3092            "_Static_assert(__builtin_offsetof(struct C, d) == 6, \"C.d\");\n",
3093            // A member the program aligned, which `pack` caps and `packed` would not.
3094            "struct K { char c; int i __attribute__((aligned(8))); };\n",
3095            "_Static_assert(sizeof(struct K) == 6 && _Alignof(struct K) == 2, \"K\");\n",
3096            "_Static_assert(__builtin_offsetof(struct K, i) == 2, \"K.i\");\n",
3097            // The record's own `aligned` is not a member's, so it is not capped.
3098            "struct J { char c; int i; } __attribute__((aligned(8)));\n",
3099            "_Static_assert(sizeof(struct J) == 8 && _Alignof(struct J) == 8, \"J\");\n",
3100            "#pragma pack()\n",
3101            "#pragma pack(push, 1)\n",
3102            "struct D { char c; short s; };\n",
3103            "_Static_assert(sizeof(struct D) == 3 && _Alignof(struct D) == 1, \"D\");\n",
3104            "#pragma pack(pop)\n",
3105            "struct E { char c; short s; };\n",
3106            "_Static_assert(sizeof(struct E) == 4 && _Alignof(struct E) == 2, \"E\");\n",
3107            // Written in the middle of a body, and it still settles the whole record.
3108            "struct H { char c;\n",
3109            "#pragma pack(1)\n",
3110            "  int i; };\n",
3111            "_Static_assert(sizeof(struct H) == 5 && _Alignof(struct H) == 1, \"H\");\n",
3112            "#pragma pack(1)\n",
3113            "struct I { char c;\n",
3114            "#pragma pack()\n",
3115            "  int i; };\n",
3116            "_Static_assert(sizeof(struct I) == 8 && _Alignof(struct I) == 4, \"I\");\n",
3117            "#pragma pack()\n",
3118            // Nested pushes, each one giving back what the one under it had.
3119            "#pragma pack(push, 8)\n",
3120            "#pragma pack(push, 1)\n",
3121            "struct P { char c; int i; };\n",
3122            "_Static_assert(sizeof(struct P) == 5 && _Alignof(struct P) == 1, \"P\");\n",
3123            "#pragma pack(pop)\n",
3124            "struct Q { char c; int i; };\n",
3125            "_Static_assert(sizeof(struct Q) == 8 && _Alignof(struct Q) == 4, \"Q\");\n",
3126            "#pragma pack(pop)\n",
3127            // A cap above what every member already asks for changes nothing at all.
3128            "#pragma pack(16)\n",
3129            "struct R { char c; int i; };\n",
3130            "_Static_assert(sizeof(struct R) == 8 && _Alignof(struct R) == 4, \"R\");\n",
3131            "#pragma pack()\n",
3132            "#pragma pack(1)\n",
3133            "struct S { char c; int i : 5; int j : 20; };\n",
3134            "_Static_assert(sizeof(struct S) == 5 && _Alignof(struct S) == 1, \"S\");\n",
3135            "union T { char c; int i; };\n",
3136            "_Static_assert(sizeof(union T) == 4 && _Alignof(union T) == 1, \"T\");\n",
3137            "#pragma pack()\n",
3138        ));
3139    }
3140
3141    /// A line the reader cannot make sense of is a warning and the line is dropped, which is
3142    /// what GCC does with one, and these are its words for each of them. The last line is the
3143    /// one nothing else would reach, since it stands after every record in the file.
3144    #[test]
3145    fn a_pack_line_that_is_not_one_is_reported_in_the_words_gcc_uses() {
3146        let result = run(
3147            &options(),
3148            concat!(
3149                "#pragma pack 4\n",
3150                "#pragma pack(pop)\n",
3151                "#pragma pack(3)\n",
3152                "#pragma pack(1) junk\n",
3153                "#pragma pack(push, 1\n",
3154                "#pragma pack(x)\n",
3155                // These two are well formed and say nothing. Zero is how a line asks for the
3156                // target's own alignments back without writing empty parentheses.
3157                "#pragma pack(0)\n",
3158                "#pragma pack(push)\n",
3159                "struct s { char c; int i; };\n",
3160                "#pragma pack(pop)\n",
3161                "#pragma pack(pop, foo)\n",
3162            ),
3163        );
3164        let expected = [
3165            "missing `(` after `#pragma pack` - ignored",
3166            "`#pragma pack (pop)` encountered without matching `#pragma pack (push)`",
3167            "alignment must be a small power of two, not 3",
3168            "junk at end of `#pragma pack`",
3169            "malformed `#pragma pack(push[, id][, <n>])` - ignored",
3170            "unknown action `x` for `#pragma pack` - ignored",
3171            "`#pragma pack(pop, foo)` encountered without matching `#pragma pack(push, foo)`",
3172        ];
3173        assert_eq!(result.messages.len(), expected.len(), "{:?}", result.messages);
3174        for (message, want) in result.messages.iter().zip(expected) {
3175            assert!(message.contains(want), "expected {want:?} in {message:?}");
3176        }
3177    }
3178
3179    /// A pragma line ends where the next line starts, so a macro that comes to nothing and was
3180    /// written first on that next line has to hand the line on rather than take it away. This
3181    /// is SQLite through mingw-w64's headers: `<stdarg.h>` leaves a `#pragma pack(pop)` behind
3182    /// it and `sqlite3.h` writes every declaration with `SQLITE_API` in front, which is empty.
3183    /// Without it the pragma swallows the declaration, the program is left without it, and the
3184    /// only thing said about any of it is that there was junk on the pragma.
3185    #[test]
3186    fn a_declaration_behind_an_empty_macro_is_not_eaten_by_the_pragma_above_it() {
3187        let result = run(
3188            &options(),
3189            concat!(
3190                "#pragma pack(push, 1)\n",
3191                "#pragma pack(pop)\n",
3192                "#define API\n",
3193                "API const char version[] = \"3.53.4\";\n",
3194                "const char *get(void) { return version; }\n",
3195            ),
3196        );
3197        assert!(result.messages.is_empty(), "{:?}", result.messages);
3198    }
3199
3200    /// The two typedef spellings of the 128 bit types. gcc offers them as keywords rather
3201    /// than as typedefs in a header, which is the only way a program that includes nothing at
3202    /// all can still use them, and Apple's `<mach/arm/_structs.h>` is one such program.
3203    #[test]
3204    fn the_wide_integer_answers_to_all_three_of_its_names() {
3205        let text = tast("__uint128_t a; __int128_t b; unsigned __int128 c;\n");
3206        assert!(text.contains("decl #0 a : unsigned __int128"), "{text}");
3207        assert!(text.contains("decl #1 b : __int128"), "{text}");
3208        assert!(text.contains("decl #2 c : unsigned __int128"), "{text}");
3209    }
3210
3211    #[test]
3212    fn every_conversion_the_language_performs_is_a_node_in_the_output() {
3213        // The point of a typed tree. The source has one operator and the output has the
3214        // widening that operator asked for, spelled out, so that nothing downstream has to
3215        // work out the conversion rules a second time.
3216        let text = tast("long f(int a, long b) { return a + b; }\n");
3217        assert!(text.contains("convert arithmetic"), "{text}");
3218    }
3219
3220    #[test]
3221    fn a_mistake_in_each_phase_reaches_the_caller_and_writes_no_tree() {
3222        for source in [
3223            "#error stop\n",
3224            "int f(void) { return 1 + ; }\n",
3225            "int f(void) { return undeclared; }\n",
3226        ] {
3227            let result = run(&options(), source);
3228            assert!(result.failed(), "expected this to fail:\n{source}");
3229            assert!(
3230                result.text().is_empty(),
3231                "a file that did not compile wrote a tree:\n{source}"
3232            );
3233        }
3234    }
3235
3236    #[test]
3237    fn one_undeclared_name_is_one_message_and_not_one_per_use() {
3238        // The poisoning rule from `spec/06-lexer-and-parser.md` section 6.8, seen from the
3239        // outside. Three uses of a name that was never declared, and the operators over them
3240        // say nothing at all.
3241        let result = run(&options(), "int f(void) { return nope + nope * nope; }\n");
3242        assert_eq!(result.errors, 1, "{:?}", result.messages);
3243    }
3244
3245    #[test]
3246    fn a_declaration_the_parser_skipped_does_not_become_an_undeclared_name_as_well() {
3247        // The reason the checking is skipped after a failed parse. The parser gave up on the
3248        // first line and there is no `x` in the tree, so a checker run over it would report
3249        // every use of `x` below as undeclared, which is a second message about one mistake.
3250        let result = run(&options(), "int x = ;\nint f(void) { return x; }\n");
3251        assert_eq!(result.errors, 1, "{:?}", result.messages);
3252    }
3253
3254    #[test]
3255    fn werror_turns_a_warning_into_an_error_in_the_count_and_in_the_word() {
3256        let source = "int f(void) { char c = 300; return c; }\n";
3257        let plain = run(&options(), source);
3258        assert_eq!(plain.errors, 0, "{:?}", plain.messages);
3259        assert_eq!(plain.messages.len(), 1, "expected a warning about the narrowed constant");
3260        assert!(!plain.text().is_empty(), "a warning is not a reason to write nothing");
3261
3262        let mut opts = options();
3263        opts.warnings_are_errors = true;
3264        let strict = run(&opts, source);
3265        assert!(strict.failed());
3266        assert!(strict.text().is_empty(), "and under -Werror it is a reason to write nothing");
3267        for message in &strict.messages {
3268            assert!(!message.contains("warning:"), "{message}");
3269        }
3270    }
3271
3272    #[test]
3273    fn w_drops_the_warning_before_werror_can_promote_it() {
3274        let source = "int f(void) { char c = 300; return c; }\n";
3275        let mut opts = options();
3276        opts.warnings = false;
3277        let quiet = run(&opts, source);
3278        assert_eq!(quiet.messages, Vec::<String>::new());
3279        assert_eq!(quiet.errors, 0);
3280        assert!(!quiet.text().is_empty(), "and the file still compiles");
3281
3282        // A build that passes both means it wants neither, and the order it wrote them in is not
3283        // something to make it think about.
3284        opts.warnings_are_errors = true;
3285        let both = run(&opts, source);
3286        assert_eq!(both.messages, Vec::<String>::new());
3287        assert!(!both.failed(), "-w -Werror is not an error about a warning nobody saw");
3288    }
3289
3290    #[test]
3291    fn the_dialect_reaches_the_keywords_and_the_checking() {
3292        // `typeof` is C23's and GNU's, so the same source is a declaration under one dialect
3293        // and a mistake under the other, which is the keyword table being built per dialect.
3294        let source = "typeof(1) x;\n";
3295        let mut opts = options();
3296        opts.std = Std::C23;
3297        opts.gnu_extensions = false;
3298        assert!(!run(&opts, source).failed(), "{:?}", run(&opts, source).messages);
3299
3300        opts.std = Std::C17;
3301        assert!(run(&opts, source).failed());
3302    }
3303
3304    #[test]
3305    fn asking_for_a_kind_that_is_not_written_yet_runs_the_front_end_and_writes_nothing() {
3306        let mut opts = options();
3307        opts.emit = EmitKind::Object;
3308        let result = run(&opts, "int x = 1;\n");
3309        assert!(!result.failed(), "{:?}", result.messages);
3310        assert!(result.text().is_empty());
3311        // And it still finds what the checking finds, so a later kind on a broken file is not
3312        // a silent success.
3313        assert!(run(&opts, "int f(void) { return undeclared; }\n").failed());
3314    }
3315
3316    /// The machine code of `source`, insisting that it compiled cleanly.
3317    fn mir(source: &str) -> String {
3318        let mut opts = options();
3319        opts.emit = EmitKind::MirFinal;
3320        let result = run(&opts, source);
3321        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3322        result.text().to_owned()
3323    }
3324
3325    /// The whole compiler in one assertion, which is what this emit kind is for.
3326    ///
3327    /// C in, machine instructions out, every register a real one and every frame offset a
3328    /// number. Everything between the two is checked somewhere else, one pass at a time. What is
3329    /// checked here is that the passes are joined up and that the driver runs them.
3330    #[test]
3331    fn a_function_goes_from_c_to_instructions_with_real_registers_in_them() {
3332        let text = mir("int add(int a, int b) { return a + b; }\n");
3333        assert!(text.starts_with("mfunc @add {"), "{text}");
3334        assert!(text.contains("x64.add_rr_32"), "{text}");
3335        assert!(text.contains("x64.ret"), "{text}");
3336        // A virtual register is what the allocator was there to remove, so one left in the
3337        // output is the difference between code and something that looks like code.
3338        assert!(!text.contains('%'), "{text}");
3339    }
3340
3341    /// A declaration has no body, so there is nothing to generate for one and nothing is.
3342    #[test]
3343    fn a_function_with_no_body_produces_no_machine_function() {
3344        let text = mir("int g(int);\nint f(int a) { return g(a); }\n");
3345        assert_eq!(text.matches("mfunc @").count(), 1, "{text}");
3346        assert!(text.contains("mfunc @f {"), "{text}");
3347        assert!(text.contains("x64.call"), "{text}");
3348    }
3349
3350    /// Two functions come out in the order the module holds them, which is source order.
3351    #[test]
3352    fn every_definition_in_the_file_is_generated_and_they_keep_their_order() {
3353        let text = mir("int a(int x) { return x; }\nint b(int x) { return x; }\n");
3354        let first = text.find("mfunc @a").expect("the first function");
3355        let second = text.find("mfunc @b").expect("the second function");
3356        assert!(first < second, "{text}");
3357    }
3358
3359    /// The target reaches the back end, so the same C is different instructions on Windows.
3360    #[test]
3361    fn the_target_decides_which_convention_the_generated_code_follows() {
3362        let mut opts = options();
3363        opts.emit = EmitKind::MirFinal;
3364        let linux = run(&opts, "int f(int a) { return a; }\n").text().to_owned();
3365        assert!(linux.contains("$rdi"), "{linux}");
3366
3367        opts.target = "x86_64-pc-windows-msvc".parse::<Triple>().unwrap();
3368        let windows = run(&opts, "int f(int a) { return a; }\n").text().to_owned();
3369        assert!(windows.contains("$rcx"), "{windows}");
3370        assert!(!windows.contains("$rdi"), "{windows}");
3371    }
3372
3373    /// And it reaches the front end, where it decides what an anonymous member is.
3374    ///
3375    /// This is the shape `<objidl.h>` writes and the Windows headers are full of: the union inside
3376    /// `STGMEDIUM` closes with `} DUMMYUNIONNAME;`, and the macro expands to nothing unless the
3377    /// program defined `NONAMELESSUNION`, so what is left is a union with a tag and no name. On a
3378    /// Windows target that is an anonymous member, and reading it as a declaration of nothing
3379    /// drops it, which loses the names and the eight bytes the member takes up both.
3380    #[test]
3381    fn a_tagged_member_with_no_name_is_a_member_on_windows_and_nothing_on_linux() {
3382        let source = concat!(
3383            "struct S { union U { int i; void *p; }; unsigned long tymed; };\n",
3384            "int size(void) { return sizeof(struct S); }\n",
3385            "int f(struct S *s) { s->i = 1; return s->i; }\n",
3386        );
3387
3388        let mut opts = options();
3389        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
3390        let windows = run(&opts, source);
3391        assert!(windows.messages.is_empty(), "{:?}", windows.messages);
3392
3393        let linux = run(&options(), source);
3394        assert_eq!(linux.messages.len(), 3, "{:?}", linux.messages);
3395        assert!(linux.messages[0].contains("does not declare anything"), "{:?}", linux.messages);
3396
3397        // And the flag answers for either of them, so a program built for Linux against a header
3398        // written for Windows can be read the way the header meant it.
3399        let mut opts = options();
3400        opts.ms_extensions = Some(true);
3401        let asked = run(&opts, source);
3402        assert!(asked.messages.is_empty(), "{:?}", asked.messages);
3403    }
3404
3405    /// A target with no back end says so rather than generating something for another machine.
3406    #[test]
3407    fn a_target_this_has_no_back_end_for_is_reported_rather_than_generated() {
3408        let mut opts = options();
3409        opts.emit = EmitKind::MirFinal;
3410        opts.target = "riscv64-unknown-linux-gnu".parse::<Triple>().unwrap();
3411        let result = run(&opts, "int f(int a) { return a; }\n");
3412        assert!(result.failed());
3413        assert!(result.messages[0].contains("no back end for riscv64"), "{:?}", result.messages);
3414        assert!(result.text().is_empty());
3415    }
3416
3417    /// AArch64 is written as its own assembly, with a function that calls keeping its return
3418    /// address in the frame record.
3419    #[test]
3420    fn an_aarch64_target_is_written_as_aarch64_assembly() {
3421        let mut opts = options();
3422        opts.emit = EmitKind::Asm;
3423        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3424        let source = "int g(int);\nint f(int a, int b) { return g(a) + b; }\n";
3425        let result = run(&opts, source);
3426        assert!(!result.failed(), "{:?}", result.messages);
3427        let text = result.text();
3428        for line in ["stp x29, x30, [sp, #-16]!", "mov x29, sp", "bl g", "ldp x29, x30, [sp], #16"]
3429        {
3430            assert!(text.contains(line), "{line} is not in\n{text}");
3431        }
3432        assert!(!text.contains('%'), "{text}");
3433    }
3434
3435    /// An object for AArch64, which is the listing read back by the assembler. The same object
3436    /// with debug information is refused rather than written without its line table.
3437    #[test]
3438    fn an_aarch64_target_reaches_an_object_file() {
3439        let mut opts = options();
3440        opts.emit = EmitKind::Object;
3441        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3442        let source = concat!(
3443            "int g(int);\n",
3444            "int table[4] = {1, 2, 3, 4};\n",
3445            "int f(int a, int b) { return g(a) + table[b & 3]; }\n",
3446        );
3447        let result = run(&opts, source);
3448        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3449        let bytes = match result.artifact {
3450            Artifact::Object { bytes, defines } => {
3451                assert_eq!(defines, ["f", "table"]);
3452                bytes
3453            }
3454            other => panic!("expected an object, got {other:?}"),
3455        };
3456        assert_eq!(&bytes[..4], b"\x7fELF");
3457        assert_eq!(&bytes[18..20], &183u16.to_le_bytes(), "EM_AARCH64");
3458
3459        // And with debug information, which the listing path builds from a label in front of
3460        // every instruction rather than refusing.
3461        opts.debug_info = true;
3462        let result = run(&opts, source);
3463        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3464        let bytes = match result.artifact {
3465            Artifact::Object { bytes, .. } => bytes,
3466            other => panic!("expected an object, got {other:?}"),
3467        };
3468        let has = |name: &[u8]| bytes.windows(name.len()).any(|at| at == name);
3469        assert!(has(b".debug_line\0") && has(b".debug_info\0"));
3470        assert!(!has(b"rucc_row"), "a row label reached the symbol table");
3471    }
3472
3473    /// gcc's AArch64 vector type names are there before any header, which glibc's `<math.h>`
3474    /// needs, a declaration can still hide one, and on x86-64 they are ordinary identifiers.
3475    #[test]
3476    fn the_aarch64_vector_type_names_are_declared_on_that_target_and_nowhere_else() {
3477        let mut opts = options();
3478        opts.emit = EmitKind::Asm;
3479        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3480        let source = "typedef __Float32x4_t f4;\n__SVFloat32_t sv(__SVFloat32_t, __SVBool_t);\n\
3481                      int n = sizeof(f4) + sizeof(__Int8x8_t);\n\
3482                      int f(f4 v) { int __Uint8x16_t = 3; return v[1] + __Uint8x16_t; }\n";
3483        let result = run(&opts, source);
3484        assert!(!result.failed(), "{:?}", result.messages);
3485        assert!(result.text().contains(".long\t24"), "{}", result.text());
3486        opts.target = "x86_64-unknown-linux-gnu".parse::<Triple>().unwrap();
3487        let result = run(&opts, "typedef __Float32x4_t f4;\n");
3488        assert!(result.failed());
3489        let result = run(&opts, "int __Float32x4_t = 1;\n");
3490        assert!(!result.failed(), "{:?}", result.messages);
3491    }
3492
3493    /// A structure too big for registers comes back through the address in x8, which AAPCS64 keeps
3494    /// apart from the arguments, so the argument after it is still in x0.
3495    #[test]
3496    fn an_aarch64_result_in_memory_is_reached_through_x8() {
3497        let mut opts = options();
3498        opts.emit = EmitKind::Asm;
3499        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3500        let source = "struct big { long a, b, c; };\nstruct big make(long v);\n\
3501                      long f(long v) { return make(v).c; }\n\
3502                      struct big g(long v) { struct big b = { v, v, v }; return b; }\n";
3503        let result = run(&opts, source);
3504        assert!(!result.failed(), "{:?}", result.messages);
3505        let text = result.text();
3506        assert!(text.contains("x8"), "{text}");
3507        assert!(text.contains("bl make"), "{text}");
3508    }
3509
3510    /// A remainder is two instructions on AArch64, the division and then a multiply subtract that
3511    /// reads the quotient the division wrote.
3512    #[test]
3513    fn an_aarch64_remainder_is_a_division_and_a_multiply_subtract() {
3514        let mut opts = options();
3515        opts.emit = EmitKind::Asm;
3516        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3517        let source = "int s(int a, int b) { return a % b; }\n\
3518                      unsigned long u(unsigned long a, unsigned long b) { return a % b; }\n";
3519        let result = run(&opts, source);
3520        assert!(!result.failed(), "{:?}", result.messages);
3521        let text = result.text();
3522        let at = |what: &str| text.find(what).unwrap_or_else(|| panic!("{what} is not in\n{text}"));
3523        assert!(at("sdiv w") < at("msub w"), "{text}");
3524        assert!(at("udiv x") < at("msub x"), "{text}");
3525    }
3526
3527    /// A dense `switch` on AArch64 reads a cell of a table after the function with `adr` and
3528    /// `ldrsw`, and each cell is the distance from the table to an arm.
3529    #[test]
3530    fn an_aarch64_jump_table_is_reached_with_adr() {
3531        let mut opts = options();
3532        opts.emit = EmitKind::Asm;
3533        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3534        let source = "int f(int x) { switch (x) { case 0: return 10; case 1: return 21; \
3535                      case 2: return 32; case 3: return 43; case 4: return 54; case 5: return 65; \
3536                      case 6: return 76; case 7: return 87; case 8: return 98; case 9: return 9; \
3537                      case 10: return 19; case 11: return 29; default: return 0; } }\n";
3538        let result = run(&opts, source);
3539        assert!(!result.failed(), "{:?}", result.messages);
3540        let text = result.text();
3541        let at = |what: &str| text.find(what).unwrap_or_else(|| panic!("{what} is not in\n{text}"));
3542        assert!(at("adr x") < at("ldrsw x"), "{text}");
3543        assert!(at("ldrsw x") < at("br x"), "{text}");
3544        assert!(text.contains("_j0:"), "{text}");
3545        assert!(text.contains(".long"), "{text}");
3546    }
3547
3548    /// An AArch64 Linux `va_start` fills in the five fields AAPCS64 gives a list. The two offsets
3549    /// count up to nothing from minus the size of what is left of each half of the save area, so
3550    /// with one integer named they start at minus fifty six and minus one hundred and twenty eight.
3551    #[test]
3552    fn an_aarch64_va_start_writes_the_five_fields_of_its_list() {
3553        let mut opts = options();
3554        opts.emit = EmitKind::Asm;
3555        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3556        let source = "typedef __builtin_va_list va_list;\n\
3557                      int f(int n, ...) { va_list ap; __builtin_va_start(ap, n); \
3558                      int x = __builtin_va_arg(ap, int); double d = __builtin_va_arg(ap, double); \
3559                      __builtin_va_end(ap); return x + (int)d; }\n";
3560        let result = run(&opts, source);
3561        assert!(!result.failed(), "{:?}", result.messages);
3562        let text = result.text();
3563        assert!(text.contains("#-56"), "{text}");
3564        assert!(text.contains("#-128"), "{text}");
3565        assert!(text.contains("#24]"), "{text}");
3566        assert!(text.contains("#28]"), "{text}");
3567        assert!(text.contains("str q"), "{text}");
3568    }
3569
3570    /// A `long double` on AArch64 Linux is a quad, moved with `ldr q` and `str q` and added with a
3571    /// call to the same routine libgcc has.
3572    #[test]
3573    fn an_aarch64_long_double_is_a_quad_in_a_vector_register() {
3574        let mut opts = options();
3575        opts.emit = EmitKind::Asm;
3576        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3577        let source = "void f(long double *p, long double x) { *p = *p + x; }\n";
3578        let result = run(&opts, source);
3579        assert!(!result.failed(), "{:?}", result.messages);
3580        let text = result.text();
3581        assert!(text.contains("ldr q"), "{text}");
3582        assert!(text.contains("str q"), "{text}");
3583        assert!(text.contains("__addtf3"), "{text}");
3584    }
3585
3586    /// A thread-local variable on AArch64 Linux is initial exec: its offset comes out of the
3587    /// global offset table, the thread pointer out of `tpidr_el0`, and one `add` joins them.
3588    #[test]
3589    fn an_aarch64_thread_local_is_reached_through_tpidr_el0() {
3590        let mut opts = options();
3591        opts.emit = EmitKind::Asm;
3592        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
3593        let source = "__thread int n;\nint *f(void) { return &n; }\n\
3594                      void *g(void) { return __builtin_thread_pointer(); }\n";
3595        let result = run(&opts, source);
3596        assert!(!result.failed(), "{:?}", result.messages);
3597        let text = result.text();
3598        assert!(text.contains(":gottprel:n"), "{text}");
3599        assert!(text.contains(":gottprel_lo12:n]"), "{text}");
3600        assert_eq!(text.matches("mrs x").count(), 2, "{text}");
3601        assert!(text.contains("tpidr_el0"), "{text}");
3602    }
3603
3604    /// Apple's platforms reach a thread-local variable by calling through its descriptor, which
3605    /// is what clang writes on both machines, and the variable is the image and the descriptor.
3606    #[test]
3607    fn a_darwin_thread_local_is_reached_through_its_descriptor() {
3608        let source = "__thread int n = 5;\nint *f(void) { return &n; }\n";
3609        for (triple, wanted) in [
3610            ("aarch64-apple-darwin", &["_n@TLVPPAGE\n", "_n@TLVPPAGEOFF]\n", "\tblr x"][..]),
3611            ("x86_64-apple-darwin", &["_n@TLVP(%rip), %rdi\n", "\tcall\t*%"][..]),
3612        ] {
3613            let mut opts = options();
3614            opts.emit = EmitKind::Asm;
3615            opts.target = triple.parse::<Triple>().unwrap();
3616            let result = run(&opts, source);
3617            assert!(!result.failed(), "{triple}: {:?}", result.messages);
3618            let text = result.text();
3619            for want in wanted {
3620                assert!(text.contains(want), "{triple} wanted {want:?}:\n{text}");
3621            }
3622            assert!(text.contains("\n_n:\n\t.quad\t__tlv_bootstrap\n"), "{text}");
3623            assert!(!text.contains("tpidr_el0") && !text.contains("%fs"), "{text}");
3624        }
3625    }
3626
3627    /// The thread pointer itself is somewhere else on Apple's platforms and is still refused.
3628    #[test]
3629    fn the_thread_pointer_is_refused_on_darwin() {
3630        let mut opts = options();
3631        opts.emit = EmitKind::Asm;
3632        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
3633        let result = run(&opts, "void *f(void) { return __builtin_thread_pointer(); }\n");
3634        assert!(result.failed());
3635        assert!(result.messages[0].contains("thread pointer"), "{:?}", result.messages);
3636    }
3637
3638    /// Darwin's list is a plain pointer and its variadic arguments are all on the stack, so a
3639    /// variadic definition saves no registers and its `va_start` stores one address.
3640    #[test]
3641    fn a_darwin_variadic_definition_saves_nothing_and_walks_the_stack() {
3642        let mut opts = options();
3643        opts.emit = EmitKind::Asm;
3644        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
3645        let source = "int f(int n, ...) { __builtin_va_list ap; __builtin_va_start(ap, n);\n\
3646                      int r = __builtin_va_arg(ap, int); __builtin_va_end(ap); return r; }\n";
3647        let result = run(&opts, source);
3648        assert!(!result.failed(), "{:?}", result.messages);
3649        let text = result.text();
3650        assert!(!text.contains("str q"), "{text}");
3651        assert!(!text.contains("x7"), "{text}");
3652    }
3653
3654    /// A call on Darwin puts every argument past the named ones in memory, even with registers
3655    /// left over, so the `double` here is stored rather than put in `d0`.
3656    #[test]
3657    fn a_darwin_call_puts_its_variadic_arguments_in_memory() {
3658        let mut opts = options();
3659        opts.emit = EmitKind::Asm;
3660        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
3661        let source = "int printf(const char *, ...);\n\
3662                      int g(double x) { return printf(\"%d %f\", 7, x); }\n";
3663        let result = run(&opts, source);
3664        assert!(!result.failed(), "{:?}", result.messages);
3665        let text = result.text();
3666        assert!(text.contains("str d0, [sp, #8]"), "{text}");
3667    }
3668
3669    /// Apple's assembler asks for part of an address after the name, a variable another image
3670    /// defines is read through the table because nothing copies it in, and the directive that
3671    /// makes a zeroed variable is also its definition, so its binding goes above it.
3672    #[test]
3673    fn a_darwin_listing_is_one_apples_assembler_reads() {
3674        let mut opts = options();
3675        opts.emit = EmitKind::Asm;
3676        opts.target = "aarch64-apple-darwin".parse::<Triple>().unwrap();
3677        let source = "extern int ext;\n\
3678                      int g[4];\n\
3679                      int f(int i) { return g[i] + ext; }\n";
3680        let result = run(&opts, source);
3681        assert!(!result.failed(), "{:?}", result.messages);
3682        let text = result.text();
3683        assert!(text.contains(", _g@PAGE\n"), "{text}");
3684        assert!(text.contains(", _g@PAGEOFF\n"), "{text}");
3685        assert!(text.contains(", _ext@GOTPAGE\n"), "{text}");
3686        assert!(text.contains(", _ext@GOTPAGEOFF]\n"), "{text}");
3687        assert!(!text.contains(":lo12:"), "{text}");
3688        assert!(text.contains("\t.globl\t_g\n\t.zerofill\t__DATA,__bss,_g,16,2\n"), "{text}");
3689    }
3690
3691    /// A `signed char` read from memory and added to at 32 bits is widened with its sign first.
3692    ///
3693    /// The widening was being taken out as unneeded, because its source is written as a `w`
3694    /// register and was taken to have 32 bits in it, so `*p + 1` added one to the byte `ldrb` had
3695    /// loaded and -9 came out as 248. At every level, since the pass runs at `-O0` too.
3696    #[test]
3697    fn a_signed_char_on_aarch64_is_widened_with_its_sign_before_it_is_added_to() {
3698        for target in ["aarch64-linux-gnu", "aarch64-apple-darwin"] {
3699            let mut opts = options();
3700            opts.emit = EmitKind::Asm;
3701            opts.target = target.parse::<Triple>().unwrap();
3702            let source = "int f(signed char *p) { return *p + 1; }\n\
3703                          unsigned g(unsigned short *p) { return *p + 1u; }\n";
3704            let result = run(&opts, source);
3705            assert!(!result.failed(), "{:?}", result.messages);
3706            let text = result.text();
3707            let signed = text.contains("\tsxtb w") || text.contains("\tldrsb w");
3708            assert!(signed, "{target}: {text}");
3709        }
3710    }
3711
3712    /// A construct the rule set does not reach yet is named, along with the function it is in.
3713    ///
3714    /// The message is about this compiler being unfinished rather than about the program, which
3715    /// is valid C either way, so it carries the note that says where the work is tracked. Both
3716    /// functions are attempted, so a file that is ahead of the back end in three places says so
3717    /// three times rather than one recompilation at a time.
3718    ///
3719    /// The construct is a local of a fixed size wanting more alignment than a call leaves the
3720    /// stack pointer on, in a function whose frame also grows. The prologue would force the
3721    /// alignment and the array would move the stack pointer afterwards, and those are two frames
3722    /// that each want the one register the rest of the frame is counted from.
3723    #[test]
3724    fn a_construct_the_back_end_cannot_reach_yet_is_reported_against_its_function() {
3725        let mut opts = options();
3726        opts.emit = EmitKind::MirFinal;
3727        let source = "void a(int n) { int v[n]; struct __attribute__((aligned(32))) S { int x; } \
3728                      s; s.x = 1; v[0] = s.x; }\n\
3729                      void b(int n) { int v[n]; struct __attribute__((aligned(32))) S { int x; } \
3730                      s; s.x = 1; v[0] = s.x; }\n";
3731        let result = run(&opts, source);
3732        assert!(result.failed());
3733        assert_eq!(result.messages.len(), 2, "{:?}", result.messages);
3734        assert!(result.messages[0].contains("cannot generate code for 'a'"), "{:?}", result);
3735        assert!(result.messages[0].contains("wants more alignment"), "{:?}", result);
3736        assert!(result.messages[1].contains("cannot generate code for 'b'"), "{:?}", result);
3737        assert!(result.text().is_empty());
3738    }
3739
3740    /// A variable length array walks its pages under the flag that says every page is touched.
3741    ///
3742    /// The pages the prologue takes are touched by the prologue. The pages the array takes are
3743    /// however many the size worked out to, so touching them is a loop written around the
3744    /// declaration rather than anything a prologue can do. What says the loop is there is the
3745    /// ordered comparison it ends each step with, which nothing else in a function writes, and the
3746    /// touch behind it. Without the flag the declaration is still the one subtraction it always was.
3747    #[test]
3748    fn a_variable_length_array_walks_its_pages_where_every_page_of_the_frame_is_to_be_touched() {
3749        let mut opts = options();
3750        opts.emit = EmitKind::MirFinal;
3751        let source = "void a(int n) { int v[n]; v[0] = 1; }\n";
3752        let plain = run(&opts, source);
3753        assert!(!plain.failed(), "{:?}", plain.messages);
3754        assert!(!plain.text().contains("cmp_set_a_64"), "{}", plain.text());
3755
3756        opts.stack_clash = true;
3757        let result = run(&opts, source);
3758        assert!(!result.failed(), "{:?}", result.messages);
3759        assert!(result.text().contains("cmp_set_a_64"), "{}", result.text());
3760        assert!(result.text().contains("or_mi_8"), "{}", result.text());
3761    }
3762
3763    /// A function that keeps a frame pointer on Windows now has an unwind record and an object.
3764    ///
3765    /// The record that platform carries counts every slot in it from where the stack pointer ends
3766    /// the prologue, and it gets to that place by taking a constant off the frame pointer, so a
3767    /// register pushed after the pointer was established has no row the format can write. The order
3768    /// that does have one is the pushes, then the frame, and only then the pointer, which is what
3769    /// the back end writes there and only there. A variable length array and an `alloca` keep a
3770    /// pointer whatever the flags asked for, so before this they were the two shapes of C that
3771    /// could not be compiled for that target at all. See tamnd/rucc#1403.
3772    #[test]
3773    fn a_function_that_keeps_a_frame_pointer_on_windows_reaches_an_object_file() {
3774        let mut opts = options();
3775        opts.emit = EmitKind::Object;
3776        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
3777        let source = concat!(
3778            "void use(void *p);\n",
3779            "void array(int n) { int v[n]; v[0] = 1; use(v); }\n",
3780            "void taken(unsigned long n) { use(__builtin_alloca(n)); }\n",
3781        );
3782        let result = run(&opts, source);
3783        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3784        let bytes = match result.artifact {
3785            Artifact::Object { bytes, .. } => bytes,
3786            other => panic!("expected an object, got {other:?}"),
3787        };
3788        assert_eq!(&bytes[..2], b"\x64\x86", "an object that says which machine it is for");
3789
3790        // And the same two functions for Linux, so that what the test is measuring is the target
3791        // rather than the program being one this compiler cannot reach yet.
3792        let mut opts = options();
3793        opts.emit = EmitKind::Object;
3794        assert_eq!(run(&opts, source).messages, Vec::<String>::new());
3795    }
3796
3797    /// The address of a name this file only declares, on the format with no table to read it out
3798    /// of.
3799    ///
3800    /// Every such name went into the table on every target, and COFF has no table, so the object
3801    /// writer was handed a relocation it has no way to write and refused the whole file. What the
3802    /// name stands for on this format is an address in the image whichever way the link supplies
3803    /// it, so the instruction pointer reaches it and gcc writes the same. Three shapes here, since
3804    /// the one that found it was a callback stored in a table of its own: a function passed as an
3805    /// argument, one put in a variable that lives past the call, and one called outright, which
3806    /// never needed the table and is here so the test says which of the three changed.
3807    #[test]
3808    fn the_address_of_a_function_this_file_only_declares_reaches_a_windows_object() {
3809        let source = concat!(
3810            "void other(void *p);\n",
3811            "void takes(void (*f)(void *));\n",
3812            "void (*held)(void *);\n",
3813            "void pass(void) { takes(other); }\n",
3814            "void keep(void) { held = other; }\n",
3815            "void call(void) { other(0); }\n",
3816        );
3817        let mut opts = options();
3818        opts.emit = EmitKind::Object;
3819        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
3820        let result = run(&opts, source);
3821        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3822        let bytes = match result.artifact {
3823            Artifact::Object { bytes, .. } => bytes,
3824            other => panic!("expected an object, got {other:?}"),
3825        };
3826        assert_eq!(&bytes[..2], b"\x64\x86", "an object that says which machine it is for");
3827
3828        // And the same source for Linux, which does have a table and still uses it, so what this
3829        // measures is the format rather than the program.
3830        let mut opts = options();
3831        opts.emit = EmitKind::Object;
3832        assert_eq!(run(&opts, source).messages, Vec::<String>::new());
3833    }
3834
3835    /// An opcode the rule language has no word for is named anyway, and pointed at.
3836    ///
3837    /// The rule language's spelling is the better name when there is one, but an opcode it has
3838    /// no word for is exactly the opcode no rule lowers, so falling back to the opcode and the
3839    /// type is what makes the message say anything at all in the cases that happen. The span is
3840    /// the instruction's own, so the message lands on the line rather than on the file.
3841    ///
3842    /// The width of the float is what keeps the program refused. Everything else here is split into
3843    /// halves by `rucc_codegen::wide`, including the divisions and the conversions to a `float` and
3844    /// a `double`, which became calls into the compiler runtime. A `long double` is the eighty bit
3845    /// float on this target, the runtime has no conversion at that width because the back end has no
3846    /// register that holds one, which is tamnd/rucc#326, so a function converting to it is left with
3847    /// its wide values and reaches the selector the way every function of this width used to.
3848    #[test]
3849    fn an_opcode_with_no_name_in_the_rule_language_is_named_by_its_own_spelling() {
3850        let mut opts = options();
3851        opts.emit = EmitKind::MirFinal;
3852        let source =
3853            "long double f(int a) {\n  __int128 wide = a;\n  return (long double) wide;\n}\n";
3854        let result = run(&opts, source);
3855        assert!(result.failed());
3856        assert!(
3857            result.messages[0].contains("no rule lowers a `sext` producing a `i128`"),
3858            "{result:?}"
3859        );
3860        assert!(result.messages[0].contains(":2:"), "the line the widening is on: {result:?}");
3861        assert!(!result.messages[0].contains("this instruction"), "{result:?}");
3862    }
3863
3864    /// The note names the issue tracker, which is where a reader finds out whether it is known.
3865    #[test]
3866    fn the_note_on_unfinished_work_points_at_the_issues_rather_than_at_the_plan() {
3867        let mut opts = options();
3868        opts.emit = EmitKind::MirFinal;
3869        let source = "long double f(int a) { __int128 wide = a; return (long double) wide; }\n";
3870        let result = run(&opts, source);
3871        assert!(result.failed());
3872        let note = result.messages.iter().find(|line| line.contains("note:")).expect("a note");
3873        assert!(note.contains("https://github.com/tamnd/rucc/issues"), "{note}");
3874        assert!(!note.contains("spec/17-milestones.md"), "{note}");
3875    }
3876
3877    /// The two frame flags reach the frame, which is the only thing either of them does.
3878    #[test]
3879    fn the_frame_flags_on_the_command_line_reach_the_generated_frame() {
3880        let source = "int f(int a) { return a; }\n";
3881        assert!(!mir(source).contains("$rbp"), "a leaf needs no frame pointer when told so");
3882
3883        let mut opts = options();
3884        opts.emit = EmitKind::MirFinal;
3885        opts.frame_pointer = Some(true);
3886        let kept = run(&opts, source).text().to_owned();
3887        assert!(kept.contains("x64.push_64 $rbp"), "{kept}");
3888
3889        // Nothing said at -O0 is a frame pointer, which is what gcc keeps there.
3890        opts.frame_pointer = None;
3891        let kept = run(&opts, source).text().to_owned();
3892        assert!(kept.contains("x64.push_64 $rbp"), "{kept}");
3893    }
3894
3895    /// The assembly of `source`, insisting that it compiled cleanly.
3896    fn asm(source: &str) -> String {
3897        let mut opts = options();
3898        opts.emit = EmitKind::Asm;
3899        let result = run(&opts, source);
3900        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3901        result.text().to_owned()
3902    }
3903
3904    /// `-S`, which is the same compiler as the kind above it with a different last step.
3905    ///
3906    /// What the assembly says is checked in `rucc-asm`, one instruction at a time and against the
3907    /// target's own description of what an instruction is. What is checked here is that a C file
3908    /// goes all the way to a listing an assembler would take, which means the directives around
3909    /// the function as well as the instructions in it.
3910    #[test]
3911    fn a_function_goes_from_c_to_assembly_an_assembler_would_take() {
3912        let text = asm("int add(int a, int b) { return a + b; }\n");
3913        assert!(text.contains("\t.globl\tadd\n"), "{text}");
3914        assert!(text.contains("\t.type\tadd, @function\n"), "{text}");
3915        assert!(text.contains("\nadd:\n"), "{text}");
3916        assert!(text.contains("\taddl\t"), "{text}");
3917        assert!(text.contains("\tret\n"), "{text}");
3918        assert!(text.contains("\t.size\tadd, .-add\n"), "{text}");
3919        // Without this the stack the program runs on is executable, which is not a default
3920        // anybody chose and is not a thing a reader would notice missing.
3921        assert!(text.contains(".note.GNU-stack"), "{text}");
3922    }
3923
3924    /// A call through a function pointer, which is a different instruction from a call to a name.
3925    ///
3926    /// Both are in the one function on purpose. What is being read is that the two calls are told
3927    /// apart all the way down: one carries a name the linker resolves and one carries a register,
3928    /// and neither turns into the other on the way.
3929    #[test]
3930    fn a_call_through_a_function_pointer_goes_through_the_register_it_is_in() {
3931        let text = asm("int g(int);\nint f(int (*p)(int), int a) { return p(a) + g(a); }\n");
3932        assert!(text.contains("\tcall\t*%"), "{text}");
3933        assert!(text.contains("\tcall\tg\n"), "{text}");
3934        // The address arrived in the first argument register and the argument the call passes has
3935        // to end up there, so the two cannot be the same register and the compiler has to have
3936        // moved one of them.
3937        assert!(text.contains("%rdi"), "{text}");
3938    }
3939
3940    /// A name at file scope, which is the one address a function cannot compute for itself. The
3941    /// `lea` that computes it is folded into the load that reads through it, so what is left to
3942    /// read is the addressing mode, which is where the instruction pointer shows up.
3943    #[test]
3944    fn the_address_of_a_global_is_read_from_the_instruction_pointer() {
3945        let text = asm("extern int counter;\nint f(void) { return counter; }\n");
3946        assert!(text.contains("\tmovl\tcounter(%rip), %eax\n"), "{text}");
3947    }
3948
3949    /// Every comparison a branch can be on, which the machine jumps on without keeping a byte.
3950    ///
3951    /// Ten conditions, and each of them comes out as its opposite because the block falls into the
3952    /// arm the comparison is true for and jumps to the other one. That is the half of this most
3953    /// worth pinning: a jump on the condition rather than on its opposite compiles, encodes and
3954    /// runs, and gets every one of these ten functions backwards. The unsigned four and the signed
3955    /// four are separate for the same reason, since `jl` where `jb` was meant is a program that
3956    /// works until an address is above two gigabytes.
3957    #[test]
3958    fn a_branch_on_a_comparison_jumps_on_the_opposite_of_what_it_compared() {
3959        let arms = "return 1; return 2;";
3960        let signed = [("==", "jne"), ("!=", "je"), ("<", "jge"), ("<=", "jg"), (">", "jle")];
3961        for (operator, jump) in signed.into_iter().chain([(">=", "jl")]) {
3962            let text = asm(&format!("int f(int a, int b) {{ if (a {operator} b) {arms} }}\n"));
3963            assert!(
3964                text.contains(&format!("\tcmpl\t%esi, %edi\n\t{jump}\t")),
3965                "{operator}: {text}"
3966            );
3967            assert!(!text.contains("\tset"), "{operator}: {text}");
3968            assert!(!text.contains("\ttest"), "{operator}: {text}");
3969        }
3970        let unsigned = [("<", "jae"), ("<=", "ja"), (">", "jbe"), (">=", "jb")];
3971        for (operator, jump) in unsigned {
3972            let source =
3973                format!("int f(unsigned a, unsigned b) {{ if (a {operator} b) {arms} }}\n");
3974            let text = asm(&source);
3975            assert!(
3976                text.contains(&format!("\tcmpl\t%esi, %edi\n\t{jump}\t")),
3977                "{operator}: {text}"
3978            );
3979        }
3980
3981        // And against a constant, which is four comparisons in five and is where the saving
3982        // mostly is, since the byte that goes was the only reason the constant was in a register.
3983        let text = asm("int f(int a) { if (a < 7) return 1; return 2; }\n");
3984        assert!(text.contains("\tcmpl\t$7, %edi\n\tjge\t"), "{text}");
3985    }
3986
3987    /// The comparison whose answer is a value rather than a branch, which keeps its byte.
3988    ///
3989    /// The one that goes is the byte nothing but the branch reads. A comparison the program asked
3990    /// for the answer of is not that, and there is no branch behind it to fold into in any case,
3991    /// so this is here to say that what was taken out was taken out of one place and not two.
3992    #[test]
3993    fn a_comparison_whose_answer_the_program_wanted_still_writes_a_byte() {
3994        let text = asm("int f(int a, int b) { return a < b; }\n");
3995        assert!(text.contains("\tsetl\t"), "{text}");
3996    }
3997
3998    /// The same source at `-O2`, which is where the optimizer's passes are in the list.
3999    fn optimized(source: &str) -> String {
4000        let mut opts = options();
4001        opts.emit = EmitKind::Asm;
4002        opts.opt_level = rucc_session::OptLevel::O2;
4003        let result = run(&opts, source);
4004        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4005        result.text().to_owned()
4006    }
4007
4008    /// What each `switch` became is an `-fopt-info` remark, and `-Zswitch=` changes what it says.
4009    #[test]
4010    fn opt_info_says_what_each_switch_became_and_a_forced_shape_is_what_it_says() {
4011        let arms: String = (0..40)
4012            .map(|k| format!("case {}: return g({k});", k * 17))
4013            .collect::<Vec<_>>()
4014            .join(" ");
4015        let source = format!("int g(int);\nint f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n");
4016        let said = |shape: Option<&str>| {
4017            let mut opts = options();
4018            opts.emit = EmitKind::Asm;
4019            opts.opt_level = rucc_session::OptLevel::O2;
4020            opts.opt_info = vec![String::new()];
4021            opts.switch_shape = shape.map(str::to_owned);
4022            let result = run(&opts, &source);
4023            assert_eq!(result.messages, Vec::<String>::new());
4024            let lines: Vec<String> = result
4025                .remarks
4026                .lines()
4027                .filter(|line| line.contains("[switch-lowering]"))
4028                .map(str::to_owned)
4029                .collect();
4030            assert_eq!(lines.len(), 1, "{}", result.remarks);
4031            lines[0].clone()
4032        };
4033        assert!(said(None).contains(": f: optimized: switch of 40 cases lowered as a tree;"));
4034        assert!(said(Some("table")).contains("lowered as a table;"));
4035        assert!(said(Some("walk")).contains("lowered as a walk;"));
4036    }
4037
4038    /// A dense `switch` whose arms are a function of the label, which is arithmetic.
4039    ///
4040    /// Sixteen labels, and the arm for label `k` gives `k + 1`. What came out of this was a
4041    /// comparison and a jump for every one of them, which is tamnd/rucc#728. What comes out now is
4042    /// one comparison and one addition, and the count is the whole of the claim: it does not grow
4043    /// with the number of labels, so sixteen and a hundred and sixty compile to the same thing.
4044    ///
4045    /// The comparison is unsigned because the range check is the label minus the lowest one, which
4046    /// is a count and not a number the program wrote.
4047    #[test]
4048    fn a_switch_whose_arms_are_a_function_of_the_label_is_a_range_check_and_arithmetic() {
4049        let arms: String =
4050            (0..16).map(|k| format!("case {k}: return {};", k + 1)).collect::<Vec<_>>().join(" ");
4051        let text = optimized(&format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
4052        assert!(text.contains("\tcmpl\t$15, %edi\n\tja\t"), "{text}");
4053        assert!(text.contains("\taddl\t$1, %edi"), "{text}");
4054        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
4055    }
4056
4057    /// The same `switch` with one arm off the line, which is a table and not arithmetic.
4058    ///
4059    /// The answers being a line is what licenses the addition, since it answers for every label in
4060    /// the range at once. One label whose arm disagrees is a label it would answer wrongly, so this
4061    /// is here to say that the pass is reading the arms and not counting the labels. What it does
4062    /// instead is look the answer up: one comparison, no jump through a jump table, and the arm off
4063    /// the line is a cell of a constant array in `.rodata`, which is gcc's `CSWTCH` and its shape.
4064    #[test]
4065    fn a_dense_switch_whose_arms_are_not_a_line_is_a_load_from_a_table() {
4066        let arms: String = (0..16)
4067            .map(|k| format!("case {k}: return {};", if k == 9 { 100 } else { k + 1 }))
4068            .collect::<Vec<_>>()
4069            .join(" ");
4070        let text = optimized(&format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
4071        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
4072        assert!(!text.contains("\tjmp\t*"), "{text}");
4073        assert!(text.contains("leaq\tCSWTCH.0(%rip)"), "{text}");
4074        let table = &text[text.find("CSWTCH.0:").expect("the table is in the output")..];
4075        let section = text[..text.find("CSWTCH.0:").unwrap_or(0)].rfind("\t.section\t.rodata");
4076        assert!(section.is_some(), "{text}");
4077        assert_eq!(table.matches("\t.long\t").count(), 16, "{text}");
4078        assert!(table.contains("\t.long\t100\n"), "{text}");
4079    }
4080
4081    /// A `switch` whose arms give string literals is a table of how far each string is from it.
4082    ///
4083    /// gcc 16 keeps the compares here, because its table would hold addresses the loader has to
4084    /// write when the program starts, and that table would have to be in `.data.rel.ro`. This one
4085    /// holds four byte distances the linker writes once, so it stays in `.rodata` with the strings.
4086    #[test]
4087    fn a_switch_whose_arms_give_strings_is_a_table_of_how_far_away_they_are() {
4088        let text = optimized(
4089            "const char *f(int k) { switch (k) { case 0: return \"zero\"; \
4090             case 1: return \"one\"; case 2: return \"two\"; case 3: return \"three\"; } \
4091             return \"many\"; }\n",
4092        );
4093        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
4094        assert!(text.contains("leaq\tCSWTCH.0(%rip)"), "{text}");
4095        assert!(!text.contains(".data.rel.ro"), "{text}");
4096        let at = text.find("CSWTCH.0:").expect("the table is in the output");
4097        assert!(text[..at].rfind("\t.section\t.rodata").is_some(), "{text}");
4098        let table = &text[at..];
4099        assert_eq!(table.matches(" - .\n").count(), 4, "{text}");
4100        assert!(table.contains("\t.long\t.Lstr.1+4 - .\n"), "{text}");
4101    }
4102
4103    /// The same table at `-Os`, where a cell is a byte because every answer fits in one.
4104    ///
4105    /// gcc 16 narrows the cells at `-Os` and not at `-O2`, and so does rucc: sixteen answers under a
4106    /// hundred and twenty eight are sixteen bytes rather than sixty four, and the byte is widened
4107    /// back with its sign.
4108    #[test]
4109    fn a_table_at_os_has_cells_as_narrow_as_its_answers() {
4110        let arms: String = (0..16)
4111            .map(|k| format!("case {k}: return {};", if k == 9 { 100 } else { k + 1 }))
4112            .collect::<Vec<_>>()
4113            .join(" ");
4114        let mut opts = options();
4115        opts.emit = EmitKind::Asm;
4116        opts.opt_level = rucc_session::OptLevel::Os;
4117        let result = run(&opts, &format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
4118        assert_eq!(result.messages, Vec::<String>::new());
4119        let text = result.text();
4120        let table = &text[text.find("CSWTCH.0:").expect("the table is in the output")..];
4121        assert_eq!(table.matches("\t.byte\t").count(), 16, "{text}");
4122        assert!(text.contains("\tmovsbl\t"), "{text}");
4123    }
4124
4125    /// A table whose labels are every value the switched value can hold, which is the range check
4126    /// `rucc_opt::prune` takes out.
4127    ///
4128    /// The operand is `x & 3` and all four values are cases, so the `return -1` is dead. With the
4129    /// default out of the switch every case goes to the load, the switch is a jump, and what is
4130    /// left is the mask and the load with no compare in front of it.
4131    #[test]
4132    fn a_table_that_covers_its_operand_has_no_range_check() {
4133        let text = optimized(
4134            "int f(unsigned x) { switch (x & 3) { case 0: return 5; case 1: return 9; \
4135             case 2: return 2; case 3: return 7; } return -1; }\n",
4136        );
4137        assert!(text.contains("leaq\tCSWTCH.0(%rip)"), "{text}");
4138        assert!(!text.contains("\tcmp"), "{text}");
4139        assert!(!text.contains("$-1"), "{text}");
4140    }
4141
4142    /// A store one path makes to a local the loop has just read, which GCC also turns into a
4143    /// conditional move and an unconditional store. The branch was on data, so it was the one the
4144    /// machine gets wrong half the time. The move reads the flags of the comparison itself, so no
4145    /// byte is set and tested in between.
4146    #[test]
4147    fn a_store_to_a_local_the_loop_just_read_is_a_conditional_move() {
4148        let text = optimized(
4149            "int f(const int *v, int n, int k) { int best[8] = {0}; \
4150             for (int i = 0; i < n; i++) if (v[i] > best[i & 7]) best[i & 7] = v[i]; \
4151             return best[k & 7]; }\n",
4152        );
4153        assert!(text.contains("\tcmovgl"), "{text}");
4154        assert!(!text.contains("\tset"), "{text}");
4155        assert!(!text.contains("\ttestb"), "{text}");
4156    }
4157
4158    /// The same loop on a global keeps its branch, because another thread may own the slot.
4159    #[test]
4160    fn a_store_to_a_global_the_loop_just_read_keeps_its_branch() {
4161        let text = optimized(
4162            "int best[8]; void f(const int *v, int n) { \
4163             for (int i = 0; i < n; i++) if (v[i] > best[i & 7]) best[i & 7] = v[i]; }\n",
4164        );
4165        assert!(!text.contains("\tcmov"), "{text}");
4166    }
4167
4168    /// A conversion whose operand the optimizer turned into a constant, which is the whole of what
4169    /// `rucc_opt::fold` does with floating point.
4170    ///
4171    /// The cast is not a constant expression, so the front end leaves it alone and the pipeline is
4172    /// what has to see it. Load forwarding turns the local back into the constant that was stored
4173    /// into it, and the conversion then has an `fconst` in front of it. What came out before was
4174    /// the sixty four bit pattern moved into a register, moved into an `xmm`, and a `cvttsd2si`.
4175    #[test]
4176    fn a_conversion_from_a_constant_double_is_the_number_it_converts_to() {
4177        let text = optimized("int f(void) { double d = 2.75; return (int) d; }\n");
4178        assert!(text.contains("movl\t$2, %eax"), "{text}");
4179        assert!(!text.contains("cvttsd2si"), "{text}");
4180    }
4181
4182    /// A slot of a `const` table read at an index the optimizer works out, which is what
4183    /// `rucc_opt::image` is for.
4184    ///
4185    /// The subscript is not a constant expression and the front end does not fold it. What it
4186    /// writes is the index sign extended, multiplied by four and added to the address of the
4187    /// table, so the offset only exists once `fold` has run and the load only folds after that.
4188    /// What came out before was a `movl t+8(%rip), %eax`.
4189    #[test]
4190    fn a_slot_of_a_read_only_table_is_the_value_the_table_holds() {
4191        let text =
4192            optimized("static const int t[4] = {10, 20, 30, 40};\nint f(void) { return t[2]; }\n");
4193        assert!(text.contains("movl\t$30, %eax"), "{text}");
4194        assert!(!text.contains("t(%rip)"), "{text}");
4195    }
4196
4197    /// A byte of a string literal, which is the same fold reading literal bytes rather than the
4198    /// scalars an `int` array is written as.
4199    #[test]
4200    fn a_byte_of_a_read_only_string_is_the_byte_the_string_spells() {
4201        let text = optimized("static const char s[] = \"abc\";\nint f(void) { return s[1]; }\n");
4202        assert!(text.contains("movl\t$98, %eax"), "{text}");
4203    }
4204
4205    /// A global something can write to, which is the condition the fold turns on and therefore
4206    /// the one worth a test of its own. Nothing here is `const`, so the store in `g` could be the
4207    /// store that ran last and the load has to happen.
4208    #[test]
4209    fn a_table_that_is_not_read_only_keeps_its_load() {
4210        let text = optimized(
4211            "static int t[4] = {10, 20, 30, 40};\nvoid g(int x) { t[2] = x; }\nint f(void) { return t[2]; }\n",
4212        );
4213        assert!(!text.contains("movl\t$30, %eax"), "{text}");
4214    }
4215
4216    /// `gcc.c-torture/execute/20030216-1.c`, which is the program the whole of this is for.
4217    ///
4218    /// It calls a function nothing defines, guarded by a condition the optimizer is meant to prove
4219    /// false, so the program links exactly when the call has been folded away. Getting there is
4220    /// three folds standing on each other: the load of the `const double`, the conversion of it to
4221    /// an `int`, and the comparison against one.
4222    #[test]
4223    fn a_call_guarded_by_a_condition_a_read_only_object_settles_is_not_emitted() {
4224        let text = optimized(
4225            "void link_error(void);\nconst double one = 1.0;\nint main(void) { if ((int) one != 1) link_error(); return 0; }\n",
4226        );
4227        assert!(!text.contains("call\tlink_error"), "{text}");
4228    }
4229
4230    /// A cast between a pointer and an integer as wide as one, which is every one C writes here.
4231    #[test]
4232    fn a_cast_between_a_pointer_and_an_integer_leaves_the_value_where_it_is() {
4233        let text = asm("long f(void *p) { return (long)p; }\n");
4234        // Every instruction in the body is a full width move or the return. The copies are the
4235        // allocator taking no hints, and what matters here is what is not among them: nothing
4236        // narrows the value and nothing widens it again, which is what a cast that did something
4237        // would look like.
4238        for line in text.lines().filter(|line| line.starts_with('\t') && !line.contains('.')) {
4239            let mnemonic = line.split_whitespace().next().unwrap_or("");
4240            assert!(matches!(mnemonic, "movq" | "ret"), "{line} in\n{text}");
4241        }
4242    }
4243
4244    /// The arguments past the sixth arrive in the caller's memory rather than in a register, and
4245    /// where that memory is depends on what the prologue did, so this is checked at the end of the
4246    /// pipeline rather than in the middle of it.
4247    #[test]
4248    fn an_argument_past_the_last_register_is_read_out_of_the_caller_s_stack() {
4249        let six = "long a, long b, long c, long d, long e, long f";
4250        let text = asm(&format!("long f({six}, long g, long h) {{ return g + h; }}\n"));
4251
4252        // Nothing is pushed and no frame is taken, so the only thing between the stack pointer and
4253        // the caller's arguments is the return address the call pushed. Which is where gcc 16.2.0
4254        // reads them from too, at `-O0`, though it reads them in three instructions where this
4255        // reads them in two: the second read is the addition's own memory operand, which is
4256        // `rucc_codegen::combine`, and the offset in it is the one the frame layout wrote into the
4257        // load before the two were put together.
4258        assert!(text.contains("\tmovq\t8(%rsp), "), "{text}");
4259        assert!(text.contains("\taddq\t16(%rsp), "), "{text}");
4260
4261        // A narrower one is read at its own width, because the bits above it are bits the
4262        // convention says nothing about, and one in the other register file with the other file's
4263        // instruction.
4264        let narrow = asm(&format!("int f({six}, int g) {{ return g; }}\n"));
4265        assert!(narrow.contains("\tmovl\t8(%rsp), "), "{narrow}");
4266        let eight =
4267            "double a, double b, double c, double d, double e, double f, double g, double h";
4268        let float = asm(&format!("double f({eight}, double i) {{ return i; }}\n"));
4269        assert!(float.contains("\tmovsd\t8(%rsp), "), "{float}");
4270    }
4271
4272    /// The other end of the same thing. What the caller writes is at the stack pointer, because
4273    /// that is the bottom of its frame and the bottom of its frame is where the callee looks.
4274    #[test]
4275    fn a_call_writes_the_arguments_with_no_register_left_at_the_stack_pointer() {
4276        let six = "1, 2, 3, 4, 5, 6";
4277        let decl = "long g(long, long, long, long, long, long, long, long);\n";
4278        let text = asm(&format!("{decl}long f(void) {{ return g({six}, 7, 8); }}\n"));
4279
4280        assert!(text.contains("\tmovq\t%"), "{text}");
4281        assert!(text.contains(", (%rsp)\n"), "{text}");
4282        assert!(text.contains(", 8(%rsp)\n"), "{text}");
4283        // And it reserved the bytes it wrote into, so nothing else in the frame is on top of them.
4284        assert!(text.contains("\tsubq\t$"), "{text}");
4285
4286        // A narrower one is written at its own width, matching what the callee reads it back with.
4287        let narrow = "int g(int, int, int, int, int, int, int);\n";
4288        let text = asm(&format!("{narrow}int f(void) {{ return g({six}, 7); }}\n"));
4289        assert!(text.contains("\tmovl\t%"), "{text}");
4290        assert!(text.contains(", (%rsp)\n"), "{text}");
4291    }
4292
4293    /// The count a variadic callee on this convention reads is a count of vector registers, so a
4294    /// float that ran out of them and went to memory is not in it.
4295    #[test]
4296    fn a_variadic_call_counts_registers_and_not_arguments() {
4297        let nine = "1., 2., 3., 4., 5., 6., 7., 8., 9.";
4298        let decl = "int g(int, ...);\n";
4299        let text = asm(&format!("{decl}int f(void) {{ return g(0, {nine}); }}\n"));
4300
4301        assert!(text.contains("\tmovl\t$8, "), "eight registers, not nine: {text}");
4302        assert!(text.contains("\tmovsd\t%"), "{text}");
4303        assert!(text.contains(", (%rsp)\n"), "{text}");
4304    }
4305
4306    /// The callee's half of the same convention. Every argument register it was handed is written
4307    /// into its frame on the way in, because which of them hold anything is a thing only the caller
4308    /// knew, and the ones the signature does name are left out because `va_start` sets the offsets
4309    /// past them and nothing ever reads their slots.
4310    #[test]
4311    fn a_variadic_function_writes_the_argument_registers_it_was_handed_into_its_frame() {
4312        let body =
4313            "__builtin_va_list ap; __builtin_va_start(ap, n); __builtin_va_end(ap); return n;";
4314        let text = asm(&format!("int f(int n, ...) {{ {body} }}\n"));
4315
4316        // Five general purpose registers and eight vector ones, since the one parameter the
4317        // signature names took the first of the six.
4318        let stores = |mnemonic: &str| text.matches(&format!("\t{mnemonic}\t%")).count();
4319        assert!(text.contains(", 8(%r"), "the second slot, not the first: {text}");
4320        assert!(!text.contains(", 0(%r"), "{text}");
4321        // All sixteen bytes of each vector register, which is what gcc writes and what a `va_arg`
4322        // of a `_Float128` reads back, so the mnemonic is the one that moves a whole register.
4323        assert_eq!(stores("movaps"), 8, "every vector register: {text}");
4324        assert_eq!(stores("movsd"), 0, "and the whole of each one: {text}");
4325
4326        // And the area is one of the function's own stack objects, so the frame holds it.
4327        assert!(text.contains("\tsubq\t$"), "{text}");
4328    }
4329
4330    /// What `va_start` writes is the four fields of the list, and the two numbers among them are
4331    /// where the arguments the signature names left the walk over each file's registers.
4332    #[test]
4333    fn va_start_writes_the_four_fields_the_psabi_describes() {
4334        let start = "__builtin_va_list ap; __builtin_va_start(ap, d);";
4335        let params = "int a, int b, int c, double d";
4336        let text = asm(&format!("int f({params}, ...) {{ {start} return a; }}\n"));
4337
4338        // Three integers took three of the six general purpose registers, and one double took one
4339        // of the eight vector ones, so the walk starts at twenty four bytes into the first half and
4340        // sixteen bytes into the second, which begins at forty eight.
4341        assert!(text.contains("	movl	$24, "), "{text}");
4342        assert!(text.contains("	movl	$64, "), "{text}");
4343        // The other two fields are addresses rather than numbers, so each is stored as a word and
4344        // each is a `lea` away. One of them reaches above the frame, which is where the caller's
4345        // arguments are and is the only thing in this function that is not below the stack pointer.
4346        assert!(text.contains(", 8(%r"), "{text}");
4347        assert!(text.contains(", 16(%r"), "{text}");
4348        let frame: u32 = text
4349            .lines()
4350            .find_map(|line| line.trim().strip_prefix("subq	$")?.split(',').next()?.parse().ok())
4351            .expect("a variadic function takes a frame for the save area");
4352        let above = |line: &str| {
4353            let at: u32 = line.trim().strip_prefix("leaq	")?.split('(').next()?.parse().ok()?;
4354            Some(at > frame)
4355        };
4356        assert!(text.lines().filter_map(above).any(|it| it), "{frame}: {text}");
4357    }
4358
4359    /// A `va_arg` is a branch on whether the argument it wants is still in the save area, and which
4360    /// of the two halves it walks is the type's answer.
4361    #[test]
4362    fn va_arg_branches_on_whether_the_argument_is_still_in_the_save_area() {
4363        let read = "__builtin_va_list ap; __builtin_va_start(ap, n);";
4364        let ints = format!("int f(int n, ...) {{ {read} return __builtin_va_arg(ap, int); }}\n");
4365        let text = asm(&ints);
4366
4367        // The last general purpose slot begins at forty, so an offset above it is an argument the
4368        // caller left in its own memory instead.
4369        assert!(text.contains("$40, "), "{text}");
4370        assert!(text.contains("	cmpl	"), "{text}");
4371        // The jump is the unsigned one, since an offset is a count of bytes. It is the opposite
4372        // of the comparison the front end wrote, because the block falls into the half taken when
4373        // the argument is still in the save area and jumps to the other one.
4374        assert!(text.contains("	ja	"), "{text}");
4375
4376        let arg = "__builtin_va_arg(ap, double)";
4377        let text = asm(&format!("double f(int n, ...) {{ {read} return {arg}; }}\n"));
4378        assert!(text.contains("$160, "), "the last vector slot: {text}");
4379    }
4380
4381    /// A structure assigned is a copy of a known size, and a copy of a known size is a run of
4382    /// moves rather than a call to a library this compiler has no way to reach yet.
4383    #[test]
4384    fn a_structure_assignment_is_a_move_for_each_word_of_it() {
4385        let decl = "struct pair { long a, b; };\n";
4386        let body = "struct pair p = *q; return p.a + p.b;";
4387        let text = asm(&format!("{decl}long f(struct pair *q) {{ {body} }}\n"));
4388
4389        assert!(!text.contains("memcpy"), "nothing calls the library: {text}");
4390        assert!(!text.contains("\tcall"), "{text}");
4391        // Sixteen bytes aligned to eight is two words, and each is a load and a store.
4392        assert!(text.matches("\tmovq\t").count() >= 4, "two words each way: {text}");
4393    }
4394
4395    /// A word is as wide as the object is aligned to and no wider, so a character array is copied
4396    /// a byte at a time and a structure of longs eight bytes at a time.
4397    #[test]
4398    fn how_wide_a_word_of_a_copy_is_follows_the_alignment() {
4399        let decl = "struct bytes { char a[8]; };\n";
4400        let body = "struct bytes p = *q; return p.a[0];";
4401        let text = asm(&format!("{decl}int f(struct bytes *q) {{ {body} }}\n"));
4402
4403        // Eight bytes aligned to one is eight words, and each is a load and a store.
4404        assert!(text.matches("\tmovb\t").count() >= 16, "a byte at a time: {text}");
4405    }
4406
4407    /// What an initialiser does not name is zero, which the front end writes as a fill and this
4408    /// writes as the byte spread across each word.
4409    #[test]
4410    fn the_part_of_an_initialiser_that_names_nothing_is_stored_as_zero() {
4411        let decl = "struct wide { long a, b, c; };\n";
4412        let text = asm(&format!("{decl}long f(void) {{ struct wide w = {{ 7 }}; return w.c; }}\n"));
4413
4414        assert!(!text.contains("memset"), "nothing calls the library: {text}");
4415        // Either spelling of a zero in a register, the move of one or the exclusive or of the
4416        // register with itself that `rucc_codegen::shorten` writes instead where it is free. The
4417        // exclusive or is the thirty-two bit one whatever the width of the word, since the half of
4418        // the register it does not write is cleared rather than left alone.
4419        assert!(text.contains("\tmovq\t$0, ") || text.contains("\txorl\t"), "the zero: {text}");
4420    }
4421
4422    /// A copy too large to be worth unrolling is a call to the runtime, which is the C library on
4423    /// a hosted target and `rucc-builtins` on a freestanding one.
4424    #[test]
4425    fn a_copy_too_large_to_unroll_calls_the_runtime() {
4426        let decl = "struct huge { char a[4096]; };\n";
4427        let mut opts = options();
4428        opts.emit = EmitKind::Asm;
4429        let source = format!("{decl}void f(struct huge *p, struct huge *q) {{ *p = *q; }}\n");
4430        let result = run(&opts, &source);
4431        assert!(!result.failed(), "{:?}", result.messages);
4432        let text = result.text();
4433        assert!(text.contains("call") && text.contains("memcpy"), "{text}");
4434        // The size in the register the convention passes the third argument in, which is what
4435        // says the call was built from the convention and not from the shape of the IR.
4436        assert!(text.contains("4096"), "the size travels: {text}");
4437    }
4438
4439    /// And an object passed by value with more words in it than that is the same call again,
4440    /// written in front of the call the object is an argument of.
4441    ///
4442    /// The copy is one the caller owes the callee, since the callee is free to write to what it
4443    /// was handed, so it is not an optimization that the size decides but the only way the call
4444    /// can be made at all.
4445    #[test]
4446    fn a_structure_too_large_to_unroll_is_copied_into_the_argument_area_by_the_runtime() {
4447        let decl = "struct huge { char a[4096]; };\nint take(struct huge);\n";
4448        let text = asm(&format!("{decl}int f(struct huge *p) {{ return take(*p); }}\n"));
4449
4450        let copy = text.find("call\tmemcpy").expect("the copy");
4451        let call = text.find("call\ttake").expect("the call");
4452        assert!(copy < call, "the copy comes first: {text}");
4453        // Into the bottom of the outgoing area, which is where the stack pointer already is, and
4454        // with the size in the register the convention passes the third argument in. The address
4455        // of the bottom of the frame is the stack pointer itself, so what carries it is the move
4456        // rather than the address computation the selector wrote. See `rucc_codegen::shorten`.
4457        assert!(text.contains("movq\t%rsp, %rdi"), "the destination: {text}");
4458        assert!(text.contains("$4096, %edx"), "the size: {text}");
4459    }
4460
4461    /// A frame that had to force its own alignment cannot say how far away the caller's stack
4462    /// pointer was, so it reaches back through the frame pointer instead.
4463    #[test]
4464    fn a_realigned_frame_reads_them_through_the_frame_pointer() {
4465        let six = "long a, long b, long c, long d, long e, long f";
4466        let body = "_Alignas(32) long wide[4]; wide[0] = g; return wide[0];";
4467        let text = asm(&format!("long f({six}, long g) {{ {body} }}\n"));
4468
4469        // The frame pointer is saved and pointed at where it was saved before the alignment is
4470        // forced, so the caller's arguments stay a constant distance from it: one word for the
4471        // saved frame pointer and one for the return address.
4472        assert!(text.contains("\tandq\t$-32, %rsp"), "{text}");
4473        assert!(text.contains("\tmovq\t16(%rbp), "), "{text}");
4474        assert!(!text.contains("\tmovq\t16(%rsp), "), "{text}");
4475    }
4476
4477    /// The object format decides the directives, and the target decides the object format.
4478    #[test]
4479    fn the_target_decides_how_the_assembly_is_spelled() {
4480        let mut opts = options();
4481        opts.emit = EmitKind::Asm;
4482        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
4483        let text = run(&opts, "int f(void) { return 0; }\n").text().to_owned();
4484        assert!(text.contains("__TEXT,__text"), "{text}");
4485        assert!(text.contains("\n_f:\n"), "{text}");
4486        assert!(!text.contains(".note.GNU-stack"), "{text}");
4487    }
4488
4489    /// The object file of `source`, insisting that it compiled cleanly.
4490    fn obj(source: &str) -> Vec<u8> {
4491        let mut opts = options();
4492        opts.emit = EmitKind::Object;
4493        let result = run(&opts, source);
4494        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4495        match result.artifact {
4496            Artifact::Object { bytes, .. } => bytes,
4497            other => panic!("expected an object, got {other:?}"),
4498        }
4499    }
4500
4501    /// `-c`, which is the last step of the three the back end can end with.
4502    ///
4503    /// What is in the file is checked in `rucc-object`, a field at a time. What is checked here is
4504    /// that a C file goes all the way to one, which is the whole compiler in one line and the
4505    /// thing that stops working when a layer between them changes its mind about something.
4506    #[test]
4507    fn a_function_goes_from_c_to_an_object_a_linker_would_take() {
4508        let bytes = obj("int add(int a, int b) { return a + b; }\n");
4509        assert_eq!(&bytes[..4], b"\x7fELF", "an object file starts by saying it is one");
4510        let text = asm("int add(int a, int b) { return a + b; }\n");
4511        assert!(
4512            text.contains("\taddl\t"),
4513            "and the listing of it is the same instructions:\n{text}"
4514        );
4515    }
4516
4517    /// A variable this file defines, which is what a reference to one has to resolve against.
4518    #[test]
4519    fn a_variable_goes_from_c_to_the_section_it_belongs_in() {
4520        let text = asm("int counter = 42;\nstatic int hidden;\nconst int fixed = 7;\n");
4521        assert!(text.contains("\t.data\n\t.globl\tcounter\n"), "{text}");
4522        assert!(text.contains("\ncounter:\n\t.long\t42\n"), "{text}");
4523        assert!(text.contains("\t.size\tcounter, .-counter\n"), "{text}");
4524        // A zeroed variable carries its size and none of its bytes, and a `static` one is not
4525        // announced to the linker at all, which is the whole of what `static` means here.
4526        assert!(text.contains("\t.bss\n\t.p2align\t2\n"), "{text}");
4527        assert!(text.contains("\nhidden:\n\t.space\t4\n"), "{text}");
4528        assert!(!text.contains(".globl\thidden"), "{text}");
4529        // Nothing writes through it, so it goes in a page the loader can map read only and every
4530        // process running the program can share.
4531        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
4532    }
4533
4534    /// A bit-field with a value in it, which is written as the bytes the value lands in.
4535    ///
4536    /// The interesting one is the field whose lowest byte is zero. The bytes a bit-field
4537    /// initializer makes are put together first and then taken back out as the run they make,
4538    /// and taking them out starts at the byte the field starts at, so a zero byte at the front
4539    /// used to end the object up in `.bss` with the rest of its value thrown away.
4540    #[test]
4541    fn a_bit_field_initializer_writes_every_byte_of_the_value_and_not_only_the_ones_that_are_set() {
4542        let text = asm("struct s { unsigned f : 20; } x = { 0x12300 };\n");
4543        assert!(text.contains("\t.data\n"), "there is something to write: {text}");
4544        assert!(text.contains("\nx:\n\t.ascii\t\"\\000#\\001\"\n"), "and it is the value: {text}");
4545
4546        // Two fields, the first of them zero, which is the same thing said with the zero byte
4547        // inside the run rather than at the front of it.
4548        let text = asm("struct s { unsigned a : 8; unsigned b : 8; } x = { 0, 3 };\n");
4549        assert!(text.contains("\nx:\n\t.ascii\t\"\\000\\003\"\n"), "{text}");
4550
4551        // Wider than an `int`, which is the same code and is worth saying because the value no
4552        // longer fits in the thirty two bits a bit-field used to be read at.
4553        let text = asm("struct s { unsigned long long f : 40; } x = { 0x100000 };\n");
4554        assert!(text.contains("\nx:\n\t.ascii\t\"\\000\\000\\020\"\n\t.space\t5\n"), "{text}");
4555
4556        // Nothing in it, which still costs no bytes in the file.
4557        let text = asm("struct s { unsigned f : 20; } x = { 0 };\n");
4558        assert!(text.contains("\t.bss\n"), "an object of zeroes is zeroes: {text}");
4559        assert!(text.contains("\nx:\n\t.space\t4\n"), "{text}");
4560    }
4561
4562    /// A string literal, which is a variable the program never named.
4563    #[test]
4564    fn a_string_literal_is_a_variable_with_a_name_no_program_could_write() {
4565        let text = asm("const char *f(void) { return \"hi\"; }\n");
4566        assert!(text.contains("\t.ascii\t\"hi\\000\"\n"), "{text}");
4567        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
4568        let label = text
4569            .lines()
4570            .find(|line| line.starts_with(".Lstr"))
4571            .unwrap_or_else(|| panic!("a label for the literal in\n{text}"));
4572        assert!(!text.contains(&format!(".globl\t{}", label.trim_end_matches(':'))), "{text}");
4573    }
4574
4575    /// A variable holding the address of another one, which is the only hole an image has in it.
4576    #[test]
4577    fn an_address_in_an_initializer_is_left_to_the_linker() {
4578        let source = "int counter;\nint *p = &counter;\n";
4579        let text = asm(source);
4580        assert!(text.contains("\np:\n\t.quad\tcounter\n"), "{text}");
4581        // And in the object it is eight zero bytes and a relocation, which is what the two paths
4582        // being one description is for.
4583        let bytes = obj(source);
4584        assert!(bytes.windows(8).any(|w| w == b"counter\0"), "the object has to name it");
4585    }
4586
4587    /// A const table of function pointers, which is the shape that made SQLite link with a warning.
4588    ///
4589    /// The table is const so nothing in the program writes it, but the addresses in it are not
4590    /// numbers a link knows, so the loader writes it once at startup. Putting it in `.rodata`
4591    /// leaves a relocation in a section that is never writable, and what the linker does about
4592    /// that is set `DT_TEXTREL` on the whole image and say so. `.data.rel.ro` is writable for
4593    /// exactly as long as the loader is writing it and read only afterwards, which is what the
4594    /// program asked for in the first place.
4595    #[test]
4596    fn a_constant_holding_an_address_goes_in_the_section_the_loader_may_write_once() {
4597        // Both names are `static` and both are defined here, so nothing else can be the one that
4598        // defines them and the linker may lay the table out in the first pages of the segment.
4599        let text = asm("static void a(void) {}\nstatic void b(void) {}\n\
4600             struct m { void (*x)(void); void (*y)(void); };\n\
4601             const struct m t = { a, b };\n");
4602        assert!(text.contains("\t.section\t.data.rel.ro.local,\"aw\",@progbits\n"), "{text}");
4603        assert!(text.contains("\nt:\n\t.quad\ta\n\t.quad\tb\n"), "{text}");
4604
4605        // One name this file only declares is enough to lose the `.local` half, because a name the
4606        // link resolves from somewhere else is one another object may turn out to define.
4607        let text =
4608            asm("void a(void);\nstruct m { void (*x)(void); };\nconst struct m t = { a };\n");
4609        assert!(text.contains("\t.section\t.data.rel.ro,\"aw\",@progbits\n"), "{text}");
4610
4611        // And a constant with no address in it stays exactly where it was.
4612        let text = asm("const int fixed = 7;\n");
4613        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
4614    }
4615
4616    /// A thread-local variable, which is the whole of one: the storage and the way to reach it.
4617    ///
4618    /// The two halves are in one test on purpose. Either one alone is worse than neither: a
4619    /// definition with no way to reach it is a variable nothing can read, and a reference with no
4620    /// definition behind it is the bug this pair was written to prevent, where a thread-local is
4621    /// read as though it were an ordinary global and every thread quietly shares one copy.
4622    #[test]
4623    fn a_thread_local_variable_is_storage_a_thread_gets_a_copy_of_and_an_offset_into_it() {
4624        let text = asm("_Thread_local int x = 1;\nint read(void) { return x; }\n");
4625        // The storage: the section the loader makes a copy of for every thread, and the symbol
4626        // type that makes a linker refuse an ordinary relocation aimed at it.
4627        assert!(text.contains("\t.section\t.tdata,\"awT\",@progbits\n"), "{text}");
4628        assert!(text.contains("\t.type\tx, @tls_object\n"), "{text}");
4629        // The way to reach it: how far into a thread's block it sits, out of the table, plus where
4630        // this thread's block is, out of the segment register.
4631        assert!(text.contains("x@GOTTPOFF(%rip)"), "{text}");
4632        assert!(text.contains("%fs:0"), "{text}");
4633    }
4634
4635    /// The second half of that on its own, which is what a program asks for when the number it
4636    /// wants is the thread rather than anything in it.
4637    ///
4638    /// rpmalloc writes this to find its per thread cache, and it is the whole of what stood
4639    /// between that library and a build. gcc 16 writes the same one instruction.
4640    #[test]
4641    fn the_address_of_this_thread_s_own_storage_is_read_out_of_the_segment_register() {
4642        let text = asm("void *here(void) { return __builtin_thread_pointer(); }\n");
4643        assert!(text.contains("movq\t%fs:0, "), "{text}");
4644        // No table slot and no addition, because there is no variable to find inside the block.
4645        assert!(!text.contains("GOTTPOFF"), "{text}");
4646    }
4647
4648    /// The four hints and the one thing that decides between them, which is the locality.
4649    ///
4650    /// A prefetch promises nothing, so what is checked here is the instruction rather than any
4651    /// effect: the program runs the same whichever of the four it gets, and the whole point of
4652    /// writing one is which. The four spellings are what gcc 16.2.0 writes for the same four
4653    /// programs, measured on x86-64 rather than read off a manual.
4654    ///
4655    /// The write hint is not one of them. `prefetchw` is not in the base instruction set and gcc
4656    /// writes it only when the command line says the part has it, so a prefetch for a write is the
4657    /// same instruction as a prefetch for a read, which is the fourth line here.
4658    #[test]
4659    fn a_prefetch_is_one_of_four_instructions_and_the_locality_is_what_picks() {
4660        for (locality, wanted) in
4661            [(0, "prefetchnta"), (1, "prefetcht2"), (2, "prefetcht1"), (3, "prefetcht0")]
4662        {
4663            let source =
4664                format!("void warm(void *p) {{ __builtin_prefetch(p, 0, {locality}); }}\n");
4665            let text = asm(&source);
4666            assert!(text.contains(&format!("\t{wanted}\t")), "locality {locality}: {text}");
4667        }
4668        // The one argument form, which means a read that wants all of the data afterwards.
4669        let text = asm("void warm(void *p) { __builtin_prefetch(p); }\n");
4670        assert!(text.contains("\tprefetcht0\t"), "{text}");
4671        // A prefetch for a write, which on a part nobody said has `prefetchw` is the same
4672        // instruction as the read above.
4673        let text = asm("void warm(void *p) { __builtin_prefetch(p, 1); }\n");
4674        assert!(text.contains("\tprefetcht0\t"), "{text}");
4675        assert!(!text.contains("prefetchw"), "{text}");
4676    }
4677
4678    /// The same eight programs on AArch64, where the write hint is in the base instruction set and
4679    /// so is a different instruction, which is what gcc 16.2.0 writes for them.
4680    #[test]
4681    fn an_aarch64_prefetch_is_a_prfm_that_says_the_locality_and_whether_it_writes() {
4682        let mut opts = options();
4683        opts.emit = EmitKind::Asm;
4684        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
4685        for (write, kind) in [(0, "pld"), (1, "pst")] {
4686            for (locality, wanted) in [(0, "l1strm"), (1, "l3keep"), (2, "l2keep"), (3, "l1keep")] {
4687                let source = format!(
4688                    "void warm(void *p) {{ __builtin_prefetch(p, {write}, {locality}); }}\n"
4689                );
4690                let result = run(&opts, &source);
4691                assert_eq!(result.messages, Vec::<String>::new(), "{source}");
4692                let text = result.text();
4693                assert!(text.contains("prfm"), "{source}{text}");
4694                assert!(text.contains(&format!("{kind}{wanted}, [x0]")), "{source}{text}");
4695            }
4696        }
4697    }
4698
4699    /// The stop, which is the one instruction the machine is promised never to have a meaning for.
4700    ///
4701    /// What is checked is the instruction and not any effect, because the effect is a fault and a
4702    /// unit test has nowhere to take one. gcc 16.2.0 writes the same instruction for the same
4703    /// program, and it is not a call, which is the half that matters in a kernel and in a
4704    /// freestanding program: neither has an `abort` for a call to reach.
4705    ///
4706    /// The second half is the block going on after it. A statement written under a stop is
4707    /// compiled the way it would have been without one, so the addition is still there, and that
4708    /// is the front end declining to treat a stop as the end of a path.
4709    #[test]
4710    fn a_trap_is_the_instruction_the_machine_has_no_meaning_for() {
4711        let text = asm("void stop(void) { __builtin_trap(); }\n");
4712        assert!(text.contains("\tud2\n"), "{text}");
4713        assert!(!text.contains("\tcall"), "a stop is not a call to anything: {text}");
4714
4715        let text = asm("int stop(int a) { __builtin_trap(); return a + 1; }\n");
4716        assert!(text.contains("\tud2\n"), "{text}");
4717        assert!(text.contains("\taddl\t"), "the block goes on after a stop: {text}");
4718    }
4719
4720    /// `__builtin_cpu_init` is a call to libgcc's `__cpu_indicator_init` and nothing else, which
4721    /// is what gcc 16.2.0 writes for it. The name the program wrote does not reach the object
4722    /// file, because no library defines it.
4723    #[test]
4724    fn cpu_init_is_a_call_to_the_libgcc_function_that_fills_in_the_model() {
4725        let text = asm("void start(void) { __builtin_cpu_init(); }\n");
4726        assert!(text.contains("\tcall\t__cpu_indicator_init"), "{text}");
4727        assert!(!text.contains("__builtin_cpu_init"), "{text}");
4728    }
4729
4730    /// `__builtin_cpu_supports` is a load of the word the feature's bit is in and an `and` with
4731    /// the bit, and the answer is the bit where it stands, which is gcc 16.2.0's lowering.
4732    ///
4733    /// Three names, one from each place libgcc keeps the bits: sse4.2 is bit 8 of the last word of
4734    /// `__cpu_model`, vpclmulqdq is bit 1 of the first word of `__cpu_features2`, and xsave is bit
4735    /// 17 of its second word. The fourth is the top bit of a word, which gcc answers one for
4736    /// rather than the bit, so there is a compare after the `and`.
4737    #[test]
4738    fn cpu_supports_is_a_bit_of_the_words_libgcc_fills_in() {
4739        let text = asm("int f(void) { return __builtin_cpu_supports(\"sse4.2\"); }\n");
4740        assert!(text.contains("__cpu_model"), "{text}");
4741        assert!(text.contains("12(%"), "the fourth word of the model: {text}");
4742        assert!(text.contains("$256"), "{text}");
4743        assert!(!text.contains("\tcall"), "the answer is a read and not a call: {text}");
4744
4745        let text = asm("int f(void) { return __builtin_cpu_supports(\"vpclmulqdq\"); }\n");
4746        assert!(text.contains("__cpu_features2"), "{text}");
4747        assert!(text.contains("$2,"), "{text}");
4748
4749        let text = asm("int f(void) { return __builtin_cpu_supports(\"xsave\"); }\n");
4750        assert!(text.contains("__cpu_features2"), "{text}");
4751        assert!(text.contains("4(%"), "the second word of the second object: {text}");
4752        assert!(text.contains("$131072"), "{text}");
4753
4754        let text = asm("int f(void) { return __builtin_cpu_supports(\"avx512vbmi2\"); }\n");
4755        assert!(text.contains("set"), "the top bit is answered as a one: {text}");
4756    }
4757
4758    /// `__builtin_cpu_is` is a compare of one word of `__cpu_model` with a number: the vendor for
4759    /// `amd`, which is 2, and the subtype for `znver4`, which is 29.
4760    #[test]
4761    fn cpu_is_compares_one_word_of_the_model_with_a_number() {
4762        let text = asm("int f(void) { return __builtin_cpu_is(\"amd\"); }\n");
4763        assert!(text.contains("__cpu_model"), "{text}");
4764        assert!(text.contains("$2,"), "{text}");
4765
4766        let text = asm("int f(void) { return __builtin_cpu_is(\"znver4\"); }\n");
4767        assert!(text.contains("8(%"), "the subtype is the third word: {text}");
4768        assert!(text.contains("$29,"), "{text}");
4769    }
4770
4771    /// The name picks the word and the bit, so it has to be a string literal, and it has to be
4772    /// one gcc knows. Both are errors in gcc 16.2.0's words, and so is asking on a target other
4773    /// than x86-64, where nothing defines what these read.
4774    #[test]
4775    fn a_cpu_builtin_takes_a_name_it_knows_written_as_a_literal() {
4776        let mut opts = options();
4777        opts.emit = EmitKind::Ir;
4778        for (source, wanted) in [
4779            (
4780                "int f(const char *s) { return __builtin_cpu_supports(s); }\n",
4781                "parameter to builtin must be a string constant or literal",
4782            ),
4783            (
4784                "int f(void) { return __builtin_cpu_supports(\"sse5\"); }\n",
4785                "parameter to builtin not valid: sse5",
4786            ),
4787            (
4788                "int f(void) { return __builtin_cpu_is(\"sse\"); }\n",
4789                "parameter to builtin not valid: sse",
4790            ),
4791        ] {
4792            let result = run(&opts, source);
4793            assert!(
4794                result.messages.iter().any(|m| m.contains(wanted)),
4795                "{source}{:?}",
4796                result.messages
4797            );
4798        }
4799        // A cast in front of the literal is looked through, the way gcc looks through it.
4800        let text = asm("int f(void) { return __builtin_cpu_supports((const char *)\"avx2\"); }\n");
4801        assert!(text.contains("$1024"), "{text}");
4802
4803        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
4804        for source in [
4805            "void f(void) { __builtin_cpu_init(); }\n",
4806            "int f(void) { return __builtin_cpu_supports(\"sse4.2\"); }\n",
4807        ] {
4808            let result = run(&opts, source);
4809            assert!(
4810                result.messages.iter().any(|m| m.contains("only available on x86-64")),
4811                "{source}{:?}",
4812                result.messages
4813            );
4814        }
4815    }
4816
4817    /// The promise about the low bits of an address, whose value is the address.
4818    ///
4819    /// Nothing here reads an alignment fact about a value yet, so what the call leaves behind is
4820    /// its first argument and no instruction at all. The claim worth checking end to end is that
4821    /// the name is gone: a builtin nothing lowers reaches the assembler as a call to a name no
4822    /// object file defines, which is how this one used to fail to link out of glibc's string
4823    /// headers.
4824    ///
4825    /// The arguments behind the address are still evaluated, because gcc 16.2.0 evaluates them at
4826    /// every optimization level even though it has folded the call away. A constant has nothing to
4827    /// run and is dropped, and a call does, so the second half asks for the callee by name.
4828    #[test]
4829    fn assume_aligned_is_its_first_argument_and_keeps_the_rest() {
4830        let text = asm("void *aligned(char *p) { return __builtin_assume_aligned(p, 16); }\n");
4831        assert!(!text.contains("assume_aligned"), "{text}");
4832        assert!(!text.contains("\tcall"), "nothing is called for an alignment fact: {text}");
4833
4834        let source = "unsigned long width(void);\n\
4835                      void *aligned(char *p) { return __builtin_assume_aligned(p, width()); }\n";
4836        let text = asm(source);
4837        assert!(!text.contains("assume_aligned"), "{text}");
4838        assert!(text.contains("width"), "the argument that is not the answer still runs: {text}");
4839    }
4840
4841    /// Where a frame is, which on this machine is what the frame pointer holds.
4842    ///
4843    /// The first half is a function that would have kept no frame pointer at all, since it is a
4844    /// leaf with no locals, and keeps one because it asked where its frame is. The answer being
4845    /// `%rbp` rather than an offset off `%rsp` is the whole of the builtin at a depth of zero.
4846    ///
4847    /// The second half is the walk. Each link above zero is one load through the register the last
4848    /// one wrote, so a depth of two is two loads and a depth of three is three, which is what gcc
4849    /// 16.2.0 writes for the same programs at `-O2`.
4850    #[test]
4851    fn the_frame_address_is_the_frame_pointer_after_walking_that_many_links() {
4852        let text = asm("void *here(void) { return __builtin_frame_address(0); }\n");
4853        assert!(text.contains("pushq\t%rbp"), "a function that asks keeps a frame pointer: {text}");
4854        assert!(text.contains("movq\t%rbp, %rax"), "{text}");
4855        assert!(!text.contains("\tcall"), "a frame address is not a call to anything: {text}");
4856
4857        let walk = |depth: u32| {
4858            let source = format!("void *up(void) {{ return __builtin_frame_address({depth}); }}\n");
4859            asm(&source).matches("movq\t(%r").count()
4860        };
4861        assert_eq!(walk(1), 1, "one link is one load");
4862        assert_eq!(walk(3), 3, "three links are three loads");
4863    }
4864
4865    /// The address a frame returns to, which is one word above the frame the walk ended at.
4866    ///
4867    /// A word is eight bytes here and the `8(...)` is the whole claim: the call instruction pushed
4868    /// the return address and the prologue pushed the caller's frame pointer under it, so what the
4869    /// frame pointer points at is the link and what is above it is where control goes back to.
4870    /// gcc 16.2.0 writes `movq 8(%rbp), %rax` for the first of these, measured at `-O2`.
4871    ///
4872    /// The second half is the same walk the frame address does, with the load at the end of it
4873    /// reading one word further along rather than the register itself being the answer.
4874    #[test]
4875    fn the_return_address_is_one_word_above_the_frame_the_walk_ended_at() {
4876        let text = asm("void *back(void) { return __builtin_return_address(0); }\n");
4877        assert!(text.contains("pushq\t%rbp"), "a function that asks keeps a frame pointer: {text}");
4878        assert!(text.contains("movq\t8(%rbp), %rax"), "{text}");
4879        assert!(!text.contains("\tcall"), "a return address is not a call to anything: {text}");
4880
4881        let text = asm("void *back(void) { return __builtin_return_address(2); }\n");
4882        assert_eq!(text.matches("movq\t(%r").count(), 2, "two links are two loads: {text}");
4883        assert!(text.contains("movq\t8(%r"), "and the answer is above the last of them: {text}");
4884    }
4885
4886    /// A depth that is not a constant is refused, and so is one past the limit.
4887    ///
4888    /// The first is gcc's rule and not a convenience: what the call becomes is a walk that many
4889    /// links long, written out, so a number that is not known until the program runs has nothing
4890    /// to walk. gcc 16.2.0 says `invalid argument to '__builtin_return_address'` for the same
4891    /// program.
4892    ///
4893    /// The second is where this and gcc part company. gcc writes the walk however long it is, and
4894    /// this refuses a depth no program has a use for rather than filling an object file with loads
4895    /// that fault part way up.
4896    #[test]
4897    fn a_depth_that_is_not_a_small_constant_is_refused() {
4898        let mut opts = options();
4899        opts.emit = EmitKind::Ir;
4900        for source in [
4901            "void *up(int n) { return __builtin_return_address(n); }\n",
4902            "void *up(void) { return __builtin_frame_address(1000); }\n",
4903        ] {
4904            let messages = run(&opts, source).messages;
4905            let named = messages.iter().any(|m| m.contains("E0705"));
4906            assert!(named, "expected a refusal in {messages:?}");
4907        }
4908    }
4909
4910    /// Bytes off the frame, which is the stack pointer moving down and the answer being where it
4911    /// moved to.
4912    ///
4913    /// The rounding is the alignment: the size is taken up to the next sixteen before it is
4914    /// subtracted, so the pointer suits anything the program puts behind it. gcc 16.2.0 rounds the
4915    /// same way at `-O0` and spends a division doing it, which is the one place the two differ and
4916    /// is about how the rounding is written rather than about what it answers.
4917    ///
4918    /// There is no call anywhere in either program. An alloca that had reached the linker would
4919    /// have found the C library's, which is a real function with a real frame and is not what a
4920    /// program writing the builtin asked for.
4921    #[test]
4922    fn an_alloca_takes_the_bytes_off_the_stack_pointer_and_answers_where_they_are() {
4923        let text =
4924            asm("void use(void *p); void f(unsigned long n) { use(__builtin_alloca(n)); }\n");
4925        assert!(text.contains("andq\t$-16"), "the size is rounded up to sixteen: {text}");
4926        assert!(text.contains("subq\t%rdi, %rsp"), "and taken off the stack pointer: {text}");
4927        assert_eq!(text.matches("\tcall").count(), 1, "the only call is the one written: {text}");
4928
4929        // The plain name, which a program that declares it the way the C library does means the
4930        // same thing by. `gcc.c-torture/execute/20010122-1.c` is exactly this program.
4931        let plain = concat!(
4932            "extern void *alloca(__SIZE_TYPE__);\n",
4933            "void use(void *p);\n",
4934            "void f(unsigned long n) { use(alloca(n)); }\n",
4935        );
4936        let text = asm(plain);
4937        assert!(text.contains("subq\t%rdi, %rsp"), "the plain name is the same bytes: {text}");
4938        assert_eq!(text.matches("\tcall").count(), 1, "and is not a call either: {text}");
4939
4940        // And a program that means something of its own by the name keeps it, which is what the
4941        // declaration is looked at for.
4942        let own = concat!(
4943            "static void *alloca(unsigned long n) { return 0; }\n",
4944            "void *f(unsigned long n) { return alloca(n); }\n",
4945        );
4946        assert!(asm(own).contains("\tcall"), "a name the program took back is a call");
4947    }
4948
4949    /// A name nothing declared that the implementation knows the type of is declared with that
4950    /// type rather than with the `extern int f()` C89 6.3.2.2 writes down.
4951    ///
4952    /// That is gcc's rule and it is measurable: gcc 16.2.0 compiles an undeclared `alloca` with
4953    /// no call in it at all, and says `incompatible implicit declaration of built-in function`
4954    /// beside the implicit declaration warning. A C89 declaration would have made the call return
4955    /// an `int` and reach a function no C library defines, since every header that offers
4956    /// `alloca` offers it as a macro for the builtin. Four torture programs turn on it,
4957    /// `execute/20020314-1.c`, `20040223-1.c`, `941202-1.c` and `pr22061-1.c`, each of which
4958    /// calls `alloca` with nothing above it.
4959    ///
4960    /// The rule is the builtin table's rather than this one name's, so an undeclared `strlen` is
4961    /// the builtin too. What it is not is a declaration the program wrote that disagrees with the
4962    /// builtin's type, which gcc keeps and calls, and that was measured as well.
4963    #[test]
4964    fn a_builtin_the_program_never_declared_is_the_builtin_rather_than_the_one_c89_wrote_down() {
4965        // `-fpermissive`, because the implicit declaration itself is an error in every dialect
4966        // after C89 and the program would never get as far as a type without it. Each of the four
4967        // torture programs asks for either that or `-std=gnu89` on its own options line.
4968        let mut opts = options();
4969        opts.permissive = true;
4970        let undeclared = "void use(void *p);
4971void f(unsigned long n) { use(alloca(n)); }
4972";
4973        assert_eq!(
4974            run(&opts, undeclared).messages,
4975            [
4976                "/main.c:2:31: warning: implicit declaration of function 'alloca' [E0521]",
4977                "/main.c:2:31: warning: incompatible implicit declaration of built-in function \
4978                 'alloca' [E0713]",
4979            ]
4980        );
4981
4982        opts.emit = EmitKind::Asm;
4983        let text = run(&opts, undeclared).text().to_owned();
4984        assert!(text.contains("subq\t%rdi, %rsp"), "the bytes come off the stack: {text}");
4985        assert_eq!(text.matches("\tcall").count(), 1, "the only call is the one written: {text}");
4986
4987        // The table's rule and not this one name's, so a name whose whole answer is the library
4988        // function of the same name gets that function's type and still reaches it.
4989        let string = "unsigned long f(void) { return strlen(\"abc\"); }\n";
4990        let text = run(&opts, string).text().to_owned();
4991        assert!(text.contains("call\tstrlen"), "strlen is still a call: {text}");
4992
4993        // A declaration the program wrote is the program's, whatever the table says. gcc keeps
4994        // this one and writes the call, which is what makes the type worth looking at.
4995        let own = concat!(
4996            "static void *alloca(unsigned long n) { return 0; }\n",
4997            "void *f(unsigned long n) { return alloca(n); }\n",
4998        );
4999        assert!(asm(own).contains("\tcall"), "a name the program took back is a call");
5000    }
5001
5002    /// The bytes an alloca took live until the function returns and not until the end of the block
5003    /// the call was written in.
5004    ///
5005    /// That is what makes it different from a variable length array, and the way it is kept is that
5006    /// every scope open where the call was written stops giving the stack back. The second program
5007    /// is the mixed case: an array in the outer block and an alloca in the inner one, where the
5008    /// inner block gives nothing back either even though an array is in scope that ordinarily
5009    /// would. gcc 16.2.0 at `-O0` writes no restore at the end of either block, measured rather
5010    /// than read off the manual.
5011    #[test]
5012    fn the_bytes_an_alloca_took_are_still_there_at_the_end_of_the_block_that_took_them() {
5013        let inner = "{ use(__builtin_alloca(n)); }";
5014        for body in [inner.to_owned(), format!("int a[n]; {inner} use(a);")] {
5015            let source = format!("void use(void *p);\nvoid f(unsigned long n) {{ {body} }}\n");
5016            let text = asm(&source);
5017            // Every instruction that writes the stack pointer, which in a function that gives
5018            // nothing back is the alloca taking bytes and the epilogue putting the frame pointer
5019            // there. A restore would be a third kind, a move out of a register the save wrote.
5020            for line in text.lines().filter(|line| line.trim_end().ends_with(", %rsp")) {
5021                let taking = line.contains("subq");
5022                let leaving = line.contains("%rbp");
5023                assert!(taking || leaving, "nothing puts the stack back: {line} in {text}");
5024            }
5025        }
5026    }
5027
5028    /// Not a rewording of the check above: what the two paths agree about is the point.
5029    #[test]
5030    fn the_object_and_the_listing_are_two_spellings_of_one_compilation() {
5031        // A call, because it is the one thing whose spelling in the two differs completely: the
5032        // listing writes a name and the object writes four zero bytes and a relocation asking the
5033        // linker for the same name. If either path had lost the callee, one of these would fail.
5034        let source = "int callee(void); int g(void) { return callee(); }\n";
5035        let bytes = obj(source);
5036        assert!(
5037            bytes.windows(7).any(|w| w == b"callee\0"),
5038            "the object has to name the callee for the linker to find it"
5039        );
5040        let text = asm(source);
5041        assert!(text.contains("\tcall\tcallee\n"), "{text}");
5042    }
5043
5044    /// What a file of a link contributes is an object, and the default emit is a link.
5045    ///
5046    /// This is here because getting it wrong is silent in the worst way: an empty file is a valid
5047    /// empty linker script, so a link fed one gets as far as reporting every symbol of the file as
5048    /// undefined and says nothing about the compilation that produced nothing.
5049    #[test]
5050    fn compiling_for_an_executable_produces_an_object_and_not_a_dump() {
5051        let mut opts = options();
5052        // What a command line with no `-c` and no `-S` on it asks for.
5053        opts.emit = EmitKind::Executable;
5054        let result = run(&opts, "int main(void) { return 0; }\n");
5055        assert_eq!(result.messages, Vec::<String>::new());
5056        match result.artifact {
5057            Artifact::Object { bytes, .. } => assert_eq!(&bytes[..4], b"\x7fELF"),
5058            other => panic!("expected an object, got {other:?}"),
5059        }
5060    }
5061
5062    /// A target with a back end but no object writer says so rather than writing the wrong file.
5063    #[test]
5064    fn a_platform_with_no_object_writer_is_said_so_rather_than_written_as_elf() {
5065        let mut opts = options();
5066        opts.emit = EmitKind::Object;
5067        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
5068        let result = run(&opts, "int f(void) { return 0; }\n");
5069        assert!(result.failed(), "an object nobody can read is worse than a message");
5070        assert!(
5071            result.messages.iter().any(|m| m.contains("no object writer")),
5072            "{:?}",
5073            result.messages
5074        );
5075    }
5076
5077    /// The IR of `source`, insisting that it compiled cleanly.
5078    fn ir(source: &str) -> String {
5079        let mut opts = options();
5080        opts.emit = EmitKind::Ir;
5081        let result = run(&opts, source);
5082        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5083        result.text().to_owned()
5084    }
5085
5086    /// What was said about `source`, insisting that something was.
5087    fn errors(source: &str) -> Vec<String> {
5088        let mut opts = options();
5089        opts.emit = EmitKind::Ir;
5090        let result = run(&opts, source);
5091        assert!(result.failed(), "expected this to be refused:\n{source}");
5092        result.messages
5093    }
5094
5095    /// The body of the one function in `source`, which is what most of these are about.
5096    fn body(source: &str) -> String {
5097        let text = ir(source);
5098        let (_, rest) = text.split_once("{\n").expect("a function definition");
5099        let (body, _) = rest.rsplit_once("}\n").expect("a function definition");
5100        body.to_owned()
5101    }
5102
5103    /// What `-fgnu89-inline` is for, seen at the only place it shows: whether a body reached the
5104    /// module or only a declaration did.
5105    ///
5106    /// The C99 reading is the one an inline definition is written for and is not being changed
5107    /// here. What the flag is for is a program written before C99 swapped the two, which relies on
5108    /// `inline` alone leaving something behind for another unit to call, and there are twelve of
5109    /// those in the GCC torture suite alone.
5110    #[test]
5111    fn gnu89_inline_is_what_decides_whether_a_bare_inline_definition_reaches_the_module() {
5112        let source = "inline int f(int x) { return x + 1; }\n";
5113        let with = |flag: bool| {
5114            let mut opts = options();
5115            opts.emit = EmitKind::Ir;
5116            opts.gnu89_inline = flag;
5117            let result = run(&opts, source);
5118            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
5119            result.text().to_owned()
5120        };
5121
5122        // Under C's reading the module holds the declaration and the calls in this unit go to
5123        // whatever definition another unit has, which is C 6.7.4p7 and is what gcc does too.
5124        assert!(!with(false).contains("block0"), "no body: {}", with(false));
5125
5126        // Under GNU's it is an ordinary external definition, so the body is there and the symbol
5127        // is one the linker can resolve against.
5128        assert!(with(true).contains("block0"), "a body: {}", with(true));
5129    }
5130
5131    /// Every shape that reads or writes through a C type names that type.
5132    ///
5133    /// The tree itself is `rucc_lower::aliasing`'s and is tested there. What this is about is that
5134    /// the walk reaches it from every shape a program actually writes, since a node on the scalar
5135    /// load and nothing on the member load would be a layer that answers for a third of the
5136    /// accesses in a program and is not worth having.
5137    #[test]
5138    fn an_access_through_a_type_names_the_type_it_went_through() {
5139        let source = "\
5140struct s { int a; float b; };\n\
5141union u { int i; float f; };\n\
5142int scalar(int *p) { return *p; }\n\
5143float member(struct s *p) { p->a = 1; return p->b; }\n\
5144int element(int *a, long i) { return a[i]; }\n\
5145float through_a_union(union u *p) { p->i = 1; return p->f; }\n";
5146        let text = ir(source);
5147        assert!(text.contains(r#"!0 = tbaa "char""#), "the root: {text}");
5148        assert!(text.contains(r#"tbaa "int", parent !0"#), "int under it: {text}");
5149        assert!(text.contains(r#"tbaa "float", parent !0"#), "float under it: {text}");
5150        // One per access, and a function whose accesses all go through one type says so once per
5151        // access rather than once per function.
5152        let named = text.lines().filter(|line| line.contains(", tbaa !")).count();
5153        assert_eq!(named, 6, "six accesses: {text}");
5154    }
5155
5156    /// `-fno-strict-aliasing` is the front end leaving the name off.
5157    ///
5158    /// Nothing asks the alias analysis anything yet, so no program compiles differently for having
5159    /// passed this today. What this test is for is the day one does: the flag has to be the
5160    /// absence of the names rather than a condition somewhere downstream, since that is the only
5161    /// version of it that a pass added later cannot forget about.
5162    #[test]
5163    fn turning_strict_aliasing_off_leaves_the_type_off_every_access() {
5164        let source = "int punned(float *f, int *i) { *i = 1; *f = 2.0f; return *i; }\n";
5165        let mut opts = options();
5166        opts.emit = EmitKind::Ir;
5167        opts.strict_aliasing = false;
5168        let result = run(&opts, source);
5169        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
5170        let text = result.text().to_owned();
5171        assert!(!text.contains("tbaa"), "not even the root: {text}");
5172    }
5173
5174    /// `-finstrument-functions` puts one call to the entry hook in front of the body and one call
5175    /// to the exit hook in front of every return, each given the function's own address and the
5176    /// address it returns to. A function declared `no_instrument_function` gets neither, and the
5177    /// hooks are declared that way here as they are in `execute/eeprof-1.c`, since a hook that
5178    /// called itself would never get as far as its body.
5179    #[test]
5180    fn instrumenting_functions_calls_the_hooks_around_every_body_but_the_hooks() {
5181        let source = concat!(
5182            "#define NOCHK __attribute__((no_instrument_function))\n",
5183            "void __cyg_profile_func_enter(void *, void *) NOCHK;\n",
5184            "void __cyg_profile_func_exit(void *, void *) NOCHK;\n",
5185            "int calls;\n",
5186            "int pick(int x) { if (x) return 1; return 2; }\n",
5187            "void quiet(void) NOCHK;\n",
5188            "void quiet(void) { calls++; }\n",
5189            "void __cyg_profile_func_enter(void *fn, void *site) { calls++; }\n",
5190            "void __cyg_profile_func_exit(void *fn, void *site) { calls--; }\n",
5191        );
5192        let mut opts = options();
5193        opts.emit = EmitKind::Ir;
5194        opts.instrument_functions = true;
5195        let result = run(&opts, source);
5196        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
5197        let text = result.text().to_owned();
5198        let body = |name: &str| -> String {
5199            let open = format!("func @{name}(");
5200            let start = text.find(&open).unwrap_or_else(|| panic!("no {name}: {text}"));
5201            let rest = &text[start..];
5202            rest[..rest.find("\n}").unwrap_or(rest.len())].to_owned()
5203        };
5204        let pick = body("pick");
5205        assert_eq!(pick.matches("call @__cyg_profile_func_enter(").count(), 1, "{pick}");
5206        assert_eq!(pick.matches("call @__cyg_profile_func_exit(").count(), 2, "{pick}");
5207        assert!(pick.contains("return_address"), "{pick}");
5208        assert!(pick.contains("global_addr @pick"), "{pick}");
5209        for quiet in ["quiet", "__cyg_profile_func_enter", "__cyg_profile_func_exit"] {
5210            assert!(!body(quiet).contains("call "), "{quiet} is left alone: {text}");
5211        }
5212
5213        opts.instrument_functions = false;
5214        let result = run(&opts, source);
5215        assert!(!result.text().contains("call @__cyg_profile"), "off unless asked for");
5216    }
5217
5218    /// Under `-fexceptions` a `cleanup` handler is owed a call on an unwind as well. On x86-64 ELF
5219    /// a call inside a handler's scope gets a landing pad that runs the handler and resumes the
5220    /// unwind, a handler with no call in its scope needs none, and without the flag the same source
5221    /// compiles as it always did. Everywhere else the call is turned down by name, since no pad is
5222    /// built there.
5223    #[test]
5224    fn a_call_an_unwind_would_leave_a_cleanup_behind_gets_a_landing_pad_under_exceptions() {
5225        let source = concat!(
5226            "void done(int *p);\n",
5227            "void work(void);\n",
5228            "void calls(void) { int x __attribute__((cleanup(done))) = 1; work(); }\n",
5229            "int quiet(int y) { int x __attribute__((cleanup(done))) = y; return x + 1; }\n",
5230            "void after(void) { { int x __attribute__((cleanup(done))) = 1; } work(); }\n",
5231        );
5232        let mut opts = options();
5233        opts.emit = EmitKind::Ir;
5234        let result = run(&opts, source);
5235        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
5236        assert!(!result.text().contains("landing"), "{}", result.text());
5237
5238        opts.exceptions = true;
5239        let result = run(&opts, source);
5240        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5241        let text = result.text();
5242        assert_eq!(text.matches("= landing").count(), 1, "only the call in calls: {text}");
5243        assert!(text.contains("_Unwind_Resume"), "{text}");
5244
5245        opts.emit = EmitKind::Asm;
5246        let result = run(&opts, source);
5247        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5248        let text = result.text();
5249        assert!(text.contains(".cfi_personality 0x9b,DW.ref.__gcc_personality_v0"), "{text}");
5250        assert!(text.contains(".cfi_lsda 0x1b,.LLSDA_calls"), "{text}");
5251        assert!(text.contains(".gcc_except_table"), "{text}");
5252        assert_eq!(text.matches(".cfi_lsda").count(), 1, "{text}");
5253
5254        opts.emit = EmitKind::Object;
5255        let result = run(&opts, source);
5256        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5257        let bytes = result.artifact.bytes();
5258        let has = |what: &[u8]| bytes.windows(what.len()).any(|window| window == what);
5259        assert!(has(b".gcc_except_table\0"), "the call site table has a section");
5260        assert!(has(b"zPLR\0"), "a header naming the personality routine");
5261        assert!(has(b"zR\0"), "and the plain one for the functions with no pad");
5262        assert!(has(b"DW.ref.__gcc_personality_v0\0"), "the pointer the header reads through");
5263
5264        opts.emit = EmitKind::Ir;
5265        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
5266        let result = run(&opts, source);
5267        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
5268        assert!(result.messages[0].contains("landing pad"), "{:?}", result.messages);
5269        assert!(result.messages[0].contains(":3:"), "the call in calls: {:?}", result.messages);
5270    }
5271
5272    /// An `asm` at file scope with an instruction in it, which is how a unit writes a whole
5273    /// function in assembly. The template goes into the listing as it was written, between the
5274    /// markers gcc writes, and an object is assembled from that listing, so the function it
5275    /// defines is defined in the object and the C that calls it calls it there. tcc's
5276    /// `85_asm-outside-function.c` and `98_al_ax_extend.c` are this.
5277    #[test]
5278    fn an_asm_at_file_scope_with_an_instruction_in_it_is_assembled() {
5279        let source = concat!(
5280            "extern void vide(void);\n",
5281            "__asm__(\".text;.globl _us;_us:;movl $0x1234ABCD, %eax;ret\");\n",
5282            "__asm__(\"vide: ret\");\n",
5283            "unsigned short _us(void);\n",
5284            "int main(void) { vide(); return _us() == 0xABCD ? 0 : 1; }\n",
5285        );
5286        let mut opts = options();
5287        opts.emit = EmitKind::Ir;
5288        let result = run(&opts, source);
5289        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5290        assert_eq!(result.text().matches("module asm ").count(), 2, "{}", result.text());
5291
5292        opts.emit = EmitKind::Asm;
5293        let result = run(&opts, source);
5294        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5295        let text = result.text();
5296        assert!(text.contains("#APP\nvide: ret\n#NO_APP\n"), "{text}");
5297        let main = text.find("main:").expect("main");
5298        assert!(text.find("#NO_APP").expect("the markers") < main, "templates first: {text}");
5299
5300        opts.emit = EmitKind::Object;
5301        let result = run(&opts, source);
5302        assert_eq!(result.messages, Vec::<String>::new(), "{:?}", result.messages);
5303        let (bytes, defines) = match result.artifact {
5304            Artifact::Object { bytes, defines } => (bytes, defines),
5305            other => panic!("expected an object, got {other:?}"),
5306        };
5307        assert!(defines.iter().any(|name| name == "_us"), "{defines:?}");
5308        // `mov $0x1234abcd, %eax` and the `ret` after it, which only the assembler wrote.
5309        let us = [0xb8, 0xcd, 0xab, 0x34, 0x12, 0xc3];
5310        assert!(bytes.windows(us.len()).any(|window| window == us), "the template's bytes");
5311
5312        // Elsewhere there is no reader for the listing, so the template is still refused there.
5313        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
5314        opts.emit = EmitKind::Ir;
5315        let result = run(&opts, source);
5316        assert!(!result.messages.is_empty(), "refused on Mach-O");
5317        assert!(result.messages[0].contains("the instruction 'movl'"), "{:?}", result.messages);
5318    }
5319
5320    /// `return;` from a function that promised a value, which only C89 lets through and which
5321    /// therefore only reaches the IR builder under that dialect.
5322    ///
5323    /// Zero goes back. The alternatives are worse: an empty return list builds a `ret` the
5324    /// verifier refuses, which is what a torture case found, and `unreachable` would be a claim
5325    /// that the branch reaching this never runs, which is a claim about the program rather than
5326    /// about the value and lets the optimizer delete the path that led here.
5327    #[test]
5328    fn a_bare_return_from_a_function_that_promised_a_value_gives_back_a_zero() {
5329        let mut opts = options();
5330        opts.emit = EmitKind::Ir;
5331        opts.std = Std::C89;
5332        let compiled = |source: &str| {
5333            let result = run(&opts, source);
5334            assert_eq!(result.messages, Vec::<String>::new(), "C89 has nothing to say about this");
5335            result.text().to_owned()
5336        };
5337
5338        let text = compiled("int f(int x) { if (x) return; return 3; }\n");
5339        assert!(text.contains("iconst.i32 0\n    return"), "zero goes back: {text}");
5340        assert!(!text.contains("unreachable"), "the branch that reached it is kept: {text}");
5341
5342        // A floating point return needs the constant of its own kind rather than an integer one.
5343        let text = compiled("double f(int x) { if (x) return; return 1.0; }\n");
5344        assert!(text.contains("fconst.f64 0x0\n    return"), "a float zero goes back: {text}");
5345    }
5346
5347    /// What C89 6.3.2.2 declares for a call to a name nothing declared, seen in the IR rather than
5348    /// in what was said about it.
5349    ///
5350    /// `extern int f();`, so the call gives back an `int` and its arguments are promoted rather
5351    /// than converted to parameters there are none of. The declaration lasts for the file, which
5352    /// is what makes a second call to the same name ordinary and is why gcc says this once per
5353    /// file rather than once per call.
5354    #[test]
5355    fn a_call_to_a_name_nothing_declared_declares_it_as_c89_said_to() {
5356        let mut opts = options();
5357        opts.emit = EmitKind::Ir;
5358        opts.std = Std::C89;
5359        let compiled = |source: &str| {
5360            let result = run(&opts, source);
5361            assert_eq!(result.messages, Vec::<String>::new(), "C89 has nothing to say about this");
5362            result.text().to_owned()
5363        };
5364
5365        // An `int` back, which is the whole of what the implicit declaration says.
5366        let text = compiled("int f(void) { return g(); }\n");
5367        assert!(text.contains("call @g"), "the call is to the name that was written: {text}");
5368        assert!(text.contains("i32"), "and it gives back an int: {text}");
5369
5370        // No prototype, so a `char` argument arrives promoted to `int` the way an argument to a
5371        // function whose parameters are unspecified does.
5372        let text = compiled("int f(char c) { return g(c); }\n");
5373        assert!(text.contains("sext.i32"), "the argument is promoted: {text}");
5374
5375        // A name written as a value rather than called is still undeclared, since the rule is
5376        // about a call and nothing else.
5377        let mut opts = options();
5378        opts.std = Std::C89;
5379        let said = run(&opts, "int f(void) { return h; }\n").messages.join("\n");
5380        assert!(said.contains("'h' undeclared"), "not a call, so not declared: {said}");
5381    }
5382
5383    /// A file that calls a name above the definition of it, which is the shape the implicit
5384    /// declaration has to survive rather than swallow.
5385    ///
5386    /// The definition merges into the declaration the call already made rather than making a
5387    /// second one, so a declaration the tree does not carry at the top level takes the definition
5388    /// down with it: the body is attached to a node nothing walks and no function comes out.
5389    /// Nothing about the call itself looks wrong when that happens, and the program gets to the
5390    /// linker before anyone finds out, which is where `execute/cmpsi-1.c` in the torture suite
5391    /// found it, as an undefined reference to a name defined eleven lines further down.
5392    #[test]
5393    fn a_name_called_before_it_is_defined_still_gets_its_definition() {
5394        let mut opts = options();
5395        opts.emit = EmitKind::Ir;
5396        opts.std = Std::C89;
5397        let text = run(&opts, "int f(void) { return dummy(); }\ndummy () { return 7; }\n")
5398            .text()
5399            .to_owned();
5400        assert!(text.contains("func @f()"), "the caller is there: {text}");
5401        assert!(text.contains("func @dummy"), "and so is what it calls: {text}");
5402        assert!(text.contains("iconst.i32 7"), "with the body it was given: {text}");
5403    }
5404
5405    /// An old style definition whose parameter is narrower than what a call passes it.
5406    ///
5407    /// There is no prototype for a call to convert its argument to, so the argument is promoted
5408    /// and an `int` arrives for a parameter the body reads as an `unsigned char`. The entry block
5409    /// is where the two meet, and gcc writes the same pair of instructions there: store the low
5410    /// byte, read it back widened. `execute/950605-1.c` in the torture suite calls `f(-1)` and
5411    /// checks the parameter against `0xFF`, which is the difference between converting and not.
5412    #[test]
5413    fn an_old_style_parameter_is_converted_from_what_the_call_promoted_it_to() {
5414        let mut opts = options();
5415        opts.emit = EmitKind::Ir;
5416        opts.std = Std::C89;
5417        let compiled = |source: &str| run(&opts, source).text().to_owned();
5418
5419        let text = compiled("f (c) unsigned char c; { return c; }\n");
5420        assert!(text.contains("func @f(i32"), "an int arrives: {text}");
5421        assert!(text.contains("trunc.i8"), "and is cut down to what was declared: {text}");
5422        assert!(text.contains("zext.i32"), "then read back unsigned: {text}");
5423
5424        // A `short` is the same shape and signed, so it comes back the other way.
5425        let text = compiled("f (s) short s; { return s; }\n");
5426        assert!(text.contains("trunc.i16"), "cut down: {text}");
5427        assert!(text.contains("sext.i32"), "and read back signed: {text}");
5428
5429        // A `float` parameter is promoted to `double`, and without the conversion the multiply
5430        // below has one f64 operand and one f32, which the verifier refuses as invalid IR.
5431        let text = compiled("f (x) float x; { return x * 2; }\n");
5432        assert!(text.contains("func @f(f64"), "a double arrives: {text}");
5433        assert!(text.contains("fptrunc.f32"), "and is narrowed to the float: {text}");
5434
5435        // A parameter a prototype named arrives as itself and nothing is converted, which is the
5436        // case this must not have changed.
5437        let text = compiled("int f(unsigned char c) { return c; }\n");
5438        assert!(text.contains("func @f(i8)"), "the declared type arrives: {text}");
5439        assert!(!text.contains("trunc"), "so there is nothing to cut down: {text}");
5440    }
5441
5442    /// The six rules gcc 14 turned from a warning into an error, and the three answers each one
5443    /// gets depending on the dialect and on `-fpermissive`.
5444    ///
5445    /// The table is a measurement rather than a reading of the release notes. Six files, one per
5446    /// rule, put through gcc 16.2.0 on x86-64 Linux under each of the four command lines below
5447    /// with no `-W` flags on any of them, and what came back is what is written here. The three
5448    /// rules that say nothing under C89 are the three C89 did not have, and the three that warn
5449    /// there were constraint violations then as well.
5450    #[test]
5451    fn the_rules_gcc_promoted_are_decided_by_the_dialect_and_by_fpermissive() {
5452        // `-std=gnu89`, `-std=gnu17`, `-std=gnu17 -fpermissive`, and `-std=gnu23`.
5453        let modes = [(Std::C89, false), (Std::C17, false), (Std::C17, true), (Std::C23, false)];
5454        let cases = [
5455            ("static counted;\n", ["", "error", "warning", "error"]),
5456            ("int f(void) { return g(); }\n", ["", "error", "warning", "error"]),
5457            ("int f(x) { return x; }\n", ["", "error", "warning", "error"]),
5458            ("int *p;\nvoid h(void) { p = 1; }\n", ["warning", "error", "warning", "error"]),
5459            (
5460                "char *q;\nint *r;\nvoid k(void) { r = q; }\n",
5461                ["warning", "error", "warning", "error"],
5462            ),
5463            ("int f(void) { return; }\n", ["", "error", "warning", "error"]),
5464            ("void g(void) { return 1; }\n", ["warning", "error", "warning", "error"]),
5465        ];
5466
5467        for (source, wanted) in cases {
5468            for (&(std, permissive), wanted) in modes.iter().zip(wanted) {
5469                let mut opts = options();
5470                opts.std = std;
5471                opts.permissive = permissive;
5472                let said = run(&opts, source).messages.join("\n");
5473                let severity = if said.contains(": error: ") {
5474                    "error"
5475                } else if said.contains(": warning: ") {
5476                    "warning"
5477                } else {
5478                    ""
5479                };
5480                let how = if permissive { " -fpermissive" } else { "" };
5481                assert_eq!(
5482                    severity,
5483                    wanted,
5484                    "under -std={}{how}, {source} was answered with `{said}`",
5485                    std.as_str()
5486                );
5487                if wanted.is_empty() {
5488                    assert!(said.is_empty(), "nothing to say, but said `{said}`");
5489                }
5490            }
5491        }
5492    }
5493
5494    /// A first argument that is not a list, which the four variadic operators answer in two ways.
5495    ///
5496    /// gcc has `va_arg` as an operator, since it takes a type name and no function can, and the
5497    /// other three as builtin functions taking the address of a list. The difference is not a
5498    /// naming one: the operator's complaint is its own and is an error under every dialect, and
5499    /// the three functions go through the ordinary rule about an argument of the wrong type,
5500    /// which is one of the rules the table above is about. The same four command lines through
5501    /// gcc 16.2.0 on x86-64 Linux is where these came from.
5502    #[test]
5503    fn the_three_variadic_builtins_answer_a_bad_list_the_way_a_call_answers_a_bad_argument() {
5504        let modes = [(Std::C89, false), (Std::C17, false), (Std::C17, true), (Std::C23, false)];
5505        let cases = [
5506            (
5507                "int f(int n, ...) { char *p; return __builtin_va_arg(p, int); }\n",
5508                "first argument to 'va_arg' not of type 'va_list'",
5509                ["error", "error", "error", "error"],
5510            ),
5511            (
5512                "void f(int n, ...) { char *p; __builtin_va_start(p, n); }\n",
5513                "passing argument 1 of '__builtin_va_start' from incompatible pointer type",
5514                ["warning", "error", "warning", "error"],
5515            ),
5516            (
5517                "void f(int n, ...) { int x; __builtin_va_end(x); }\n",
5518                "passing argument 1 of '__builtin_va_end' makes pointer from integer without a \
5519                 cast",
5520                ["warning", "error", "warning", "error"],
5521            ),
5522            (
5523                "void f(int n, ...) { __builtin_va_list a; char *p; __builtin_va_copy(a, p); }\n",
5524                "passing argument 2 of '__builtin_va_copy' from incompatible pointer type",
5525                ["warning", "error", "warning", "error"],
5526            ),
5527        ];
5528
5529        for (source, message, wanted) in cases {
5530            for (&(std, permissive), wanted) in modes.iter().zip(wanted) {
5531                let mut opts = options();
5532                opts.std = std;
5533                opts.permissive = permissive;
5534                let said = run(&opts, source).messages.join("\n");
5535                let how = if permissive { " -fpermissive" } else { "" };
5536                assert!(
5537                    said.contains(&format!(": {wanted}: {message}")),
5538                    "under -std={}{how}, {source} was answered with `{said}`",
5539                    std.as_str()
5540                );
5541            }
5542        }
5543    }
5544
5545    /// The IR of `source` at one safety tier, insisting that it compiled cleanly.
5546    fn safe_ir(tier: rucc_session::Safety, source: &str) -> String {
5547        let mut opts = options();
5548        opts.emit = EmitKind::Ir;
5549        opts.safety = tier;
5550        let result = run(&opts, source);
5551        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5552        result.text().to_owned()
5553    }
5554
5555    const READS_THROUGH_A_POINTER: &str = "int read(int *p) { return p[1]; }\n";
5556
5557    /// The IR for a source built with a tier and a padding mode.
5558    fn padded_ir(padding: Padding, source: &str) -> String {
5559        let mut opts = options();
5560        opts.emit = EmitKind::Ir;
5561        opts.safety = rucc_session::Safety::Detect;
5562        opts.padding = padding;
5563        let result = run(&opts, source);
5564        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5565        result.text().to_owned()
5566    }
5567
5568    const FILLS_A_RECORD_A_MEMBER_AT_A_TIME: &str = "struct padded { char tag; int value; };\n\
5569         void fill(struct padded *p) { p->tag = 1; p->value = 2; }\n";
5570
5571    #[test]
5572    fn a_record_filled_a_member_at_a_time_comes_out_whole_when_padding_does_not_participate() {
5573        // Section 9.3 of document 09, and the reason the default is the one it gives library code.
5574        // Four bytes from the `char` and four from the `int` is the whole of an eight byte record,
5575        // so the `memcmp` or the hash or the `write` that reads it back is not refused.
5576        let text = padded_ir(Padding::Ignored, FILLS_A_RECORD_A_MEMBER_AT_A_TIME);
5577        assert_eq!(text.matches("owns 4").count(), 2, "{text}");
5578    }
5579
5580    #[test]
5581    fn a_store_says_only_what_it_wrote_when_padding_does_participate() {
5582        // The kernel profile's default, which is section 9.3's actual rule: the padding stays
5583        // unwritten and the read of the record that would leak it is the one that reports.
5584        let text = padded_ir(Padding::Tracked, FILLS_A_RECORD_A_MEMBER_AT_A_TIME);
5585        assert!(!text.contains("owns"), "{text}");
5586    }
5587
5588    #[test]
5589    fn a_member_of_a_union_owns_nothing_after_it() {
5590        // The bytes after a short member of a union belong to a longer member rather than to
5591        // padding, and saying a store through the short one wrote them would be saying the longer
5592        // one holds a value nobody put there.
5593        let text = padded_ir(
5594            Padding::Ignored,
5595            "union u { char tag; long wide; };\nvoid fill(union u *p) { p->tag = 1; }\n",
5596        );
5597        assert!(!text.contains("owns"), "{text}");
5598    }
5599
5600    #[test]
5601    fn an_inner_records_trailing_padding_reaches_the_outer_records() {
5602        // The composition. `in` owns four bytes of `outer` because `x` starts there, and `c` is
5603        // the last member of `in`, so what it owns is what `in` owns rather than its own one byte.
5604        // Without that the three bytes between them would stay unwritten and a read of the whole
5605        // thing would report.
5606        let text = padded_ir(
5607            Padding::Ignored,
5608            "struct inner { char c; };\n\
5609             struct outer { struct inner in; int x; };\n\
5610             void fill(struct outer *p) { p->in.c = 1; p->x = 2; }\n",
5611        );
5612        assert_eq!(text.matches("owns 4").count(), 2, "{text}");
5613    }
5614
5615    #[test]
5616    fn a_build_that_did_not_ask_for_the_monitor_is_compiled_the_way_it_always_was() {
5617        // This is the load bearing test of the whole flag. The monitor is being built in the open
5618        // and every build in the world is compiled by this compiler with the flag absent, so a
5619        // check that leaked into that path would be a regression for everybody.
5620        let text = ir(READS_THROUGH_A_POINTER);
5621        assert!(!text.contains("check_"), "{text}");
5622        assert!(!text.contains("cap_of"), "{text}");
5623    }
5624
5625    #[test]
5626    fn asking_for_a_tier_puts_the_checks_in_before_the_optimizer_sees_them() {
5627        let text = safe_ir(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
5628        assert!(text.contains("cap_of"), "{text}");
5629        assert!(text.contains("check_bounds"), "{text}");
5630        assert!(text.contains("check_live"), "{text}");
5631        // The subscript is address arithmetic, so J2 applies to it as well as J1.
5632        assert!(text.contains("check_deriv"), "{text}");
5633        // And the read names a type, so it asks the type plane about the bytes as well.
5634        assert!(text.contains("check_type"), "{text}");
5635    }
5636
5637    #[test]
5638    fn the_three_tiers_that_are_not_off_all_check_the_same_accesses_so_far() {
5639        // What separates them is the reporter and the boundary, which are milestones S2 and S3.
5640        // Pinning it here means the day they stop agreeing, this test says so rather than the
5641        // difference going unnoticed.
5642        let detect = safe_ir(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
5643        for tier in [rucc_session::Safety::Enforce, rucc_session::Safety::Kernel] {
5644            assert_eq!(safe_ir(tier, READS_THROUGH_A_POINTER), detect, "{tier}");
5645        }
5646    }
5647
5648    /// The safety summary of `source` at one tier, insisting that it compiled cleanly.
5649    fn summary(tier: rucc_session::Safety, source: &str) -> String {
5650        let mut opts = options();
5651        opts.emit = EmitKind::SafetySummary;
5652        opts.safety = tier;
5653        let result = run(&opts, source);
5654        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5655        result.text().to_owned()
5656    }
5657
5658    #[test]
5659    fn the_summary_counts_the_checks_that_went_in_and_the_ones_still_standing() {
5660        let text = summary(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
5661        assert!(text.contains("\"tier\": \"detect\""), "{text}");
5662        // One load, so one of each of the two access checks, and the subscript is a derivation.
5663        assert!(
5664            text.contains("\"bounds\": { \"emitted\": 1, \"remaining\": 1, \"discharged\": 0 }"),
5665            "{text}"
5666        );
5667        assert!(
5668            text.contains(
5669                "\"derivation\": { \"emitted\": 1, \"remaining\": 1, \"discharged\": 0 }"
5670            ),
5671            "{text}"
5672        );
5673    }
5674
5675    #[test]
5676    fn a_build_without_the_monitor_summarises_as_a_build_with_no_checks_in_it() {
5677        // Which is the honest summary rather than an error. A build system that emits a summary
5678        // for every unit should get one for the units nobody asked to instrument too, and the
5679        // zeroes are what say that the guarantee over that file is nothing at all.
5680        let text = summary(rucc_session::Safety::Off, READS_THROUGH_A_POINTER);
5681        assert!(text.contains("\"tier\": \"off\""), "{text}");
5682        assert!(
5683            text.contains("\"bounds\": { \"emitted\": 0, \"remaining\": 0, \"discharged\": 0 }"),
5684            "{text}"
5685        );
5686    }
5687
5688    #[test]
5689    fn a_call_the_boundary_models_is_counted_apart_from_one_it_does_not() {
5690        let text = summary(
5691            rucc_session::Safety::Detect,
5692            "void *memcpy(void *, const void *, unsigned long);\n\
5693             int puts(const char *);\n\
5694             void f(char *d, char *s) { memcpy(d, s, 4); puts(d); }\n",
5695        );
5696        assert!(text.contains("\"interposed\": 1"), "{text}");
5697        assert!(text.contains("\"puts\""), "{text}");
5698        // The wrapper it was pointed at is ours, so it is not on the list of things this build
5699        // failed to model. Counting it there would make instrumenting a file look worse than
5700        // leaving it alone.
5701        assert!(!text.contains("__rucc_wrap_memcpy\""), "{text}");
5702    }
5703
5704    #[test]
5705    fn an_address_taken_of_a_library_function_is_counted_the_way_a_call_to_one_is() {
5706        // The shape SQLite's syscall table has, cut down to two rows. `memcpy` has a wrapper so the
5707        // table holds the wrapper's address and the build modelled it; `puts` has none, so what the
5708        // table holds is the real function and the build did not, and section 10.1 says the one it
5709        // did not is named rather than passed over.
5710        let text = summary(
5711            rucc_session::Safety::Detect,
5712            "void *memcpy(void *, const void *, unsigned long);\n\
5713             int puts(const char *);\n\
5714             void *table[2] = { (void *)memcpy, (void *)puts };\n\
5715             void *f(int i) { return table[i]; }\n",
5716        );
5717        assert!(text.contains("\"interposed\": 1"), "{text}");
5718        assert!(text.contains("\"puts\""), "{text}");
5719        assert!(!text.contains("\"memcpy\""), "{text}");
5720    }
5721
5722    #[test]
5723    fn the_two_directions_a_pointer_crosses_the_boundary_are_counted_apart() {
5724        // `f` is a name the linker can bind to and takes a pointer, so a pointer arrives there.
5725        // `notes_open` is a library this build did not instrument, so a pointer comes back from
5726        // it. Both are crossings and neither is the other, which is why there are two numbers.
5727        let text = summary(
5728            rucc_session::Safety::Detect,
5729            "void *notes_open(void);\n\
5730             char *f(char *p) { char *q = notes_open(); return q ? q : p; }\n",
5731        );
5732        assert!(text.contains("\"crossings\": { \"entered\": 1, \"returned\": 1 }"), "{text}");
5733        assert!(text.contains("\"notes_open\""), "{text}");
5734    }
5735
5736    #[test]
5737    fn a_static_function_nobody_takes_the_address_of_is_not_a_crossing() {
5738        // Nothing outside the file can reach it, so a witness on its parameters would be counting
5739        // a crossing that does not happen.
5740        let text = summary(
5741            rucc_session::Safety::Detect,
5742            "static int len(const char *p) { return p ? 1 : 0; }\n\
5743             int f(void) { return len(\"x\"); }\n",
5744        );
5745        assert!(text.contains("\"crossings\": { \"entered\": 0, \"returned\": 0 }"), "{text}");
5746    }
5747
5748    /// The granule report for `source`, insisting that it compiled cleanly.
5749    fn granules(source: &str) -> String {
5750        let mut opts = options();
5751        opts.emit = EmitKind::TypeGranules;
5752        let result = run(&opts, source);
5753        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5754        result.text().to_owned()
5755    }
5756
5757    #[test]
5758    fn the_granule_report_names_every_record_and_both_keyings() {
5759        let text = granules(
5760            "struct hot { char *p; int a; int b; };\n\
5761             int f(struct hot *h) { return h->a; }\n",
5762        );
5763        assert!(text.contains("struct hot"), "{text}");
5764        // Both keyings are reported because which types count as one is a decision the design
5765        // has not made yet, and a report that picked one would be hiding the cost of the other.
5766        assert!(text.contains("every type distinct"), "{text}");
5767        assert!(text.contains("every pointer one type"), "{text}");
5768        assert!(text.contains("budget"), "{text}");
5769    }
5770
5771    #[test]
5772    fn a_record_nothing_uses_is_still_measured() {
5773        // The measurement is about what a program declares, not about what it runs, so a type
5774        // that is only ever declared still costs the plane whatever its layout costs.
5775        let text = granules("struct unused { long a; double b; };\nint f(void) { return 0; }\n");
5776        assert!(text.contains("struct unused"), "{text}");
5777    }
5778
5779    #[test]
5780    fn the_granule_report_stops_before_anything_is_lowered() {
5781        // A layout is settled at the closing brace, so lowering the function bodies would take
5782        // minutes on an amalgamation and answer nothing. The evidence that it stops is that a
5783        // body the back end has no way to compile still produces a report.
5784        let text = granules(
5785            "struct wide { long double d; };\n\
5786             long double f(long double x) { return x * x; }\n",
5787        );
5788        assert!(text.contains("struct wide"), "{text}");
5789    }
5790
5791    #[test]
5792    fn a_witness_reaches_the_assembler_as_a_call_to_the_runtime() {
5793        // The count only means anything if the call is really there, and a summary saying one is
5794        // there is not evidence that the back end emitted it.
5795        let text = safe_asm(rucc_session::Safety::Detect, "char *f(char *p) { return p; }\n");
5796        assert!(text.contains("\tcall\t__rucc_cap_witness\n"), "{text}");
5797    }
5798
5799    #[test]
5800    fn a_pointer_turned_into_an_integer_is_on_the_trust_set() {
5801        let text = summary(
5802            rucc_session::Safety::Detect,
5803            "unsigned long f(int *p) { return (unsigned long) p; }\n",
5804        );
5805        assert!(text.contains("\"exposed\": 1"), "{text}");
5806    }
5807
5808    /// The assembly of `source` at one safety tier, insisting that it compiled cleanly.
5809    fn safe_asm(tier: rucc_session::Safety, source: &str) -> String {
5810        let mut opts = options();
5811        opts.emit = EmitKind::Asm;
5812        opts.safety = tier;
5813        let result = run(&opts, source);
5814        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5815        result.text().to_owned()
5816    }
5817
5818    #[test]
5819    fn a_check_reaches_the_assembler_as_a_call_to_the_runtime() {
5820        let text = safe_asm(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
5821        assert!(text.contains("\tcall\t__rucc_check_bounds\n"), "{text}");
5822        assert!(text.contains("\tcall\t__rucc_check_live\n"), "{text}");
5823        assert!(text.contains("\tcall\t__rucc_check_deriv\n"), "{text}");
5824        // The type check and the init check of one read reach the assembler as the one call that
5825        // asks both planes about it. `rucc_safety::lower::partner` is what recognises the pair.
5826        assert!(text.contains("\tcall\t__rucc_check_typed_init\n"), "{text}");
5827    }
5828
5829    #[test]
5830    fn every_check_that_reached_the_assembler_has_a_row_describing_it() {
5831        // Four calls and four descriptors, each in the section the runtime's reporter reads. The
5832        // width is `rucc_safety::lower::WIDTH` and the row is `rucc_safe_rt::fail::Descriptor`, and
5833        // the two agreeing is what makes the address a check is handed mean anything. Four rather
5834        // than five because the read's two plane questions are one call carrying one row, which the
5835        // two of them can share because a type check's row and an init check's row are identical.
5836        let text = safe_asm(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
5837        let section = format!("\t.section\t{},", rucc_safety::SECTION);
5838        assert_eq!(text.matches(&section).count(), 4, "{text}");
5839        for index in 0..4 {
5840            let name = format!("__rucc_safety_desc_{index}");
5841            // Defined once and referenced once, because a descriptor nothing points at describes
5842            // nothing and a reference with no definition does not link.
5843            assert!(text.contains(&format!("{name}:\n")), "{text}");
5844            assert!(text.contains(&format!("{name}(%rip)")), "{text}");
5845        }
5846        assert!(!text.contains("__rucc_safety_desc_4"), "{text}");
5847    }
5848
5849    /// `__builtin_constant_p` is answered in the front end and never reaches the IR.
5850    ///
5851    /// gcc folds it after optimization, so its answer for an argument that is not written as a
5852    /// constant can differ between `-O0` and `-O2`. What is checked here is the front end's
5853    /// answer, which is the same at every level, and the four cases where gcc gives the same
5854    /// answer at both levels are the ones measured on gcc 16: a literal is one, a variable is
5855    /// zero, a string literal is one and the address of an object is zero.
5856    #[test]
5857    fn builtin_constant_p_is_folded_where_it_is_written_rather_than_called() {
5858        let text = ir(concat!(
5859            "int g;\n",
5860            "int a = __builtin_constant_p(1);\n",
5861            "int b = __builtin_constant_p(g);\n",
5862            "int c = __builtin_constant_p(\"abc\");\n",
5863            "int d = __builtin_constant_p(&g);\n",
5864            "int e = __builtin_constant_p(1.5);\n",
5865            "int h = __builtin_choose_expr(__builtin_constant_p(3), 11, 22);\n",
5866        ));
5867        assert!(text.contains("global @a : i32 = 1,"), "{text}");
5868        assert!(text.contains("global @b : i32 = 0,"), "{text}");
5869        assert!(text.contains("global @c : i32 = 1,"), "{text}");
5870        assert!(text.contains("global @d : i32 = 0,"), "{text}");
5871        assert!(text.contains("global @e : i32 = 1,"), "{text}");
5872        assert!(text.contains("global @h : i32 = 11,"), "{text}");
5873        assert!(!text.contains("__builtin_constant_p"), "it is not a call to anything:\n{text}");
5874
5875        // The argument is not evaluated, which is what gcc does with it as well, so `i` is
5876        // still zero. The second constant is the answer, which nothing reads and which the
5877        // first pass that looks for dead code will take out.
5878        let text = body("int f(void) { int i = 0; __builtin_constant_p(i++); return i; }\n");
5879        assert_eq!(text, "block0:\n    %0 = iconst.i32 0\n    %1 = iconst.i32 0\n    return %0\n");
5880    }
5881
5882    /// A library builtin is the library function of the same name, and the call says so.
5883    ///
5884    /// A program writes `__builtin_strlen` rather than `strlen` to reach the function the C
5885    /// library promises where its own name has been taken by a macro, and to say that the usual
5886    /// meaning is the one intended. So the name in the program and the name in the object file
5887    /// are two different names and the call carries the second one. gcc folds several of these
5888    /// when the arguments allow it, which is an optimization on top of a call that is already
5889    /// right rather than instead of it, so nothing here depends on any folding happening.
5890    #[test]
5891    fn a_call_to_a_library_builtin_reaches_the_library_function() {
5892        let text = body("void f(void) { __builtin_abort(); }\n");
5893        assert_eq!(text, "block0:\n    call @abort() : ()\n    return\n");
5894
5895        // Nothing declared either of these and nothing had to: the prefix is what says the name
5896        // belongs to the implementation, and the type comes out of `features.toml`.
5897        let text = ir("int f(const char *s) { return __builtin_puts(s) + __builtin_strlen(s); }\n");
5898        assert!(text.contains("call @puts(%0) : (ptr) -> i32"), "{text}");
5899        assert!(text.contains("call @strlen(%0) : (ptr) -> i64"), "{text}");
5900        assert!(!text.contains("__builtin_"), "the prefix is not part of any name here:\n{text}");
5901    }
5902
5903    /// A `_chk` builtin reaches the checking function in the library with the object size still
5904    /// on the end of it.
5905    ///
5906    /// This is what a fortified `string.h` turns every copy into, so it is what a program built
5907    /// the way a distribution builds one is full of, and the whole of what makes the call right
5908    /// is that the size goes with it. The checking function takes `(size_t) -1` to mean nothing
5909    /// is known and does no check, which is what the header passes when the destination's object
5910    /// is not in sight, so the unconditional call means the same thing in both cases and costs a
5911    /// call gcc would have folded away in the second.
5912    ///
5913    /// The name is the one place this family reads like an exception and is not one:
5914    /// `__builtin___memcpy_chk` with `__builtin_` taken off is `__memcpy_chk`.
5915    #[test]
5916    fn a_chk_builtin_reaches_the_checking_function_and_keeps_the_size() {
5917        let text = ir(concat!(
5918            "char d[8];\n",
5919            "void f(const char *s, unsigned long n) {\n",
5920            "  __builtin___memcpy_chk(d, s, n, __builtin_object_size(d, 0));\n",
5921            "  __builtin___strcpy_chk(d, s, __builtin_object_size(d, 1));\n",
5922            "  __builtin___memset_chk(d, 0, n, 8);\n",
5923            "}\n",
5924        ));
5925        assert!(text.contains("call @__memcpy_chk("), "{text}");
5926        assert!(text.contains("call @__strcpy_chk("), "{text}");
5927        assert!(text.contains("call @__memset_chk("), "{text}");
5928        assert!(text.contains("iconst.i64 8"), "the object size reaches the call: {text}");
5929        assert!(!text.contains("__builtin_"), "the prefix is not part of any name here:\n{text}");
5930    }
5931
5932    /// A checking call whose object size says nothing is known is the plain library call.
5933    ///
5934    /// That is the whole of the folding half of the family. The checking function reads the all
5935    /// ones value as do not check, so the call it was going to make is the function it guards with
5936    /// an argument nobody reads on the end of it, and gcc drops the argument and calls the plain
5937    /// function at every level including `-O0`. Where the size is a real number the checking call
5938    /// stands, because the check is the point.
5939    #[test]
5940    fn a_checking_call_whose_size_says_nothing_is_known_is_the_plain_library_call() {
5941        let text = ir(concat!(
5942            "extern char *p;\n",
5943            "char d[8];\n",
5944            "void f(const char *s, unsigned long n) {\n",
5945            "  __builtin___memcpy_chk(d, s, n, __builtin_object_size(d, 0));\n",
5946            "  __builtin___memcpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
5947            "  __builtin___strcpy_chk(p, s, __builtin_object_size(p, 0));\n",
5948            "  __builtin___stpncpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
5949            "  __builtin___sprintf_chk(p, 1, __builtin_object_size(p, 0), s);\n",
5950            "}\n",
5951        ));
5952
5953        // The destination whose object is in sight keeps its check, size and all.
5954        assert!(
5955            text.contains("call @__memcpy_chk(%2, %0, %1, %3) : (ptr, ptr, i64, i64)"),
5956            "{text}"
5957        );
5958
5959        // The three whose object is not lose the argument and the name along with it. The type of
5960        // the call goes with them, which is what says the argument is gone rather than ignored.
5961        assert!(text.contains("call @memcpy(%6, %0, %1) : (ptr, ptr, i64) -> ptr"), "{text}");
5962        assert!(text.contains("call @strcpy(%10, %0) : (ptr, ptr) -> ptr"), "{text}");
5963        assert!(text.contains("call @stpncpy(%14, %0, %1) : (ptr, ptr, i64) -> ptr"), "{text}");
5964
5965        // The formatted one never folds, whatever the size says, because refusing a `%n` in a
5966        // writable format is the other half of what it was asked to do.
5967        assert!(text.contains("call @__sprintf_chk("), "{text}");
5968
5969        // Nothing is left behind in the instructions either. The size the folded calls no longer
5970        // take is a constant nobody reads, and no instruction is written for one.
5971        let asm = asm(concat!(
5972            "void f(char *p, const char *s, unsigned long n) {\n",
5973            "  __builtin___memcpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
5974            "}\n",
5975        ));
5976        assert!(asm.contains("call\tmemcpy"), "{asm}");
5977        assert!(!asm.contains("$-1"), "the size that went away leaves no instruction:\n{asm}");
5978    }
5979
5980    /// The `v` spellings take a `__builtin_va_list`, which is the first type in the table the
5981    /// target chooses the shape of rather than the width of.
5982    ///
5983    /// On x86-64 it is an array of one, so what the prototype has to say is the pointer that
5984    /// array decays to, which is the same adjustment C makes to any parameter written as an array
5985    /// and is what a `va_list` parameter already holds. A prototype that kept the array would be
5986    /// one no argument could ever match.
5987    #[test]
5988    fn the_v_spellings_of_the_chk_family_take_the_list_a_va_list_parameter_holds() {
5989        let text = ir(concat!(
5990            "char d[64];\n",
5991            "int f(const char *fmt, ...) {\n",
5992            "  __builtin_va_list ap;\n",
5993            "  __builtin_va_start(ap, fmt);\n",
5994            "  int n = __builtin___vsprintf_chk(d, 1, __builtin_object_size(d, 0), fmt, ap);\n",
5995            "  __builtin_va_end(ap);\n",
5996            "  return n;\n",
5997            "}\n",
5998        ));
5999        assert!(text.contains("call @__vsprintf_chk("), "{text}");
6000        assert!(text.contains("iconst.i64 64"), "the object size reaches the call: {text}");
6001    }
6002
6003    /// The absolute value family is four instructions and not a call, whoever declared the name.
6004    ///
6005    /// `abs`, `labs` and `llabs` are reserved to the implementation, so a program that writes one
6006    /// means the one the C library promises and the compiler is allowed to know what it does. The
6007    /// program in `gcc.c-torture/execute/20021127-1.c` is the one that insists: it defines `llabs`
6008    /// to abort and expects the call not to reach it. Measured against gcc 16.2.0, which writes a
6009    /// `neg` and a `cmovns` and never calls the definition either.
6010    ///
6011    /// The most negative value comes back as itself, which is what the arithmetic gives and what
6012    /// gcc's pair of instructions gives, and C says the answer is undefined there.
6013    #[test]
6014    fn the_absolute_value_family_is_the_magnitude_and_not_a_call() {
6015        let text = body(concat!(
6016            "long long llabs(long long);\n",
6017            "long long f(long long x) { return llabs(x); }\n",
6018        ));
6019        assert!(text.contains("%1 = iconst.i64 63"), "{text}");
6020        assert!(text.contains("%2 = ashr %0, %1"), "{text}");
6021        assert!(text.contains("%3 = xor %0, %2"), "{text}");
6022        assert!(text.contains("%4 = sub %3, %2"), "{text}");
6023        assert!(!text.contains("call"), "the call does not happen:\n{text}");
6024
6025        // The narrower two, whose width comes from the type the library gives the name and not
6026        // from anything at the call.
6027        let text = body("int abs(int);\nint f(int x) { return abs(x); }\n");
6028        assert!(text.contains("iconst.i32 31"), "{text}");
6029        let text = body("long labs(long);\nlong f(long x) { return labs(x); }\n");
6030        assert!(text.contains("iconst.i64 63"), "{text}");
6031
6032        // The prefixed spelling is the same node, and it is what a program writes to reach the
6033        // library's meaning where the plain name has been taken.
6034        let text = body("long long f(long long x) { return __builtin_llabs(x); }\n");
6035        assert!(!text.contains("call"), "{text}");
6036
6037        // A definition of the name in the same file changes nothing, which is the whole point.
6038        let text = ir(concat!(
6039            "long long llabs(long long b);\n",
6040            "long long g(long long x) { return llabs(x); }\n",
6041            "long long llabs(long long b) { return 7; }\n",
6042        ));
6043        assert!(!text.contains("call @llabs"), "{text}");
6044    }
6045
6046    /// A byte swap is one instruction and not a call, and nothing had to declare it.
6047    ///
6048    /// SQLite writes these for its page headers and glibc's `<endian.h>` defines `htobe32` and its
6049    /// neighbours as exactly these, so a program that reads a file format reaches one without ever
6050    /// naming it. There is no object file anywhere that defines `__builtin_bswap32`, so a call left
6051    /// standing here would not link.
6052    #[test]
6053    fn a_byte_swap_is_arithmetic_and_not_a_call() {
6054        let text = body("unsigned f(unsigned x) { return __builtin_bswap32(x); }\n");
6055        assert_eq!(text, "block0(%0: i32):\n    %1 = bswap %0\n    return %1\n");
6056
6057        // The argument is converted by the prototype the way any other call's would be, so the
6058        // swap happens at the width the name says and not at the width the program wrote.
6059        let text = body("unsigned f(unsigned char c) { return __builtin_bswap32(c); }\n");
6060        assert!(text.contains("zext.i32 %0"), "widened first: {text}");
6061        assert!(text.contains("bswap %1"), "and swapped at four bytes: {text}");
6062    }
6063
6064    /// Each of the three reverses in the width its name says, which is the type of the node.
6065    ///
6066    /// The width matters more here than it looks. `__builtin_bswap16` is the two bytes of a
6067    /// `uint16_t` exchanged, and if the node came out at the machine's width instead then the bits
6068    /// above the value would be dragged into the answer and the result would be zero.
6069    #[test]
6070    fn the_byte_swaps_reverse_at_the_width_their_name_says() {
6071        for (name, ty, width) in [
6072            ("__builtin_bswap16", "unsigned short", "i16"),
6073            ("__builtin_bswap32", "unsigned", "i32"),
6074            ("__builtin_bswap64", "unsigned long long", "i64"),
6075        ] {
6076            let source = format!("{ty} f({ty} x) {{ return {name}(x); }}\n");
6077            let text = body(&source);
6078            assert_eq!(
6079                text,
6080                format!("block0(%0: {width}):\n    %1 = bswap %0\n    return %1\n"),
6081                "{name}"
6082            );
6083        }
6084    }
6085
6086    /// The three bit counts the IR has an instruction for are that instruction and not a call.
6087    ///
6088    /// Eighteen rows of `features.toml` come out of six questions, and three of the six are one
6089    /// instruction each. The kernel's bitmap search is built on them, ffmpeg counts leading zeroes
6090    /// in its bitstream reader and SQLite uses one to size a page, so a call left standing here
6091    /// would not link against anything and would be slow if it did.
6092    #[test]
6093    fn the_bit_counts_are_instructions_and_not_calls() {
6094        let text = body("int f(unsigned x) { return __builtin_clz(x); }\n");
6095        assert_eq!(text, "block0(%0: i32):\n    %1 = ctlz %0\n    return %1\n");
6096
6097        let text = body("int f(unsigned x) { return __builtin_ctz(x); }\n");
6098        assert_eq!(text, "block0(%0: i32):\n    %1 = cttz %0\n    return %1\n");
6099
6100        let text = body("int f(unsigned x) { return __builtin_popcount(x); }\n");
6101        assert_eq!(text, "block0(%0: i32):\n    %1 = ctpop %0\n    return %1\n");
6102    }
6103
6104    /// The width counted is the operand's and the width answered is `int`, which are two different
6105    /// things at every spelling but the narrowest.
6106    ///
6107    /// This is the mistake the family invites. `__builtin_clz` of a value counts the leading zeroes
6108    /// of it narrowed to `unsigned int` and `__builtin_clzll` counts them at sixty four bits, and
6109    /// those are different numbers for the same value. What decides it is the prototype the row
6110    /// carries, so the count happens after the conversion and the narrowing back to `int` happens
6111    /// after the count.
6112    #[test]
6113    fn the_bit_counts_ask_about_the_width_their_name_says() {
6114        let text = body("int f(unsigned long long x) { return __builtin_clzll(x); }\n");
6115        assert!(text.starts_with("block0(%0: i64):"), "counted at eight bytes: {text}");
6116        assert!(text.contains("%1 = ctlz %0"), "{text}");
6117        assert!(text.contains("trunc.i32 %1"), "and answered in an int: {text}");
6118
6119        // The same value asked about at the narrower width, which converts first and so counts
6120        // something else.
6121        let text = body("int f(unsigned long long x) { return __builtin_clz(x); }\n");
6122        assert!(text.contains("trunc.i32 %0"), "narrowed to what was asked about: {text}");
6123        assert!(text.contains("ctlz %1"), "and counted there: {text}");
6124
6125        let text = body("int f(unsigned long x) { return __builtin_popcountl(x); }\n");
6126        assert!(text.contains("%1 = ctpop %0"), "{text}");
6127        assert!(!text.contains("call"), "{text}");
6128    }
6129
6130    /// A parity is whether the count of set bits is odd, which is that count and its low bit.
6131    ///
6132    /// Not the machine's parity flag, which on x86-64 is over the low byte of a result and so is a
6133    /// different question, and not the count itself, since C says the answer is zero or one.
6134    #[test]
6135    fn a_parity_is_the_low_bit_of_the_set_bit_count() {
6136        let text = body("int f(unsigned x) { return __builtin_parity(x); }\n");
6137        assert!(text.contains("%1 = ctpop %0"), "{text}");
6138        assert!(text.contains("iconst.i32 1"), "{text}");
6139        assert!(text.contains("and %1, %2"), "the low bit of it: {text}");
6140    }
6141
6142    /// `__builtin_ffs` is the trailing zero count and one, kept only when there was a bit to find.
6143    ///
6144    /// The one in the family defined at zero, where it answers zero. Written as a mask rather than
6145    /// as a branch: the count and the comparison do not depend on each other and both are cheap, so
6146    /// a branch would buy nothing and cost two blocks and a join.
6147    #[test]
6148    fn the_first_set_bit_is_one_based_and_zero_for_a_zero() {
6149        let text = body("int f(int x) { return __builtin_ffs(x); }\n");
6150        assert!(text.contains("%1 = cttz %0"), "{text}");
6151        assert!(text.contains("%4 = add %1, %2"), "one more than the count: {text}");
6152        assert!(text.contains("%5 = icmp ne %0, %3"), "whether there was a bit at all: {text}");
6153        assert!(text.contains("%7 = sub %3, %6"), "spread to a mask: {text}");
6154        assert!(text.contains("%8 = and %4, %7"), "and kept only then: {text}");
6155        assert!(!text.contains("br_if"), "no branch: {text}");
6156    }
6157
6158    /// `__builtin_clrsb` is how many bits below the sign bit repeat it, which is a leading zero
6159    /// count of the value folded onto its own sign.
6160    ///
6161    /// Exclusive or with the sign spread over every bit turns a negative value into its complement
6162    /// and leaves one that is not negative alone, so in both cases the top bit is clear and there
6163    /// is one zero above the highest bit that does not repeat the sign. The answer is one less
6164    /// than that count, and the shift left is what takes the one off, with the low bit set on the
6165    /// way so that zero and minus one have something to count: both of them fold to a word with no
6166    /// bits in it, which is the one input a leading zero count says nothing about.
6167    #[test]
6168    fn the_redundant_sign_bit_count_is_instructions_and_not_a_call() {
6169        let text = body("int f(int x) { return __builtin_clrsb(x); }\n");
6170        assert!(text.contains("%1 = iconst.i32 31"), "{text}");
6171        assert!(text.contains("%2 = ashr %0, %1"), "the sign over every bit: {text}");
6172        assert!(text.contains("%3 = xor %0, %2"), "folded onto it: {text}");
6173        assert!(text.contains("%5 = shl %3, %4"), "one less than the count: {text}");
6174        assert!(text.contains("%6 = or %5, %4"), "with something to count at zero: {text}");
6175        assert!(text.contains("%7 = ctlz %6"), "{text}");
6176        assert!(!text.contains("call"), "{text}");
6177        assert!(!text.contains("br_if"), "no branch: {text}");
6178    }
6179
6180    /// The unsigned four are the same four instructions answering in the unsigned type.
6181    ///
6182    /// Which on a two's complement machine is the same bits, so what this checks is that the type
6183    /// of the answer is the unsigned one. The reason the family exists is the most negative value,
6184    /// whose magnitude is not representable in the signed type and is representable in this one.
6185    #[test]
6186    fn the_unsigned_absolute_value_family_answers_in_the_unsigned_type() {
6187        let text = body("unsigned f(int x) { return __builtin_uabs(x); }\n");
6188        assert!(text.contains("%1 = iconst.i32 31"), "{text}");
6189        assert!(text.contains("%4 = sub %3, %2"), "{text}");
6190        assert!(!text.contains("call"), "nothing declares uabs, so a call would not link: {text}");
6191
6192        let text = body("unsigned long long f(long long x) { return __builtin_ullabs(x); }\n");
6193        assert!(text.contains("iconst.i64 63"), "at the width the name says: {text}");
6194
6195        // The answer is the unsigned type and not the signed one, which is what a comparison
6196        // against it is decided by.
6197        let text = body("int f(int x) { return __builtin_uabs(x) > 2147483647u; }\n");
6198        assert!(text.contains("icmp ugt"), "compared unsigned: {text}");
6199    }
6200
6201    /// `intmax_t` is not a fixed type, so the two widest spellings ask the target what it is.
6202    ///
6203    /// `long` where that is sixty four bits wide and `long long` where it is not, which is the rule
6204    /// `rucc_pp::predef` writes `__INTMAX_TYPE__` out of. The three targets here are all LP64, so
6205    /// the answer is `long` and the shift is sixty three, and the point of the test is that the
6206    /// signature was understood at all rather than refused for naming a type the table could not
6207    /// spell.
6208    #[test]
6209    fn the_widest_absolute_value_is_whichever_type_the_target_makes_intmax_t() {
6210        let text = body("long f(long x) { return __builtin_imaxabs(x); }\n");
6211        assert!(text.contains("iconst.i64 63"), "{text}");
6212        assert!(text.contains("%4 = sub %3, %2"), "{text}");
6213        assert!(!text.contains("call"), "{text}");
6214
6215        let text = body("unsigned long f(long x) { return __builtin_umaxabs(x); }\n");
6216        assert!(text.contains("iconst.i64 63"), "{text}");
6217        assert!(!text.contains("call"), "{text}");
6218    }
6219
6220    /// The `_p` spellings ask the same question, write nothing, and do not evaluate the third
6221    /// argument.
6222    ///
6223    /// gcc says the third argument is there for its type alone, so a call is two operands and a
6224    /// type by the time it reaches the IR. What the type decides is the same thing it decides for
6225    /// the three that write: whether the exact answer would have fit there, which is why the
6226    /// second call below is done at a wider width than the first.
6227    #[test]
6228    fn an_overflow_predicate_writes_nothing_and_answers_the_bit_the_check_would() {
6229        let text =
6230            body("int f(int a, int b) { return __builtin_add_overflow_p(a, b, (int) 0); }\n");
6231        assert!(text.contains("%2, %3 = sadd_overflow.(i32, i1) %0, %1"), "{text}");
6232        assert!(!text.contains("store"), "nothing is written: {text}");
6233        assert!(!text.contains("call"), "{text}");
6234
6235        // A wider destination is a wider arithmetic, and the narrowing test that goes with it is
6236        // what says whether the answer got there, exactly as for the spelling that stores.
6237        let text =
6238            body("int f(int a, int b) { return __builtin_mul_overflow_p(a, b, (long long) 0); }\n");
6239        assert!(text.contains("smul_overflow.(i64, i1)"), "{text}");
6240        assert!(!text.contains("store"), "{text}");
6241
6242        // The third argument is a value and not a pointer, and a side effect written in it does
6243        // not happen, because what the argument is there for is its type.
6244        let text = body(concat!(
6245            "int g(void);\n",
6246            "int f(int a, int b) { return __builtin_sub_overflow_p(a, b, g()); }\n",
6247        ));
6248        assert!(!text.contains("call @g"), "the third argument is not evaluated: {text}");
6249    }
6250
6251    /// The three overflow checks are arithmetic and a flag, and not a call to anything.
6252    ///
6253    /// gcc has emitted these since 5.0 and there is no object file that defines one, so a call left
6254    /// standing here would not link. SQLite reaches all three within twenty lines of each other, in
6255    /// `sqlite3AddInt64` and its two neighbours, which is the reason they were done now.
6256    ///
6257    /// The IR instruction answers two things at once, the wrapped value and whether it wrapped,
6258    /// which is a shape nothing else in the IR has. The store is the builtin writing the answer
6259    /// through the pointer it was handed.
6260    #[test]
6261    fn an_overflow_check_is_arithmetic_and_not_a_call() {
6262        let text =
6263            body("int f(int a, int b, int *r) { return __builtin_add_overflow(a, b, r); }\n");
6264        assert!(text.contains("%3, %4 = sadd_overflow.(i32, i1) %0, %1"), "{text}");
6265        assert!(text.contains("store %3 -> %2"), "{text}");
6266        assert!(!text.contains("call"), "{text}");
6267
6268        let text =
6269            body("int f(int a, int b, int *r) { return __builtin_sub_overflow(a, b, r); }\n");
6270        assert!(text.contains("ssub_overflow.(i32, i1) %0, %1"), "{text}");
6271
6272        let text =
6273            body("int f(int a, int b, int *r) { return __builtin_mul_overflow(a, b, r); }\n");
6274        assert!(text.contains("smul_overflow.(i32, i1) %0, %1"), "{text}");
6275
6276        // Unsigned operands get the unsigned form, which is a different question about the same
6277        // arithmetic: an unsigned sum wraps where a signed one of the same bits does not.
6278        let text = body(
6279            "int f(unsigned a, unsigned b, unsigned *r) { return __builtin_add_overflow(a, b, r); }\n",
6280        );
6281        assert!(text.contains("uadd_overflow.(i32, i1) %0, %1"), "{text}");
6282    }
6283
6284    /// The arithmetic happens at a type that holds every value all three written types can hold.
6285    ///
6286    /// That is what makes the check exact. `unsigned int` and `int` in one call need thirty three
6287    /// bits between them, so the add is done at sixty four with each operand extended the way its
6288    /// own signedness says: the unsigned one zero extended, the signed one sign extended. Sign
6289    /// extending the unsigned one would turn three billion into a negative number before the
6290    /// addition ever saw it.
6291    #[test]
6292    fn an_overflow_check_is_done_at_a_type_that_holds_every_operand() {
6293        let text = body(
6294            "int f(unsigned a, int b, long long *r) { return __builtin_add_overflow(a, b, r); }\n",
6295        );
6296        assert!(text.contains("%3 = zext.i64 %0"), "the unsigned operand keeps its value: {text}");
6297        assert!(text.contains("%4 = sext.i64 %1"), "and so does the signed one: {text}");
6298        assert!(text.contains("sadd_overflow.(i64, i1) %3, %4"), "{text}");
6299
6300        // Three types that agree need no extension at all, which is what nearly every real call
6301        // is written as.
6302        let text = body(
6303            "int f(long long a, long long b, long long *r) { return __builtin_mul_overflow(a, b, r); }\n",
6304        );
6305        assert!(text.contains("smul_overflow.(i64, i1) %0, %1"), "{text}");
6306        assert!(!text.contains("sext."), "{text}");
6307        // The one widening left is the answer, which is a bit becoming the `int` C says it is.
6308        assert!(!text.contains("zext.i64"), "{text}");
6309    }
6310
6311    /// The wrapped answer is written through the pointer whether or not it fit.
6312    ///
6313    /// That is gcc's rule and it is what makes the builtin usable as a wrapping add with a flag on
6314    /// the side. A destination narrower than the arithmetic is narrowed and widened back, and the
6315    /// answer being different is the second half of the test: the instruction says whether the
6316    /// arithmetic itself needed more room, and the round trip says whether what came out survived
6317    /// the trip down to where it was going.
6318    #[test]
6319    fn an_overflow_check_writes_the_wrapped_answer_whether_or_not_it_fit() {
6320        let text =
6321            body("int f(int a, int b, char *r) { return __builtin_sub_overflow(a, b, r); }\n");
6322        assert!(text.contains("%3, %4 = ssub_overflow.(i32, i1) %0, %1"), "{text}");
6323        assert!(text.contains("%5 = trunc.i8 %3"), "narrowed to where it goes: {text}");
6324        assert!(text.contains("%6 = sext.i32 %5"), "and back: {text}");
6325        assert!(text.contains("%7 = icmp ne %6, %3"), "which is whether it fit: {text}");
6326        assert!(text.contains("store %5 -> %2"), "the narrowed value is stored either way: {text}");
6327        assert!(text.contains("%8 = or %4, %7"), "and either bit is an overflow: {text}");
6328    }
6329
6330    /// A call needing more than the widest type there is compiles, by not asking for such a type.
6331    ///
6332    /// One way to reach it: an unsigned `__int128` mixed with a signed type, which needs a hundred
6333    /// and twenty nine bits to represent both and so has nowhere left to go. That used to be refused
6334    /// by name. It is done now by carrying the sign of each operand alongside its value rather than
6335    /// inside it, which is what gcc does, so all three of the family compile for that mix.
6336    #[test]
6337    fn a_call_needing_more_than_the_widest_type_still_compiles() {
6338        for name in ["add", "sub", "mul"] {
6339            let source = format!(
6340                "int f(unsigned __int128 a, long long b, __int128 *r) {{\n    \
6341                 return __builtin_{name}_overflow(a, b, r);\n}}\n"
6342            );
6343            let mut opts = options();
6344            opts.emit = EmitKind::MirFinal;
6345            assert!(!run(&opts, &source).failed(), "{name} was refused or stopped the back end");
6346        }
6347    }
6348
6349    /// An operand that is not an integer at all is the older message, from the type checking every
6350    /// type generic builtin shares.
6351    #[test]
6352    fn an_overflow_check_over_something_that_is_not_an_integer_says_so() {
6353        let messages =
6354            errors("int f(double a, int b, int *r) { return __builtin_add_overflow(a, b, r); }\n");
6355        assert!(messages.iter().any(|line| line.contains("E0671")), "{messages:?}");
6356
6357        let messages =
6358            errors("int f(int a, int b, double *r) { return __builtin_add_overflow(a, b, r); }\n");
6359        assert!(messages.iter().any(|line| line.contains("E0671")), "{messages:?}");
6360    }
6361
6362    /// An ordered access is an ordered access in the IR, with the ordering the program wrote.
6363    ///
6364    /// Which is the point of the node existing at all. An ordering is not an argument anything is
6365    /// passed, it is a thing the IR says about an access, so the number in the source is read once
6366    /// in the front end and after that the ordering travels on the instruction where every pass
6367    /// that moves code can see it.
6368    ///
6369    /// SQLite is why these are done: `AtomicLoad` and `AtomicStore` in `sqlite3.c` are
6370    /// `__atomic_load_n` and `__atomic_store_n` at the relaxed ordering, and there are thirty five
6371    /// calls to the pair.
6372    #[test]
6373    fn an_ordered_access_is_ordered_in_the_ir() {
6374        let text = body("int f(int *p) { return __atomic_load_n(p, 0); }\n");
6375        assert!(text.contains("atomic_load.i32 %0, align 4, relaxed"), "{text}");
6376
6377        let text = body("long f(long *p) { return __atomic_load_n(p, 2); }\n");
6378        assert!(text.contains("atomic_load.i64 %0, align 8, acquire"), "{text}");
6379
6380        let text = body("void f(int *p, int v) { __atomic_store_n(p, v, 3); }\n");
6381        assert!(text.contains("atomic_store %1 -> %0, align 4, release"), "{text}");
6382
6383        let text = body("void f(int *p, int v) { __atomic_store_n(p, v, 5); }\n");
6384        assert!(text.contains("atomic_store %1 -> %0, align 4, seq_cst"), "{text}");
6385
6386        // The value is converted to what the pointer points at before it is stored, which is what
6387        // the call would have done if it had a prototype to convert against.
6388        let text = body("void f(char *p, int v) { __atomic_store_n(p, v, 0); }\n");
6389        assert!(text.contains("trunc.i8 %1"), "{text}");
6390        assert!(text.contains("atomic_store %2 -> %0, align 1, relaxed"), "{text}");
6391    }
6392
6393    /// On this machine the ordered access is the plain instruction, except at the strongest
6394    /// ordering of a store.
6395    ///
6396    /// x86-64 is total store order: every load is already an acquire and every store is already a
6397    /// release, and an aligned access no wider than a word is indivisible whether or not anybody
6398    /// asked. So the whole family is `mov` and the one thing the machine does not give away is a
6399    /// store staying in front of a later load, which is `mfence` behind the store. Every line below
6400    /// is what gcc 16.2.0 writes for the same function.
6401    #[test]
6402    fn an_ordered_access_is_the_plain_instruction_on_this_machine() {
6403        let text = asm("int f(int *p) { return __atomic_load_n(p, 5); }\n");
6404        assert!(text.contains("movl\t(%rdi), %eax"), "{text}");
6405        assert!(!text.contains("mfence"), "a load needs no barrier here: {text}");
6406
6407        let text = asm("void f(int *p, int v) { __atomic_store_n(p, v, 3); }\n");
6408        assert!(text.contains("movl\t%esi, (%rdi)"), "{text}");
6409        assert!(!text.contains("mfence"), "a release store needs no barrier here: {text}");
6410
6411        let text = asm("void f(int *p, int v) { __atomic_store_n(p, v, 5); }\n");
6412        let (before, after) = text.split_once("mfence").expect("a barrier: {text}");
6413        assert!(before.contains("movl\t%esi, (%rdi)"), "the store comes first: {text}");
6414        assert!(!after.contains("movl"), "and nothing else is between them: {text}");
6415    }
6416
6417    /// A barrier is one instruction at the strongest ordering and no instruction below it.
6418    ///
6419    /// The same reasoning the other way round. An acquire, a release and an acquire release fence
6420    /// are already true of every program running on this machine, and what a program wanted from
6421    /// one is that the compiler not move accesses across it, which is already so by the time any
6422    /// instruction is picked. Sequential consistency is the one that costs something.
6423    ///
6424    /// `__sync_synchronize` is the older family's spelling of the strongest one and compiles to
6425    /// exactly the same instruction, which is what SQLite calls twice in `sqlite3.c`.
6426    #[test]
6427    fn a_barrier_is_one_instruction_at_the_strongest_ordering_and_none_below_it() {
6428        assert!(asm("void f(void) { __atomic_thread_fence(5); }\n").contains("mfence"));
6429        assert!(asm("void f(void) { __sync_synchronize(); }\n").contains("mfence"));
6430
6431        for weaker in ["1", "2", "3", "4"] {
6432            let source = format!("void f(void) {{ __atomic_thread_fence({weaker}); }}\n");
6433            assert!(!asm(&source).contains("mfence"), "{weaker} costs nothing here");
6434        }
6435    }
6436
6437    /// The three x86 fences under gcc's names are that same barrier at that same ordering.
6438    ///
6439    /// Exact for `mfence` and stronger than asked for the other two, which is a safe answer: a
6440    /// program that wanted its stores ordered gets that and more. Narrowing the two is worth doing
6441    /// once an instruction can be named from there, which is the note the shipped `xmmintrin.h`
6442    /// already carries at `_mm_sfence`.
6443    ///
6444    /// Each carries a signature, so an argument written on one is reported like an argument
6445    /// written on any other call, which is the whole reason they have one.
6446    #[test]
6447    fn the_three_x86_fences_are_the_barrier_the_strongest_ordering_gives() {
6448        for name in ["__builtin_ia32_sfence", "__builtin_ia32_lfence", "__builtin_ia32_mfence"] {
6449            let source = format!("void f(void) {{ {name}(); }}\n");
6450            assert!(asm(&source).contains("mfence"), "{name} is a barrier");
6451            let text = body(&source);
6452            assert!(text.contains("fence seq_cst"), "{name}: {text}");
6453        }
6454
6455        let result = run(&options(), "void f(void) { __builtin_ia32_sfence(1); }\n");
6456        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
6457        assert!(result.messages[0].contains("too many arguments"), "{:?}", result.messages);
6458    }
6459
6460    /// The four compare and exchange names are one IR instruction producing two values.
6461    ///
6462    /// Which of the two the expression answers is the difference between three of the four names,
6463    /// and the fourth difference is the C11 pair writing what they found back through the pointer
6464    /// they were handed, which is the branch after the instruction.
6465    #[test]
6466    fn a_compare_and_exchange_is_one_instruction_answering_two_things() {
6467        // The older family, whose two names are the same instruction read two ways. Neither has a
6468        // memory order argument and both are a full barrier, which is what `seq_cst` says.
6469        let text =
6470            body("int f(int *p, int e, int d) { return __sync_val_compare_and_swap(p, e, d); }\n");
6471        assert!(text.contains("%3, %4 = cmpxchg.(i32, i1) %0, %1, %2, align 4, seq_cst"), "{text}");
6472        assert!(text.contains("return %3"), "the value it found: {text}");
6473
6474        let text =
6475            body("int f(int *p, int e, int d) { return __sync_bool_compare_and_swap(p, e, d); }\n");
6476        assert!(text.contains("%3, %4 = cmpxchg.(i32, i1) %0, %1, %2, align 4, seq_cst"), "{text}");
6477        assert!(text.contains("zext.i32 %4"), "whether it happened: {text}");
6478
6479        // The C11 form, whose value expected arrives by pointer and is read before the exchange,
6480        // and whose answer is whether it happened. The write back is on the path where it did not.
6481        let text = body(
6482            "int f(int *p, int *e, int d) { return __atomic_compare_exchange_n(p, e, d, 0, 4, 2); }\n",
6483        );
6484        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
6485        assert!(text.contains("%4, %5 = cmpxchg.(i32, i1) %0, %3, %2, align 4, acq_rel"), "{text}");
6486        assert!(text.contains("br_if %5, block2, block1"), "{text}");
6487        assert!(text.contains("store %4 -> %1, align 4"), "{text}");
6488
6489        // And the form that takes the value to put there by pointer as well, which is one more
6490        // read and is otherwise the same node.
6491        let text = body(
6492            "int f(int *p, int *e, int *d) { return __atomic_compare_exchange(p, e, d, 0, 5, 5); }\n",
6493        );
6494        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
6495        assert!(text.contains("%4 = load.i32 %2, align 4"), "{text}");
6496        assert!(text.contains("%5, %6 = cmpxchg.(i32, i1) %0, %3, %4, align 4, seq_cst"), "{text}");
6497    }
6498
6499    /// On this machine it is `lock cmpxchg`, at the width of the object and at every ordering.
6500    ///
6501    /// The `lock` is what makes the whole of it one step as far as every other processor is
6502    /// concerned, and it is also what makes the instruction a full barrier, which is why the
6503    /// ordering the program wrote changes nothing in what is written here. Every line below is what
6504    /// gcc 16.2.0 writes for the same function.
6505    #[test]
6506    fn a_compare_and_exchange_is_a_locked_instruction_at_the_width_of_the_object() {
6507        let widths = [("char", "b", "%dl"), ("short", "w", "%dx"), ("int", "l", "%edx")];
6508        for (ty, suffix, reg) in widths {
6509            let source = format!(
6510                "int f({ty} *p, {ty} e, {ty} d) {{ return __sync_bool_compare_and_swap(p, e, d); }}\n"
6511            );
6512            let text = asm(&source);
6513            assert!(text.contains("\tlock\n"), "{ty}: {text}");
6514            assert!(text.contains(&format!("cmpxchg{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
6515            assert!(text.contains("sete\t"), "{ty}: {text}");
6516        }
6517        let source =
6518            "int f(long *p, long e, long d) { return __sync_bool_compare_and_swap(p, e, d); }\n";
6519        assert!(asm(source).contains("cmpxchgq\t%rdx, (%rdi)"), "{}", asm(source));
6520
6521        // The ordering the program asked for changes nothing, because a locked instruction on this
6522        // machine orders everything whatever it was asked for, so there is never a barrier beside
6523        // it either.
6524        for order in ["0", "2", "3", "4", "5"] {
6525            let call = format!("__atomic_compare_exchange_n(p, e, d, 0, {order}, 0)");
6526            let source = format!("int f(int *p, int *e, int d) {{ return {call}; }}\n");
6527            let text = asm(&source);
6528            assert!(text.contains("cmpxchgl\t"), "{order}: {text}");
6529            assert!(!text.contains("mfence"), "{order} needs no barrier here: {text}");
6530        }
6531    }
6532
6533    /// A read modify write is one IR instruction, and a name that asks for the value afterwards is
6534    /// that instruction and one more operation.
6535    ///
6536    /// The instruction answers what was there before, which is the convention every machine and
6537    /// every language in this area uses. Half the names in the family ask for the value afterwards
6538    /// instead, and that is the answer and the operand put together again, which is arithmetic on
6539    /// two values already in registers rather than a second flavour of the instruction.
6540    ///
6541    /// The two lock names are here too. They are not read modify writes in the same sense: one is
6542    /// an exchange and the other is a store of a zero, and what makes them a pair is the ordering,
6543    /// which is the one place in the older family that is not sequential consistency.
6544    #[test]
6545    fn a_read_modify_write_is_one_instruction_and_the_arithmetic_a_name_asks_for() {
6546        let text = body("int f(int *p, int v) { return __atomic_fetch_add(p, v, 5); }\n");
6547        assert!(text.contains("%2 = atomic_rmw.i32 add %0, %1, align 4, seq_cst"), "{text}");
6548        assert!(text.contains("return %2"), "the value that was there: {text}");
6549
6550        let text = body("int f(int *p, int v) { return __atomic_add_fetch(p, v, 5); }\n");
6551        assert!(text.contains("%2 = atomic_rmw.i32 add %0, %1, align 4, seq_cst"), "{text}");
6552        assert!(text.contains("%3 = add %2, %1"), "and the value afterwards: {text}");
6553
6554        let text = body("int f(int *p, int v) { return __atomic_sub_fetch(p, v, 5); }\n");
6555        assert!(text.contains("%2 = atomic_rmw.i32 sub %0, %1, align 4, seq_cst"), "{text}");
6556        assert!(text.contains("%3 = sub %2, %1"), "{text}");
6557
6558        // The older family, which passes no ordering and is a full barrier.
6559        let text = body("int f(int *p, int v) { return __sync_fetch_and_sub(p, v); }\n");
6560        assert!(text.contains("%2 = atomic_rmw.i32 sub %0, %1, align 4, seq_cst"), "{text}");
6561
6562        // The exchange, and the older family's spelling of it, which is taking a lock and so is an
6563        // acquire rather than the full barrier the rest of that family is.
6564        let text = body("int f(int *p, int v) { return __atomic_exchange_n(p, v, 5); }\n");
6565        assert!(text.contains("%2 = atomic_rmw.i32 xchg %0, %1, align 4, seq_cst"), "{text}");
6566
6567        let text = body("int f(int *p, int v) { return __sync_lock_test_and_set(p, v); }\n");
6568        assert!(text.contains("%2 = atomic_rmw.i32 xchg %0, %1, align 4, acquire"), "{text}");
6569
6570        // Giving the lock back, which is one of the two names in the family that is handed no value
6571        // to put there, because what it puts there is a zero.
6572        let text = body("void f(int *p) { __sync_lock_release(p); }\n");
6573        assert!(text.contains("release"), "{text}");
6574        assert!(text.contains("%1 = iconst.i32 0"), "{text}");
6575
6576        // And with something after the pointer, which is the list of variables the call promises to
6577        // protect rather than a value to write. Reading it as a value would store whatever the
6578        // caller happened to name there, which is the one thing giving a lock back must not do.
6579        let text = body("void f(int *p, int guard) { __sync_lock_release(p, guard); }\n");
6580        assert!(text.contains("%2 = iconst.i32 0"), "{text}");
6581        assert!(text.contains("atomic_store %2 -> %0, align 4, release"), "{text}");
6582
6583        // The bitwise four, which look no different here from the arithmetic ones: what the machine
6584        // has an instruction for is a question further down and this level does not ask it.
6585        let text = body("int f(int *p, int v) { return __atomic_fetch_and(p, v, 5); }\n");
6586        assert!(text.contains("%2 = atomic_rmw.i32 and %0, %1, align 4, seq_cst"), "{text}");
6587
6588        let text = body("int f(int *p, int v) { return __sync_or_and_fetch(p, v); }\n");
6589        assert!(text.contains("%2 = atomic_rmw.i32 or %0, %1, align 4, seq_cst"), "{text}");
6590        assert!(text.contains("%3 = or %2, %1"), "and the value afterwards: {text}");
6591
6592        // The nand, which is the one of the six that is two operations. The flip is an exclusive or
6593        // against every bit set because the IR has no not and that is what one is.
6594        let text = body("int f(int *p, int v) { return __atomic_nand_fetch(p, v, 5); }\n");
6595        assert!(text.contains("%2 = atomic_rmw.i32 nand %0, %1, align 4, seq_cst"), "{text}");
6596        assert!(text.contains("%3 = and %2, %1"), "{text}");
6597        assert!(text.contains("%4 = iconst.i32 -1"), "{text}");
6598        assert!(text.contains("%5 = xor %3, %4"), "{text}");
6599    }
6600
6601    /// The four operations with no instruction on this machine are a loop around `lock cmpxchg`.
6602    ///
6603    /// The shape is the one every architecture manual writes out by hand: read the word, work out
6604    /// what should be there instead, put it back if nothing else got in first, and go round again
6605    /// when something did. What is checked is that the loop is there at every width, that the
6606    /// operation is inside it, and that no `xchg` or `xadd` got used for something neither of them
6607    /// does.
6608    ///
6609    /// gcc 16.2.0 writes the same loop for the same functions, down to which register holds the
6610    /// value that was read.
6611    #[test]
6612    fn a_bitwise_read_modify_write_is_a_loop_around_the_compare_and_exchange() {
6613        let widths = [("char", "b", "%dl"), ("short", "w", "%dx"), ("int", "l", "%edx")];
6614        for (ty, suffix, reg) in widths {
6615            for (name, call, insn) in [
6616                ("and", "__atomic_fetch_and(p, v, 5)", "and"),
6617                ("or", "__sync_fetch_and_or(p, v)", "or"),
6618                ("xor", "__atomic_xor_fetch(p, v, 5)", "xor"),
6619            ] {
6620                let source = format!("{ty} f({ty} *p, {ty} v) {{ return {call}; }}\n");
6621                let text = asm(&source);
6622                assert!(text.contains("\tlock\n"), "{ty} {name}: {text}");
6623                assert!(
6624                    text.contains(&format!("cmpxchg{suffix}\t{reg}, (%rdi)")),
6625                    "{ty} {name}: {text}"
6626                );
6627                assert!(text.contains(&format!("{insn}{suffix}\t")), "{ty} {name}: {text}");
6628                // The tab matters on the second of these, since `cmpxchg` ends in the other name.
6629                assert!(!text.contains("\txadd"), "{ty} {name} is not an add: {text}");
6630                assert!(!text.contains("\txchg"), "{ty} {name} is not an exchange: {text}");
6631            }
6632        }
6633        let source = "long f(long *p, long v) { return __atomic_fetch_or(p, v, 5); }\n";
6634        assert!(asm(source).contains("cmpxchgq\t%rdx, (%rdi)"), "{}", asm(source));
6635
6636        // The nand, which puts two instructions inside the loop rather than one. The flip is an
6637        // exclusive or against every bit set in the IR and the folder turns that into the `not` the
6638        // machine has, which is what gcc writes here too.
6639        let text = asm("int f(int *p, int v) { return __sync_fetch_and_nand(p, v); }\n");
6640        assert!(text.contains("cmpxchgl\t"), "{text}");
6641        assert!(text.contains("andl\t"), "{text}");
6642        assert!(text.contains("notl\t"), "{text}");
6643    }
6644
6645    /// The three names that pass a value through a pointer are the same access and one plain one.
6646    ///
6647    /// They exist for an object too big to come back in a register, and the front end takes them at
6648    /// their word rather than folding them into the `_n` spellings, because the extra access is real:
6649    /// the caller handed over somewhere to read from or write into and that is where the value has
6650    /// to come from or go. Both of those accesses are plain. The object at the end of the caller's
6651    /// pointer is the caller's own and no other thread has its address, which is what the whole
6652    /// shape is for.
6653    #[test]
6654    fn an_access_through_a_second_pointer_is_the_same_access_and_one_more() {
6655        let text = body("void f(int *p, int *r) { __atomic_load(p, r, 5); }\n");
6656        assert!(text.contains("%2 = atomic_load.i32 %0, align 4, seq_cst"), "{text}");
6657        assert!(text.contains("store %2 -> %1, align 4"), "and out through the place: {text}");
6658
6659        let text = body("void f(int *p, int *v) { __atomic_store(p, v, 3); }\n");
6660        assert!(text.contains("%2 = load.i32 %1, align 4"), "in through the place: {text}");
6661        assert!(text.contains("atomic_store %2 -> %0, align 4, release"), "{text}");
6662
6663        // The exchange, which reads through one pointer and writes through another and is the same
6664        // instruction in between as the spelling that takes and answers values.
6665        let text = body("void f(int *p, int *v, int *r) { __atomic_exchange(p, v, r, 5); }\n");
6666        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
6667        assert!(text.contains("%4 = atomic_rmw.i32 xchg %0, %3, align 4, seq_cst"), "{text}");
6668        assert!(text.contains("store %4 -> %2, align 4"), "{text}");
6669    }
6670
6671    /// The flag pair is an exchange of one byte and a store of a zero over the same byte.
6672    ///
6673    /// One byte whatever the pointer was written as, which is the standard's reading rather than a
6674    /// liberty: the object is an `atomic_flag`, there is no other way to read or write one, so the
6675    /// type the pointer carries says nothing about the access and the width is the implementation's
6676    /// to fix. gcc 16.2.0 writes `xchgb` here through an `int *` too.
6677    ///
6678    /// The answer is a comparison against zero rather than the byte itself, because the type of the
6679    /// call is `_Bool` and a byte that is neither zero nor one is not one. gcc answers the raw byte,
6680    /// and the two agree wherever the flag is only ever touched through this pair.
6681    #[test]
6682    fn a_flag_is_an_exchange_of_one_byte_and_a_store_of_a_zero_over_the_same_byte() {
6683        for pointer in ["char", "int", "void"] {
6684            let source = format!("int f({pointer} *p) {{ return __atomic_test_and_set(p, 5); }}\n");
6685            let text = body(&source);
6686            assert!(text.contains("%1 = iconst.i8 1"), "{pointer}: {text}");
6687            assert!(
6688                text.contains("%2 = atomic_rmw.i8 xchg %0, %1, align 1, seq_cst"),
6689                "{pointer}: {text}"
6690            );
6691            assert!(text.contains("%4 = icmp ne %2, %3"), "{pointer}: {text}");
6692
6693            let source = format!("void f({pointer} *p) {{ __atomic_clear(p, 3); }}\n");
6694            let text = body(&source);
6695            assert!(text.contains("atomic_store %2 -> %0, align 1, release"), "{pointer}: {text}");
6696        }
6697
6698        // And on this machine, where the exchange carries no `lock` because one with memory locks
6699        // the bus whether it was asked to or not. Both lines are what gcc 16.2.0 writes.
6700        let text = asm("int f(int *p) { return __atomic_test_and_set(p, 5); }\n");
6701        assert!(text.contains("xchgb\t%al, (%rdi)"), "{text}");
6702        assert!(text.contains("setne\t"), "{text}");
6703    }
6704
6705    /// On this machine it is `xchg` where the machine has an exchange and `lock xadd` where it has
6706    /// an add, at the width of the object.
6707    ///
6708    /// The exchange carries no prefix and the add carries one, which is the machine rather than an
6709    /// oversight: an exchange with memory locks the bus whether it is asked to or not. Both are
6710    /// therefore full barriers whatever ordering the program wrote, so no ordering costs an
6711    /// `mfence` beside them. Every line below is what gcc 16.2.0 writes for the same function.
6712    #[test]
6713    fn a_read_modify_write_is_an_exchange_or_a_locked_add_at_the_width_of_the_object() {
6714        let widths = [("char", "b", "%sil"), ("short", "w", "%si"), ("int", "l", "%esi")];
6715        for (ty, suffix, reg) in widths {
6716            let source =
6717                format!("{ty} f({ty} *p, {ty} v) {{ return __atomic_fetch_add(p, v, 5); }}\n");
6718            let text = asm(&source);
6719            assert!(text.contains("\tlock\n"), "{ty}: {text}");
6720            assert!(text.contains(&format!("xadd{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
6721
6722            let source =
6723                format!("{ty} f({ty} *p, {ty} v) {{ return __atomic_exchange_n(p, v, 5); }}\n");
6724            let text = asm(&source);
6725            assert!(text.contains(&format!("xchg{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
6726            assert!(!text.contains("\tlock\n"), "an exchange is locked already: {ty}: {text}");
6727        }
6728        let source = "long f(long *p, long v) { return __atomic_fetch_add(p, v, 5); }\n";
6729        assert!(asm(source).contains("xaddq\t%rsi, (%rdi)"), "{}", asm(source));
6730
6731        // A subtraction is the same instruction over the negated operand, which is right at every
6732        // width because the machine's arithmetic wraps.
6733        let source = "int f(int *p, int v) { return __atomic_fetch_sub(p, v, 5); }\n";
6734        let text = asm(source);
6735        assert!(text.contains("negl\t"), "{text}");
6736        assert!(text.contains("xaddl\t"), "{text}");
6737
6738        // The ordering changes nothing, for the reason it changes nothing for a compare and
6739        // exchange: a locked instruction on this machine orders everything whatever it was asked.
6740        for order in ["0", "2", "3", "4", "5"] {
6741            let source =
6742                format!("int f(int *p, int v) {{ return __atomic_fetch_add(p, v, {order}); }}\n");
6743            let text = asm(&source);
6744            assert!(text.contains("xaddl\t"), "{order}: {text}");
6745            assert!(!text.contains("mfence"), "{order} needs no barrier here: {text}");
6746        }
6747
6748        // And the lock pair, which is the exchange and a store of a zero. Neither is a barrier
6749        // instruction: the exchange is one already and the store is a release, which this machine
6750        // gives away.
6751        let text = asm("int f(int *p, int v) { return __sync_lock_test_and_set(p, v); }\n");
6752        assert!(text.contains("xchgl\t%esi, (%rdi)"), "{text}");
6753        // The zero goes through a register on the way, which is where every constant this
6754        // compiler stores goes: gcc writes the one instruction because it has a store that takes an
6755        // immediate and no rule here does. That is a rule this rule set is missing rather than
6756        // anything about the builtin, and it is the same two instructions a plain `*p = 0` makes.
6757        // The register gets its zero from an exclusive or with itself rather than from a move of a
6758        // zero, which is `rucc_codegen::shorten` writing the shorter of the two spellings.
6759        let text = asm("void f(int *p) { __sync_lock_release(p); }\n");
6760        assert!(text.contains("xorl\t%eax, %eax"), "{text}");
6761        assert!(text.contains("movl\t%eax, (%rdi)"), "{text}");
6762        assert!(!text.contains("mfence"), "a release store needs no barrier here: {text}");
6763    }
6764
6765    /// The two lock free questions are numbers in the program rather than calls to anything.
6766    ///
6767    /// Both answer from the size, which has to be a power of two no wider than the widest access
6768    /// this compiler writes, and from what the pointer says about the alignment. Sixteen bytes is
6769    /// no here and is no in gcc without `-mcx16`, because `cmpxchg16b` is not in the baseline and
6770    /// nothing here writes it. Three bytes is no because there is no three byte access at all.
6771    ///
6772    /// The whole point of both names is that the answer is available before the program runs, so
6773    /// what is checked is that a `mov` of a constant is the whole function and that no call was
6774    /// left behind. A call would be to `__atomic_is_lock_free` in libatomic, which is not a library
6775    /// this links against.
6776    #[test]
6777    fn the_lock_free_questions_are_answered_as_constants() {
6778        for size in ["1", "2", "4", "8"] {
6779            let source =
6780                format!("int f(void) {{ return __atomic_always_lock_free({size}, 0); }}\n");
6781            let text = asm(&source);
6782            assert!(text.contains("movb\t$1, %al"), "{size} bytes is lock free: {text}");
6783            assert!(!text.contains("call"), "and is not a call: {text}");
6784        }
6785        for size in ["3", "16", "sizeof(long double)"] {
6786            let source = format!("int f(void) {{ return __atomic_is_lock_free({size}, 0); }}\n");
6787            let text = asm(&source);
6788            assert!(text.contains("movb\t$0, %al"), "{size} bytes is not: {text}");
6789            assert!(!text.contains("call"), "and is not a call either: {text}");
6790        }
6791
6792        // A size the compiler cannot work out, which is no rather than a refusal, and an object
6793        // whose type is aligned under the size asked about, which is the whole of what the second
6794        // argument is for.
6795        let text = asm("int f(int n) { return __atomic_is_lock_free(n, 0); }\n");
6796        assert!(text.contains("movb\t$0, %al"), "a size nobody knows is not lock free: {text}");
6797        let text = asm("int f(int *p) { return __atomic_always_lock_free(8, p); }\n");
6798        assert!(text.contains("movb\t$0, %al"), "eight bytes at four is not: {text}");
6799        let text = asm("int f(long *p) { return __atomic_always_lock_free(8, p); }\n");
6800        assert!(text.contains("movb\t$1, %al"), "and at eight it is: {text}");
6801    }
6802
6803    /// A memory order an operation cannot carry is read as the strongest one, and said so about.
6804    ///
6805    /// There are three ways the number is not one the operation can take: it is not a constant at
6806    /// all, it is not one of the six the headers define, or it is one of them and means nothing for
6807    /// this operation, which is a release load or an acquire store. All three become sequential
6808    /// consistency, which is stronger than anything the program could have meant, so a program that
6809    /// wrote nonsense gets a correct answer rather than a fast one. gcc does the same.
6810    ///
6811    /// The last two also warn, because the number was written down and is wrong. The first does
6812    /// not: gcc takes a computed order, and so does the C11 spelling, so a warning there would fire
6813    /// on correct programs.
6814    #[test]
6815    fn a_memory_order_an_operation_cannot_carry_is_read_as_the_strongest() {
6816        let mut opts = options();
6817        opts.emit = EmitKind::Ir;
6818
6819        let acquire_store = run(&opts, "void f(int *p, int v) { __atomic_store_n(p, v, 2); }\n");
6820        assert!(acquire_store.text().contains("seq_cst"), "{:?}", acquire_store.text());
6821        assert!(acquire_store.messages[0].contains("[W0333]"), "{:?}", acquire_store.messages);
6822
6823        let nonsense = run(&opts, "int f(int *p) { return __atomic_load_n(p, 99); }\n");
6824        assert!(nonsense.text().contains("seq_cst"), "{:?}", nonsense.text());
6825        assert!(nonsense.messages[0].contains("[W0333]"), "{:?}", nonsense.messages);
6826
6827        let computed = run(&opts, "int f(int *p, int n) { return __atomic_load_n(p, n); }\n");
6828        assert!(computed.text().contains("seq_cst"), "{:?}", computed.text());
6829        assert_eq!(computed.messages, Vec::<String>::new(), "a computed order is not a mistake");
6830    }
6831
6832    /// A conversion between a float and the widest unsigned integer, which the machine has not got.
6833    ///
6834    /// Every other conversion between a float and an integer is the signed one at some width with a
6835    /// widening in front or a narrowing behind. These two are not, because there is no signed width
6836    /// that holds every value of an unsigned sixty four bit integer, so each is the signed
6837    /// conversion with arithmetic around it that brings the value into range and puts it back.
6838    ///
6839    /// What is checked here is that the conversion happens at all and that it happens without a
6840    /// branch. gcc writes a branch for both; this writes the choice as a mask, because every rewrite
6841    /// in that pass stays inside the block it started in. The arithmetic itself is checked in
6842    /// `rucc-codegen`, where it can be run against the answer rather than read in the assembly.
6843    #[test]
6844    fn a_conversion_between_a_float_and_the_widest_unsigned_integer_is_written_without_a_branch() {
6845        let text = asm("double f(unsigned long long x) { return (double)x; }\n");
6846        assert!(text.contains("cvtsi2sdq"), "the signed conversion is what runs: {text}");
6847        assert!(text.contains("shrq"), "with the value halved first: {text}");
6848        assert!(text.contains("addsd"), "and doubled after: {text}");
6849        assert!(!text.contains("\tj"), "and no branch anywhere: {text}");
6850
6851        let text = asm("unsigned long long f(double d) { return (unsigned long long)d; }\n");
6852        assert!(text.contains("cvttsd2siq"), "the signed conversion is what runs: {text}");
6853        assert!(text.contains("subsd"), "with half the range taken off first: {text}");
6854        assert!(text.contains("shlq\t$63"), "and the top bit put back: {text}");
6855        assert!(!text.contains("\tj"), "and no branch anywhere: {text}");
6856    }
6857
6858    /// The plain names are the library's only where nothing else has taken them.
6859    ///
6860    /// Four ways a program says it means something else. A `static` definition is its own
6861    /// function and the name outside the file is somebody else's. A declaration of another type
6862    /// is another function. `-fno-builtin` and `-fno-builtin-<name>` say so outright, and
6863    /// `-ffreestanding` says there is no C library for the name to be the name of. Every one of
6864    /// these was measured against gcc 16.2.0, which calls the program's function in all of them.
6865    ///
6866    /// The `__builtin_` spelling goes on meaning the library's function through all of it, which
6867    /// is what the prefix is for and what lets a freestanding build reach one deliberately.
6868    #[test]
6869    fn a_plain_name_the_program_took_is_the_programs_own_function() {
6870        let taken = concat!(
6871            "static long long llabs(long long b) { return 7; }\n",
6872            "long long f(long long x) { return llabs(x); }\n",
6873        );
6874        assert!(ir(taken).contains("call @llabs"), "a static definition is the program's own");
6875
6876        let retyped = concat!("int llabs(int b);\n", "int f(int x) { return llabs(x); }\n",);
6877        assert!(ir(retyped).contains("call @llabs"), "another type is another function");
6878
6879        let plain = concat!(
6880            "long long llabs(long long b);\n",
6881            "long long f(long long x) { return llabs(x); }\n",
6882        );
6883        let mut opts = options();
6884        opts.emit = EmitKind::Ir;
6885        assert!(!run(&opts, plain).text().contains("call @llabs"), "the library's by default");
6886
6887        opts.builtins = false;
6888        assert!(run(&opts, plain).text().contains("call @llabs"), "-fno-builtin");
6889
6890        opts.builtins = true;
6891        opts.no_builtin = vec!["llabs".to_owned()];
6892        assert!(run(&opts, plain).text().contains("call @llabs"), "-fno-builtin-llabs");
6893        let one = "long labs(long b);\nlong f(long x) { return labs(x); }\n";
6894        assert!(!run(&opts, one).text().contains("call @labs"), "one name and not the family");
6895
6896        // `-ffreestanding` reaches the front end as the same answer, which is what the driver
6897        // does with it in `compile`, and the prefixed spelling is untouched by any of it.
6898        opts.no_builtin = Vec::new();
6899        opts.builtins = false;
6900        let prefixed = "long long f(long long x) { return __builtin_llabs(x); }\n";
6901        assert!(!run(&opts, prefixed).text().contains("call @llabs"), "the prefix is a promise");
6902    }
6903
6904    /// The hint builtins are their first argument, and nothing is left of the hint.
6905    ///
6906    /// Which way a branch is expected to go is the whole of what they say, and there is nothing
6907    /// here that reads a branch weight yet, so what reaches the IR is the value and the hint is
6908    /// gone. The one thing the prototype has to keep doing is converting: gcc gives both of them
6909    /// a `long` result, so `sizeof(__builtin_expect((char)1, 1))` is eight and a narrower argument
6910    /// widens before it is answered with.
6911    ///
6912    /// Whether a side effect in the hint happens depends on the first argument, which is gcc's
6913    /// answer rather than a rule anybody designed. A constant first argument folds the whole call
6914    /// where it is written and the hint goes with it, and a first argument that is not a constant
6915    /// leaves the hint standing. Both halves are below and both were measured on gcc 16.2.0.
6916    #[test]
6917    fn the_hint_builtins_are_their_first_argument_and_the_hint_leaves_no_trace() {
6918        let text = ir(concat!(
6919            "long a = __builtin_expect(7, 1);\n",
6920            "long b = __builtin_expect_with_probability(9, 1, 0.9);\n",
6921            "unsigned long c = sizeof(__builtin_expect((char)1, 1));\n",
6922        ));
6923        assert!(text.contains("global @a : i64 = 7,"), "{text}");
6924        assert!(text.contains("global @b : i64 = 9,"), "{text}");
6925        assert!(text.contains("global @c : i64 = 8,"), "{text}");
6926        assert!(!text.contains("__builtin_expect"), "it is not a call to anything:\n{text}");
6927
6928        // A narrower argument is widened by the prototype before it is handed back, and it is
6929        // widened with its sign, since the parameter is a signed `long`.
6930        let text = body("long f(char c) { return __builtin_expect(c, 1); }\n");
6931        assert!(text.contains("sext"), "{text}");
6932
6933        // The first argument is a constant, so the second is not evaluated and `i` is still zero,
6934        // and neither is the third. What is left of each statement is the first argument widened,
6935        // which nothing reads and which the first pass that looks for dead code will take out.
6936        let one = "block0:\n    %0 = iconst.i32 0\n    %1 = iconst.i32 1\n    %2 = sext.i64 %1\n    return %0\n";
6937        assert_eq!(body("int f(void) { int i = 0; __builtin_expect(1, i++); return i; }\n"), one);
6938        let source = "int g(void) { int i = 0; __builtin_expect_with_probability(1, i++, 0.5); return i; }\n";
6939        assert_eq!(body(source), one);
6940
6941        // The first argument is not a constant, so the hint runs and `i` comes back one. There is
6942        // an increment in the body and the value it returns is the load after it, which is what
6943        // gcc gives for the same program, and the whole of tamnd/rucc#584 is that this used to
6944        // come out the same as the pair above.
6945        let kept = body("int f(int n) { int i = 0; __builtin_expect(n, i++); return i; }\n");
6946        assert!(kept.contains("add.nsw"), "the hint still runs: {kept}");
6947        assert!(kept.ends_with("return %3\n"), "and the answer is what it left behind: {kept}");
6948        let both = "int g(int n) { int i = 0; __builtin_expect_with_probability(n, i++, 0.5); return i; }\n";
6949        assert!(body(both).contains("add.nsw"), "and so does the one with three arguments");
6950    }
6951
6952    /// A point control does not arrive at, in both of the ways the compiler has one.
6953    ///
6954    /// `__builtin_unreachable()` is the promise written down, and a function whose body can run
6955    /// off the bottom is the walk arriving at the same place on its own. Neither writes an
6956    /// instruction, which is what gcc 16.2.0 does at `-O0`: it emits the epilogue and the `ret`
6957    /// for both of the functions below and nothing else, and the two of them come out byte for
6958    /// byte the same there.
6959    ///
6960    /// The `ret` is the part worth holding on to. It is not there because anything runs it, it is
6961    /// there because a function whose last instruction is not a return is one that falls into
6962    /// whatever the assembler puts after it.
6963    #[test]
6964    fn a_promise_that_control_does_not_arrive_writes_no_instruction() {
6965        let promised = "int f(int x) { if (x) return 1; __builtin_unreachable(); }\n";
6966        let text = ir(promised);
6967        assert!(text.contains("    unreachable_hint\n"), "{text}");
6968        assert!(!text.contains("call"), "it is not a call to anything:\n{text}");
6969
6970        // The statement after it is still lowered. Continuing to translate a path the program
6971        // promised is dead is one of the things a compiler may do with undefined behaviour, and
6972        // it is the one that keeps a program built at `-O0` behaving the way it was watched to.
6973        let after = body("int g(int x) { __builtin_unreachable(); return x; }\n");
6974        assert!(after.contains("return"), "{after}");
6975
6976        // Both functions are the same instructions, because the hint writes none of them and the
6977        // terminator underneath it writes none either.
6978        let text = asm(promised);
6979        let mine = text.split_once("\nf:\n").expect("a definition").1;
6980        let mine = mine.split_once("\t.size").expect("a definition").0;
6981        let plain = asm("int f(int x) { if (x) return 1; }\n");
6982        let plain = plain.split_once("\nf:\n").expect("a definition").1;
6983        let plain = plain.split_once("\t.size").expect("a definition").0;
6984        assert_eq!(mine, plain);
6985        // The last instruction, rather than the last line, because the unwind record is closed
6986        // after it and a directive is not something the machine runs.
6987        let last = mine.lines().rfind(|line| !line.trim_start().starts_with('.'));
6988        assert_eq!(last.map(str::trim), Some("ret"), "{mine}");
6989        assert!(!mine.contains("ud2"), "{mine}");
6990    }
6991
6992    /// The two names stay apart, which is what having both of them is for.
6993    ///
6994    /// The one the program wrote is what the call is checked against and what a diagnostic about
6995    /// it says, and the one the library defines is what the call ends up carrying. A compiler
6996    /// that kept only the second would report this against `abort`, which is a function the
6997    /// program never mentions.
6998    #[test]
6999    fn a_library_builtin_is_diagnosed_under_the_name_the_program_wrote() {
7000        let mut opts = options();
7001        opts.emit = EmitKind::Ir;
7002        let messages = run(&opts, "void f(void) { __builtin_abort(1); }\n").messages;
7003        assert!(
7004            messages.iter().any(|m| m.contains("__builtin_abort")),
7005            "expected the written name in {messages:?}"
7006        );
7007    }
7008
7009    /// A builtin nothing lowers is refused where it is written, rather than at the link.
7010    ///
7011    /// One name is left, which is the last of the atomic family that is refused and is also the
7012    /// one whose prefix is not `__builtin_`; its older half has nothing left in it at all, and so
7013    /// does the half of the family that carries a prototype. What the message has to carry is the
7014    /// name, because the whole complaint about the link error this replaces is that the name in it
7015    /// was one the compiler chose.
7016    #[test]
7017    fn a_builtin_nothing_lowers_is_refused_by_name() {
7018        let mut opts = options();
7019        opts.emit = EmitKind::Ir;
7020        let builtin = "__atomic_signal_fence";
7021        let source = format!("int counter;\nint f(void) {{ return ({builtin}(5), 0); }}\n");
7022        let messages = run(&opts, &source).messages;
7023        let named = messages.iter().any(|m| m.contains(builtin) && m.contains("E0686"));
7024        assert!(named, "expected {builtin} to be refused by name in {messages:?}");
7025    }
7026
7027    /// The refusal is about a call and not about the name, so a program that defines the name
7028    /// itself gets the function it wrote.
7029    ///
7030    /// That is not the reason the refusal exists, but a definition in front of us is a definition
7031    /// and the call to it links. It works here because the name is one with no prototype and no
7032    /// meaning the front end knows, which is what is left once the rest of the family is
7033    /// implemented: a `__builtin_` name the front end does answer is answered whatever the program
7034    /// declares, the way gcc answers one.
7035    #[test]
7036    fn what_is_refused_is_the_call_and_not_the_name() {
7037        let text = ir(concat!(
7038            "void __atomic_signal_fence(int order) { (void)order; }\n",
7039            "void f(void) { __atomic_signal_fence(5); }\n",
7040        ));
7041        assert!(text.contains("call @__atomic_signal_fence"), "{text}");
7042    }
7043
7044    /// How many bytes are behind an address is read off the layout, for every shape the walk
7045    /// covers.
7046    ///
7047    /// This is what `_FORTIFY_SOURCE` runs on, so the numbers matter one at a time rather than in
7048    /// aggregate: a size too small turns a correct copy into an abort, and a size too large turns
7049    /// a checked copy back into an unchecked one. Every answer here was measured against gcc
7050    /// 16.2.0 first. They are written as initializers so that each one is a constant in the
7051    /// output and the test reads as the table it is.
7052    #[test]
7053    fn the_object_size_of_an_address_is_what_the_layout_leaves_in_front_of_it() {
7054        let text = ir(concat!(
7055            "struct S { char a[8]; int n; char b[12]; };\n",
7056            "char g[32];\n",
7057            "struct S gs;\n",
7058            "unsigned long whole = __builtin_object_size(g, 0);\n",
7059            "unsigned long moved = __builtin_object_size(g + 4, 0);\n",
7060            "unsigned long back = __builtin_object_size(g + 30 - 2, 0);\n",
7061            "unsigned long outer = __builtin_object_size(gs.a, 0);\n",
7062            "unsigned long inner = __builtin_object_size(gs.a, 1);\n",
7063            "unsigned long scalar = __builtin_object_size(&gs.n, 1);\n",
7064            "unsigned long after = __builtin_object_size(&gs.n, 0);\n",
7065            "unsigned long into = __builtin_object_size(&gs.b[2], 1);\n",
7066            "unsigned long text = __builtin_object_size(\"hello\", 0);\n",
7067            "unsigned long dyn = __builtin_dynamic_object_size(gs.b, 1);\n",
7068        ));
7069        for (name, size) in [
7070            ("whole", 32),
7071            ("moved", 28),
7072            ("back", 4),
7073            ("outer", 24),
7074            ("inner", 8),
7075            ("scalar", 4),
7076            ("after", 16),
7077            ("into", 10),
7078            ("text", 6),
7079            ("dyn", 12),
7080        ] {
7081            let said = format!("global @{name} : i64 = {size},");
7082            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
7083        }
7084    }
7085
7086    /// A local is as knowable as a global, which is the whole point of asking on the way into a
7087    /// copy.
7088    ///
7089    /// A fortified header expands around the destination the caller wrote, and the destination a
7090    /// program most wants checked is the buffer on its own stack. Nothing in the answer depends on
7091    /// storage duration, unlike in a constant expression, where the address of a local is exactly
7092    /// what is not allowed.
7093    #[test]
7094    fn the_object_behind_an_address_can_be_one_with_automatic_storage() {
7095        let text = body(concat!(
7096            "struct S { char a[8]; int n; char b[12]; };\n",
7097            "unsigned long f(void) {\n",
7098            "  char loc[20];\n",
7099            "  struct S ls;\n",
7100            "  return __builtin_object_size(loc + 3, 0) + __builtin_object_size(ls.b + 2, 1);\n",
7101            "}\n",
7102        ));
7103        assert!(text.contains("iconst.i64 17"), "twenty bytes with three used: {text}");
7104        assert!(text.contains("iconst.i64 10"), "twelve bytes with two used: {text}");
7105    }
7106
7107    /// An address whose object the walk cannot see answers at whichever end of the range the kind
7108    /// asks for.
7109    ///
7110    /// The two bits are a question and the answer has to fit it. A kind wanting the largest object
7111    /// the address could be in has to name a size nothing is bigger than, and a kind wanting the
7112    /// smallest has to name a size nothing is smaller than, so the unknown answers are all ones
7113    /// and zero. That pair is what a fortified header compares against to decide whether to check
7114    /// at all, and getting either of them the wrong way round turns every unknown copy into an
7115    /// abort.
7116    #[test]
7117    fn an_address_with_no_object_in_sight_answers_at_the_end_of_the_range_its_kind_asks_for() {
7118        let text = ir(concat!(
7119            "struct T { int n; char f[]; };\n",
7120            "extern char *p;\n",
7121            "extern struct T *t;\n",
7122            "unsigned long largest = __builtin_object_size(p, 0);\n",
7123            "unsigned long nearest = __builtin_object_size(p, 1);\n",
7124            "unsigned long least = __builtin_object_size(p, 2);\n",
7125            "unsigned long tight = __builtin_object_size(p, 3);\n",
7126            "unsigned long flex = __builtin_object_size(t->f, 1);\n",
7127            "int says = __builtin_object_size(p, 0) == (unsigned long)-1;\n",
7128        ));
7129        for name in ["largest", "nearest", "flex"] {
7130            // All ones, printed as the signed rendering of the sixty four bits it is held in.
7131            // `says` is what pins the pattern itself, since it is the comparison a fortified
7132            // header writes and it folds only if every bit is set.
7133            let said = format!("global @{name} : i64 = -1,");
7134            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
7135        }
7136        for name in ["least", "tight"] {
7137            let said = format!("global @{name} : i64 = 0,");
7138            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
7139        }
7140        assert!(text.contains("global @says : i32 = 1,"), "{text}");
7141    }
7142
7143    /// The address is not evaluated, which is the rule `sizeof` follows and for the same reason.
7144    ///
7145    /// What the builtin reads is the shape of the expression rather than the value it would
7146    /// produce, so there is nothing to run. It matters because a fortified header writes the
7147    /// destination twice, once into the copy and once into the size, and a program whose
7148    /// destination is `*next()` would advance twice if this evaluated.
7149    #[test]
7150    fn the_address_an_object_size_is_asked_about_is_not_evaluated() {
7151        let text = body(concat!(
7152            "extern char *side(void);\n",
7153            "unsigned long f(void) { return __builtin_object_size(side(), 0); }\n",
7154        ));
7155        assert!(!text.contains("call"), "nothing is called: {text}");
7156    }
7157
7158    /// The kind has to be a constant in range, because it says which of four questions was asked.
7159    ///
7160    /// A number that is not known until the program runs decides nothing, and one outside the two
7161    /// bits names no question at all. gcc refuses both in one sentence and so does this.
7162    #[test]
7163    fn a_kind_that_is_not_one_of_the_four_is_refused() {
7164        for source in [
7165            "extern char *p;\nextern int k;\nunsigned long f(void) ".to_owned()
7166                + "{ return __builtin_object_size(p, k); }\n",
7167            "extern char *p;\nunsigned long f(void) { return __builtin_object_size(p, 4); }\n"
7168                .to_owned(),
7169            "extern char *p;\nunsigned long f(void) ".to_owned()
7170                + "{ return __builtin_dynamic_object_size(p, -1); }\n",
7171        ] {
7172            let messages = errors(&source);
7173            let named = messages.iter().any(|m| m.contains("E0709") && m.contains("0 to 3"));
7174            assert!(named, "expected a complaint about the kind in {messages:?}");
7175        }
7176    }
7177
7178    /// The pair that saves a place in a function and comes back to it, which is not a call.
7179    ///
7180    /// What the IR has to show is one instruction each and no call to anything: there is no
7181    /// function of either name for a call to reach, and a program that got one would fail to link.
7182    /// The save answers an `int`, which is the value that says how control got there.
7183    #[test]
7184    fn the_pair_that_saves_a_place_lowers_to_the_two_markers() {
7185        let text = ir(concat!(
7186            "void *buf[5];\n",
7187            "int f(void) {\n",
7188            "  if (__builtin_setjmp(buf)) return 2;\n",
7189            "  return 1;\n",
7190            "}\n",
7191            "void g(void) { __builtin_longjmp(buf, 1); }\n",
7192        ));
7193        assert!(text.contains("= setjmp_marker.i32 %0\n"), "the save answers a value: {text}");
7194        assert!(text.contains("    longjmp_marker %0\n"), "the restore answers nothing: {text}");
7195        assert!(!text.contains("call @"), "neither of them is a call: {text}");
7196    }
7197
7198    /// Every local of a function that saves a place lives in the frame, and not in a value.
7199    ///
7200    /// The edge a restore travels is not an edge of the graph, so a local the SSA construction
7201    /// renamed would answer the write that reached the read along the edges there are rather than
7202    /// the write that last ran. The second function here is the same code without the save, where
7203    /// the local is a value and there is no slot at all, which is what makes the first one a rule
7204    /// about the save and not about the shape of the code.
7205    #[test]
7206    fn a_local_of_a_function_that_saves_a_place_gets_a_slot() {
7207        let text = ir(concat!(
7208            "void *buf[5];\n",
7209            "int f(int x) { int a = 0; if (__builtin_setjmp(buf)) return a; a = 1; return x; }\n",
7210            "int g(int x) { int a = 0; if (x) return a; a = 1; return x; }\n",
7211        ));
7212        let (saves, plain) = text.split_once("func @g").expect("both functions");
7213        assert_eq!(saves.matches("= alloca").count(), 2, "the parameter and the local: {text}");
7214        assert!(saves.contains("store %9 -> %2"), "the local is written through: {text}");
7215        assert!(!plain.contains("alloca"), "nothing in the plain one needs a slot: {text}");
7216    }
7217
7218    /// A value set before a library `sigsetjmp` and read after the `siglongjmp` keeps a spill slot
7219    /// of its own.
7220    ///
7221    /// The shape of Postgres's `PG_TRY`. Five values are live across the call, one more than the
7222    /// callee saved registers left over, so some go to the stack. They are dead on the arm that
7223    /// runs first, and before this that arm's own values were given the same slots, so the arm the
7224    /// jump lands in read them back. Every slot is written by one value, so no offset is stored to
7225    /// twice.
7226    #[test]
7227    fn a_value_live_across_sigsetjmp_keeps_its_spill_slot() {
7228        each_spill_slot_written_once(&across("int __sigsetjmp(sigjmp_buf, int);\n", "__sigsetjmp"));
7229    }
7230
7231    /// The same shape through a function with a name nobody knows, which only the attribute says
7232    /// comes back twice. tamnd/rucc#2012.
7233    #[test]
7234    fn a_value_live_across_a_returns_twice_call_keeps_its_spill_slot() {
7235        let declared = "int save_here(sigjmp_buf, int) __attribute__((__returns_twice__));\n";
7236        each_spill_slot_written_once(&across(declared, "save_here"));
7237    }
7238
7239    /// A value set before `setjmp` and read after the `longjmp` keeps its slot to itself, at `-O0`
7240    /// and at `-O2`.
7241    ///
7242    /// The reduction in tamnd/rucc#2035, which glibc's `<setjmp.h>` turns into a call to
7243    /// `_setjmp`. `v` is dead on the arm that runs first, so that arm's own values were given its
7244    /// slot and the handler printed `v + 1`. The handler reads `v` from a slot, and nothing between
7245    /// the `setjmp` and the call that jumps back writes that slot.
7246    #[test]
7247    fn a_value_live_across_setjmp_shares_its_slot_with_nothing_in_the_first_arm() {
7248        let source = concat!(
7249            "typedef long jmp_buf[25];\n",
7250            "int _setjmp(jmp_buf);\n",
7251            "void longjmp(jmp_buf, int) __attribute__((noreturn));\n",
7252            "int printf(const char *, ...);\n",
7253            "static jmp_buf *stack;\n",
7254            "static volatile long long sink;\n",
7255            "static int cells[64];\n",
7256            "static volatile int seed_in = 3;\n",
7257            "static void work(void) { longjmp(*stack, 1); }\n",
7258            "int main(void) {\n",
7259            "  int seed = seed_in;\n",
7260            "  int v = seed * 2;\n",
7261            "  jmp_buf buf;\n",
7262            "  if (_setjmp(buf) == 0) {\n",
7263            "    stack = &buf;\n",
7264            "    int *p = &cells[seed + 3];\n",
7265            "    int a = v + 8;\n",
7266            "    int b = seed * 2005;\n",
7267            "    int *q = &cells[v + 1];\n",
7268            "    work();\n",
7269            "    sink = *p + a + b + *q;\n",
7270            "  } else {\n",
7271            "    printf(\"%d\\n\", v);\n",
7272            "  }\n",
7273            "  return 0;\n",
7274            "}\n",
7275        );
7276        for level in [rucc_session::OptLevel::O0, rucc_session::OptLevel::O2] {
7277            let mut opts = options();
7278            opts.emit = EmitKind::Asm;
7279            opts.opt_level = level;
7280            let result = run(&opts, source);
7281            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
7282            let text = result.text();
7283            let body = text.split_once("\nmain:\n").expect("the function").1;
7284            let lines: Vec<&str> = body.lines().map(str::trim).collect();
7285            let save = lines.iter().position(|l| *l == "call\t_setjmp").expect("the save");
7286            let jump = lines[save..]
7287                .iter()
7288                .position(|l| *l == "call\twork" || *l == "call\tlongjmp")
7289                .map(|at| save + at)
7290                .unwrap_or_else(|| panic!("the call that jumps back at {level:?}:\n{text}"));
7291            let printf = lines.iter().position(|l| *l == "call\tprintf").expect("the handler");
7292            // The load that hands `v` to `printf` as its second argument.
7293            let slot = lines[jump..printf]
7294                .iter()
7295                .rev()
7296                .find_map(|l| l.strip_suffix(", %rsi").or_else(|| l.strip_suffix(", %esi")))
7297                .and_then(|l| l.split_once('\t'))
7298                .map(|(_, place)| place)
7299                .filter(|place| place.ends_with("(%rsp)") || place.ends_with("(%rbp)"))
7300                .unwrap_or_else(|| panic!("the handler reads v from a slot at {level:?}:\n{text}"));
7301            let writes = |l: &&str| {
7302                !l.starts_with("cmp") && !l.starts_with("test") && l.ends_with(&format!(", {slot}"))
7303            };
7304            assert!(
7305                lines[..save].iter().any(writes),
7306                "{slot} is written before the save at {level:?}:\n{text}"
7307            );
7308            assert!(
7309                !lines[save..jump].iter().any(writes),
7310                "{slot} is written again before the jump at {level:?}:\n{text}"
7311            );
7312        }
7313    }
7314
7315    /// Five values live across a call to `save`, declared by `declared`, and five more that die
7316    /// before the jump back, which is enough to spill on x86-64.
7317    fn across(declared: &str, save: &str) -> String {
7318        asm(&format!(
7319            "typedef long sigjmp_buf[25];\n{declared}int id(int);\nvoid thrower(int);\n\
7320             int work(int n) {{\n\
7321             \x20 int v0 = id(n), v1 = id(n + 1), v2 = id(n + 2), v3 = id(n + 3), v4 = id(n + 4);\n\
7322             \x20 sigjmp_buf b;\n\
7323             \x20 if ({save}(b, 0) == 0) {{\n\
7324             \x20   int w0 = id(v0 + v1), w1 = id(v1 + v2), w2 = id(v2 + v3);\n\
7325             \x20   int w3 = id(v3 + v4), w4 = id(v4 + v0);\n\
7326             \x20   thrower(n);\n\
7327             \x20   return w0 ^ w1 ^ w2 ^ w3 ^ w4;\n\
7328             \x20 }}\n\
7329             \x20 return v0 + v1 + v2 + v3 + v4;\n\
7330             }}\n"
7331        ))
7332    }
7333
7334    /// No two spills in the text go to the same slot, and there is at least one.
7335    fn each_spill_slot_written_once(text: &str) {
7336        let mut stored = Vec::new();
7337        for line in text.lines().map(str::trim) {
7338            let Some(operands) = line.strip_prefix("movq\t%") else { continue };
7339            if let Some((_, place)) = operands.split_once(", ") {
7340                if place.ends_with("(%rsp)") {
7341                    assert!(!stored.contains(&place), "{place} is written twice:\n{text}");
7342                    stored.push(place);
7343                }
7344            }
7345        }
7346        assert!(!stored.is_empty(), "something should have been spilled:\n{text}");
7347    }
7348
7349    /// What the save writes and where it leaves control, which is a new block.
7350    ///
7351    /// Four words: the frame pointer, the address to come back to, the stack pointer, and the
7352    /// address of the word the answer arrives in, which is this compiler's own and is why the
7353    /// block after the save opens with a load. The frame pointer is kept although the function
7354    /// asked for nothing and calls nothing, since the epilogue has to find the caller's frame
7355    /// after control has come back, and the frame is grown although there is one word in it,
7356    /// since a function control comes back into cannot use the red zone.
7357    #[test]
7358    fn the_save_writes_four_words_and_carries_on_in_a_new_block() {
7359        let text =
7360            asm(concat!("void *buf[5];\n", "int f(void) { return __builtin_setjmp(buf); }\n",));
7361        let body = text.split_once("\nf:\n").expect("the function").1;
7362        assert!(body.contains("\tmovq\t%rsp, %rbp\n"), "a frame pointer whatever: {text}");
7363        assert!(body.contains("\tsubq\t$8, %rsp\n"), "no red zone: {text}");
7364        assert!(body.contains("\tmovq\t%rbp, (%rax)\n"), "the frame pointer: {text}");
7365        assert!(body.contains("\tmovq\t%rsp, 16(%rax)\n"), "the stack pointer: {text}");
7366        assert!(body.contains("\tleaq\t.Lf_1(%rip), %rcx\n"), "where to come back to: {text}");
7367        assert!(body.contains("\tmovq\t%rcx, 8(%rax)\n"), "and that goes in the buffer: {text}");
7368        let back = body.split_once(".Lf_1:\n").expect("the block control comes back to").1;
7369        assert!(back.starts_with("\tmovq\t(%rsp), %rax\n"), "the answer is read back: {text}");
7370    }
7371
7372    /// Nothing stays in a register across the save, which is said with a write of every one of
7373    /// them and shows up as the callee-saved registers the function saves and restores.
7374    ///
7375    /// The restore puts back two registers and no others, so a function coming back through one
7376    /// finds every other register holding whatever the code between the two put there. The pushes
7377    /// are what makes the epilogue right on that path: the values popped are the caller's, off the
7378    /// stack the restore put back, rather than whatever is in the registers when control arrives.
7379    #[test]
7380    fn a_save_destroys_every_register_the_allocator_hands_out() {
7381        let text =
7382            asm(concat!("void *buf[5];\n", "int f(void) { return __builtin_setjmp(buf); }\n",));
7383        for reg in ["%rbx", "%r12", "%r13", "%r14", "%r15"] {
7384            assert!(text.contains(&format!("\tpushq\t{reg}\n")), "{reg} is saved: {text}");
7385            assert!(text.contains(&format!("\tpopq\t{reg}\n")), "{reg} is restored: {text}");
7386        }
7387    }
7388
7389    /// The restore puts both registers back before it goes, at every level.
7390    ///
7391    /// The jump reads the two of them as well as the address it goes through, which is what keeps
7392    /// it behind them. Without that the two instructions write registers nothing reads, and the
7393    /// scheduler at `-O2` puts the jump in front of both and the program comes back to a frame
7394    /// that is not there.
7395    #[test]
7396    fn the_restore_puts_the_frame_back_before_it_jumps() {
7397        for level in [rucc_session::OptLevel::O0, rucc_session::OptLevel::O2] {
7398            let mut opts = options();
7399            opts.emit = EmitKind::Asm;
7400            opts.opt_level = level;
7401            let source = "void *buf[5];\nvoid g(void) { __builtin_longjmp(buf, 1); }\n";
7402            let result = run(&opts, source);
7403            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
7404            let text = result.text().to_owned();
7405            let jump = text.find("\tjmp\t*%").unwrap_or_else(|| panic!("an indirect jump: {text}"));
7406            let stack = text.find(", %rsp\n").unwrap_or_else(|| panic!("the stack back: {text}"));
7407            let frame = text.find(", %rbp\n").unwrap_or_else(|| panic!("the frame back: {text}"));
7408            assert!(stack < jump, "the stack goes back first at {level:?}: {text}");
7409            assert!(frame < jump, "and so does the frame at {level:?}: {text}");
7410        }
7411    }
7412
7413    /// The second argument of the restore has one allowed value, which gcc 16.2.0 also insists on.
7414    ///
7415    /// This pair does not carry a value back the way the library's `longjmp` does, because what
7416    /// the matching save answers is decided by which way control reached it. So the argument is a
7417    /// place-holder, and a program that wrote anything else meant the library's function.
7418    #[test]
7419    fn a_longjmp_whose_second_argument_is_not_one_is_turned_down() {
7420        for source in [
7421            "void *buf[5];\nvoid f(void) { __builtin_longjmp(buf, 0); }\n",
7422            "void *buf[5];\nextern int v;\nvoid f(void) { __builtin_longjmp(buf, v); }\n",
7423        ] {
7424            let messages = errors(source);
7425            let named = messages.iter().any(|m| m.contains("E0710"));
7426            assert!(named, "expected a complaint about the value in {messages:?}");
7427        }
7428    }
7429
7430    /// A `static` function nothing refers to is not emitted, and one that is refered to is.
7431    ///
7432    /// The pair is written as one program so that the two answers come out of one walk. What
7433    /// makes the difference is the call in `main` and nothing else about either definition.
7434    #[test]
7435    fn a_static_function_nothing_refers_to_is_not_emitted() {
7436        let text = ir("static int dropped(void) { return 1; }\n\
7437                       static int kept(void) { return 2; }\n\
7438                       int main(void) { return kept(); }\n");
7439        assert!(text.contains("func @kept"), "{text}");
7440        assert!(!text.contains("dropped"), "{text}");
7441    }
7442
7443    /// The set is transitive, so two of them that only call each other are both dropped.
7444    ///
7445    /// Counting the references to a name would keep this pair, since each is named once, and
7446    /// that is the mistake this is here to catch: what decides it is whether a root reaches the
7447    /// definition, and a root is something the file has a reason to emit on its own.
7448    #[test]
7449    fn two_static_functions_that_only_call_each_other_are_both_dropped() {
7450        let text = ir("static int ping(void);\n\
7451                       static int pong(void) { return ping(); }\n\
7452                       static int ping(void) { return pong(); }\n\
7453                       int main(void) { return 0; }\n");
7454        assert!(!text.contains("ping"), "{text}");
7455        assert!(!text.contains("pong"), "{text}");
7456    }
7457
7458    /// Everything that names a function keeps it, whether or not the name is being called.
7459    ///
7460    /// An address taken in a body, an image that holds one, and a body that is only reached
7461    /// through another `static` function are three different ways for a definition to be needed
7462    /// and none of them is a call at the top level of a reachable function.
7463    #[test]
7464    fn naming_a_static_function_anywhere_keeps_it() {
7465        let text = ir("static int by_address(void) { return 1; }\n\
7466                       static int in_an_image(void) { return 2; }\n\
7467                       static int deeper(void) { return 3; }\n\
7468                       static int reaches_deeper(void) { return deeper(); }\n\
7469                       static int (*table[1])(void) = {in_an_image};\n\
7470                       int main(void) {\n\
7471                         int (*p)(void) = by_address;\n\
7472                         return p() + table[0]() + reaches_deeper();\n\
7473                       }\n");
7474        for kept in ["by_address", "in_an_image", "deeper", "reaches_deeper"] {
7475            assert!(text.contains(&format!("func @{kept}")), "expected {kept} in:\n{text}");
7476        }
7477    }
7478
7479    /// An attribute that says something outside the file reaches it keeps the definition.
7480    ///
7481    /// None of the five is implemented as anything else yet, and this is the part of each of
7482    /// them that a program notices first: a symbol a linker script names or a function the
7483    /// run-up to `main` calls is not written about anywhere a C file can see.
7484    #[test]
7485    fn an_attribute_keeps_a_static_function_nothing_refers_to() {
7486        for attribute in ["used", "retain", "constructor", "destructor", "__used__"] {
7487            let source = format!(
7488                "__attribute__(({attribute})) static int kept(void) {{ return 1; }}\n\
7489                 int main(void) {{ return 0; }}\n"
7490            );
7491            let text = ir(&source);
7492            assert!(text.contains("func @kept"), "for {attribute}:\n{text}");
7493        }
7494    }
7495
7496    /// A function with external linkage is emitted whatever this file does with it, because
7497    /// another one may call it, and that is what external linkage is.
7498    #[test]
7499    fn a_function_anything_could_call_is_emitted_without_being_called() {
7500        let text =
7501            ir("int nobody_here_calls_it(void) { return 1; }\nint main(void) { return 0; }\n");
7502        assert!(text.contains("func @nobody_here_calls_it"), "{text}");
7503    }
7504
7505    /// Four of the classification builtins are operators C already has, and become those.
7506    ///
7507    /// What the standard's macro promises over the operator is that it does not raise the
7508    /// invalid operation exception on a quiet NaN. This compiler does not model floating point
7509    /// exceptions, so there is nothing left for a node of its own to carry and a second way of
7510    /// spelling a comparison would be a second thing every pass has to know about.
7511    #[test]
7512    fn a_classification_c_has_an_operator_for_is_that_operator() {
7513        for (builtin, operator) in [
7514            ("__builtin_isgreater", "binary >"),
7515            ("__builtin_isgreaterequal", "binary >="),
7516            ("__builtin_isless", "binary <"),
7517            ("__builtin_islessequal", "binary <="),
7518        ] {
7519            let source = format!("int f(double x, double y) {{ return {builtin}(x, y); }}\n");
7520            let text = tast(&source);
7521            assert!(text.contains(&format!("{operator} : int")), "for {builtin}:\n{text}");
7522        }
7523    }
7524
7525    /// The rest of the family are comparisons in the IR and never a call to anything.
7526    ///
7527    /// `math.h` defines the macro of each of these names as the builtin of the same name, so
7528    /// there is no function under any of them for a call to reach. `isunordered` and
7529    /// `islessgreater` are predicates the IR's comparison already has, `isnan` is the value that
7530    /// is unordered with itself, and the two that ask about a magnitude are written against the
7531    /// infinities. `signbit` is the one that is not a question about the value, since a negative
7532    /// zero compares equal to a positive one, so its answer comes from the bits.
7533    #[test]
7534    fn the_classification_builtins_are_comparisons_and_not_calls() {
7535        let text = body("int f(double x, double y) { return __builtin_isunordered(x, y); }\n");
7536        assert_eq!(
7537            text,
7538            "block0(%0: f64, %1: f64):\n    %2 = fcmp uno %0, %1\n    %3 = zext.i32 \
7539                          %2\n    return %3\n"
7540        );
7541
7542        // Not `x != y`, which is true when the two are unordered and so is true of a NaN.
7543        let text = body("int f(double x, double y) { return __builtin_islessgreater(x, y); }\n");
7544        assert!(text.contains("fcmp one %0, %1"), "{text}");
7545
7546        let text = body("int f(double x) { return __builtin_isnan(x); }\n");
7547        assert!(text.contains("fcmp uno %0, %0"), "{text}");
7548
7549        let text = body("int f(double x) { return __builtin_isinf(x); }\n");
7550        assert!(text.contains("fconst.f64 0x7ff0000000000000"), "{text}");
7551        assert!(text.contains("fconst.f64 0xfff0000000000000"), "{text}");
7552        assert!(text.contains("%3 = fcmp oeq %0, %1"), "{text}");
7553        assert!(text.contains("%4 = fcmp oeq %0, %2"), "{text}");
7554        assert!(text.contains("%5 = or %3, %4"), "{text}");
7555
7556        // Strictly between the two infinities, which a NaN is not, because an ordered comparison
7557        // against either of them is false. That is what makes this one test rather than two.
7558        let text = body("int f(double x) { return __builtin_isfinite(x); }\n");
7559        assert!(text.contains("%3 = fcmp olt %2, %0"), "{text}");
7560        assert!(text.contains("%4 = fcmp olt %0, %1"), "{text}");
7561        assert!(text.contains("%5 = and %3, %4"), "{text}");
7562
7563        let text = body("int f(double x) { return __builtin_signbit(x); }\n");
7564        assert!(text.contains("%1 = bitcast.i64 %0"), "{text}");
7565        assert!(text.contains("icmp slt %1, %2"), "{text}");
7566
7567        // The same question of a value in the target's widest format, where the bits are eighty
7568        // and the object they sit in is sixteen bytes. No integer is that wide, so the sign is
7569        // read from the word at the top of the value once it is in memory.
7570        let text = body("int f(long double x) { return __builtin_signbitl(x); }\n");
7571        assert!(text.contains("load.i16"), "{text}");
7572        assert!(text.contains("icmp slt"), "{text}");
7573        assert!(!text.contains("i80"), "{text}");
7574
7575        // The operand is evaluated once however many times it is compared, which is the whole
7576        // reason these are nodes rather than a rewriting into the operators.
7577        let text = body("double g(void);\nint f(void) { return __builtin_isnan(g()); }\n");
7578        assert_eq!(text.matches("call @g()").count(), 1, "{text}");
7579    }
7580
7581    /// A spelling that names a width converts its argument before it asks.
7582    ///
7583    /// gcc gives `__builtin_isinff` a `float` parameter and `__builtin_isinf` no parameter type
7584    /// at all, and the difference is visible rather than academic: `1e300` does not fit in a
7585    /// `float`, so converting it first is an infinity and not converting it is not. Both numbers
7586    /// here are what gcc 16 gives.
7587    #[test]
7588    fn a_classification_spelling_that_names_a_width_converts_before_it_asks() {
7589        let text = ir(concat!(
7590            "int a = __builtin_isinff(1e300);\n",
7591            "int b = __builtin_isinf(1e300);\n",
7592            // Folded here rather than compared at run time, because a question about a value has
7593            // an answer as soon as the value is a constant, and an initializer for an object
7594            // with static storage duration has to have one.
7595            "int c = __builtin_isnan(0.0);\n",
7596            "int d = __builtin_signbit(-0.0);\n",
7597            "int e = __builtin_islessgreater(1.0, 2.0);\n",
7598        ));
7599        assert!(text.contains("global @a : i32 = 1,"), "{text}");
7600        assert!(text.contains("global @b : i32 = 0,"), "{text}");
7601        assert!(text.contains("global @c : i32 = 0,"), "{text}");
7602        assert!(text.contains("global @d : i32 = 1,"), "{text}");
7603        assert!(text.contains("global @e : i32 = 1,"), "{text}");
7604    }
7605
7606    /// An argument that is not floating point is refused, in gcc's words.
7607    #[test]
7608    fn a_classification_builtin_refuses_an_argument_that_is_not_floating_point() {
7609        let mut opts = options();
7610        opts.emit = EmitKind::Ir;
7611        let source = concat!(
7612            "int a(int x) { return __builtin_isnan(x); }\n",
7613            "int b(int x, int y) { return __builtin_isunordered(x, y); }\n",
7614            "int c(double x) { return __builtin_isnan(x, x); }\n",
7615        );
7616        let messages = run(&opts, source).messages;
7617        assert_eq!(
7618            messages,
7619            [
7620                "/main.c:1:23: error: non-floating-point argument in call to function \
7621                 '__builtin_isnan' [E0685]",
7622                "/main.c:2:30: error: non-floating-point arguments in call to function \
7623                 '__builtin_isunordered' [E0685]",
7624                "/main.c:3:26: error: too many arguments to function '__builtin_isnan' [E0511]",
7625            ]
7626        );
7627    }
7628
7629    /// The three of the family that need a constant of the format other than an infinity.
7630    ///
7631    /// `isnormal` is the one that needs the smallest normal, and it is asked of the magnitude, so
7632    /// the sign comes off first and what is left is the same shape as `isfinite`. `isinf_sign` is
7633    /// the one whose answer is a number: the two comparisons `isinf` builds, subtracted rather
7634    /// than combined. `fpclassify` is four questions of one value and five answers to pick from,
7635    /// and the picking is a mask because all five are constants and neither of them can have an
7636    /// effect.
7637    #[test]
7638    fn the_last_three_classification_builtins_are_comparisons_and_not_calls() {
7639        let text = body("int f(double x) { return __builtin_isnormal(x); }\n");
7640        // The sign off, which is the magnitude, and then the range, asked of the bits rather than
7641        // of the number, since the encoding of a value whose sign bit is clear rises with the
7642        // value in every format this compiles for.
7643        assert!(text.contains("%1 = bitcast.i64 %0"), "{text}");
7644        assert!(text.contains("%2 = iconst.i64 9223372036854775807"), "{text}");
7645        assert!(text.contains("%3 = and %1, %2"), "{text}");
7646        assert!(text.contains("%4 = iconst.i64 4503599627370496"), "{text}");
7647        assert!(text.contains("%5 = iconst.i64 9218868437227405312"), "{text}");
7648        assert!(text.contains("%6 = icmp uge %3, %4"), "{text}");
7649        assert!(text.contains("%7 = icmp ult %3, %5"), "{text}");
7650        assert!(text.contains("%8 = and %6, %7"), "{text}");
7651
7652        // The same question in the target's widest format, where the smallest normal has the
7653        // leading significand bit stored rather than implied, so its encoding is two bits and not
7654        // one. There is no integer that wide to compare the bits in, so it is the magnitude that
7655        // is compared, as a value.
7656        let text = body("int f(long double x) { return __builtin_isnormal(x); }\n");
7657        assert!(text.contains("fconst.f80 0x18000000000000000"), "{text}");
7658        assert!(text.contains("fconst.f80 0x7fff8000000000000000"), "{text}");
7659        assert!(text.contains("fcmp oge"), "{text}");
7660        assert!(text.contains("fcmp olt"), "{text}");
7661
7662        let text = body("int f(double x) { return __builtin_isinf_sign(x); }\n");
7663        assert!(text.contains("%3 = fcmp oeq %0, %1"), "{text}");
7664        assert!(text.contains("%4 = fcmp oeq %0, %2"), "{text}");
7665        assert!(text.contains("%7 = sub %5, %6"), "{text}");
7666
7667        let text = body("int f(double x) { return __builtin_fpclassify(0, 1, 2, 3, 4, x); }\n");
7668        assert!(text.contains("fcmp uno %0, %0"), "{text}");
7669        assert!(text.contains("fcmp oeq %0, %6"), "{text}");
7670        // Four questions, each of them a bit widened into the type of the answer and then spread
7671        // into a mask that picks between the answer and whatever the questions after it settled
7672        // on. Nothing sign extends, because no rule lowers a sign extension out of one bit.
7673        assert_eq!(text.matches(" = zext.i32 ").count(), 4, "{text}");
7674        assert_eq!(text.matches(" = xor ").count(), 4, "{text}");
7675        assert!(!text.contains("call"), "{text}");
7676
7677        // The value is evaluated once however many questions are asked of it, which is the whole
7678        // reason `fpclassify` is a node rather than the chain of tests it turns into.
7679        let text = body(concat!(
7680            "double g(void);\n",
7681            "int f(void) { return __builtin_fpclassify(0, 1, 2, 3, 4, g()); }\n",
7682        ));
7683        assert_eq!(text.matches("call @g()").count(), 1, "{text}");
7684    }
7685
7686    /// Each of the three answers a constant where its operand is one.
7687    ///
7688    /// glibc's `fpclassify` macro is exactly this builtin, so a program that writes
7689    /// `fpclassify(0.0)` in a static initializer is writing this, and it has to have a value at
7690    /// translation time or the program is refused rather than merely compiled slowly. Every
7691    /// number here is what gcc 16 gives.
7692    #[test]
7693    fn the_last_three_classification_builtins_fold_where_their_operand_is_a_constant() {
7694        let text = ir(concat!(
7695            "int a = __builtin_isnormal(1.0);\n",
7696            "int b = __builtin_isnormal(0.0);\n",
7697            "int c = __builtin_isnormal(1.0 / 0.0);\n",
7698            "int d = __builtin_isinf_sign(-1.0 / 0.0);\n",
7699            "int e = __builtin_isinf_sign(1.0);\n",
7700            "int g = __builtin_fpclassify(0, 1, 2, 3, 4, 0.0);\n",
7701            "int h = __builtin_fpclassify(0, 1, 2, 3, 4, 1.0);\n",
7702            "int i = __builtin_fpclassify(0, 1, 2, 3, 4, 1.0 / 0.0);\n",
7703        ));
7704        assert!(text.contains("global @a : i32 = 1,"), "{text}");
7705        assert!(text.contains("global @b : i32 = 0,"), "{text}");
7706        assert!(text.contains("global @c : i32 = 0,"), "{text}");
7707        assert!(text.contains("global @d : i32 = -1,"), "{text}");
7708        assert!(text.contains("global @e : i32 = 0,"), "{text}");
7709        assert!(text.contains("global @g : i32 = 4,"), "{text}");
7710        assert!(text.contains("global @h : i32 = 2,"), "{text}");
7711        assert!(text.contains("global @i : i32 = 1,"), "{text}");
7712    }
7713
7714    /// `fpclassify` refuses what gcc refuses, in gcc's words.
7715    ///
7716    /// The five answers have to be integer constant expressions, because what the builtin does is
7717    /// pick one of them and a pick between values that are not known here would be a chain of
7718    /// conditionals over expressions the call has already evaluated.
7719    #[test]
7720    fn fpclassify_refuses_an_answer_that_is_not_an_integer_constant() {
7721        let mut opts = options();
7722        opts.emit = EmitKind::Ir;
7723        let source = concat!(
7724            "int a(double x, int n) { return __builtin_fpclassify(0, 1, n, 3, 4, x); }\n",
7725            "int b(double x) { return __builtin_fpclassify(0, 1, 2, 3, x); }\n",
7726            "int c(int x) { return __builtin_fpclassify(0, 1, 2, 3, 4, x); }\n",
7727        );
7728        let messages = run(&opts, source).messages;
7729        assert_eq!(
7730            messages,
7731            [
7732                "/main.c:1:60: error: non-const integer argument 3 in call to function \
7733                 '__builtin_fpclassify' [E0687]",
7734                "/main.c:2:26: error: too few arguments to function '__builtin_fpclassify' \
7735                 [E0511]",
7736                "/main.c:3:23: error: non-floating-point argument in call to function \
7737                 '__builtin_fpclassify' [E0685]",
7738            ]
7739        );
7740    }
7741
7742    /// A builtin whose answer is a constant is one, and is not a call to the library.
7743    ///
7744    /// This is the reason the family is answered in the front end at all. `double x =
7745    /// __builtin_inf();` at file scope initializes an object with static storage duration, so
7746    /// there is no point in the program at which a call could be made, and a compiler that
7747    /// lowered it to one would reject a program gcc accepts. Every number here is the encoding
7748    /// gcc 16 gives on x86-64.
7749    #[test]
7750    fn a_builtin_whose_answer_is_a_constant_is_one_and_not_a_call() {
7751        let text = ir(concat!(
7752            "double a = __builtin_inf();\n",
7753            "float b = __builtin_huge_valf();\n",
7754            "long double c = __builtin_infl();\n",
7755            "double d = __builtin_huge_val();\n",
7756        ));
7757        assert!(text.contains("global @a : f64 = 0x7ff0000000000000,"), "{text}");
7758        assert!(text.contains("global @b : f32 = 0x7f800000,"), "{text}");
7759        assert!(text.contains("f80 0x7fff8000000000000000"), "{text}");
7760        assert!(text.contains("global @d : f64 = 0x7ff0000000000000,"), "{text}");
7761        assert!(!text.contains("call"), "{text}");
7762    }
7763
7764    /// A nan is written with the payload the program asked for.
7765    ///
7766    /// The string is read the way `strtoull` reads a number, which is what the library function
7767    /// of the same name does with it, and a string that is not one at all leaves the call for the
7768    /// library to answer at run time. A quiet nan has the high fraction bit set and a signalling
7769    /// one does not, except that a signalling nan with nothing in it would be an infinity, so it
7770    /// gets the next bit down instead. Every encoding here was measured against gcc 16, the two
7771    /// `long double` ones on a machine with the x87 format.
7772    #[test]
7773    fn a_nan_is_written_with_the_payload_the_program_asked_for() {
7774        let text = ir(concat!(
7775            "double a = __builtin_nan(\"\");\n",
7776            "double b = __builtin_nan(\"0x1\");\n",
7777            // Octal, since there is a leading zero, so this is eight and not ten.
7778            "double c = __builtin_nan(\"010\");\n",
7779            "double d = __builtin_nans(\"\");\n",
7780            "double e = __builtin_nans(\"0x1\");\n",
7781            "float f = __builtin_nanf(\"0x1\");\n",
7782            "float g = __builtin_nansf(\"\");\n",
7783            "long double h = __builtin_nansl(\"\");\n",
7784        ));
7785        assert!(text.contains("global @a : f64 = 0x7ff8000000000000,"), "{text}");
7786        assert!(text.contains("global @b : f64 = 0x7ff8000000000001,"), "{text}");
7787        assert!(text.contains("global @c : f64 = 0x7ff8000000000008,"), "{text}");
7788        assert!(text.contains("global @d : f64 = 0x7ff4000000000000,"), "{text}");
7789        assert!(text.contains("global @e : f64 = 0x7ff0000000000001,"), "{text}");
7790        assert!(text.contains("global @f : f32 = 0x7fc00001,"), "{text}");
7791        assert!(text.contains("global @g : f32 = 0x7fa00000,"), "{text}");
7792        assert!(text.contains("f80 0x7fffa000000000000000"), "{text}");
7793
7794        // A payload that is not a number, and one that is not known until run time, are both
7795        // left to the library, which is the same thing gcc emits for either of them.
7796        let text = ir(concat!(
7797            "double f(const char *p) { return __builtin_nan(p); }\n",
7798            "double g(void) { return __builtin_nans(\"1x\"); }\n",
7799        ));
7800        assert_eq!(text.matches("call @nan(").count(), 1, "{text}");
7801        assert_eq!(text.matches("call @nans(").count(), 1, "{text}");
7802    }
7803
7804    /// The length and the order of a string literal are known here.
7805    ///
7806    /// A program that asks for either of them is asking about something the translation already
7807    /// has in front of it, and folding is not only an optimization: `execute/921007-1.c` in the
7808    /// torture suite calls `__builtin_strcmp` in a file that defines its own `strcmp` with a
7809    /// different signature, so leaving the call behind is a name collision that gcc does not
7810    /// have. The comparison is over `unsigned char`, which is why the second one is negative.
7811    #[test]
7812    fn the_length_and_the_order_of_a_string_literal_are_known_here() {
7813        let text = ir(concat!(
7814            "unsigned long a = __builtin_strlen(\"hello\");\n",
7815            "unsigned long b = __builtin_strlen(\"a\\0bc\");\n",
7816            "int c = __builtin_strcmp(\"X\", \"X\\376\") < 0;\n",
7817            "int d = __builtin_strcmp(\"abc\", \"abc\");\n",
7818            "int e = __builtin_strcmp(\"abc\", \"ab\") > 0;\n",
7819        ));
7820        assert!(text.contains("global @a : i64 = 5,"), "{text}");
7821        assert!(text.contains("global @b : i64 = 1,"), "{text}");
7822        assert!(text.contains("global @c : i32 = 1,"), "{text}");
7823        assert!(text.contains("global @d : i32 = 0,"), "{text}");
7824        assert!(text.contains("global @e : i32 = 1,"), "{text}");
7825        assert!(!text.contains("call"), "{text}");
7826
7827        // An argument that is not a literal is the library's to answer, as it has to be.
7828        let text = ir("unsigned long f(const char *p) { return __builtin_strlen(p); }\n");
7829        assert!(text.contains("call @strlen("), "{text}");
7830    }
7831
7832    /// A sign builtin is a mask over the bits, and is not a call.
7833    ///
7834    /// `fabs` and `copysign` are in the math library rather than the C one, so a program that
7835    /// only ever wrote the prefixed spelling never asked for `-lm` and a call left behind here
7836    /// would not link. Neither needs anything the library has: one clears the sign bit and the
7837    /// other takes it from the second operand, and every other bit goes through untouched.
7838    #[test]
7839    fn a_sign_builtin_is_a_mask_over_the_bits_and_not_a_call() {
7840        let text = body("double f(double x) { return __builtin_fabs(x); }\n");
7841        assert!(text.contains("bitcast.i64 %0"), "{text}");
7842        assert!(text.contains("iconst.i64 9223372036854775807"), "{text}");
7843        assert!(text.contains("and %1, %2"), "{text}");
7844        assert!(text.contains("bitcast.f64 %3"), "{text}");
7845        assert!(!text.contains("call"), "{text}");
7846
7847        let text = body("double f(double x, double y) { return __builtin_copysign(x, y); }\n");
7848        assert!(text.contains("iconst.i64 -9223372036854775808"), "{text}");
7849        assert!(text.contains("%8 = or %4, %7"), "{text}");
7850        assert!(!text.contains("call"), "{text}");
7851
7852        // The x87 format, whose value is eighty bits sitting in an object of sixteen. There is no
7853        // integer that wide, so the mask is on the word at the top of the value, in memory.
7854        let text = body("long double f(long double x) { return __builtin_fabsl(x); }\n");
7855        assert!(text.contains("iconst.i16 32767"), "{text}");
7856        assert!(text.contains("load.f80"), "{text}");
7857        assert!(!text.contains("call"), "{text}");
7858
7859        // The width a name does not spell out is `double`, so a `float` argument widens first and
7860        // the answer is a `double`, which is what gcc's declaration of it says.
7861        let text = body("double f(float x) { return __builtin_fabs(x); }\n");
7862        assert!(text.contains("fpext.f64 %0"), "{text}");
7863        assert!(text.contains("bitcast.i64 %1"), "{text}");
7864    }
7865
7866    /// A shuffle reads each lane of the answer out of a copy of its sources, at the index the mask
7867    /// lane gives with only its low bits kept, and is not a call.
7868    ///
7869    /// The copy is what makes `*v = __builtin_shuffle(*v, m)` right, since the answer is written
7870    /// over the vector it reads, and the mask is what `pr85331.c` checks: gcc keeps as many bits
7871    /// of an index as it takes to name a lane, so `10000000001` picks lane one of two.
7872    #[test]
7873    fn a_shuffle_picks_each_lane_by_the_low_bits_of_the_mask() {
7874        let text = body(concat!(
7875            "typedef int v2 __attribute__((vector_size(8)));\n",
7876            "void f(v2 *v, v2 m) { *v = __builtin_shuffle(*v, m); }\n",
7877        ));
7878        assert!(text.contains("memcpy"), "{text}");
7879        assert_eq!(text.matches("iconst.i32 1\n").count(), 2, "{text}");
7880        assert_eq!(text.matches(" = and ").count(), 2, "{text}");
7881        assert!(!text.contains("call"), "{text}");
7882
7883        // Two sources of four lanes are eight to pick from, so three bits of each index are
7884        // kept, and a mask of bytes is widened to a word before it is masked.
7885        let text = body(concat!(
7886            "typedef char v4 __attribute__((vector_size(4)));\n",
7887            "v4 f(v4 a, v4 b, v4 m) { return __builtin_shuffle(a, b, m); }\n",
7888        ));
7889        assert_eq!(text.matches("iconst.i32 7\n").count(), 4, "{text}");
7890        assert!(text.contains("zext.i32"), "{text}");
7891        assert!(!text.contains("call"), "{text}");
7892    }
7893
7894    /// A function holding `__builtin_apply_args` writes every argument register into its frame
7895    /// before anything else runs, the ones its parameters took as well as the ones they did not,
7896    /// and the answer is the address of where it wrote them.
7897    #[test]
7898    fn the_arguments_a_function_was_called_with_are_saved_on_the_way_in() {
7899        let text =
7900            mir("void *f(int a, double b) { (void)a; (void)b; return __builtin_apply_args(); }\n");
7901        // Six words and the address the arguments in memory start at, and eight vectors.
7902        assert!(text.matches("x64.mov_mr_64").count() >= 7, "{text}");
7903        assert!(text.matches("x64.movaps_mr").count() >= 8, "{text}");
7904        for reg in ["$rdi", "$rsi", "$rdx", "$rcx", "$r8", "$r9", "$xmm0", "$xmm7"] {
7905            assert!(text.contains(reg), "{reg} is not saved in\n{text}");
7906        }
7907
7908        // And a function without one saves nothing.
7909        let text = mir("int f(int a) { return a; }\n");
7910        assert!(!text.contains("movaps_mr"), "{text}");
7911    }
7912
7913    /// `__builtin_apply` loads every argument register out of the block it is given, copies the
7914    /// bytes of arguments in memory it was told about, and calls through the address, with eight
7915    /// in `%al` since every vector register may hold an argument.
7916    #[test]
7917    fn a_call_built_from_saved_arguments_loads_every_argument_register() {
7918        let text = mir(concat!(
7919            "void *g(void *args, void (*h)()) {\n",
7920            "  return __builtin_apply(h, args, 64);\n",
7921            "}\n",
7922        ));
7923        assert!(text.matches("x64.mov_rm_64").count() >= 7, "{text}");
7924        assert!(text.matches("x64.movaps_rm").count() >= 8, "{text}");
7925        assert!(text.contains("call"), "{text}");
7926        // What came back is written out, two words and two vectors.
7927        assert!(text.matches("x64.movaps_mr").count() >= 2, "{text}");
7928
7929        // The size is a number the frame can be laid out with, and nothing else is.
7930        let mut opts = options();
7931        opts.emit = EmitKind::Ir;
7932        let result = run(
7933            &opts,
7934            "void *g(void *a, void (*h)(), int n) { return __builtin_apply(h, a, n); }\n",
7935        );
7936        assert!(result.failed(), "{:?}", result.messages);
7937        assert!(
7938            result
7939                .messages
7940                .iter()
7941                .any(|m| m.contains("the size given to '__builtin_apply' is a constant")),
7942            "{:?}",
7943            result.messages
7944        );
7945    }
7946
7947    /// A shuffle whose operands gcc would refuse is refused, in gcc's words.
7948    #[test]
7949    fn a_shuffle_refuses_what_gcc_refuses() {
7950        let mut opts = options();
7951        opts.emit = EmitKind::Ir;
7952        let source = concat!(
7953            "typedef int v4 __attribute__((vector_size(16)));\n",
7954            "typedef float f4 __attribute__((vector_size(16)));\n",
7955            "typedef short s8 __attribute__((vector_size(16)));\n",
7956            "typedef long long l4 __attribute__((vector_size(32)));\n",
7957            "void a(v4 x, f4 m) { __builtin_shuffle(x, m); }\n",
7958            "void b(int x, v4 m) { __builtin_shuffle(x, m); }\n",
7959            "void c(v4 x, f4 y, v4 m) { __builtin_shuffle(x, y, m); }\n",
7960            "void d(v4 x, s8 m) { __builtin_shuffle(x, m); }\n",
7961            "void e(f4 x, l4 m) { __builtin_shuffle(x, m); }\n",
7962            "void g(v4 x) { __builtin_shuffle(x); }\n",
7963        );
7964        let messages = run(&opts, source).messages;
7965        let wanted = [
7966            "last argument must be an integer vector [E0715]",
7967            "arguments must be vectors [E0715]",
7968            "argument vectors must be of the same type [E0715]",
7969            "number of elements of the argument vector(s) and the mask vector should be the same \
7970             [E0715]",
7971            "argument vector(s) inner type must have the same size as inner type of the mask \
7972             [E0715]",
7973            "too few arguments to function '__builtin_shuffle' [E0511]",
7974        ];
7975        assert_eq!(messages.len(), wanted.len(), "{messages:?}");
7976        for (message, wanted) in messages.iter().zip(wanted) {
7977            assert!(message.ends_with(wanted), "{message}");
7978        }
7979    }
7980
7981    /// The plain math library names are the same mask, which is what makes a program link.
7982    ///
7983    /// `math.h` declares `fabs` and never spells `__builtin_fabs`, so the plain name is the one
7984    /// every program that includes the header reaches. Recognising only the prefixed spelling
7985    /// leaves a call to the math library behind, and the math library is not on the link line
7986    /// unless the program asked for `-lm`. parson is the project that shows it: its makefile has
7987    /// no `-lm`, it does not need one under gcc, and `undefined reference to 'fabs'` is where the
7988    /// build stopped. That is issue 630.
7989    #[test]
7990    fn the_plain_math_names_are_the_same_mask_and_not_a_call() {
7991        let text =
7992            body(concat!("double fabs(double x);\n", "double f(double x) { return fabs(x); }\n",));
7993        assert!(text.contains("iconst.i64 9223372036854775807"), "{text}");
7994        assert!(!text.contains("call"), "{text}");
7995
7996        let text =
7997            body(concat!("float fabsf(float x);\n", "float f(float x) { return fabsf(x); }\n",));
7998        assert!(text.contains("bitcast.i32 %0"), "{text}");
7999        assert!(!text.contains("call"), "{text}");
8000
8001        let text = body(concat!(
8002            "double copysign(double x, double y);\n",
8003            "double f(double x, double y) { return copysign(x, y); }\n",
8004        ));
8005        assert!(text.contains("iconst.i64 -9223372036854775808"), "{text}");
8006        assert!(!text.contains("call"), "{text}");
8007
8008        let text = body(concat!(
8009            "float copysignf(float x, float y);\n",
8010            "float f(float x, float y) { return copysignf(x, y); }\n",
8011        ));
8012        assert!(!text.contains("call"), "{text}");
8013
8014        // The `long double` pair is left alone on purpose. The prefixed spelling of both stops in
8015        // the back end with `no rule lowers a bitcast producing an i80`, so expanding the plain
8016        // name would trade a link error for a worse one. They go in with issue 540.
8017        let text = ir(concat!(
8018            "long double fabsl(long double x);\n",
8019            "long double f(long double x) { return fabsl(x); }\n",
8020        ));
8021        assert!(text.contains("call @fabsl"), "{text}");
8022    }
8023
8024    /// A plain math name the program took is the program's own function.
8025    ///
8026    /// The same four ways as the absolute value family next door, asked again here because these
8027    /// two go through a different path: the plain names of this family are taken after the call
8028    /// has been checked against the declaration, and the declaration is the whole reason the
8029    /// question can be answered at all. Measured against gcc 16.2.0, which calls the program's
8030    /// function in every one of them.
8031    #[test]
8032    fn a_plain_math_name_the_program_took_is_the_programs_own_function() {
8033        let taken = concat!(
8034            "static double fabs(double b) { return 7; }\n",
8035            "double f(double x) { return fabs(x); }\n",
8036        );
8037        assert!(ir(taken).contains("call @fabs"), "a static definition is the program's own");
8038
8039        let retyped = concat!("int fabs(int b);\n", "int f(int x) { return fabs(x); }\n");
8040        assert!(ir(retyped).contains("call @fabs"), "another type is another function");
8041
8042        let plain = concat!("double fabs(double b);\n", "double f(double x) { return fabs(x); }\n");
8043        let mut opts = options();
8044        opts.emit = EmitKind::Ir;
8045        assert!(!run(&opts, plain).text().contains("call @fabs"), "the library's by default");
8046
8047        opts.builtins = false;
8048        assert!(run(&opts, plain).text().contains("call @fabs"), "-fno-builtin");
8049
8050        opts.builtins = true;
8051        opts.no_builtin = vec!["fabs".to_owned()];
8052        assert!(run(&opts, plain).text().contains("call @fabs"), "-fno-builtin-fabs");
8053        let one = concat!(
8054            "double copysign(double a, double b);\n",
8055            "double f(double x) { return copysign(x, 1.0); }\n",
8056        );
8057        assert!(!run(&opts, one).text().contains("call @copysign"), "one name and not the family");
8058
8059        // The prefixed spelling is untouched by any of it, which is what the prefix is for.
8060        opts.no_builtin = Vec::new();
8061        opts.builtins = false;
8062        let prefixed = "double f(double x) { return __builtin_fabs(x); }\n";
8063        assert!(!run(&opts, prefixed).text().contains("call @fabs"), "the prefix is not a library");
8064    }
8065
8066    /// The sign builtins answer a zero and a nan the way the bits say.
8067    ///
8068    /// This is why they are described over the bits rather than written with comparisons and
8069    /// negation. A negative zero compares equal to a positive one and has a sign bit to clear,
8070    /// and a nan compares equal to nothing at all and keeps its payload through both operations.
8071    /// `execute/ieee/copysign1.c` in the torture suite is the test that notices, because it
8072    /// compares its answers with `memcmp`. Every number here is what gcc 16 gives, the two in the
8073    /// x87 format measured on a machine that has it.
8074    #[test]
8075    fn the_sign_builtins_answer_a_zero_and_a_nan_the_way_the_bits_say() {
8076        let text = ir(concat!(
8077            "double a = __builtin_fabs(-3.5);\n",
8078            "double b = __builtin_copysign(1.0, -0.0);\n",
8079            "double c = __builtin_copysign(0.0, -2.0);\n",
8080            // The payload survives both, and only the sign bit moves.
8081            "double d = __builtin_copysign(-__builtin_nan(\"\"), 1.0);\n",
8082            "double e = __builtin_fabs(-__builtin_nan(\"0x1\"));\n",
8083            "float g = __builtin_copysignf(-0.0f, 2.0f);\n",
8084            "long double h = __builtin_copysignl(1.0L, -1.0L);\n",
8085            "long double i = __builtin_fabsl(-__builtin_infl());\n",
8086        ));
8087        assert!(text.contains("global @a : f64 = 0x400c000000000000,"), "{text}");
8088        assert!(text.contains("global @b : f64 = 0xbff0000000000000,"), "{text}");
8089        assert!(text.contains("global @c : f64 = 0x8000000000000000,"), "{text}");
8090        assert!(text.contains("global @d : f64 = 0x7ff8000000000000,"), "{text}");
8091        assert!(text.contains("global @e : f64 = 0x7ff8000000000001,"), "{text}");
8092        assert!(text.contains("global @g : f32 = 0x0,"), "{text}");
8093        assert!(text.contains("f80 0xbfff8000000000000000"), "{text}");
8094        assert!(text.contains("f80 0x7fff8000000000000000"), "{text}");
8095    }
8096
8097    /// The sign of a `long double` is read and written in the word at the top of it.
8098    ///
8099    /// The other formats have their sign tested and set on an integer as wide as the value, and
8100    /// there is no eighty bit integer for the x87 one to go to: no rule lowers it, and
8101    /// `execute/20080502-1.c` and `execute/ieee/copysign1.c` in the torture suite stopped on that.
8102    /// The value goes through memory instead, and the word holding its sign is what is looked at.
8103    #[test]
8104    fn the_sign_of_a_long_double_is_in_the_word_at_the_top_of_it() {
8105        for source in [
8106            "int f(long double x) { return __builtin_signbit(x); }\n",
8107            "long double f(long double x) { return __builtin_fabsl(x); }\n",
8108            "long double f(long double x, long double y) { return __builtin_copysignl(x, y); }\n",
8109            "int f(long double x) { return __builtin_isnormal(x); }\n",
8110        ] {
8111            let text = body(source);
8112            assert!(!text.contains("i80"), "{text}");
8113            assert!(text.contains("i16"), "{text}");
8114        }
8115    }
8116
8117    /// The complex builtins are the halves of the value, and are not a call.
8118    ///
8119    /// `conj`, `creal` and `cimag` are `~`, `__real__` and `__imag__` under the names `complex.h`
8120    /// gives them, so there is nothing for the math library to do that the translation cannot do
8121    /// with the object in front of it. Leaving the call behind would not link either, since all
8122    /// three are in the math library and a program that wrote one never had a reason to ask for
8123    /// `-lm`. Measured against gcc 16.2.0, which emits no call for any of them even at `-O0`.
8124    #[test]
8125    fn the_complex_builtins_are_the_halves_of_the_value_and_not_a_call() {
8126        let text = body("double f(_Complex double z) { return __builtin_creal(z); }\n");
8127        assert!(!text.contains("call"), "{text}");
8128        let text = body("double f(_Complex double z) { return __builtin_cimag(z); }\n");
8129        assert!(!text.contains("call"), "{text}");
8130
8131        // The conjugate is the imaginary half negated and the real half as it stands, so there is
8132        // one negation in it. A complex negation is the one with two.
8133        let text = body("_Complex double f(_Complex double z) { return __builtin_conj(z); }\n");
8134        assert_eq!(text.matches("fneg").count(), 1, "{text}");
8135        assert!(!text.contains("call"), "{text}");
8136        let negated = body("_Complex double f(_Complex double z) { return -z; }\n");
8137        assert_eq!(negated.matches("fneg").count(), 2, "{negated}");
8138
8139        // `~` on a complex operand is the same operator, which is the spelling the language has
8140        // had all along and the one a program that never included the header writes.
8141        let written = body("_Complex double f(_Complex double z) { return ~z; }\n");
8142        assert_eq!(written, text, "the name and the operator are the same thing");
8143
8144        // The plain names, which are the ones the header declares and so the ones programs write.
8145        let text = body(concat!(
8146            "double creal(_Complex double z);\n",
8147            "double f(_Complex double z) { return creal(z); }\n",
8148        ));
8149        assert!(!text.contains("call"), "{text}");
8150        let text = body(concat!(
8151            "_Complex float conjf(_Complex float z);\n",
8152            "_Complex float f(_Complex float z) { return conjf(z); }\n",
8153        ));
8154        assert_eq!(text.matches("fneg").count(), 1, "{text}");
8155        assert!(!text.contains("call"), "{text}");
8156
8157        // A program that took the name means its own function, the same four ways the absolute
8158        // value family next door asks it.
8159        let taken = concat!(
8160            "static double creal(_Complex double z) { return 7; }\n",
8161            "double f(_Complex double z) { return creal(z); }\n",
8162        );
8163        assert!(ir(taken).contains("call @creal"), "a static definition is the program's own");
8164        let retyped = concat!("int cimag(int z);\n", "int f(int z) { return cimag(z); }\n");
8165        assert!(ir(retyped).contains("call @cimag"), "another type is another function");
8166        let plain = concat!(
8167            "double cimag(_Complex double z);\n",
8168            "double f(_Complex double z) { return cimag(z); }\n",
8169        );
8170        let mut opts = options();
8171        opts.emit = EmitKind::Ir;
8172        opts.builtins = false;
8173        assert!(run(&opts, plain).text().contains("call @cimag"), "-fno-builtin");
8174        opts.builtins = true;
8175        opts.no_builtin = vec!["cimag".to_owned()];
8176        assert!(run(&opts, plain).text().contains("call @cimag"), "-fno-builtin-cimag");
8177
8178        // A constant folds, which is what a static initializer written with one needs.
8179        let text = ir(concat!(
8180            "double a = __builtin_creal(1.5 + 2.5i);\n",
8181            "double b = __builtin_cimag(1.5 + 2.5i);\n",
8182            "_Complex double c = __builtin_conj(1.5 + 2.5i);\n",
8183        ));
8184        assert!(text.contains("global @a : f64 = 0x3ff8000000000000,"), "{text}");
8185        assert!(text.contains("global @b : f64 = 0x4004000000000000,"), "{text}");
8186        assert!(
8187            text.contains("{ f64 0x3ff8000000000000, f64 0xc004000000000000 }"),
8188            "the conjugate of a constant is the constant with the second half negated: {text}"
8189        );
8190        assert!(!text.contains("call"), "{text}");
8191    }
8192
8193    /// A math library builtin handed a constant is the answer, and is not a call.
8194    ///
8195    /// This is the reason the family is answered in the front end at all. `double x =
8196    /// __builtin_ceil(1.5);` at file scope initializes an object with static storage duration, so
8197    /// there is no point in the program at which a call could be made, and a compiler that lowered
8198    /// it to one would refuse a program gcc accepts. Every number here is the encoding gcc 16.2.0
8199    /// gives on x86-64, read out of the object file one initializer at a time.
8200    #[test]
8201    fn a_math_library_builtin_of_a_constant_is_the_answer_and_not_a_call() {
8202        let text = ir(concat!(
8203            "double a = __builtin_ceil(1.5);\n",
8204            "double b = __builtin_floor(1.5);\n",
8205            "double c = __builtin_trunc(-1.5);\n",
8206            // A half goes away from zero and not to even, which is where C and the default
8207            // rounding of IEEE 754 part company.
8208            "double d = __builtin_round(2.5);\n",
8209            // The sign survives a number that rounds away to nothing, so this is a negative zero.
8210            "double e = __builtin_ceil(-0.5);\n",
8211            "double f = __builtin_fmax(1.0, 2.0);\n",
8212            "double g = __builtin_fmin(1.0, 2.0);\n",
8213            "float h = __builtin_ceilf(1.25f);\n",
8214            // The plain name is the same answer, which is what a program that included `math.h`
8215            // and never wrote a prefix reaches.
8216            "double ceil(double x);\n",
8217            "double i = ceil(2.25);\n",
8218        ));
8219        assert!(text.contains("global @a : f64 = 0x4000000000000000,"), "{text}");
8220        assert!(text.contains("global @b : f64 = 0x3ff0000000000000,"), "{text}");
8221        assert!(text.contains("global @c : f64 = 0xbff0000000000000,"), "{text}");
8222        assert!(text.contains("global @d : f64 = 0x4008000000000000,"), "{text}");
8223        assert!(text.contains("global @e : f64 = 0x8000000000000000,"), "{text}");
8224        assert!(text.contains("global @f : f64 = 0x4000000000000000,"), "{text}");
8225        assert!(text.contains("global @g : f64 = 0x3ff0000000000000,"), "{text}");
8226        assert!(text.contains("global @h : f32 = 0x40000000,"), "{text}");
8227        assert!(text.contains("global @i : f64 = 0x4008000000000000,"), "{text}");
8228        assert!(!text.contains("call"), "{text}");
8229    }
8230
8231    /// A math library builtin handed anything else is a call to the library function it is.
8232    ///
8233    /// gcc emits `jmp ceil` for `__builtin_ceil` on x86-64 at the default architecture, measured
8234    /// on gcc 16.2.0, and reaches the `roundsd` instruction only under `-msse4.1`. So the call is
8235    /// what a program gets from gcc too, and the name on it is the plain one, which is the whole
8236    /// point of the prefixed spelling: a program writing it reaches the library's function even
8237    /// where a macro or a definition of its own has taken the short name.
8238    #[test]
8239    fn a_math_library_builtin_of_anything_else_is_a_call_to_the_library() {
8240        let text = ir(concat!(
8241            "double f(double x) { return __builtin_ceil(x); }\n",
8242            "float g(float x) { return __builtin_floorf(x); }\n",
8243            "double h(double x, double y) { return __builtin_fmax(x, y); }\n",
8244        ));
8245        assert!(text.contains("call @ceil("), "{text}");
8246        assert!(text.contains("call @floorf("), "{text}");
8247        assert!(text.contains("call @fmax("), "{text}");
8248
8249        // The two the rounding mode decides are calls even when the argument is a constant, since
8250        // what they answer is not known until the program runs. gcc refuses a static initializer
8251        // written with one for that reason, so there is nothing to fold here either.
8252        let text = ir(concat!(
8253            "double f(void) { return __builtin_rint(2.5); }\n",
8254            "double g(void) { return __builtin_nearbyint(2.5); }\n",
8255        ));
8256        assert!(text.contains("call @rint("), "{text}");
8257        assert!(text.contains("call @nearbyint("), "{text}");
8258
8259        // A nan operand is the library's rule rather than the machine's, 7.12.12.2 saying the
8260        // answer is the other operand, and gcc will not fold that one either.
8261        let text = ir("double f(void) { return __builtin_fmin(__builtin_nan(\"\"), 1.0); }\n");
8262        assert!(text.contains("call @fmin("), "{text}");
8263
8264        // `-fno-builtin-ceil` is a program saying it means its own `ceil`, and it leaves the
8265        // prefixed spelling alone, which is what writing the prefix is for.
8266        let plain = concat!("double ceil(double x);\n", "double f(void) { return ceil(2.25); }\n");
8267        let mut opts = options();
8268        opts.emit = EmitKind::Ir;
8269        opts.no_builtin = vec!["ceil".to_owned()];
8270        assert!(run(&opts, plain).text().contains("call @ceil("), "-fno-builtin-ceil");
8271    }
8272
8273    /// A `constexpr` object is a named constant, which is the whole reason the keyword exists.
8274    ///
8275    /// C23 6.6p8 puts two of them on the list an integer constant expression is built from: one
8276    /// of an arithmetic type, and a member of one of a structure or union type. A subscript of
8277    /// one is not on the list and is a variably modified type in gcc 16 as well, and every
8278    /// number here is what gcc 16 gives on x86-64.
8279    #[test]
8280    fn a_constexpr_object_is_a_constant_wherever_one_is_required() {
8281        let text = ir(concat!(
8282            "constexpr int side = 4;\n",
8283            "constexpr int wider = side + 1;\n",
8284            "constexpr double half = 1.5;\n",
8285            "struct point { int x; int y; };\n",
8286            "constexpr struct point origin = { 5, 6 };\n",
8287            "int square[side * side];\n",
8288            "int rectangle[wider];\n",
8289            "int rounded[(int)half * 2];\n",
8290            "int across[origin.y];\n",
8291            "enum named { four = side };\n",
8292            "int e = four;\n",
8293        ));
8294        assert!(text.contains("global @square : bytes 64 ="), "{text}");
8295        assert!(text.contains("global @rectangle : bytes 20 ="), "{text}");
8296        assert!(text.contains("global @rounded : bytes 8 ="), "{text}");
8297        assert!(text.contains("global @across : bytes 24 ="), "{text}");
8298        assert!(text.contains("global @e : i32 = 4,"), "{text}");
8299
8300        // A `const` object is not one of them, which is what makes `int a[n];` a variable
8301        // length array in C and is the distinction the keyword was added to draw.
8302        let mut opts = options();
8303        opts.emit = EmitKind::Ir;
8304        let konst = "const int n = 1;\nint a[n];\n";
8305        let message = "/main.c:2:5: error: variably modified 'a' at file scope [E0538]";
8306        assert_eq!(run(&opts, konst).messages, [message]);
8307
8308        // Nor is a subscript of one, which gcc 16 refuses in the same words.
8309        let subscript = "constexpr int t[3] = { 1, 2, 3 };\nint a[t[1]];\n";
8310        assert_eq!(run(&opts, subscript).messages, [message]);
8311
8312        // And `constexpr` implies `const`, so the address of one is an address of a `const`.
8313        let address = "constexpr int c = 3;\nint *p = &c;\n";
8314        let warning = "/main.c:2:6: warning: initialization discards 'const' qualifier from \
8315             pointer target type [E0514]";
8316        assert_eq!(run(&opts, address).messages, [warning]);
8317    }
8318
8319    /// A member whose size was refused is not a flexible array member, whatever it looks like.
8320    ///
8321    /// The refusal leaves the member with no size, which is also how `int a[]` is written, so
8322    /// without the count that tells the two apart the rules about where a flexible array member
8323    /// may sit read the wreckage of the first error as a second mistake. gcc 16.2.0 says one
8324    /// thing about each of these and so does this, which is what the program can act on: adding
8325    /// a named member to `struct D` makes the message about `k` no clearer, and moving `a` to
8326    /// the end of `struct E` does not either.
8327    #[test]
8328    fn a_member_whose_size_was_refused_is_not_a_flexible_array_member() {
8329        let mut opts = options();
8330        opts.emit = EmitKind::Ir;
8331
8332        let alone = "int k;\nextern struct D { int a[k]; } ed;\n";
8333        let message = "/main.c:2:23: error: variably modified 'a' at file scope [E0538]";
8334        assert_eq!(run(&opts, alone).messages, [message]);
8335
8336        // And not one in the wrong place either, which is the other half of the same rule.
8337        let first = "int k;\nextern struct E { int a[k]; int b; } ee;\n";
8338        assert_eq!(run(&opts, first).messages, [message]);
8339
8340        // A size that is refused for a reason of its own, to show the count is about the
8341        // refusal rather than about the one message that happens to have been found first.
8342        let negative = "struct F { int a[-1]; };\n";
8343        let refused = "/main.c:1:18: error: size of array 'a' is negative [E0536]";
8344        assert_eq!(run(&opts, negative).messages, [refused]);
8345
8346        // The member that was written with no size at all is still a flexible array member, and
8347        // a structure with nothing else in it still has no named member to hang one off.
8348        let flexible = "struct G { int a[]; };\n";
8349        let named = "/main.c:1:16: error: flexible array member in a struct with no named \
8350             members [E0554]";
8351        assert_eq!(run(&opts, flexible).messages, [named]);
8352    }
8353
8354    /// A pointer to an array, where the qualifiers are on the element and the comparison is not.
8355    ///
8356    /// 6.7.3p10 says the qualifiers in an array declaration belong to the element, so `const int
8357    /// [4]` is an unqualified array of `const int` and not a qualified array of `int`. Compatibility
8358    /// then reads the element types, finds one `const` and one not, and calls the two arrays
8359    /// incompatible, which makes `const int (*)[4] = p` an incompatible pointer rather than a
8360    /// pointer that gained a qualifier. That is what the wording said before C23 and it is not what
8361    /// any compiler does: gcc and clang take it, C23 wrote the rule the way they read it, and the
8362    /// two directions are told apart the way they are everywhere else, which is that adding a
8363    /// qualifier is silent and dropping one is worth a word.
8364    ///
8365    /// Found in libwebp, where `src/enc/vp8l_enc.c` takes the address of a `HistogramBuckets` out of
8366    /// a structure into a `const HistogramBuckets *const`, and a whole file of a real library did
8367    /// not compile for it.
8368    #[test]
8369    fn a_pointer_to_an_array_gains_a_qualifier_the_same_way_a_pointer_to_anything_else_does() {
8370        let mut opts = options();
8371        opts.emit = EmitKind::Ir;
8372        let prefix = "typedef unsigned int B[4];\nstruct H { B category[2]; };\n";
8373
8374        // Adding it, which is the direction the library writes and the one nothing is owed for.
8375        let adding = format!("{prefix}const B *f(struct H *h) {{ return &h->category[0]; }}\n");
8376        assert_eq!(run(&opts, &adding).messages, [] as [String; 0]);
8377
8378        // And the same thing written out rather than through the typedef, since the typedef is a
8379        // spelling and the rule is about the array.
8380        let plain = concat!(
8381            "const unsigned int (*f(unsigned int (*p)[4]))[4] { return p; }\n",
8382            "const unsigned int (*g(unsigned int (*p)[2][3]))[2][3] { return p; }\n",
8383        );
8384        assert_eq!(run(&opts, plain).messages, [] as [String; 0]);
8385
8386        // Dropping it, which is the direction that is worth a word, and the word is the one every
8387        // other pointer target gets rather than a complaint about the types not matching.
8388        let dropping = format!("{prefix}B *f(const B *p) {{ return p; }}\n");
8389        let warning = "/main.c:3:27: warning: return discards 'const' qualifier from pointer target type \
8390             [E0514]";
8391        assert_eq!(run(&opts, &dropping).messages, [warning]);
8392
8393        // A pointer to an array of something else is still an incompatible pointer, because
8394        // nothing here is about the element being a different type.
8395        let wrong = "const unsigned int (*f(unsigned short (*p)[4]))[4] { return p; }\n";
8396        let error = "/main.c:1:61: error: returning 'unsigned short (*)[4]' from a function with \
8397             incompatible return type 'const unsigned int (*)[4]' [E0512]";
8398        assert_eq!(run(&opts, wrong).messages, [error]);
8399    }
8400
8401    /// A definition that names its parameters and then declares them under the list.
8402    ///
8403    /// The declarations say what the types are, 6.9.1p6, and what the function takes is those
8404    /// types with the default argument promotions over them, which is what a caller of an
8405    /// unprototyped function hands over. A prototype already in scope overrules the promoted
8406    /// types, since a header saying `int narrow(char);` over a definition written this way is
8407    /// the pairing all the code written this way relies on and 6.7.6.3p15 is read that way by
8408    /// every compiler.
8409    #[test]
8410    fn an_old_style_definition_takes_its_types_from_the_declarations_under_its_list() {
8411        // C17, since the default dialect is the one that warns about the form and this is
8412        // about what it means rather than about the warning.
8413        let mut opts = options();
8414        opts.std = Std::C17;
8415        let source = concat!(
8416            "int add(a, b)\n",
8417            "int a;\n",
8418            "int b;\n",
8419            "{ return a + b; }\n",
8420            "int promoted(c)\n",
8421            "char c;\n",
8422            "{ return c; }\n",
8423            "int narrow(char);\n",
8424            "int narrow(c)\n",
8425            "char c;\n",
8426            "{ return c; }\n",
8427            "int first(a)\n",
8428            "int a[4];\n",
8429            "{ return a[0]; }\n",
8430        );
8431        let result = run(&opts, source);
8432        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
8433        let text = result.text();
8434        assert!(text.contains("add : int(int, int) function external defined"), "{text}");
8435        assert!(text.contains("promoted : int(int) function external defined"), "{text}");
8436        // The body still sees the `char` it was declared as, whatever the caller hands over.
8437        assert!(text.contains("c : char object automatic defined"), "{text}");
8438        assert!(text.contains("narrow : int(char) function external defined"), "{text}");
8439        // An array parameter is a pointer here as much as it is in a prototype.
8440        assert!(text.contains("first : int(int *) function external defined"), "{text}");
8441    }
8442
8443    /// What the two halves of an old-style parameter list can disagree about.
8444    ///
8445    /// Each of these is a sentence gcc 16 has, and every message below is the one it prints,
8446    /// read off it on x86-64 rather than reasoned about. The last two are the dialect: a name
8447    /// with no declaration is an `int` in C89 and a diagnostic from C99 on, and the whole form
8448    /// left the language in C23, where gcc still takes it and warns.
8449    #[test]
8450    fn the_two_halves_of_an_old_style_parameter_list_have_to_agree() {
8451        let mut opts = options();
8452        opts.std = Std::C17;
8453        for (source, message) in [
8454            ("int f(a, a)\nint a;\n{ return a; }\n", "1:10: error: multiple parameters named 'a'"),
8455            (
8456                "int f(a)\nint a;\nint b;\n{ return a; }\n",
8457                "3:5: error: declaration for parameter 'b' but no such parameter",
8458            ),
8459            ("int f(a)\nint a;\nint a;\n{ return a; }\n", "3:5: error: redefinition of parameter"),
8460            ("int f(a)\nint a = 1;\n{ return a; }\n", "2:5: error: parameter 'a' is initialized"),
8461            (
8462                "int f(a)\nstatic int a;\n{ return a; }\n",
8463                "2:12: error: storage class specified for parameter 'a'",
8464            ),
8465            (
8466                "int f(char);\nint f(a)\nshort a;\n{ return a; }\n",
8467                "2:7: error: argument 'a' doesn't match prototype",
8468            ),
8469        ] {
8470            let result = run(&opts, source);
8471            assert!(result.failed(), "expected this to fail:\n{source}");
8472            assert!(result.messages[0].contains(message), "{:?}", result.messages);
8473        }
8474
8475        // A name the declarations never mention. C89 gave it an `int` and gcc still takes it
8476        // in that dialect, and every dialect after it made the same line a diagnostic.
8477        let implicit = "int f(a, b)\nint a;\n{ return a + b; }\n";
8478        let mut older = options();
8479        older.std = Std::C89;
8480        assert!(!run(&older, implicit).failed(), "{:?}", run(&older, implicit).messages);
8481        let result = run(&opts, implicit);
8482        assert!(
8483            result.messages[0].contains("1:10: error: type of 'b' defaults to 'int'"),
8484            "{:?}",
8485            result.messages
8486        );
8487
8488        // C23 took the form out of the language and gcc kept accepting it with a warning, and
8489        // a warning is what this is, because the code written this way is not going to be
8490        // rewritten and refusing it would put the compiler out of reach of it.
8491        let mut newer = options();
8492        newer.std = Std::C23;
8493        let plain = "int f(a)\nint a;\n{ return a; }\n";
8494        let result = run(&newer, plain);
8495        assert!(!result.failed(), "{:?}", result.messages);
8496        assert_eq!(
8497            result.messages,
8498            ["/main.c:1:5: warning: old-style function definition [E0412]"]
8499        );
8500        assert!(run(&opts, plain).messages.is_empty(), "and nothing to say in the dialects before");
8501    }
8502
8503    /// The two obsolete designators, which are silent until `-pedantic` asks about them.
8504    ///
8505    /// `[3] 7` is what GCC had for an array before C99 settled on `[3] = 7`, and `x: 7` is the
8506    /// same era's spelling for a member. Both are still in code written against a compiler of
8507    /// that era, and gcc 16 takes both without a word unless it is asked to be pedantic, which
8508    /// is where the columns below come from as well.
8509    #[test]
8510    fn the_obsolete_designators_are_taken_and_are_pedantic_warnings() {
8511        let array = "int a[8] = { [3] 7 };\n";
8512        let member = "struct s { int x; } v = { x: 7 };\n";
8513        for source in [array, member] {
8514            let result = run(&options(), source);
8515            assert!(!result.failed(), "{:?}", result.messages);
8516            assert!(result.messages.is_empty(), "nothing to say: {:?}", result.messages);
8517        }
8518
8519        let mut asked = options();
8520        asked.pedantic = true;
8521        assert_eq!(
8522            run(&asked, array).messages,
8523            ["/main.c:1:18: warning: obsolete designator, write `[i] =` instead [E0415]"]
8524        );
8525        assert_eq!(
8526            run(&asked, member).messages,
8527            ["/main.c:1:27: warning: obsolete designator, write `.field =` instead [E0413]"]
8528        );
8529    }
8530
8531    /// A type nothing is ever an object of is a type `sizeof` still has to answer about, which
8532    /// is what `991014-1.c` in the gcc.c-torture execution suite asks.
8533    ///
8534    /// The limit is `PTRDIFF_MAX` and it is the same one for an array and for a record, so a
8535    /// record of every byte an object may have is laid out and one byte more is refused. All
8536    /// four numbers are what gcc 16 gives on x86-64.
8537    #[test]
8538    fn a_type_is_refused_when_it_passes_the_largest_object_and_not_before() {
8539        let text = ir(concat!(
8540            "struct huge_struct { short buf[(1L << 62) - 256]; int a, b, c, d; };\n",
8541            "struct brim { char buf[9223372036854775807L]; };\n",
8542            "struct bitty { char buf[9223372036854775800L]; int x : 1; };\n",
8543            "unsigned long h = sizeof(struct huge_struct);\n",
8544            "unsigned long b = sizeof(struct brim);\n",
8545            "unsigned long y = sizeof(struct bitty);\n",
8546        ));
8547        assert!(text.contains("global @h : i64 = 9223372036854775312,"), "{text}");
8548        assert!(text.contains("global @b : i64 = 9223372036854775807,"), "{text}");
8549        assert!(text.contains("global @y : i64 = 9223372036854775804,"), "{text}");
8550
8551        let mut opts = options();
8552        opts.emit = EmitKind::Ir;
8553        let over = "struct over { char buf[9223372036854775800L]; char x[8]; };\n";
8554        let message = "/main.c:1:1: error: type 'struct over' is too large [E0560]";
8555        assert_eq!(run(&opts, over).messages, [message]);
8556        let array = "struct wide { short buf[1L << 62]; };\n";
8557        let message = "/main.c:1:25: error: size of array 'buf' exceeds \
8558             maximum object size '9223372036854775807' [E0537]";
8559        assert_eq!(run(&opts, array).messages[0], message);
8560    }
8561
8562    /// A byte in the source that is not part of a character, which only a literal may hold.
8563    ///
8564    /// The source cannot be a `&str` here, which is the whole point: a file is bytes and only
8565    /// mostly text.
8566    fn compile_bytes(source: &[u8]) -> Compiled {
8567        let mut opts = options();
8568        opts.emit = EmitKind::Ir;
8569        let mut fs = MemoryFileSystem::new();
8570        fs.insert("/main.c", source.to_vec());
8571        compile(&opts, "/main.c", &fs)
8572    }
8573
8574    /// A raw byte inside a string literal is that byte, which gcc has always taken and which is
8575    /// the only place in a source file where a byte does not have to be part of a character.
8576    /// Replacing it would give the object three bytes rather than one, since the replacement
8577    /// character is three bytes of UTF-8, so the object would not be the one that was written
8578    /// even where the diagnostic is ignored. Anywhere else the byte is still a mistake, which
8579    /// is where gcc draws the same line.
8580    #[test]
8581    fn a_byte_that_is_not_a_character_is_kept_in_a_literal_and_refused_outside_one() {
8582        let mut source = b"char s[] = \"a".to_vec();
8583        source.push(0xff);
8584        source.extend_from_slice(b"b\";\nchar c = '");
8585        source.push(0xff);
8586        source.extend_from_slice(b"';\n");
8587        let result = compile_bytes(&source);
8588        assert_eq!(result.messages, Vec::<String>::new(), "a raw byte in a literal is that byte");
8589        assert!(result.text().contains(r#"bytes "a\ffb\00""#), "{}", result.text());
8590        // Plain `char` is signed on this target, so the constant is minus one rather than 255.
8591        assert!(result.text().contains("global @c : i8 = -1,"), "{}", result.text());
8592
8593        let mut stray = b"int a".to_vec();
8594        stray.push(0xff);
8595        stray.extend_from_slice(b" = 1;\n");
8596        let result = compile_bytes(&stray);
8597        assert!(
8598            result.messages.iter().any(|m| m.contains("source is not valid UTF-8 here")),
8599            "{:?}",
8600            result.messages
8601        );
8602    }
8603
8604    #[test]
8605    fn an_object_becomes_a_global_with_an_image_and_a_function_becomes_a_func() {
8606        let text = ir("int x = 7;\nint add(int a, int b) { return a + b; }\n");
8607        assert!(text.contains("global @x : i32 = 7, align 4, linkage(external)\n"), "{text}");
8608        let expected = "\
8609func @add(i32, i32) -> i32, linkage(external) {
8610block0(%0: i32, %1: i32):
8611    %2 = add.nsw %0, %1
8612    return %2
8613}
8614";
8615        assert!(text.contains(expected), "{text}");
8616    }
8617
8618    #[test]
8619    fn a_local_nothing_takes_the_address_of_is_a_value_and_never_a_stack_slot() {
8620        let text = body("int f(int n) { int a = n + 1; int b = a * 2; return a + b; }\n");
8621        assert!(!text.contains("alloca"), "{text}");
8622        assert!(!text.contains("load"), "{text}");
8623        assert!(!text.contains("store"), "{text}");
8624    }
8625
8626    #[test]
8627    fn a_local_whose_address_is_taken_gets_a_slot_in_the_entry_block() {
8628        let text = body("int g(int *);\nint f(void) { int a = 1; return g(&a); }\n");
8629        let expected = "\
8630block0:
8631    %0 = alloca, size 4, align 4
8632    %1 = iconst.i32 1
8633    store %1 -> %0, align 4, tbaa !1
8634    %2 = call @g(%0) : (ptr) -> i32
8635    return %2
8636";
8637        assert_eq!(text, expected);
8638    }
8639
8640    #[test]
8641    fn a_loop_carries_what_it_changes_as_block_parameters() {
8642        // The whole point of building SSA during the walk rather than after it: `i` and
8643        // `total` are values that arrive on an edge, and neither has ever been in memory.
8644        let text = body(
8645            "int f(int n) {\n  int total = 0;\n  for (int i = 0; i < n; i++) total += i;\n  \
8646             return total;\n}\n",
8647        );
8648        assert!(!text.contains("alloca"), "{text}");
8649        assert!(text.contains("block1(%3: i32, %4: i32):"), "{text}");
8650        assert!(text.contains("jump block1("), "{text}");
8651    }
8652
8653    #[test]
8654    fn a_comparison_used_as_a_condition_is_not_widened_and_narrowed_again() {
8655        let text = body("int f(int a, int b) { if (a < b) return 1; return 0; }\n");
8656        assert!(text.contains("icmp slt %0, %1"), "{text}");
8657        assert!(!text.contains("zext"), "{text}");
8658    }
8659
8660    #[test]
8661    fn the_right_side_of_a_short_circuit_is_in_a_block_of_its_own() {
8662        let text = body("int f(int a, int b) { return a && b; }\n");
8663        let expected = "\
8664block0(%0: i32, %1: i32):
8665    %2 = iconst.i32 0
8666    %3 = icmp ne %0, %2
8667    %4 = iconst.i1 0
8668    br_if %3, block1, block2(%4)
8669
8670block1:
8671    %5 = iconst.i32 0
8672    %6 = icmp ne %1, %5
8673    jump block2(%6)
8674
8675block2(%7: i1):
8676    %8 = zext.i32 %7
8677    return %8
8678";
8679        assert_eq!(text, expected);
8680    }
8681
8682    #[test]
8683    fn code_after_a_return_is_not_built_and_does_not_leave_an_empty_block_behind() {
8684        let text = body("int f(int a) { if (a) return 1; else return 2; return 3; }\n");
8685        // Three blocks, the test and the two arms. The join the `return 3` would need is
8686        // never created, because a block nothing branches to is not a block.
8687        assert!(!text.contains("block3"), "{text}");
8688        assert!(!text.contains("iconst.i32 3"), "{text}");
8689    }
8690
8691    #[test]
8692    fn falling_off_the_end_returns_zero_from_main_and_nothing_from_a_void_function() {
8693        assert!(body("int main(void) { }\n").contains("iconst.i32 0\n    return"));
8694        assert_eq!(body("void f(void) { }\n"), "block0:\n    return\n");
8695        assert!(body("int f(void) { }\n").contains("unreachable"));
8696    }
8697
8698    #[test]
8699    fn a_structure_is_copied_rather_than_held_in_a_value() {
8700        let text = body(
8701            "struct point { int x, y; };\n\
8702             int f(void) { struct point p = { 1, 2 }; struct point q = p; return q.x; }\n",
8703        );
8704        assert!(text.contains("memcpy"), "{text}");
8705    }
8706
8707    #[test]
8708    fn an_initializer_that_leaves_part_of_an_object_unwritten_zeroes_it_first() {
8709        let text = body("int f(void) { int a[4] = { 1 }; return a[3]; }\n");
8710        assert!(text.contains("memset"), "{text}");
8711    }
8712
8713    #[test]
8714    fn a_switch_is_one_branch_and_a_case_that_falls_through_carries_what_it_wrote() {
8715        let text = body(
8716            "int f(int x) { int r = 0; switch (x) { case 1: r = 1; case 2: r += 2; break; \
8717             default: r = 4; } return r; }\n",
8718        );
8719        let expected = "\
8720block0(%0: i32):
8721    %1 = iconst.i32 0
8722    switch %0, block1, [1 => block2, 2 => block3(%1)]
8723
8724block1:
8725    %2 = iconst.i32 4
8726    jump block4(%2)
8727
8728block2:
8729    %3 = iconst.i32 1
8730    jump block3(%3)
8731
8732block3(%4: i32):
8733    %5 = iconst.i32 2
8734    %6 = add.nsw %4, %5
8735    jump block4(%6)
8736
8737block4(%7: i32):
8738    return %7
8739";
8740        assert_eq!(text, expected);
8741    }
8742
8743    #[test]
8744    fn a_case_range_is_tested_for_rather_than_put_in_the_table() {
8745        // GNU's `case 1 ... 9`. Nine table entries would be nine here and four billion for the
8746        // range a program is allowed to write, so it is a subtraction and one unsigned compare.
8747        let text = body("int f(int x) { switch (x) { case 1 ... 9: return 1; } return 0; }\n");
8748        assert!(text.contains("%2 = sub %0, %1"), "{text}");
8749        assert!(text.contains("icmp ule"), "{text}");
8750        assert!(!text.contains("switch"), "{text}");
8751    }
8752
8753    #[test]
8754    fn break_leaves_the_switch_and_continue_leaves_the_loop_around_it() {
8755        let text = body(
8756            "int f(int n) { int t = 0; for (int i = 0; i < n; i++) { switch (i) { \
8757             case 0: continue; case 1: break; default: t += i; } t++; } return t; }\n",
8758        );
8759        // The `continue` goes to the step and the `break` goes to the `t++` after the switch,
8760        // which is also where the default falls out to.
8761        assert!(text.contains("switch %3, block4, [0 => block5, 1 => block6]"), "{text}");
8762        assert!(text.contains("block5:\n    jump block7("), "{text}");
8763        assert!(text.contains("block6:\n    jump block8("), "{text}");
8764    }
8765
8766    #[test]
8767    fn a_switch_with_nothing_to_branch_on_still_runs_what_comes_after_it() {
8768        assert_eq!(body("void f(int x) { switch (x) { } }\n"), "block0(%0: i32):\n    return\n");
8769    }
8770
8771    #[test]
8772    fn a_label_a_loop_is_only_entered_through_builds_the_loop_around_it() {
8773        // A branch into the middle of a loop that nothing else reaches, the Duff's device shape.
8774        // The `while` is not reached in order, so the walk starts a block nothing branches to and
8775        // builds it from there. What comes out is the loop with an edge straight into its body,
8776        // and the header that nothing arrives at is pruned.
8777        let text = body(
8778            "int f(int x, int n) { switch (x) { case 1: break; while (n) { case 2: n--; } } \
8779             return n; }\n",
8780        );
8781        // `case 2` lands on the body, `case 1` and the default land on the return, and the test
8782        // at the bottom of the loop comes back round to the body.
8783        assert!(text.contains("switch %0, block1(%1), [1 => block2, 2 => block3(%1)]"), "{text}");
8784        assert!(text.contains("block3(%3: i32):\n    %4 = iconst.i32 1"), "{text}");
8785        assert!(text.contains("block4:\n    jump block3("), "{text}");
8786    }
8787
8788    #[test]
8789    fn a_goto_into_a_loop_body_enters_it_without_the_test() {
8790        // The same thing through a `goto`. The first pass through the body runs whatever the
8791        // label is on, and only then does the loop reach its own test.
8792        let text = body("int f(int x, int n) { goto in; while (n) { in: n--; } return n; }\n");
8793        assert!(text.starts_with("block0(%0: i32, %1: i32):\n    jump block1(%1)"), "{text}");
8794        assert!(text.contains("block1(%2: i32):\n    %3 = iconst.i32 1"), "{text}");
8795        assert!(text.contains("br_if %6, block2, block3"), "{text}");
8796    }
8797
8798    #[test]
8799    fn a_goto_is_a_jump_to_the_block_the_label_starts() {
8800        let text = body("int f(int x) { int r = 0; if (x) goto out; r = 1; out: return r; }\n");
8801        // Both edges into `out` carry what `r` holds on the way, and neither is a stack slot. The
8802        // block the `goto` jumps out of is empty and hands its edge on, which is what moves `out`
8803        // up the block list to second place.
8804        assert!(!text.contains("alloca"), "{text}");
8805        assert!(text.contains("block2(%4: i32):\n    return %4"), "{text}");
8806        assert_eq!(text.matches("jump block2(").count(), 2, "{text}");
8807    }
8808
8809    #[test]
8810    fn a_backward_goto_is_a_loop_and_carries_what_it_changes() {
8811        let text =
8812            body("int f(int n) { int i = 0; again: if (i < n) { i++; goto again; } return i; }\n");
8813        assert!(!text.contains("alloca"), "{text}");
8814        assert!(text.contains("block1(%2: i32):"), "{text}");
8815        assert!(text.contains("jump block1(%5)"), "{text}");
8816    }
8817
8818    #[test]
8819    fn a_label_nothing_reaches_is_taken_out_rather_than_left_for_the_verifier() {
8820        // A block nothing branches to is not a legal function, and which labels are dead is not
8821        // known until the last statement has been walked, since the `goto` is allowed to be it.
8822        assert_eq!(
8823            body("int f(int x) { return x; spare: return 0; }\n"),
8824            "block0(%0: i32):\n    return %0\n"
8825        );
8826    }
8827
8828    #[test]
8829    fn a_bit_field_is_read_by_loading_the_bytes_it_lies_in_and_shifting() {
8830        let text = body(
8831            "struct s { unsigned a : 3; signed b : 5; };\nint f(struct s *p) { return p->b; }\n",
8832        );
8833        // One byte holds both fields, and the signed one needs no mask: shifting it down
8834        // arithmetically is what says its top bit is a sign.
8835        assert_eq!(
8836            text,
8837            "\
8838block0(%0: ptr):
8839    %1 = load.i8 %0, align 1
8840    %2 = iconst.i8 3
8841    %3 = ashr %1, %2
8842    %4 = sext.i32 %3
8843    return %4
8844"
8845        );
8846    }
8847
8848    #[test]
8849    fn a_store_to_a_bit_field_does_not_write_a_byte_it_has_no_bit_in() {
8850        // C11 says an ordinary member beside a bit-field is a memory location of its own, so
8851        // the four byte store this would take is a data race in a program that has none. The
8852        // three bytes of `a` go in as two and one, and `c` is not touched.
8853        let text =
8854            body("struct s { int a : 24; char c; };\nvoid f(struct s *p, int v) { p->a = v; }\n");
8855        assert_eq!(
8856            text,
8857            "\
8858block0(%0: ptr, %1: i32):
8859    %2 = iconst.i32 16777215
8860    %3 = and %1, %2
8861    %4 = trunc.i16 %3
8862    store %4 -> %0, align 2
8863    %5 = iconst.i32 16
8864    %6 = lshr %3, %5
8865    %7 = trunc.i8 %6
8866    %8 = iconst.i64 2
8867    %9 = ptr_add %0, %8
8868    store %7 -> %9, align 1
8869    return
8870"
8871        );
8872    }
8873
8874    #[test]
8875    fn what_an_assignment_to_a_bit_field_is_worth_is_what_fits_in_it() {
8876        let text =
8877            body("struct s { unsigned b : 5; };\nunsigned f(struct s *p) { return p->b = 33; }\n");
8878        // 33 does not fit in five bits, and 1 is both what goes in the field and what the
8879        // assignment is worth.
8880        assert!(text.contains("%3 = iconst.i8 31\n    %4 = and %2, %3"), "{text}");
8881        assert!(text.ends_with("%9 = zext.i32 %4\n    return %9\n"), "{text}");
8882    }
8883
8884    #[test]
8885    fn an_assignment_a_statement_throws_away_builds_none_of_what_it_is_worth() {
8886        // The value of an assignment to a bit-field takes a shift to build, and a statement
8887        // has no use for it. Nothing here reads back what was stored.
8888        let text = body("struct s { signed b : 5; };\nvoid f(struct s *p) { p->b = 3; }\n");
8889        assert_eq!(text.matches("ashr").count(), 0, "{text}");
8890        assert!(text.ends_with("store %8 -> %0, align 1\n    return\n"), "{text}");
8891    }
8892
8893    #[test]
8894    fn a_bit_field_in_an_initializer_goes_in_over_bytes_that_were_zeroed_first() {
8895        // A bit-field writes part of a byte and leaves the rest of it alone, so the object has
8896        // to be zero before it goes in or what the initializer did not name is whatever the
8897        // stack held.
8898        let text = body(
8899            "struct s { int a : 3; int b; };\nint f(void) { struct s v = { 1 }; return v.b; }\n",
8900        );
8901        assert!(text.contains("memset %0, %1, size 8, align 4"), "{text}");
8902    }
8903
8904    #[test]
8905    fn the_image_of_a_static_bit_field_is_the_bytes_the_fields_share() {
8906        // Two fields in one byte are not two entries in the image, because an image is written
8907        // in bytes: they are the byte they are both in.
8908        let text = ir("struct s { unsigned a : 3; unsigned b : 5; } g = { 1, 2 };\n");
8909        assert!(
8910            text.contains("global @g : bytes 4 = { bytes \"\\11\", zero 3 }, align 4"),
8911            "{text}"
8912        );
8913    }
8914
8915    #[test]
8916    fn an_initialized_flexible_array_member_makes_the_object_larger_than_its_type() {
8917        // `sizeof` answers without the array and the definition has to hold what was written, so
8918        // the object is the size of its image. gcc 16 gives these four, three and two bytes and
8919        // so does this. The image used to be written at the size the type had, which left the
8920        // verifier looking at twenty bytes going into four.
8921        let text = ir(concat!(
8922            "struct a { int i; int j[]; } x = { 1, { 2, 0, 2, 3 } };\n",
8923            "struct b { char c; char p[]; } y = { 'o', \"wx\" };\n",
8924            "struct c { char c; char p[]; } z = { '9', { 'e', 'b' } };\n",
8925            "char s[2] = \"hi\";\n",
8926        ));
8927        assert!(
8928            text.contains("global @x : bytes 20 = { i32 1, i32 2, i32 0, i32 2, i32 3 }"),
8929            "{text}"
8930        );
8931        assert!(text.contains("global @y : bytes 4 = { i8 111, bytes \"wx\\00\" }"), "{text}");
8932        assert!(text.contains("global @z : bytes 3 = { i8 57, i8 101, i8 98 }"), "{text}");
8933        // The array with a length of its own still cuts the literal down to it, which is the
8934        // one case in C where a string initializer drops its terminator.
8935        assert!(text.contains("global @s : bytes 2 = { bytes \"hi\" }"), "{text}");
8936    }
8937
8938    #[test]
8939    fn a_definition_takes_a_parameter_it_left_unnamed() {
8940        // The entry block's parameters are the definition's, and one the front end dropped for
8941        // having no name left the two lists different lengths, which the walk read as an
8942        // old-style definition and refused. gcc has taken these for far longer than C23 has.
8943        let text = ir("int f(int a, int) { return a; }\n");
8944        assert!(text.contains("func @f(i32, i32) -> i32"), "{text}");
8945        assert!(text.contains("block0(%0: i32, %1: i32):"), "{text}");
8946
8947        // The unnamed one first, so that the named one is the second parameter of the entry
8948        // block and not the first: the list says the order and not only how many there are.
8949        let text = ir("int g(int, int n) { return n; }\n");
8950        assert!(text.contains("block0(%0: i32, %1: i32):\n    return %1\n"), "{text}");
8951    }
8952
8953    #[test]
8954    fn an_assignment_of_a_structure_is_the_object_it_wrote() {
8955        // `d = e = c` used to be refused, because the middle assignment is a value of structure
8956        // type and the walk had nowhere to read one from. What an assignment is worth is the
8957        // value it stored, so the object it stored into is the answer and the chain is three
8958        // copies out of the one source with no temporary in it.
8959        let text = body(concat!(
8960            "struct s { int f; int g; };\n",
8961            "void h(struct s *a, struct s *c, struct s *d, struct s *e)\n",
8962            "{ *d = *e = a[0] = *c; }\n",
8963        ));
8964        assert_eq!(text.matches("memcpy").count(), 3, "{text}");
8965        assert!(text.contains("memcpy %8, %1, size 8, align 4\n"), "{text}");
8966        assert!(text.contains("memcpy %3, %8, size 8, align 4\n"), "{text}");
8967        assert!(text.contains("memcpy %2, %3, size 8, align 4\n"), "{text}");
8968    }
8969
8970    #[test]
8971    fn a_string_literal_stops_at_the_end_of_the_array_it_is_filling() {
8972        // The excess used to be laid into the object anyway, so the row after was written over
8973        // and the image refused the entry that came to it. C 6.7.10p14 says the terminator goes
8974        // in only if there is room for it, and gcc discards the rest of a literal that is longer
8975        // still, which is what the first of these is and why it warns.
8976        let mut opts = options();
8977        opts.emit = EmitKind::Ir;
8978        let result = run(
8979            &opts,
8980            concat!(
8981                "const char a[2][3] = { \"1234\", \"xyz\" };\n",
8982                "static const char b[3][5] = { \"12345\", \"678\", \"9\" };\n",
8983                "union u { struct { char x[4]; char y[4]; }; struct { char z[8]; }; };\n",
8984                "const union u c = { { \"1234\", \"567\" } };\n",
8985            ),
8986        );
8987        let text = result.text();
8988        assert_eq!(
8989            result.messages,
8990            ["/main.c:1:24: warning: initializer-string for array of 'const char' is too long \
8991              (5 chars into 3 available) [E0637]"]
8992        );
8993        assert!(text.contains("global @a : bytes 6 = { bytes \"123\", bytes \"xyz\" }"), "{text}");
8994        assert!(
8995            text.contains(
8996                "global @b : bytes 15 = { bytes \"12345\", bytes \"678\\00\", zero 1, \
8997                 bytes \"9\\00\", zero 3 }"
8998            ),
8999            "{text}"
9000        );
9001        // The eight bytes are four, three and a terminator, and then the byte the shorter
9002        // literal left for the string in the other member of the union to end at.
9003        assert!(
9004            text.contains("global @c : bytes 8 = { bytes \"1234\", bytes \"567\\00\" }"),
9005            "{text}"
9006        );
9007    }
9008
9009    #[test]
9010    fn a_cast_of_a_record_to_its_own_type_is_the_object_that_was_cast() {
9011        // gcc accepts one and does nothing with it, which sema already had. Lowering asked for
9012        // the object under it and had no arm for a cast, so `(struct s)x` in an initializer was
9013        // refused with E0519. It is one copy out of the object named, not two.
9014        let text = body(concat!(
9015            "struct s { int a, b; };\nstruct v { struct s s; int t; };\n",
9016            "void g(struct v *);\n",
9017            "void f(struct s *p) { struct v w = { (struct s)*p, 5 }; g(&w); }\n",
9018        ));
9019        assert_eq!(text.matches("memcpy").count(), 1, "{text}");
9020    }
9021
9022    #[test]
9023    fn a_compound_literal_read_in_a_static_initializer_lays_its_bytes_into_the_image() {
9024        // C 6.7.11p4 says a compound literal at file scope has static storage duration, which
9025        // makes it a constant element, and tcc and c-testsuite both write one. Sema used to call
9026        // it a non constant because reading it is a node of its own and the read was what it
9027        // looked at, and lowering had no way to put an object where it wanted a number.
9028        let text = ir(concat!(
9029            "struct s { int x; };\n",
9030            "struct t { struct s s; int o; } a = { (struct s){ 2 }, 3 };\n",
9031            "int n = (int){ 7 };\n",
9032            "struct u { struct s p; struct s q; } b = { (struct s){ 1 }, (struct s){ } };\n",
9033        ));
9034        assert!(text.contains("global @a : bytes 8 = { i32 2, i32 3 }"), "{text}");
9035        assert!(text.contains("global @n : i32 = 7,"), "{text}");
9036        // The second literal names nothing, so what it puts in is the zeros of its own size and
9037        // not the tail of the object it went in, which would have been the same bytes by luck.
9038        assert!(text.contains("global @b : bytes 8 = { i32 1, zero 4 }"), "{text}");
9039    }
9040
9041    #[test]
9042    fn the_address_of_a_compound_literal_asks_for_the_object_it_points_at() {
9043        // Nothing declares a compound literal, so the reference is the only thing that can ask
9044        // for it to be emitted. The image named `.Lanon.0` and the module defined no such
9045        // symbol, which the link would have been the first to find out.
9046        let text = ir("struct s { int x; };\nstruct s *q = &(struct s){ 9 };\n");
9047        assert!(text.contains("global @.Lanon.0 : i32 = 9, align 4, linkage(internal)"), "{text}");
9048        assert!(text.contains("global @q : bytes 8 = { addr.8 @.Lanon.0 }"), "{text}");
9049    }
9050
9051    #[test]
9052    fn an_object_of_no_size_at_all_has_an_image_with_nothing_in_it() {
9053        // A zero length array, which gcc allows and real code uses as the tail of a structure.
9054        // The image is there and holds nothing, which is not the global that has no image at
9055        // all, and the IR reader used to stop on the empty one.
9056        let text = ir("unsigned char foo[1][0];\n");
9057        assert!(text.contains("global @foo : bytes 0 = {}, align 1"), "{text}");
9058    }
9059
9060    #[test]
9061    fn a_null_pointer_in_an_image_is_the_bits_an_address_has_room_for() {
9062        // `NULL` in a static initializer, which every program has. The IR type is `ptr` and a
9063        // `ptr` has no width of its own, so the width the bits are cut to is the target's.
9064        let text = ir("void *p = 0;\nchar *q = (char *) 4096;\n");
9065        assert!(text.contains("global @p : i64 = 0, align 8"), "{text}");
9066        assert!(text.contains("global @q : i64 = 4096, align 8"), "{text}");
9067    }
9068
9069    #[test]
9070    fn an_object_another_module_defines_may_be_one_that_cannot_be_written_through() {
9071        // Which the verifier used to refuse, having read a declaration as a definition with
9072        // nothing in it. `extern const` is how a program names something in the library's read
9073        // only data, and glibc and Darwin both have one in a header a real program includes.
9074        let text = ir("extern const int limit;\nint f(void) { return limit; }\n");
9075        assert!(
9076            text.contains("global @limit : bytes 4, align 4, linkage(external), constant"),
9077            "{text}"
9078        );
9079    }
9080
9081    #[test]
9082    fn a_conditional_whose_value_is_an_object_answers_where_the_object_is() {
9083        // A structure is not a value in the IR, so the two arms cannot be joined as one. The
9084        // addresses can, and the answer is the address of whichever arm was taken rather than
9085        // a copy of it into a third place: both arms outlive the expression, so a copy would
9086        // be one nothing could observe. SQLite's parser writes one of these.
9087        let text = body(
9088            "\
9089struct s { int a, b; };
9090struct s pick(int c, struct s x, struct s y) { return c ? x : y; }
9091",
9092        );
9093        // The join takes an address, each arm hands it the one it has, and nothing is copied.
9094        assert!(text.contains("block3(%7: ptr)"), "{text}");
9095        assert!(text.contains("jump block3(%3)") && text.contains("jump block3(%4)"), "{text}");
9096        assert!(!text.contains("memcpy"), "the arms are joined rather than copied: {text}");
9097    }
9098
9099    /// GNU's `a ?: b` evaluates `a` once, and the arm answers the value that was tested.
9100    ///
9101    /// The checking keeps one node for `a` and converts it in two directions, to the bit the
9102    /// branch is taken on and to the type the whole expression has. Walking into the arm used to
9103    /// reach that node a second time and build a second copy of whatever it says, so `++i ?: 10`
9104    /// incremented twice and `f() ?: 10` called twice. Measured against gcc 16.2.0, which
9105    /// increments once.
9106    #[test]
9107    fn the_left_side_of_a_conditional_with_no_middle_is_evaluated_once() {
9108        let text = body("int f(int i) { return ++i ?: 10; }\n");
9109        assert!(text.contains("jump block3(%2)"), "the arm is the value that was tested: {text}");
9110        assert_eq!(text.matches("add.nsw").count(), 1, "incremented once: {text}");
9111
9112        // The arm still converts, since what the whole expression is worth is a `long` here and
9113        // the node under it is an `int`. What it converts is the value in hand.
9114        let text = body("long f(int i) { return ++i ?: 10L; }\n");
9115        assert!(text.contains("%5 = sext.i64 %2"), "the arm widens what was tested: {text}");
9116        assert_eq!(text.matches("add.nsw").count(), 1, "incremented once: {text}");
9117
9118        // A call, which is where evaluating twice is a wrong answer rather than a slow one.
9119        let text = body("int g(void);\nint f(void) { return g() ?: 10; }\n");
9120        assert_eq!(text.matches("call @g").count(), 1, "called once: {text}");
9121
9122        // Written out in full it is two reads of `i`, which is what C says it is, so the middle
9123        // operand being absent is the whole of the difference.
9124        let text = body("int f(int i) { return ++i ? ++i : 10; }\n");
9125        assert_eq!(text.matches("add.nsw").count(), 2, "incremented twice: {text}");
9126    }
9127
9128    #[test]
9129    fn a_structure_that_fits_in_registers_travels_as_the_registers_it_fits_in() {
9130        // `struct pair` is two eightbytes on SysV, one of them integer, so the signature says
9131        // one `i64` in each direction and the body takes the object apart and puts it back
9132        // together around the call.
9133        let text = ir("\
9134struct pair { int a, b; };
9135struct pair make(int a, int b);
9136struct pair twice(struct pair p) { return make(p.a, p.b); }
9137");
9138        assert!(text.contains("func @make(i32, i32) -> i64"), "{text}");
9139        assert!(text.contains("func @twice(i64) -> i64"), "{text}");
9140    }
9141
9142    #[test]
9143    fn a_structure_too_large_for_the_registers_travels_as_where_its_bytes_are() {
9144        // Over two eightbytes the caller passes the bytes in the argument area, which is
9145        // `byval`, and passes somewhere to write the return value, which is `sret`. Neither is
9146        // a parameter the program wrote and both are parameters the function has.
9147        let text = ir("\
9148struct big { double v[8]; };
9149struct big grow(struct big b);
9150struct big twice(struct big b) { return grow(grow(b)); }
9151");
9152        assert!(
9153            text.contains("func @grow(ptr sret(64, align 8), ptr byval(64, align 8))"),
9154            "{text}"
9155        );
9156        assert!(text.contains("block0(%0: ptr, %1: ptr):"), "{text}");
9157        // The inner call writes into a slot and the outer one reads the same slot, so the
9158        // object between the two calls is never copied anywhere.
9159        assert_eq!(text.matches("call @grow").count(), 2, "{text}");
9160    }
9161
9162    #[test]
9163    fn a_structure_passed_to_a_variadic_function_says_so_at_the_call() {
9164        // The bytes travel in the argument area the same way they would for a parameter, and
9165        // `printf` has no parameter there to say it on, so the call says it instead. The one
9166        // that fits in registers says nothing, because travelling as the registers it fits in
9167        // is what an argument does when nothing says otherwise.
9168        let text = ir("\
9169struct big { double v[8]; };
9170struct pair { int a, b; };
9171int p(const char *, ...);
9172int f(struct big b, struct pair q) { return p(\"\", 1, b, q); }
9173");
9174        assert!(
9175            text.contains("call @p(%4, %5, %2 byval(64, align 8), %6) : (ptr, ...) -> i32"),
9176            "{text}"
9177        );
9178    }
9179
9180    #[test]
9181    fn what_a_call_produced_is_somewhere_before_anything_is_read_out_of_it() {
9182        // `make(1, 2).b` has no object to read a member of until one is made, and what makes it
9183        // is a slot the returned registers are written to.
9184        let body = body(
9185            "\
9186struct pair { int a, b; };
9187struct pair make(int a, int b);
9188int second(void) { return make(1, 2).b; }
9189",
9190        );
9191        assert!(body.starts_with("block0:\n    %0 = alloca, size 8, align 4\n"), "{body}");
9192        assert!(body.contains("store %3 -> %0, align 4\n"), "{body}");
9193    }
9194
9195    #[test]
9196    fn a_structure_of_floats_travels_in_floating_point_registers_on_aarch64() {
9197        // The same declaration, classified by a different ABI: three `float` members are an
9198        // eightbyte of two of them and a half eightbyte of the third on SysV, and three vector
9199        // registers on AAPCS64.
9200        let source = "\
9201struct hfa { float x, y, z; };
9202int take(struct hfa h);
9203int give(struct hfa h) { return take(h); }
9204";
9205        assert!(ir(source).contains("func @take(f64, f32) -> i32"), "{}", ir(source));
9206        let mut opts = options();
9207        opts.emit = EmitKind::Ir;
9208        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
9209        let result = run(&opts, source);
9210        assert_eq!(result.messages, Vec::<String>::new());
9211        assert!(result.text().contains("func @take(f32, f32, f32) -> i32"), "{}", result.text());
9212    }
9213
9214    #[test]
9215    fn an_array_whose_length_is_not_a_constant_is_a_slot_made_where_its_declaration_is() {
9216        // The size is a multiplication rather than a number, the slot is taken from the stack
9217        // where the declaration is, and the scope it was declared in gives it back.
9218        let source = "\
9219int use(int *);
9220void f(int n) {
9221  {
9222    int a[n];
9223    use(a);
9224  }
9225  use(0);
9226}
9227";
9228        let body = body(source);
9229        assert!(body.contains("mul.nsw"), "{body}");
9230        assert!(body.contains("stacksave"), "{body}");
9231        assert!(body.contains("alloca %"), "{body}");
9232        assert!(body.contains("stackrestore"), "{body}");
9233    }
9234
9235    #[test]
9236    fn a_goto_out_of_the_scope_of_one_gives_its_stack_back_on_the_way() {
9237        // The label is outside the block the array is in, so arriving there means the array is
9238        // gone, and the restore that says so goes in front of the branch. The `goto` is written
9239        // before the walk knows where the label is, which is why the restore is put there at
9240        // the end rather than built where the branch was.
9241        let source = "\
9242int use(int *);
9243int f(int n) {
9244  {
9245    int a[n];
9246    if (use(a)) goto out;
9247    use(0);
9248  }
9249out:
9250  return 0;
9251}
9252";
9253        let body = body(source);
9254        // Two ways out of the block and a restore on each: the jump and the end of the block.
9255        assert_eq!(body.matches("stackrestore").count(), 2, "{body}");
9256        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
9257        assert!(after.starts_with(" %4\n    jump block"), "{body}");
9258    }
9259
9260    #[test]
9261    fn a_goto_to_a_label_the_array_is_still_alive_at_leaves_the_stack_alone() {
9262        // The label is after the declaration and in the same block, so control that arrives
9263        // there arrives somewhere the array exists. Giving it back would be giving back an
9264        // object the next statement reads.
9265        let source = "\
9266int use(int *);
9267int f(int n) {
9268  int a[n];
9269again:
9270  if (use(a)) goto again;
9271  return 0;
9272}
9273";
9274        let body = body(source);
9275        assert!(body.contains("stacksave"), "{body}");
9276        assert!(!body.contains("stackrestore"), "{body}");
9277    }
9278
9279    #[test]
9280    fn a_goto_back_to_a_label_in_front_of_one_gives_it_back_every_time_round() {
9281        // A loop written out of a `goto`, with the array made inside it. The label is in the
9282        // same block as the declaration and before it, which is a place where the array does
9283        // not exist yet, so the jump there leaves its scope and has to give the stack back. A
9284        // compiler that skips this restore grows the stack once per iteration.
9285        let source = "\
9286int use(int *);
9287int f(int n) {
9288again:
9289  {
9290    int a[n];
9291    if (use(a)) goto again;
9292  }
9293  return 0;
9294}
9295";
9296        let body = body(source);
9297        assert_eq!(body.matches("stacksave").count(), 1, "{body}");
9298        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
9299        assert!(after.starts_with(" %4\n    jump block1\n"), "{body}");
9300    }
9301
9302    #[test]
9303    fn the_head_of_a_for_loop_is_a_scope_that_closes_where_the_loop_is_left() {
9304        // The scope opened for `for (int a[n];;)` used to stay open, and a scope left open is
9305        // not one mark nobody reads. The marks are a stack, so the next close took this one
9306        // instead of its own, and the body of the loop gave back nothing while the block after
9307        // the loop restored a pointer saved inside it. The verifier refused that, which is how
9308        // it was found.
9309        let source = "\
9310int f(void);
9311void t(void) {
9312  int count = 10;
9313  for (; count--;) {
9314    int b[f()];
9315    int i;
9316    for (i = 0; i < f(); i++) {
9317      b[i] = count;
9318    }
9319  }
9320}
9321";
9322        let body = body(source);
9323        // One save, in the body, and one restore for it, also in the body: the block the
9324        // restore is in is the one the inner loop leaves through, and it goes back round the
9325        // outer loop rather than out of it.
9326        assert_eq!(body.matches("stacksave").count(), 1, "{body}");
9327        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
9328        // The rest of the block the restore is in, which is the last block here, so there is not
9329        // always another one after it to split on.
9330        let next = after.split("\n\n").next().expect("the block the restore is in");
9331        assert!(next.contains("jump block1("), "{body}");
9332    }
9333
9334    #[test]
9335    fn how_long_one_of_those_is_was_decided_where_it_was_declared_and_not_where_it_is_asked() {
9336        // What C says about the length being evaluated once: `sizeof a` after `n` changed is
9337        // still as long as the array is, which is what `n` was when the array came into being.
9338        let source = "\
9339unsigned long f(int n) {
9340  int a[n];
9341  n = 0;
9342  return sizeof a;
9343}
9344";
9345        let body = body(source);
9346        // One read of the parameter, at the declaration, and the answer is built out of it.
9347        assert_eq!(body.matches("sext.i64 %0").count(), 2, "{body}");
9348    }
9349
9350    #[test]
9351    fn a_block_in_the_middle_of_an_expression_is_walked_where_the_expression_is() {
9352        // GNU's statement expression: the statements happen where they are written and the last
9353        // one is the value, so the temporary in it never becomes a slot and never is copied.
9354        let source = "\
9355int use(int);
9356int f(int x) {
9357  return ({
9358    int t = use(x);
9359    t * t;
9360  });
9361}
9362";
9363        let expected = "\
9364block0(%0: i32):
9365    %1 = call @use(%0) : (i32) -> i32
9366    %2 = mul.nsw %1, %1
9367    return %2
9368";
9369        assert_eq!(body(source), expected);
9370    }
9371
9372    #[test]
9373    fn a_comma_whose_value_is_an_object_names_the_object_the_right_side_named() {
9374        // What janet writes, which is a call that does not return and then a value after it so
9375        // that the arm is worth something. The left side happens for what it did and the answer
9376        // is where the right side is, so there is nothing to copy and no temporary for a copy.
9377        let source = "\
9378struct pair { int a, b; };
9379void bail(void);
9380int f(struct pair p) {
9381  return (bail(), p).b;
9382}
9383";
9384        let expected = "\
9385block0(%0: i64):
9386    %1 = alloca, size 8, align 4
9387    store %0 -> %1, align 4
9388    call @bail() : ()
9389    %2 = iconst.i64 4
9390    %3 = ptr_add %1, %2
9391    %4 = load.i32 %3, align 4, tbaa !1
9392    return %4
9393";
9394        assert_eq!(body(source), expected);
9395    }
9396
9397    #[test]
9398    fn one_of_those_that_control_never_leaves_is_lowered_and_what_follows_it_is_dropped() {
9399        // A macro that always jumps, which is what this shape is in real code. The value is
9400        // never taken, and the block the rest of the expression would have been built in is
9401        // one nothing branches to, so it goes with the other unreachable blocks.
9402        let source = "int f(int x) { return ({ return x; 0; }); }\n";
9403        assert_eq!(body(source), "block0(%0: i32):\n    return %0\n");
9404    }
9405
9406    #[test]
9407    fn one_argument_off_a_variable_argument_list_stays_an_intrinsic() {
9408        // What it becomes is the target's answer, and this is not where the target's answers
9409        // are, so the walk writes down which list and which type and leaves it at that. Two of
9410        // them are two instructions, since each moves the list on.
9411        let source = "double f(__builtin_va_list ap) { return __builtin_va_arg(ap, double) + __builtin_va_arg(ap, double); }\n";
9412        let expected = "\
9413block0(%0: ptr):
9414    %1 = va_arg.f64 %0
9415    %2 = va_arg.f64 %0
9416    %3 = fadd %1, %2
9417    return %3
9418";
9419        assert_eq!(body(source), expected);
9420    }
9421
9422    #[test]
9423    fn one_that_reads_a_structure_answers_where_the_object_is() {
9424        // An aggregate is not a value, so there is nothing for the result of `va_arg` to be and
9425        // the object form is a second instruction. What it answers is an address, so it is a
9426        // place already and the walk copies nothing out of it: the copy here is the one the
9427        // initializer asks for, into the variable being declared. The size and the alignment
9428        // travel with it because they are what steps the list on and what a target that has to
9429        // put registers somewhere needs to know. So does the classification, which says the two
9430        // halves of this one arrived in general purpose registers: that is an answer about a C
9431        // type, and this is the last place that still has one.
9432        //
9433        // The slot is aligned to sixteen and the copy into it to eight, which is not a
9434        // disagreement. Sixteen is what a local aggregate of sixteen bytes gets whatever its
9435        // members ask for, and eight is what the type asks for and so what the copy may assume
9436        // about the object it is reading from.
9437        let source = "\
9438struct s { int a; long b; };
9439long f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.b; }
9440";
9441        let expected = "\
9442block0(%0: ptr):
9443    %1 = alloca, size 16, align 16
9444    %2 = va_object %0, size 16, align 8, in(int 8 at 0, int 8 at 8)
9445    memcpy %1, %2, size 16, align 8
9446    %3 = iconst.i64 8
9447    %4 = ptr_add %1, %3
9448    %5 = load.i64 %4, align 8, tbaa !1
9449    return %5
9450";
9451        assert_eq!(body(source), expected);
9452    }
9453
9454    /// Which register file each eightbyte arrived in is the whole of what the classification adds,
9455    /// and an object with no slots at all is one it sent to the caller's argument area, which is
9456    /// what everything over two eightbytes is whatever its members are.
9457    #[test]
9458    fn the_classification_says_which_registers_the_object_arrived_in() {
9459        let source = "\
9460struct s { double a; double b; };
9461double f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.a; }
9462";
9463        assert!(
9464            body(source)
9465                .contains("va_object %0, size 16, align 8, in(float f64 at 0, float f64 at 8)"),
9466            "{}",
9467            body(source)
9468        );
9469
9470        let big = "\
9471struct s { long a[4]; };
9472long f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.a[0]; }
9473";
9474        assert!(body(big).contains("va_object %0, size 32, align 8\n"), "{}", body(big));
9475    }
9476
9477    #[test]
9478    fn a_jump_to_an_address_branches_to_every_label_the_function_takes_the_address_of() {
9479        // GNU's computed goto. Which label the address holds is not known here, so all of them
9480        // are listed, and the values arriving at one are passed on every edge the same way they
9481        // are on an ordinary branch.
9482        let source = "\
9483int f(int c) {
9484  void *p = c ? &&one : &&two;
9485  goto *p;
9486one:
9487  return 1;
9488two:
9489  return 2;
9490}
9491";
9492        let expected = "\
9493block0(%0: i32):
9494    %1 = iconst.i32 0
9495    %2 = icmp ne %0, %1
9496    br_if %2, block1, block2
9497
9498block1:
9499    %3 = block_addr block3
9500    jump block4(%3)
9501
9502block2:
9503    %4 = block_addr block5
9504    jump block4(%4)
9505
9506block3:
9507    %5 = iconst.i32 1
9508    return %5
9509
9510block4(%6: ptr):
9511    indirect_br %6, block3, block5
9512
9513block5:
9514    %7 = iconst.i32 2
9515    return %7
9516";
9517        assert_eq!(body(source), expected);
9518    }
9519
9520    /// An interpreter, cut down to the shape that matters: a table of labels, a few values the
9521    /// loop keeps in hand, and a jump through the table at the end of every one of them.
9522    fn dispatch(labels: usize) -> String {
9523        let mask = labels - 1;
9524        let mut source = String::from("int spin(int n)\n{\n\tstatic void *table[] = {");
9525        for index in 0..labels {
9526            source.push_str(&format!(" &&a{index},"));
9527        }
9528        source.push_str(" };\n\tint w = n, x = n + 1, y = n + 2, z = n + 3;\n");
9529        source.push_str(&format!("\tif (n < 0) return 0;\n\tgoto *table[n & {mask}];\n"));
9530        for index in 0..labels {
9531            let step = match index % 4 {
9532                0 => "w += x;",
9533                1 => "x += y;",
9534                2 => "y += z;",
9535                _ => "z += w;",
9536            };
9537            source.push_str(&format!("a{index}:\n\t{step}\n"));
9538            source.push_str("\tif (--n <= 0) return w + x + y + z;\n");
9539            source.push_str(&format!("\tgoto *table[n & {mask}];\n"));
9540        }
9541        source.push_str("}\n");
9542        source
9543    }
9544
9545    /// How many moves are written in front of the first jump through a register.
9546    fn in_front_of_the_jump(text: &str) -> usize {
9547        let (before, _) = text.split_once("\tjmp\t*%").expect("a jump through a register");
9548        before.lines().rev().take_while(|line| line.starts_with("\tmov")).count()
9549    }
9550
9551    /// What a branch writes in front of its jump is what it carries, not what every label it can
9552    /// reach would like to be handed.
9553    ///
9554    /// A label an indirect branch reaches is given its values in registers the branch writes
9555    /// before it goes, because the moves cannot go after a jump and cannot go across the register
9556    /// the jump reads. Writing a register for each parameter of each label costs the table's
9557    /// length on every dispatch, which is a few moves in a program with two labels and five
9558    /// hundred in an interpreter with seventy. The values are the same values, so the registers
9559    /// are the same registers, and the cost stays where the number of values puts it.
9560    #[test]
9561    fn a_jump_through_a_register_writes_what_it_carries_and_not_the_whole_table() {
9562        let small = in_front_of_the_jump(&asm(&dispatch(4)));
9563        let large = in_front_of_the_jump(&asm(&dispatch(32)));
9564        assert_eq!(small, large, "eight times the labels and the same values in hand");
9565        assert!(large <= 8, "the values the loop keeps, and not a set of them per label: {large}");
9566    }
9567
9568    /// The same interpreter with more values in hand than there are registers, which is what makes
9569    /// the allocator send some of them to the stack at every label.
9570    fn crowded(labels: usize) -> String {
9571        const VALUES: usize = 24;
9572        let mask = labels - 1;
9573        let mut source = String::from("int spin(int n)\n{\n\tstatic void *table[] = {");
9574        for index in 0..labels {
9575            source.push_str(&format!(" &&a{index},"));
9576        }
9577        source.push_str(" };\n\t");
9578        for value in 0..VALUES {
9579            source.push_str(&format!("int v{value} = n + {value}; "));
9580        }
9581        let sum: Vec<String> = (0..VALUES).map(|value| format!("v{value}")).collect();
9582        source.push_str(&format!("\n\tif (n < 0) return 0;\n\tgoto *table[n & {mask}];\n"));
9583        for index in 0..labels {
9584            let (to, from) = (index % VALUES, (index + 1) % VALUES);
9585            source.push_str(&format!("a{index}:\n\tv{to} += v{from};\n"));
9586            source.push_str(&format!("\tif (--n <= 0) return {};\n", sum.join(" + ")));
9587            source.push_str(&format!("\tgoto *table[n & {mask}];\n"));
9588        }
9589        source.push_str("}\n");
9590        source
9591    }
9592
9593    /// How many bytes of frame the first function in a listing opens.
9594    fn the_frame(text: &str) -> u64 {
9595        text.lines()
9596            .find_map(|line| {
9597                let (size, _) = line.strip_prefix("\tsubq\t$")?.split_once(", %rsp")?;
9598                size.parse().ok()
9599            })
9600            .expect("a function that opens a frame")
9601    }
9602
9603    /// A frame holds what a function wants at once, and an interpreter does not want the whole
9604    /// table at once.
9605    ///
9606    /// Every label a dispatch table reaches is handed the values the loop keeps, and what the
9607    /// allocator has no register for goes on the stack. They are the same few values one label at
9608    /// a time, so they are the same bytes. A slot each put forty kilobytes on the frame of lua's
9609    /// interpreter and ran the C stack out at a depth lua's own limit was supposed to catch,
9610    /// which is tamnd/rucc#1630.
9611    #[test]
9612    fn a_frame_holds_what_is_wanted_at_once_and_not_a_slot_for_every_label() {
9613        let small = the_frame(&asm(&crowded(16)));
9614        let large = the_frame(&asm(&crowded(64)));
9615        assert_eq!(small, large, "four times the labels and the same values: {small}, {large}");
9616    }
9617
9618    /// A template that saves the callee-saved registers by name, which is micropython's non local
9619    /// return and is tamnd/rucc#1583.
9620    ///
9621    /// Every register in it is one the template named rather than one the statement handed over,
9622    /// because the buffer is defined as holding those registers and there is no constraint letter
9623    /// that means `%rsp`. The instructions come out naming what the program named, and the
9624    /// allocator, which was told about the writes rather than left to find out, saves the ones the
9625    /// calling convention says belong to whoever called.
9626    #[test]
9627    fn a_template_that_names_its_own_registers_gets_the_ones_it_named() {
9628        let source = "void save(void *nlr) {
9629    __asm volatile (
9630        \"movq   %%rsp, 32(%%rdi)   \\n\"
9631        \"movq   %%rbx, 40(%%rdi)   \\n\"
9632        \"movq   %%r12, 48(%%rdi)   \\n\"
9633        : : \"D\" (nlr) : \"memory\");
9634}
9635";
9636        let text = asm(source);
9637        assert!(text.contains("\tmovq\t%rsp, 32(%rdi)\n"), "{text}");
9638        assert!(text.contains("\tmovq\t%rbx, 40(%rdi)\n"), "{text}");
9639        assert!(text.contains("\tmovq\t%r12, 48(%rdi)\n"), "{text}");
9640    }
9641
9642    #[test]
9643    fn a_jump_to_an_address_no_label_in_the_function_has_arrives_nowhere() {
9644        // The address came from outside the function, and a jump to a label in another function
9645        // is undefined. The expression is still evaluated, since a call in it has to happen.
9646        let source = "void **next(void);
9647void f(void) { goto *next(); }
9648";
9649        let expected = "\
9650block0:
9651    %0 = call @next() : () -> ptr
9652    unreachable
9653";
9654        assert_eq!(body(source), expected);
9655    }
9656
9657    #[test]
9658    fn an_asm_with_no_operands_is_volatile_and_the_clobbers_are_the_whole_of_what_it_says() {
9659        // Nothing reads a result, so the only thing that keeps it is that it is volatile, which
9660        // a basic asm implies.
9661        let source = "void f(void) { __asm__(\"mfence\" ::: \"memory\"); }\n";
9662        let expected = "\
9663block0:
9664    inline_asm.volatile \"mfence\", \"\", \"memory\"()
9665    return
9666";
9667        assert_eq!(body(source), expected);
9668    }
9669
9670    #[test]
9671    fn the_constraints_are_one_list_in_the_order_the_template_counts_the_operands() {
9672        // The outputs first and then the inputs, which is the numbering `%0` and `%1` use. An
9673        // output in a register is a result, and one that is read as well is an argument too.
9674        let source = "\
9675int f(int x, int y) {
9676  int r;
9677  __asm__(\"addl %2, %0\" : \"=r\"(r), \"+r\"(y) : \"r\"(x));
9678  return r + y;
9679}
9680";
9681        let expected = "\
9682block0(%0: i32, %1: i32):
9683    %2, %3 = inline_asm.(i32, i32) \"addl %2, %0\", \"=r,+r,r\", \"\"(%1, %0)
9684    %4 = add.nsw %2, %3
9685    return %4
9686";
9687        assert_eq!(body(source), expected);
9688    }
9689
9690    #[test]
9691    fn a_memory_operand_travels_as_the_address_of_an_object_that_is_given_a_slot() {
9692        // The assembly is handed a pointer, so the object cannot live in a value, and the scan
9693        // that runs before the walk has to have known that or there would be nothing to point
9694        // at. A structure travels this way whatever else its constraint allows, since there is
9695        // no register that holds one.
9696        let source = "\
9697struct pair { int a, b; };
9698int f(int x) {
9699  int slot = x;
9700  struct pair p = { x, x };
9701  __asm__(\"incl %0\" : \"+m\"(slot), \"=m\"(p));
9702  return slot + p.a;
9703}
9704";
9705        let text = body(source);
9706        assert!(text.contains("inline_asm \"incl %0\", \"+m,=m\", \"\"(%1, %2)\n"), "{text}");
9707        assert!(text.contains("%1 = alloca, size 4, align 4\n"), "{text}");
9708        assert!(text.contains("%2 = alloca, size 8, align 4\n"), "{text}");
9709    }
9710
9711    #[test]
9712    fn an_asm_goto_falls_through_to_its_first_target_and_writes_its_outputs_there() {
9713        // The output is only in scope where the instruction dominates, which is the fall through
9714        // block, so the edge to the label carries the value the object had before the assembly
9715        // ran. That is what document 11 asks for and it is what putting the fall through first
9716        // buys.
9717        let source = "\
9718int f(int x) {
9719  int r = 7;
9720  __asm__ goto(\"cbnz %0, %l1\" : \"=r\"(r) : \"r\"(x) :: away);
9721  return r;
9722away:
9723  return r;
9724}
9725";
9726        let expected = "\
9727block0(%0: i32):
9728    %1 = iconst.i32 7
9729    %2 = inline_asm.volatile \"cbnz %0, %l1\", \"=r,r\", \"\"(%0), labels [block1, block2]
9730
9731block1:
9732    return %2
9733
9734block2:
9735    return %1
9736";
9737        assert_eq!(body(source), expected);
9738    }
9739
9740    #[test]
9741    fn an_asm_statement_that_is_not_well_formed_is_reported_in_the_words_gcc_uses() {
9742        // The operands are checked here rather than by the assembler, because by the time the
9743        // assembler sees the template the operands have become registers and it has nothing left
9744        // to say about the C that named them.
9745        let mut opts = options();
9746        opts.emit = EmitKind::Ir;
9747        for (source, expected) in [
9748            (
9749                "void f(int x) { __asm__(\"\" : \"r\"(x)); }\n",
9750                "output operand constraint lacks '='",
9751            ),
9752            (
9753                "void f(int x) { __asm__(\"\" : \"=r\"(x + 1)); }\n",
9754                "lvalue required in 'asm' statement",
9755            ),
9756            (
9757                "const int g = 1;\nvoid f(void) { __asm__(\"\" : \"=r\"(g)); }\n",
9758                "read-only variable 'g' used as 'asm' output",
9759            ),
9760            (
9761                "void f(int x) { __asm__(\"\" : : \"=r\"(x)); }\n",
9762                "input operand constraint contains '='",
9763            ),
9764            (
9765                "void f(void) { __asm__(\"\" : : \"m\"(1)); }\n",
9766                "memory input 0 is not directly addressable",
9767            ),
9768            ("void f(void) { __asm__(L\"\"); }\n", "wide string literal in 'asm'"),
9769            (
9770                "void f(int x, int y) { __asm__(\"\" : [a] \"=r\"(x) : [a] \"r\"(y)); }\n",
9771                "duplicate asm operand name 'a'",
9772            ),
9773            ("void f(int x) { __asm__(\"%[in]\" : \"=r\"(x)); }\n", "undefined named operand 'in'"),
9774        ] {
9775            let result = run(&opts, source);
9776            assert!(result.failed(), "expected this to be reported:\n{source}");
9777            assert!(
9778                result.messages.iter().any(|m| m.contains(expected)),
9779                "{expected}\n{:?}",
9780                result.messages
9781            );
9782        }
9783    }
9784
9785    /// An `asm` at file scope whose template is directives is the whole of what the incbin
9786    /// header, an alias table and a hand written jump table each write, and what it says is a
9787    /// section holding named bytes. So it becomes the globals it names, in the order it names
9788    /// them, which is what `spec/11-asm-objects-debug.md` section 11.2 asks for.
9789    #[test]
9790    fn an_asm_at_file_scope_that_is_directives_becomes_the_objects_it_defines() {
9791        let text = ir(concat!(
9792            "__asm__(\n",
9793            "  \".section .rodata\\n\"\n",
9794            "  \".globl first\\n\"\n",
9795            "  \".balign 8\\n\"\n",
9796            "  \"first:\\n\"\n",
9797            "  \".long 1\\n\"\n",
9798            "  \".long 2\\n\"\n",
9799            "  \".globl last\\n\"\n",
9800            "  \"last:\\n\"\n",
9801            "  \".quad last - first\\n\");\n",
9802            "extern const int first[];\n",
9803            "extern const long last;\n",
9804        ));
9805        assert!(text.contains("global @first : bytes 8 = { i32 1, i32 2 }, align 8"), "{text}");
9806        assert!(text.contains("global @last : i64 = 8"), "{text}");
9807    }
9808
9809    /// The distance between two labels is what the incbin header hands a program as the size of
9810    /// the data, so a declaration of one of the names has to find the definition the template
9811    /// made rather than turn it back into something the linker is asked for.
9812    #[test]
9813    fn a_name_an_asm_at_file_scope_defined_is_not_undone_by_a_declaration_of_it() {
9814        let text = ir(concat!(
9815            "__asm__(\".data\\n.globl counter\\ncounter:\\n.long 7\\n\");\n",
9816            "extern int counter;\n",
9817            "int read(void) { return counter; }\n",
9818        ));
9819        assert!(text.contains("global @counter : i32 = 7"), "{text}");
9820    }
9821
9822    /// Bytes written before any label are a global with a name minted for them, in front of the
9823    /// label written under them, which is what makes the first byte of the name the one written
9824    /// under it. The block is the one tcc's test file writes, without the line of it that measures
9825    /// from one section to another.
9826    #[test]
9827    fn bytes_under_no_label_at_file_scope_are_a_global_in_front_of_the_label() {
9828        let text = ir(concat!(
9829            "__asm__(\".data\\n.byte 41\\nstuff:\\n661:\\n.byte 42\\n662:\\n",
9830            ".pushsection .data.ignore\\n.byte 7\\n.popsection\\n.byte 662b - 661b\\n\");\n",
9831            "extern unsigned char stuff[];\n",
9832            "int read(void) { return stuff[0]; }\n",
9833        ));
9834        let under = text.find("global @.Lasm.0 : i8 = 41").expect(&text);
9835        let named = text.find("global @stuff : i8 = 42").expect(&text);
9836        assert!(under < named, "the bytes under no label come first: {text}");
9837        assert!(text.contains("global @.Lasm.1 : i8 = 7, align 1, linkage(internal), section"));
9838        // The byte after the pop is a run of its own, because coming back to a section finishes
9839        // what was being written to it the way a label does. It is the next global of that
9840        // section all the same, so the byte lands where the template put it, which is the one
9841        // after the byte under `stuff`.
9842        let after = text.find("global @.Lasm.2 : i8 = 1").expect(&text);
9843        assert!(named < after, "{text}");
9844    }
9845
9846    /// How far a place is from the bytes holding the answer, which is what tcc's test file writes
9847    /// last and what the alternative instruction tables in a kernel header are made of. It is the
9848    /// linker's answer rather than the compiler's, because the two sections are placed by the
9849    /// linker, so the image holds a hole and a name for it.
9850    #[test]
9851    fn a_distance_from_here_at_file_scope_is_a_hole_naming_the_global_it_measures_to() {
9852        let text = ir(concat!(
9853            "__asm__(\".data\\n.byte 41\\nstuff:\\n661:\\n.byte 42\\n",
9854            ".pushsection .data.ignore\\n.long 661b - .\\n.popsection\\n\");\n",
9855            "extern unsigned char stuff[];\n",
9856            "int read(void) { return stuff[0]; }\n",
9857        ));
9858        // The label the template measured to is a local one and no symbol, so what the hole names
9859        // is the global it stands inside, which is the byte under `stuff`, and nothing further on
9860        // since it is the first byte of it.
9861        assert!(text.contains("global @.Lasm.1 : bytes 4 = { away.4 @stuff }"), "{text}");
9862    }
9863
9864    /// A `.set` says one name stands for another, which is a second symbol at the first one's
9865    /// address and is an alias and nothing else. What the directives around it said about the
9866    /// name is what the name gets, and a name the file defines itself keeps its own definition,
9867    /// which is what gcc's symbol table shows for the block tcc's test file writes.
9868    #[test]
9869    fn a_set_at_file_scope_is_a_second_name_for_what_it_names() {
9870        let text = ir(concat!(
9871            "void base(void) {}\n",
9872            "__asm__(\".weak one\\n.set one, base\");\n",
9873            "__asm__(\".globl two\\n.set two, base\");\n",
9874            "__asm__(\".set three, base\");\n",
9875            "void three(void) {}\n",
9876        ));
9877        assert!(text.contains("alias @one = @base, linkage(weak)"), "{text}");
9878        assert!(text.contains("alias @two = @base"), "{text}");
9879        assert!(!text.contains("alias @three"), "a definition of the name wins: {text}");
9880        assert!(text.contains("func @three"), "{text}");
9881    }
9882
9883    /// The target has to be something this file defines, because an alias is a symbol at an
9884    /// address in this object and a name only declared here has none to be at. The same rule and
9885    /// the same words as for `__attribute__((alias))`, since it is the same thing written another
9886    /// way.
9887    #[test]
9888    fn a_set_of_a_name_this_file_does_not_define_says_so() {
9889        let messages = errors("__asm__(\".set here, elsewhere\");\n");
9890        assert!(
9891            messages
9892                .iter()
9893                .any(|m| m.contains("'here' is aliased to undefined symbol 'elsewhere'")
9894                    && m.contains("E0697")),
9895            "{messages:?}"
9896        );
9897    }
9898
9899    /// `.incbin` is the one directive that reads something, and what it reads comes through the
9900    /// same file system the sources did.
9901    #[test]
9902    fn an_incbin_at_file_scope_is_the_bytes_of_the_file_it_names() {
9903        let mut opts = options();
9904        opts.emit = EmitKind::Ir;
9905        let mut fs = MemoryFileSystem::new();
9906        fs.insert(
9907            "/main.c",
9908            b"__asm__(\".data\\n.globl blob\\nblob:\\n.incbin \\\"seed\\\"\\n\");\n".to_vec(),
9909        );
9910        fs.insert("seed", b"hi".to_vec());
9911        let result = compile(&opts, "/main.c", &fs);
9912        assert_eq!(result.messages, Vec::<String>::new());
9913        let text = result.text();
9914        assert!(text.contains("global @blob : bytes 2 = { bytes \"hi\" }"), "{text}");
9915    }
9916
9917    /// A file that is not there is the mistake a build makes when it runs the compiler from the
9918    /// wrong directory, and it is worth saying which file rather than saying the template failed.
9919    #[test]
9920    fn an_incbin_naming_a_file_that_is_not_there_says_which_file() {
9921        let messages = errors("__asm__(\".data\\nb:\\n.incbin \\\"nowhere\\\"\\n\");\n");
9922        assert!(
9923            messages
9924                .iter()
9925                .any(|m| m.contains("cannot open 'nowhere' for reading") && m.contains("E0702")),
9926            "{messages:?}"
9927        );
9928    }
9929
9930    /// A template of directives the reader does not take is refused by name rather than dropped.
9931    /// One with an instruction in it goes to the assembler instead, which
9932    /// `an_asm_at_file_scope_with_an_instruction_in_it_is_assembled` covers.
9933    #[test]
9934    fn a_directive_in_an_asm_at_file_scope_is_refused_rather_than_ignored() {
9935        let source = "__asm__(\".data\\n.set alias, 4\\n\");\n";
9936        let messages = errors(source);
9937        assert!(
9938            messages
9939                .iter()
9940                .any(|m| m.contains("not supported yet") && m.contains("in an `asm` at file scope")),
9941            "{source}\n{messages:?}"
9942        );
9943    }
9944
9945    /// micropython's `nlr_push`, which is the program that asks for all of this. The body is the
9946    /// whole of the function: the return address is read out of `(%rsp)` where the call left it,
9947    /// the registers the convention preserves are saved by hand, and the frame that was just built
9948    /// is handed to a function written in C that never comes back.
9949    ///
9950    /// What is checked is what gcc writes for the same file. No prologue in front of the saves,
9951    /// since a push would move the return address the first of them reads. No epilogue and no
9952    /// `ret`, since the jump is where the function ends. And a `ud2` behind the jump, which is
9953    /// where control arrives if the jump is ever not taken and is exactly what gcc puts there.
9954    #[test]
9955    fn a_naked_function_is_its_own_prologue_and_its_own_ending() {
9956        let text = asm(concat!(
9957            "unsigned nlr_push_tail(void *nlr);\n",
9958            "__attribute__((naked)) unsigned nlr_push(void *nlr) {\n",
9959            "  __asm volatile(\n",
9960            "    \"movq (%rsp), %rax\\n\"\n",
9961            "    \"movq %rax, 16(%rdi)\\n\"\n",
9962            "    \"movq %rbx, 40(%rdi)\\n\"\n",
9963            "    \"jmp nlr_push_tail\\n\");\n",
9964            "}\n",
9965        ));
9966        assert!(text.contains("\tmovq\t(%rsp), %rax\n"), "{text}");
9967        assert!(text.contains("\tjmp\tnlr_push_tail\n"), "{text}");
9968        assert!(text.contains("\tud2\n"), "{text}");
9969        assert!(!text.contains("\tpushq\t"), "nothing is saved in front of it: {text}");
9970        assert!(!text.contains("\tret\n"), "the jump is where it ends: {text}");
9971    }
9972
9973    /// The three things a naked function may not ask for, each of which is a frame nothing sets up
9974    /// or a jump over an epilogue there is one of.
9975    #[test]
9976    fn what_a_function_without_a_prologue_cannot_be_given_is_refused() {
9977        let mut opts = options();
9978        opts.emit = EmitKind::Asm;
9979        for (source, why) in [
9980            (
9981                "__attribute__((naked)) void f(void) { volatile long a[8]; a[0] = 1; }\n",
9982                "bytes of frame",
9983            ),
9984            (
9985                "__attribute__((naked)) void f(int n) { char a[n]; __asm(\"nop\" ::\"r\"(a)); }\n",
9986                "has no prologue to point a frame pointer at it with",
9987            ),
9988            ("void elsewhere(void); void f(void) { __asm(\"jmp elsewhere\"); }\n", "jumps out of"),
9989        ] {
9990            let result = run(&opts, source);
9991            assert!(result.failed(), "expected this to be refused:\n{source}");
9992            assert!(
9993                result.messages.iter().any(|message| message.contains(why)),
9994                "{:?}",
9995                result.messages
9996            );
9997        }
9998    }
9999
10000    #[test]
10001    fn what_the_walk_cannot_build_yet_is_reported_rather_than_mislowered() {
10002        let mut opts = options();
10003        opts.emit = EmitKind::Ir;
10004        for source in [
10005            "int f(int n) { void *p = &&out; if (n) goto *p; { int a[n]; out: return 1; } }\n",
10006            "int f(int n) { int a[n]; __asm__ goto(\"\" ::::out); out: return a[0]; }\n",
10007        ] {
10008            let result = run(&opts, source);
10009            assert!(result.failed(), "expected this to be reported:\n{source}");
10010            assert!(
10011                result.messages.iter().any(|m| m.contains("not supported yet")),
10012                "{:?}",
10013                result.messages
10014            );
10015        }
10016    }
10017
10018    /// Compiles `source` to IR, reads that back as an input, and gives back both texts.
10019    fn round_trip(source: &str) -> (String, String) {
10020        let printed = ir(source);
10021        let mut opts = options();
10022        opts.emit = EmitKind::Ir;
10023        let mut fs = MemoryFileSystem::new();
10024        fs.insert("/main.ir", printed.clone().into_bytes());
10025        let result = compile_ir(&opts, "/main.ir", &fs);
10026        assert_eq!(result.messages, Vec::<String>::new(), "expected this to read back:\n{printed}");
10027        (printed, result.text().to_owned())
10028    }
10029
10030    #[test]
10031    fn ir_that_arrives_as_an_input_is_read_back_and_written_out_the_same() {
10032        // The other half of the round trip test below, through the driver rather than through
10033        // the library, which is what makes the property something to run over a real program
10034        // rather than over the modules a test builds.
10035        let (printed, again) = round_trip(
10036            "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",
10037        );
10038        assert_eq!(printed, again);
10039    }
10040
10041    #[test]
10042    fn ir_that_is_not_ir_says_which_line_stopped_it() {
10043        let mut opts = options();
10044        opts.emit = EmitKind::Ir;
10045        let mut fs = MemoryFileSystem::new();
10046        let text = "\
10047; ModuleID = 'a.c'
10048; format 0
10049target triple = \"x86_64-unknown-linux-gnu\"
10050target datalayout = \"e-p:64:64-i64:64-S128\"
10051
10052func @f(), linkage(external) {
10053block0:
10054    frobnicate
10055}
10056";
10057        fs.insert("/main.ir", text.as_bytes().to_vec());
10058        let result = compile_ir(&opts, "/main.ir", &fs);
10059        assert!(result.failed());
10060        assert!(result.messages[0].contains("/main.ir:8"), "{:?}", result.messages);
10061    }
10062
10063    #[test]
10064    fn ir_that_reads_but_does_not_hold_together_is_reported_by_the_verifier() {
10065        // A module that a person edited has not been through the verifier, and the return of
10066        // an `i32` from a function that returns nothing is the kind of thing editing produces.
10067        let mut opts = options();
10068        opts.emit = EmitKind::Ir;
10069        let mut fs = MemoryFileSystem::new();
10070        let text = "\
10071; ModuleID = 'a.c'
10072; format 0
10073target triple = \"x86_64-unknown-linux-gnu\"
10074target datalayout = \"e-p:64:64-i64:64-S128\"
10075
10076func @f(), linkage(external) {
10077block0:
10078    %0 = iconst.i32 1
10079    return %0
10080}
10081";
10082        fs.insert("/main.ir", text.as_bytes().to_vec());
10083        let result = compile_ir(&opts, "/main.ir", &fs);
10084        assert!(result.failed());
10085        assert!(result.messages[0].contains("invalid IR"), "{:?}", result.messages);
10086    }
10087
10088    #[test]
10089    fn a_typed_tree_is_not_something_an_input_of_ir_can_produce() {
10090        // The C that became this is not here any more, so there is nothing to print a tree of.
10091        let mut fs = MemoryFileSystem::new();
10092        fs.insert("/main.ir", Vec::new());
10093        let result = compile_ir(&options(), "/main.ir", &fs);
10094        assert!(result.failed());
10095        assert!(result.messages[0].contains("can only be emitted as IR"), "{:?}", result.messages);
10096    }
10097
10098    #[test]
10099    fn the_printed_ir_reads_back_as_the_same_module() {
10100        // The M2 exit criterion: the text is the module and nothing about it is lost by
10101        // writing it down. Anything the printer invents or the parser drops shows up here.
10102        let text = ir("\
10103struct point { int x, y; };
10104static const char greeting[] = \"hi\";
10105int table[4] = { 1, 2, 3 };
10106int puts(const char *);
10107double half(double x) { return x / 2.0; }
10108int f(int n) {
10109  int total = 0;
10110  for (int i = 0; i < n; i++) {
10111    if (i == 3) continue;
10112    total += table[i];
10113  }
10114  switch (n) {
10115    case 0: total = 1;
10116    case 1: total++; break;
10117    default: total = -total;
10118  }
10119  struct point p = { total, 1 };
10120  int *q = &p.y;
10121  puts(greeting);
10122  return p.x + *q;
10123}
10124int dispatch(int c) {
10125  void *p = c ? &&one : &&two;
10126  goto *p;
10127one:
10128  return 1;
10129two:
10130  return 2;
10131}
10132int assembly(int x, int *p) {
10133  int r;
10134  __asm__ volatile(\"xadd %0, %2\" : \"=r\"(r), \"+m\"(*p) : \"0\"(x) : \"cc\");
10135  __asm__ goto(\"cbnz %0, %l1\" : : \"r\"(r) : : away);
10136  return r;
10137away:
10138  return 0;
10139}
10140");
10141        let mut names = Interner::new();
10142        let module = rucc_ir::parse(&text, &mut names).expect("the printer writes what it reads");
10143        assert_eq!(rucc_ir::print(&module, &names), text);
10144    }
10145
10146    #[test]
10147    fn what_save_temps_keeps_is_the_text_that_was_compiled_and_the_assembly_that_was_assembled() {
10148        // The point of the flag is that these two are the compilation rather than a description
10149        // of one, so both come out of the run that produced the object rather than out of a
10150        // second run under different flags.
10151        let mut opts = options();
10152        opts.emit = EmitKind::Object;
10153        opts.save_temps = rucc_session::SaveTemps::Object;
10154        let result = run(&opts, "#define N 2\nint a[N];\n");
10155        assert_eq!(result.messages, Vec::<String>::new());
10156        let text = result.temps.preprocessed.expect("the preprocessed text");
10157        assert!(text.contains("int a[2];"), "{text}");
10158        assert!(text.starts_with("# 1 \"/main.c\""), "{text}");
10159        let asm = result.temps.assembly.expect("the assembly");
10160        assert!(asm.contains("a:"), "{asm}");
10161        assert!(matches!(result.artifact, Artifact::Object { .. }), "{:?}", result.artifact);
10162    }
10163
10164    #[test]
10165    fn nothing_is_kept_unless_the_flag_asked_for_it() {
10166        // A compilation that was not asked to keep anything must not pay for printing text
10167        // nobody will read, and the empty value is what says so.
10168        let mut opts = options();
10169        opts.emit = EmitKind::Object;
10170        assert_eq!(run(&opts, "int a;\n").temps, Temps::default());
10171    }
10172
10173    #[test]
10174    fn a_compilation_that_stops_before_the_back_end_keeps_the_text_and_no_assembly() {
10175        // `--emit=ir` never produces any, and the text is worth keeping all the same: it is
10176        // what a report about the file being read wrongly has to have in it.
10177        let mut opts = options();
10178        opts.emit = EmitKind::Ir;
10179        opts.save_temps = rucc_session::SaveTemps::Cwd;
10180        let result = run(&opts, "int a;\n");
10181        assert!(result.temps.preprocessed.is_some());
10182        assert_eq!(result.temps.assembly, None);
10183    }
10184
10185    /// A stretch of a local's life, written short because these tests are about nothing else.
10186    fn span(from: u64, len: u64, held: rucc_debug::Held) -> rucc_debug::Span {
10187        rucc_debug::Span { from, len, held }
10188    }
10189
10190    #[test]
10191    fn two_stretches_that_meet_and_agree_come_out_as_one() {
10192        let one = span(0, 4, rucc_debug::Held::Reg(3));
10193        let two = span(4, 4, rucc_debug::Held::Reg(3));
10194        assert_eq!(settle(vec![two, one]), vec![span(0, 8, rucc_debug::Held::Reg(3))]);
10195    }
10196
10197    #[test]
10198    fn a_stretch_another_starts_inside_and_disagrees_with_ends_where_the_other_starts() {
10199        let one = span(0, 8, rucc_debug::Held::Reg(3));
10200        let two = span(4, 8, rucc_debug::Held::Reg(4));
10201        // The second starts where the declaration was given its value, so from there it is the
10202        // second and not the first.
10203        let settled = settle(vec![one, two]);
10204        assert_eq!(
10205            settled,
10206            vec![span(0, 4, rucc_debug::Held::Reg(3)), span(4, 8, rucc_debug::Held::Reg(4))]
10207        );
10208    }
10209
10210    #[test]
10211    fn a_stretch_cut_by_one_that_ends_first_does_not_come_back_after_it() {
10212        // The old value is still live after the new one is done with, because something else
10213        // reads it, but the declaration stopped holding it where the new one started.
10214        let one = span(0, 16, rucc_debug::Held::Reg(3));
10215        let two = span(4, 4, rucc_debug::Held::Reg(4));
10216        assert_eq!(
10217            settle(vec![one, two]),
10218            vec![span(0, 4, rucc_debug::Held::Reg(3)), span(4, 4, rucc_debug::Held::Reg(4))]
10219        );
10220    }
10221
10222    #[test]
10223    fn a_stretch_inside_another_that_agrees_with_it_cuts_nothing() {
10224        let one = span(0, 16, rucc_debug::Held::Reg(3));
10225        let two = span(4, 4, rucc_debug::Held::Reg(3));
10226        assert_eq!(settle(vec![one, two]), vec![span(0, 16, rucc_debug::Held::Reg(3))]);
10227    }
10228
10229    #[test]
10230    fn a_stretch_two_others_disagree_over_the_whole_of_says_nothing_at_all() {
10231        let one = span(0, 8, rucc_debug::Held::Reg(3));
10232        let two = span(0, 8, rucc_debug::Held::Frame(-16));
10233        assert_eq!(settle(vec![one, two]), Vec::new());
10234    }
10235
10236    #[test]
10237    fn stretches_with_a_gap_between_them_keep_the_gap() {
10238        let one = span(0, 4, rucc_debug::Held::Reg(3));
10239        let two = span(16, 4, rucc_debug::Held::Reg(3));
10240        assert_eq!(settle(vec![one, two]), vec![one, two]);
10241    }
10242
10243    /// A function of `len` bytes, since that is the only thing about one these tests look at.
10244    fn extent(len: usize) -> rucc_object::Extent {
10245        rucc_object::Extent {
10246            name: "f".to_owned(),
10247            start: 0,
10248            len,
10249            align: 1,
10250            binding: rucc_object::Binding::Global,
10251            visibility: rucc_object::Visibility::Default,
10252            patch: None,
10253            landings: Vec::new(),
10254        }
10255    }
10256
10257    /// A line table row at `at` built for the source bytes `lo` to `hi`.
10258    fn row(at: usize, lo: u32, hi: u32) -> rucc_asm::Row {
10259        let span = Span::new(lo, hi);
10260        rucc_asm::Row { at, span, inst: None }
10261    }
10262
10263    #[test]
10264    fn a_row_ends_where_the_next_address_begins() {
10265        let rows = [row(0, 0, 1), row(4, 1, 2), row(10, 2, 3)];
10266        assert_eq!(ends(&extent(16), &rows), vec![4, 10, 16]);
10267    }
10268
10269    #[test]
10270    fn rows_sharing_an_address_all_end_where_the_next_address_begins() {
10271        // Two instructions that encoded to nothing sit on the address of the one after them, and
10272        // none of the three ends in front of that one.
10273        let rows = [row(0, 0, 1), row(4, 1, 2), row(4, 2, 3), row(4, 3, 4)];
10274        assert_eq!(ends(&extent(12), &rows), vec![4, 12, 12, 12]);
10275    }
10276
10277    #[test]
10278    fn the_rows_of_a_scope_that_are_next_to_each_other_come_out_as_one_stretch() {
10279        let rows = [row(0, 0, 4), row(4, 10, 14), row(8, 14, 18), row(12, 40, 44)];
10280        let ends = ends(&extent(16), &rows);
10281        let scope = Span::new(8, 20);
10282        assert_eq!(spread(scope, &ends, &rows), vec![rucc_debug::Reach { from: 4, len: 8 }]);
10283    }
10284
10285    #[test]
10286    fn a_scope_the_back_end_split_in_two_comes_out_as_two_stretches() {
10287        let rows = [row(0, 10, 14), row(4, 40, 44), row(8, 14, 18)];
10288        let ends = ends(&extent(12), &rows);
10289        let scope = Span::new(8, 20);
10290        let over = spread(scope, &ends, &rows);
10291        assert_eq!(
10292            over,
10293            vec![rucc_debug::Reach { from: 0, len: 4 }, rucc_debug::Reach { from: 8, len: 4 }]
10294        );
10295    }
10296
10297    #[test]
10298    fn a_row_with_no_source_of_its_own_belongs_to_no_scope() {
10299        // The prologue is the one of these every function has, and it is not inside any block.
10300        let rows = [rucc_asm::Row { at: 0, span: Span::DUMMY, inst: None }, row(4, 10, 14)];
10301        let ends = ends(&extent(8), &rows);
10302        let scope = Span::new(0, 20);
10303        assert_eq!(spread(scope, &ends, &rows), vec![rucc_debug::Reach { from: 4, len: 4 }]);
10304    }
10305
10306    /// A scope of the unit, written short because these tests are about nothing else.
10307    fn scope(parent: Option<usize>, lo: u32, hi: u32) -> crate::shapes::Scope {
10308        let span = Span::new(lo, hi);
10309        crate::shapes::Scope { parent, span }
10310    }
10311
10312    #[test]
10313    fn a_function_gets_the_scopes_its_own_locals_are_in_and_nothing_else() {
10314        // Two functions' worth of scopes in one table, and this one is in the second pair.
10315        let scopes = [scope(None, 0, 10), scope(None, 20, 30), scope(Some(1), 22, 26)];
10316        let rows = [row(0, 22, 24), row(4, 26, 28)];
10317        let (out, at) = nests(&[Some(2)], &scopes, &extent(8), &rows);
10318        // The one the local is in and the one that is inside, numbered from zero for this
10319        // function, with the parent named by the entry it became rather than by where it was.
10320        assert_eq!(at.get(&1), Some(&0));
10321        assert_eq!(at.get(&2), Some(&1));
10322        assert_eq!(at.get(&0), None);
10323        assert_eq!(out.len(), 2);
10324        assert_eq!(out[0].parent, None);
10325        assert_eq!(out[1].parent, Some(0));
10326        assert_eq!(out[0].over, vec![rucc_debug::Reach { from: 0, len: 8 }]);
10327        assert_eq!(out[1].over, vec![rucc_debug::Reach { from: 0, len: 4 }]);
10328    }
10329
10330    #[test]
10331    fn a_local_written_straight_into_the_body_pulls_no_scope_in() {
10332        let scopes = [scope(None, 20, 30)];
10333        let rows = [row(0, 22, 24)];
10334        let (out, at) = nests(&[None], &scopes, &extent(4), &rows);
10335        assert_eq!(out, Vec::new());
10336        assert!(at.is_empty());
10337    }
10338
10339    #[test]
10340    fn a_scope_whose_code_all_went_away_is_still_one_of_the_functions_scopes() {
10341        // Nothing was built for the bytes it covers, so there is nowhere to say its names were
10342        // live. The entry is written anyway, since dropping it would move a local up into the
10343        // function and make it answer to a name it was not declared under.
10344        let scopes = [scope(None, 20, 30)];
10345        let rows = [row(0, 40, 44)];
10346        let (out, at) = nests(&[Some(0)], &scopes, &extent(4), &rows);
10347        assert_eq!(at.get(&0), Some(&0));
10348        assert_eq!(out.len(), 1);
10349        assert_eq!(out[0].over, Vec::new());
10350    }
10351}