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;
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}
128
129/// The intermediate text a compilation went through, kept when `-save-temps` asked for it.
130///
131/// Both are `None` on a compilation that was not asked to keep anything, and the assembly is
132/// `None` on one that stopped before there was any. Holding the text rather than writing it is
133/// what keeps this function free of the file system, which is what lets it be tested against a
134/// map from path to bytes.
135#[derive(Debug, Clone, PartialEq, Eq, Default)]
136pub struct Temps {
137    /// Phase 4's output, the same text `-E` would have printed.
138    pub preprocessed: Option<String>,
139    /// The assembly the back end produced on the way to the object file.
140    pub assembly: Option<String>,
141}
142
143impl Compiled {
144    /// Whether anything went wrong badly enough that the output should not be used.
145    #[must_use]
146    pub fn failed(&self) -> bool {
147        self.errors > 0
148    }
149
150    /// The text that was produced, and the empty string for anything that is not text.
151    ///
152    /// A caller that asked for one of the text kinds knows which it asked for, so this saves it
153    /// matching on a variant it has already ruled out.
154    #[must_use]
155    pub fn text(&self) -> &str {
156        match &self.artifact {
157            Artifact::Text(text) => text,
158            _ => "",
159        }
160    }
161}
162
163/// Compiles one file as far as `opts.emit` asks for and renders the result.
164///
165/// `name` is the path as the user wrote it, which is the name every diagnostic about the file
166/// uses. Every kind but the executable produces something today, and that one runs the same front
167/// end and gives back nothing, so that a file with a mistake in it is reported the same way
168/// whichever kind was asked for, rather than compiling silently until the part that is written
169/// notices.
170///
171/// The checking is skipped when the parse reported an error. The two poisoning rules mean a
172/// diagnosed expression produces no further complaints, but a declaration the parser had to skip
173/// past leaves no declaration behind at all, and every later use of that name would be reported
174/// as undeclared. One mistake is worth one message.
175#[must_use]
176pub fn compile(opts: &Options, name: &str, fs: &dyn FileSystem) -> Compiled {
177    let mut sess = Session::new(opts.clone());
178    // Before anything else interns a name. The keyword symbols have to be one unbroken run for
179    // a lookup to be a subtraction, and the preprocessor interns every identifier it reads, so
180    // building this after the expansion would mean building it after `char` had been seen.
181    let keywords = Keywords::new(&mut sess.interner, opts.std, opts.gnu_extensions);
182    let mut diagnostics: Vec<Diagnostic> = Vec::new();
183    // Filled in by the back end when there is one, and empty for every kind that stops before it.
184    let mut fired = Fired::new();
185    // The same, and the other thing the back end is asked to record about itself.
186    let mut pressure = Pressure::new();
187    let mut lowerings = Lowerings::asked(opts.lowering_dump.is_some());
188    // Filled in by the optimizer, and only when `-fdump-ir=` asked for something.
189    let mut dumps = Vec::new();
190    let mut remarks = String::new();
191    // Filled in as the compilation goes past each of them, and only under `-save-temps`.
192    let mut temps = Temps::default();
193
194    let bytes = match fs.read(Path::new(name)) {
195        Ok(bytes) => bytes,
196        Err(e) => return failure(format!("{name}: {e}")),
197    };
198    let Ok(file) = sess.sources.add_shared(crate::phase::source_name(name), bytes, None) else {
199        return failure(format!("{name}: the source map has no room left for this file"));
200    };
201
202    // Phases 1 to 4. The expanded stream is turned into pp-tokens straight away, because the
203    // include context borrows the source map that rendering a diagnostic reads and the borrow
204    // has to end before anything is rendered.
205    let mut pp = rucc_pp::Preprocessor::with_prefix_map(opts.prefix_map.macros.clone());
206    let predef = rucc_pp::Predef::for_options(opts);
207    let expanded: Vec<PpToken> = {
208        let mut tokens = Vec::new();
209        // The inner block is the borrow. The printer under `-save-temps` reads the source map
210        // that the include context is holding, so the context has to be gone before it runs, and
211        // nothing happens in between, which is what makes the text it prints the text that is
212        // compiled below rather than a second answer to the same question.
213        {
214            let mut cx =
215                rucc_pp::Context::new(&mut sess.interner, &mut sess.sources, fs, &opts.search);
216            cx.lex = rucc_lex::Options::for_dialect(opts.std, opts.gnu_extensions);
217            cx.pedantic = opts.pedantic;
218            if pp.predefine(&sess.target, &predef, &mut cx).is_err() {
219                return failure(format!(
220                    "{name}: the source map has no room for the built in macros"
221                ));
222            }
223            if pp.preinclude(&opts.preincludes, &mut tokens, &mut cx).is_err() {
224                return failure(format!("{name}: the source map has no room for the command line"));
225            }
226            tokens.append(&mut pp.run(file, &mut cx));
227        }
228        if opts.save_temps.wanted() {
229            temps.preprocessed = Some(rucc_pp::print(
230                file,
231                &tokens,
232                pp.line_directives(),
233                &sess.sources,
234                &sess.interner,
235                rucc_pp::PrintOptions { line_markers: opts.line_markers },
236            ));
237        }
238        tokens.iter().map(|token| token.to_pp()).collect()
239    };
240    diagnostics.extend(pp.take_diagnostics());
241    // Taken here rather than at the end, because the preprocessor is done with and everything
242    // after this is about the tree it produced.
243    let deps = pp.dependencies().to_vec();
244
245    // Phase 7, which is where a spelling becomes a keyword and a preprocessing number becomes
246    // a constant of a type.
247    let cx = Convert {
248        keywords: &keywords,
249        interner: &sess.interner,
250        target: &sess.target,
251        std: opts.std,
252        gnu: opts.gnu_extensions,
253        pedantic: opts.pedantic,
254    };
255    let (tokens, complaints) = convert(&expanded, &cx);
256    diagnostics.extend(complaints);
257
258    let parsed = rucc_parse::parse(
259        &tokens,
260        rucc_parse::Context {
261            interner: &sess.interner,
262            std: opts.std,
263            gnu: opts.gnu_extensions,
264            pedantic: opts.pedantic,
265            error_limit: opts.error_limit as usize,
266        },
267    );
268    let parse_failed = parsed.diagnostics.iter().any(|d| d.severity.is_fatal());
269    diagnostics.extend(parsed.diagnostics);
270
271    let mut artifact = Artifact::Nothing;
272    // Zero when nothing instruments, which is the truthful summary of a file built without
273    // `-fsafety`: no checks went in, so none is standing, and every call it makes is unmodelled.
274    let mut instrumented = Instrumented::default();
275    if !parse_failed {
276        let mut checker = Checker::new(
277            &parsed.ast,
278            CheckContext {
279                names: &sess.interner,
280                target: &sess.target,
281                std: opts.std,
282                gnu: opts.gnu_extensions,
283                pedantic: opts.pedantic,
284                permissive: opts.permissive,
285                gnu89_inline: opts.gnu89_inline,
286                error_limit: opts.error_limit as usize,
287                // A freestanding program has no C library, so a name that is the library's
288                // everywhere else is the program's own here and means whatever it defined.
289                builtins: opts.builtins && opts.hosted,
290                no_builtin: &opts.no_builtin,
291                short_enums: opts.short_enums,
292                ms_extensions: sess.ms_extensions(),
293                trapping_math: opts.trapping_math,
294            },
295        );
296        checker.check_unit();
297        let checked = checker.finish();
298        if !checked.failed() {
299            match opts.emit {
300                EmitKind::Tast => {
301                    artifact = Artifact::Text(rucc_sema::print(
302                        &checked.tast,
303                        &checked.types,
304                        &sess.interner,
305                    ));
306                }
307                // Nothing past the checker, because a granule is a fact about a layout and a
308                // layout is settled the moment the closing brace is seen. Lowering the
309                // function bodies would take minutes on an amalgamation and answer nothing.
310                EmitKind::TypeGranules => {
311                    artifact = Artifact::Text(rucc_types::granule_report(
312                        &checked.types,
313                        &sess.interner,
314                        &sess.target,
315                    ));
316                }
317                EmitKind::Ir
318                | EmitKind::MirFinal
319                | EmitKind::Asm
320                | EmitKind::Object
321                | EmitKind::Archive
322                | EmitKind::Executable
323                | EmitKind::SafetySummary => {
324                    // What a `.incbin` in an `asm` at file scope names is read through the same
325                    // file system the sources came through, and from where the compiler was run
326                    // rather than from beside the source, because that is where an assembler
327                    // looks for it.
328                    let mut read = |named: &str| {
329                        fs.read(Path::new(named))
330                            .map(|bytes| bytes.as_slice().to_vec())
331                            .map_err(|why| why.to_string())
332                    };
333                    // What the debug information will say about types and signatures, taken
334                    // here because this is the last place the checker's types are readable
335                    // without the back end's borrow of the interner in the way. Nothing at all
336                    // when the build asked for no debug information, since a translation unit
337                    // the size of an amalgamation has tens of thousands of types in it.
338                    let meaning = if opts.debug_info {
339                        crate::shapes::collect(
340                            &checked.tast,
341                            &checked.types,
342                            &sess.target,
343                            &sess.interner,
344                            &sess.sources,
345                        )
346                    } else {
347                        crate::shapes::Meaning::default()
348                    };
349                    let mut lowered = rucc_lower::lower(
350                        crate::phase::source_name(name),
351                        rucc_lower::Context {
352                            tast: &checked.tast,
353                            types: &checked.types,
354                            target: &sess.target,
355                            names: &mut sess.interner,
356                            visibility: match opts.visibility {
357                                Visibility::Default => IrVisibility::Default,
358                                Visibility::Hidden => IrVisibility::Hidden,
359                                Visibility::Protected => IrVisibility::Protected,
360                            },
361                            protector: match opts.protector {
362                                Protector::None => LowerProtector::None,
363                                Protector::Buffers => LowerProtector::Buffers,
364                                Protector::Strong => LowerProtector::Strong,
365                                Protector::All => LowerProtector::All,
366                            },
367                            wrapping: rucc_lower::Wrapping {
368                                signed: opts.wrapping.signed,
369                                pointer: opts.wrapping.pointer,
370                                trap: opts.wrapping.trap,
371                            },
372                            aliasing: opts.strict_aliasing,
373                            padding: opts.padding == Padding::Ignored,
374                            contract: match opts.fp_contract {
375                                Contract::Off => FpContract::Off,
376                                Contract::On => FpContract::On,
377                                Contract::Fast => FpContract::Fast,
378                            },
379                            align: opts.align_functions,
380                            read: &mut read,
381                        },
382                    );
383                    // The walk reports what it cannot build, and what it did build is printed
384                    // anyway: a file with one construct missing from it is more use to read
385                    // than nothing at all, and the errors are what stop it being compiled.
386                    let failed = lowered.diagnostics.iter().any(|d| d.severity.is_fatal());
387                    if !failed {
388                        // The verifier runs on everything the walk builds, always. It is the
389                        // one check that a bug in the walk cannot talk its way past, and a
390                        // wrong instruction found here costs a message rather than an hour
391                        // in front of a debugger over the assembly it turned into.
392                        if let Err(errors) = rucc_ir::verify(&lowered.module, &sess.interner) {
393                            for error in errors {
394                                diagnostics.push(internal(&format!("invalid IR, {error}")));
395                            }
396                        } else if let Err(complaints) =
397                            instrument(&mut lowered.module, &mut sess.interner, opts)
398                                .map(|done| instrumented = done)
399                        {
400                            diagnostics.extend(complaints);
401                        } else if let Err(complaints) = optimize(
402                            &mut lowered.module,
403                            &mut sess.interner,
404                            &sess.target,
405                            opts,
406                            name,
407                            &mut dumps,
408                            &mut remarks,
409                        ) {
410                            diagnostics.extend(complaints);
411                        } else if opts.emit == EmitKind::SafetySummary {
412                            // After the optimizer, because the number that matters is how many
413                            // checks are still standing and there is no way to know that before it
414                            // has run. Before the back end, because the back end turns a check into
415                            // a call and a summary of calls is not a summary of checks.
416                            artifact = Artifact::Text(
417                                rucc_safety::summarize(
418                                    &lowered.module,
419                                    &sess.interner,
420                                    name,
421                                    opts.safety.as_str(),
422                                    instrumented.checks,
423                                    instrumented.interposed,
424                                    instrumented.crossings,
425                                )
426                                .render(),
427                            );
428                        } else if opts.emit == EmitKind::Ir {
429                            // After the optimizer rather than before it, so that `--emit=ir -O2`
430                            // is the IR the back end will be given rather than the IR it would
431                            // have been given at `-O0`. There is no other way to see what a pass
432                            // did without reading the assembly it turned into.
433                            artifact =
434                                Artifact::Text(rucc_ir::print(&lowered.module, &sess.interner));
435                        } else {
436                            // The back end, which is every pass after the IR and which is
437                            // where a construct nothing has a rule for is finally noticed.
438                            match generate(
439                                &mut lowered.module,
440                                &mut sess.interner,
441                                &sess.target,
442                                opts,
443                                &mut Recording {
444                                    fired: &mut fired,
445                                    pressure: &mut pressure,
446                                    lowerings: &mut lowerings,
447                                },
448                                &mut temps.assembly,
449                                Origin { map: &sess.sources, name, meaning: &meaning },
450                            ) {
451                                Ok(made) => artifact = made,
452                                Err(complaints) => diagnostics.extend(complaints),
453                            }
454                        }
455                    }
456                    diagnostics.extend(lowered.diagnostics);
457                }
458                _ => {}
459            }
460        }
461        diagnostics.extend(checked.diagnostics);
462    }
463
464    let mut messages = Vec::with_capacity(diagnostics.len());
465    let mut errors = 0;
466    for diag in &diagnostics {
467        // `-w` drops the warning here rather than at the several hundred places one is raised,
468        // and it drops it before the count, so `-w -Werror` compiles. A warning that was never
469        // raised is not a warning there is anything to promote. A warning about something in a
470        // header that came with the machine goes the same way for the same reason, unless
471        // `-Wsystem-headers` asked for it.
472        if rucc_diag::dropped(diag, &sess.sources, opts.warnings, opts.system_header_warnings) {
473            continue;
474        }
475        if diag.severity.is_fatal()
476            || (diag.severity == Severity::Warning && opts.warnings_are_errors)
477        {
478            errors += 1;
479        }
480        messages.push(render(diag, &sess.sources, opts.warnings_are_errors));
481    }
482    if errors > 0 {
483        // A tree built from a file that did not compile is not a tree anything should read.
484        artifact = Artifact::Nothing;
485    }
486    // Kept even when the compilation failed, because a rule that fired did fire and a report about
487    // which rules a corpus reaches should not lose the ones a file with a mistake in it reached.
488    Compiled { artifact, messages, errors, fired, pressure, lowerings, dumps, remarks, deps, temps }
489}
490
491/// Reads one file of IR, checks it, and prints it back.
492///
493/// This is the compiler's own textual IR arriving as an input rather than leaving as an output,
494/// which is what makes the round trip in the M2 exit criterion something to run rather than
495/// something to believe: what the printer wrote is read back, verified, and written again, and
496/// the two files are either the same bytes or they are not.
497///
498/// The verifier runs here for the reason it runs after the walk. A module that was printed by
499/// this compiler has been through it once already, and one that a person edited has not.
500#[must_use]
501pub fn compile_ir(opts: &Options, name: &str, fs: &dyn FileSystem) -> Compiled {
502    let mut sess = Session::new(opts.clone());
503    if opts.emit != EmitKind::Ir {
504        return failure(format!(
505            "{name}: an input of IR can only be emitted as IR, and `--emit={}` asks for what \
506             the C in front of it became",
507            opts.emit.as_str()
508        ));
509    }
510    let bytes = match fs.read(Path::new(name)) {
511        Ok(bytes) => bytes,
512        Err(e) => return failure(format!("{name}: {e}")),
513    };
514    let Ok(text) = std::str::from_utf8(bytes.as_slice()) else {
515        return failure(format!("{name}: this is not text, so it is not IR"));
516    };
517
518    let module = match rucc_ir::parse(text, &mut sess.interner) {
519        Ok(module) => module,
520        Err(error) => {
521            return failure(format!("{name}:{}: {}", error.line, error.message));
522        }
523    };
524    let mut diagnostics: Vec<Diagnostic> = Vec::new();
525    if let Err(errors) = rucc_ir::verify(&module, &sess.interner) {
526        for error in errors {
527            diagnostics.push(invalid(&format!("invalid IR, {error}")));
528        }
529    }
530    let mut messages = Vec::with_capacity(diagnostics.len());
531    for diag in &diagnostics {
532        messages.push(render(diag, &sess.sources, opts.warnings_are_errors));
533    }
534    let errors = u32::try_from(messages.len()).unwrap_or(u32::MAX);
535    let artifact = if errors > 0 {
536        Artifact::Nothing
537    } else {
538        Artifact::Text(rucc_ir::print(&module, &sess.interner))
539    };
540    // Nothing here reaches the back end, so no rule fired and there is nothing to record.
541    Compiled {
542        artifact,
543        messages,
544        errors,
545        fired: Fired::new(),
546        pressure: Pressure::new(),
547        lowerings: Lowerings::new(),
548        dumps: Vec::new(),
549        remarks: String::new(),
550        deps: Vec::new(),
551        temps: Temps::default(),
552    }
553}
554
555/// Puts the memory safety checks in and redirects the calls that cross the boundary, when
556/// `-fsafety=` asked for them.
557///
558/// Between the walk and the optimizer, which is where section 15.3 of
559/// `spec/safe-memory/15-integration.md` puts it and which is the whole design in one line: the
560/// checks go in while the addresses the program computes still exist, and the optimizer then
561/// discharges the ones it can prove. Every sanitizer that came before instruments after the
562/// optimizer so that its checks cannot be deleted, and pays for all of them forever.
563///
564/// The calls to the C library are redirected here too, and in the same window and for a related
565/// reason. `spec/safe-memory/10-boundaries.md` section 10.3 wants a `memcpy` modelled by a wrapper
566/// that performs the judgements, and `rucc_safety::wrap` is why that has to happen before the
567/// optimizer sees the call rather than after.
568///
569/// The verifier runs again afterwards, for the reason it runs after the walk. This pass rewrites
570/// every function in the module, and a pass that produced IR nothing else accepts should say so
571/// here rather than in the assembly it turned into.
572///
573/// # Errors
574///
575/// When the inserted checks left the module in a state the verifier refuses, which is a bug in
576/// this compiler and not in the program being compiled.
577fn instrument(
578    module: &mut rucc_ir::Module,
579    names: &mut Interner,
580    opts: &Options,
581) -> Result<Instrumented, Vec<Diagnostic>> {
582    if !opts.safety.instruments() {
583        return Ok(Instrumented::default());
584    }
585    let mut checks = rucc_safety::run(module, opts.subobject, opts.promise, opts.races);
586    // The one check that is about a call rather than about an access, so it is a walk of its own
587    // and it is here rather than in the walk above. `rucc_safety::ending` is why, and the short
588    // version is that deciding it means resolving a name, which takes the interner.
589    //
590    // Before the redirection for the same reason the redirection is before the optimizer: what this
591    // reads is the name the program wrote, and a pass that had already pointed the call somewhere
592    // else would leave it with a name this one has no row for.
593    checks.freed = rucc_safety::ending::checks(module, names);
594    // Before the optimizer rather than beside the check lowering, which is what
595    // `rucc_safety::wrap` argues out: `memcpy` is a name an optimizer knows things about, and a
596    // pass that turns a short copy into a pair of loads and stores would leave behind accesses the
597    // check insertion has already finished walking past.
598    let interposed = rucc_safety::redirect(module, names);
599    // After the redirection, so that a call this build models with a wrapper is not also counted
600    // as a crossing it did not model.
601    let crossings = rucc_safety::witness(module, names);
602    match rucc_ir::verify(module, names) {
603        Ok(()) => Ok(Instrumented { checks, interposed, crossings }),
604        Err(errors) => Err(errors
605            .iter()
606            .map(|e| internal(&format!("invalid IR after check insertion, {e}")))
607            .collect()),
608    }
609}
610
611/// What the instrumentation did, which nothing but the summary reads.
612///
613/// Carried out of [`instrument`] rather than recovered from the module afterwards because neither
614/// number survives the optimizer: a check that was discharged leaves nothing behind saying it was
615/// ever there, and a call that was pointed at a wrapper looks like a call that always named one.
616#[derive(Clone, Copy, Debug, Default)]
617struct Instrumented {
618    /// How many checks of each class went in.
619    checks: rucc_safety::Counts,
620    /// How many calls were pointed at an interposition wrapper.
621    interposed: usize,
622    /// How many places a pointer crosses to or from code this build did not instrument.
623    crossings: rucc_safety::Sites,
624}
625
626/// Runs the optimizer over the module, and collects whatever the dumps asked for.
627///
628/// The level chooses a pipeline, the `-f` flags edit it, and at `-O0` there is nothing in it, so
629/// this is a walk over an empty list rather than a branch on the level. See section 9.1 of
630/// `spec/09-optimizer.md` for why the pipelines are written out rather than assembled.
631///
632/// # Errors
633///
634/// When a pass left the module in a state the verifier refuses, which is a bug in the pass and
635/// not in the program being compiled, so it is reported as an internal error the way a bad
636/// lowering is.
637fn optimize(
638    module: &mut rucc_ir::Module,
639    names: &mut Interner,
640    target: &TargetInfo,
641    opts: &Options,
642    file: &str,
643    dumps: &mut Vec<rucc_opt::Dump>,
644    remarks: &mut String,
645) -> Result<(), Vec<Diagnostic>> {
646    let mut settings = rucc_opt::Options::for_level(opts.opt_level);
647    // What the analyses that read a body may believe about it. The same question the back end asks
648    // about addresses, with one thing on top: `-fno-semantic-interposition` is the build promising
649    // that a name it exports is the one that will run, which is what every distribution builds a
650    // library with. It says nothing about how an address is reached, and gcc does not change that
651    // under the flag either, so the back end is not given this value.
652    settings.interposition = match opts.interposition {
653        true => replaceable(target, opts),
654        false => IrPic::Executable,
655    };
656    settings.toggles.clone_from(&opts.passes);
657    // The same pair the front end reads a call to a standard name with, which is section 20.1's
658    // three way split: `-ffreestanding` says the library is not there, `-fno-builtin` says it is
659    // there and is not to be assumed to do what the standard says, and a fold that leaves behind a
660    // call to `puts` needs both of those to be off.
661    settings.builtins = opts.builtins && opts.hosted;
662    settings.no_builtin.clone_from(&opts.no_builtin);
663    settings.fuel = opts.pass_fuel.iter().cloned().collect();
664    settings.global_fuel = opts.pass_fuel_global;
665    settings.verify |= opts.verify_each;
666    for (on, spec) in &opts.pass_gates {
667        // Same argument as the dumps below: every spelling in here was checked while the
668        // arguments were parsed, so a rejection now is this compiler disagreeing with itself.
669        if let Err(why) = settings.gates.add(*on, spec) {
670            return Err(vec![internal(&why)]);
671        }
672    }
673    for spec in &opts.dump_ir {
674        // Every spelling in here was checked while the arguments were parsed, so a rejection
675        // now is this compiler disagreeing with itself rather than the command line being wrong.
676        if let Err(why) = settings.dumps.add(spec) {
677            return Err(vec![internal(&why)]);
678        }
679    }
680    let mut wants = rucc_opt::Wants::none();
681    for spec in &opts.opt_info {
682        // Same argument as the dumps above: every spelling was checked while the arguments were
683        // parsed, so a rejection now is the compiler disagreeing with itself.
684        if let Err(why) = wants.add(spec) {
685            return Err(vec![internal(&why)]);
686        }
687    }
688    let report = rucc_opt::run(module, names, &settings);
689    remarks.push_str(&rucc_opt::optinfo::render(file, &report, names, wants));
690    dumps.extend(report.dumps);
691    match report.broke.is_empty() {
692        true => Ok(()),
693        false => Err(report.broke.iter().map(|why| internal(why)).collect()),
694    }
695}
696
697/// Runs the back end over every function in `module` and writes what came out.
698///
699/// One machine function per definition in the module, in the order the module holds them, every
700/// register physical and every frame offset a constant. A declaration has no body and is skipped,
701/// because there is nothing in it to compile.
702///
703/// What the last step is, is the only thing `--emit=mir-final`, `-S` and `-c` disagree about. The
704/// three read the same functions and differ in whether they are printed as machine IR, printed as
705/// assembly, or encoded and put in a file, which is the point of section 11.1 of
706/// `spec/11-asm-objects-debug.md`: a listing that disagrees with the object file beside it is
707/// worse than no listing, and the way to make that impossible is to have one description of an
708/// instruction and two ways of writing it down.
709///
710/// # Errors
711///
712/// One diagnostic per function the back end could not compile, or one about the target when no
713/// back end covers it at all. Every function is attempted rather than stopping at the first, so a
714/// file with three constructs missing from the rule set reports three rather than one at a time.
715///
716/// `assembly` is where `-save-temps` gets its listing from on the path that does not print one,
717/// which is the same functions written the other way rather than a second compilation of the same
718/// file. A listing that disagrees with the object beside it would be worse than none.
719/// Whether a name this file exports is one another object may define or replace.
720///
721/// The link that reads the object decides half of what is in it, and the command line is where that
722/// is said, which is why the flag reaches this far down. See #756.
723///
724/// ELF only, because it is a question about a format rather than about a machine and the other two
725/// answer it differently. Mach-O has a two level namespace, so a name a library defines is bound to
726/// that library and is not replaced by a definition loaded earlier, and it has no copy relocations,
727/// so a variable defined elsewhere needs the table whichever link is coming. COFF decides what
728/// leaves a DLL by an export table the linker is handed. Neither has an object writer here yet, so
729/// what this does is decline to say the ELF answer about them.
730fn replaceable(target: &TargetInfo, opts: &Options) -> IrPic {
731    match (target.tuple.os().object_format(), opts.pic) {
732        (Some(ObjectFormat::Elf), Pic::Library) => IrPic::Library,
733        _ => IrPic::Executable,
734    }
735}
736
737/// Where the file being generated came from, which is what the debug information is about.
738///
739/// The three together rather than separately because none of them is any use on its own here: a
740/// span without the map it points into is a pair of numbers, a name without the spans is a file
741/// nothing in the object refers to, and a signature without the name of the function it belongs to
742/// is an entry with nothing to attach it to.
743#[derive(Clone, Copy)]
744struct Origin<'a> {
745    /// Where every span in the module points.
746    map: &'a SourceMap,
747    /// What the command line called the file, which is what `DW_AT_name` says.
748    name: &'a str,
749    /// The types and the signatures, and empty where the build wanted no debug information.
750    meaning: &'a crate::shapes::Meaning,
751}
752
753fn generate(
754    module: &mut rucc_ir::Module,
755    names: &mut Interner,
756    target: &TargetInfo,
757    opts: &Options,
758    recording: &mut Recording<'_>,
759    assembly: &mut Option<String>,
760    origin: Origin<'_>,
761) -> Result<Artifact, Vec<Diagnostic>> {
762    let Some(machine) = Machine::for_target(target) else {
763        return Err(vec![unsupported(&format!(
764            "there is no back end for {} in this compiler yet, so there is nothing to generate",
765            target.tuple
766        ))]);
767    };
768    // Refused rather than dropped. A command line that asks for a stack protector on a target
769    // that has nowhere to keep the word one is compared against would otherwise get code with no
770    // protection in it and no indication that the flag did nothing, which is the one outcome worse
771    // than the error. Windows is the case: it has a protector and it is a different mechanism.
772    if opts.protector != Protector::None && machine.conv.guard.is_none() {
773        return Err(vec![unsupported(&format!(
774            "{} is not supported for {} yet, because the stack protector on that target is not \
775             the one this compiler writes",
776            opts.protector, target.tuple
777        ))]);
778    }
779    // The same answer for the same reason. What says a file was built to have its control flow
780    // checked is a note, the note is an ELF one, and a target whose objects are not ELF has nowhere
781    // to put it: the landing pads would go in and nothing would ever turn the check on. Windows has
782    // the same hardware and asks for it a different way, which is a bit in the image the linker is
783    // told to set rather than anything a compiler writes into an object.
784    if opts.control.any() && target.tuple.os().object_format() != Some(ObjectFormat::Elf) {
785        return Err(vec![unsupported(&format!(
786            "-fcf-protection={} is not supported for {} yet, because what says a file was built \
787             for it there is not the note this compiler writes",
788            opts.control, target.tuple
789        ))]);
790    }
791    // And once more. A profiled build is one whose functions call a routine the runtime provides,
792    // and a target whose runtime provides no such routine would get a call to a name nothing
793    // defines, which is a link error a long way from the flag that caused it. Windows profiles a
794    // build by calling something else, asked for a different way and taking its argument in a
795    // register, so it is not this hook spelled differently.
796    let profile = match machine.conv.trace {
797        Some(trace) => opts.profile.then(|| opts.hook.early(trace.fentry)),
798        None if opts.profile => {
799            return Err(vec![unsupported(&format!(
800                "-pg is not supported for {} yet, because the profiler's hook on that target is \
801                 not the one this compiler calls",
802                target.tuple
803            ))]);
804        }
805        None => None,
806    };
807    // And once more. The room a patcher was promised is only half the feature: the other half is a
808    // section listing where every function's room is, and both the section's shape and the way it
809    // points at the text it belongs to are ELF's. A format that has no such section would take the
810    // nops and quietly lose the list, which is a build that looks patchable and is not.
811    if opts.patchable.any() && target.tuple.os().object_format() != Some(ObjectFormat::Elf) {
812        return Err(vec![unsupported(&format!(
813            "-fpatchable-function-entry= is not supported for {} yet, because what records where \
814             the room is there is not the section this compiler writes",
815            target.tuple
816        ))]);
817    }
818    let flags = pipeline::Flags {
819        frame_pointer: opts.frame_pointer,
820        red_zone: opts.red_zone,
821        stack_clash: opts.stack_clash,
822        landing: opts.control.branch(),
823        profile: match profile {
824            None => pipeline::Profile::No,
825            Some(true) => pipeline::Profile::Early,
826            Some(false) => pipeline::Profile::Late,
827        },
828        patch: pipeline::Room { after: opts.patchable.after(), before: opts.patchable.before },
829        // On at every level above `-O0`, which is where gcc turns `-freorder-blocks` on
830        // (`gcc/opts.cc:604`) and what `spec/optimizer/38-scheduling-and-layout.md` section 38.3
831        // reads off that: it is one of the earliest optimizations there is, it is nearly free,
832        // and it helps every target. `-O0` keeps the order the shape of the graph gives, so that
833        // the blocks come out in the order they were written and a person stepping through the
834        // code walks down the screen.
835        reorder: opts.reorder_blocks.unwrap_or_else(|| opts.opt_level.runs_optimizer()),
836        // On at every level above `-O0`, for the reason the line above is off at it. Sharing one
837        // run of bytes between two locals is a smaller frame and a worse debugger: a variable that
838        // is out of scope reads as whatever took its place, which is what `-O0` exists not to do.
839        // Above it the frame is the win, and `-fstack-reuse=` says either answer at any level.
840        reuse: opts.stack_reuse.unwrap_or_else(|| opts.opt_level.runs_optimizer()),
841        // On from `-O2`, which is where gcc turns `-fschedule-insns2` on and what
842        // `spec/optimizer/38-scheduling-and-layout.md` section 38.6 asks for. Not at `-O1`,
843        // because a schedule is a whole dependence graph per block and `-O1` is the level whose
844        // budget is roughly `-O0`'s. Not at `-O0` for the reason nothing else is.
845        schedule: opts.schedule_insns.unwrap_or_else(|| opts.opt_level.schedules()),
846        // Whatever the command line said, and the model's own answer when it said nothing.
847        accurate: opts.cycle_accurate_model,
848        // The same flag that turns the IR verifier on in a release build, since what it says is
849        // that this run should check itself and the back end has checks of its own.
850        verify: opts.verify_each,
851        // What the level asked for. The back end had no way to know until now, which is
852        // tamnd/rucc#741: `-Os` picked a shorter list of middle end passes and then compiled the
853        // result exactly as `-O2` would have. The level is asked whether it optimizes for size
854        // rather than matched against, so a level added later answers this without editing it.
855        goal: Goal::for_size(opts.opt_level.is_size()),
856    };
857
858    // The checks become calls here rather than beside the insertion, because the id each one
859    // carries is an index into a table and a row for a check the optimizer deleted is a row nothing
860    // will ever name. Section 6.3.1 of `spec/safe-memory/06-instrumentation.md` is what this
861    // eventually becomes and `rucc_safety::lower` says why it is not that yet.
862    //
863    // It is inside the back end rather than beside the optimizer so that `--emit=ir` still shows
864    // the checks. The IR a person reads should say what the compiler decided, not how it spelled it
865    // for the machine.
866    if opts.safety.instruments() {
867        // Which calls hand back storage, which the lowering needs and `-O0` has not worked out.
868        // `rucc_opt::pipeline` runs this only when some pass in the run reads the summaries, since a
869        // flag nothing reads is noise in a dump, and at `-O0` nothing did. Something does now: the
870        // capability for a pointer an allocator just returned is the one capability that is exact
871        // and costs a load, and `rucc_safety::slot` finds those sites by the flag. The safety suite
872        // runs at `-O0`, so without this the cheap case would be the one case that never happens.
873        //
874        // Safe to run twice and safe to run late, because it only ever sets the flag and never
875        // clears one, so a build that had it already gets the same module back.
876        rucc_opt::heap::annotate(module, names);
877        // Which calls hand their capabilities to the callee and which say there are none. Here and
878        // not beside the insertion, because the rule is what each function still has left to check
879        // and the optimizer is what makes that small: running before it would give every callee a
880        // frame for checks that are about to be discharged. `rucc_safety::handover` is the rule and
881        // the pass both, and the census in `--emit=safety-summary` reads the same rule, so the
882        // buckets it prints describe the code that was actually built.
883        rucc_safety::handover::arrange(module);
884        rucc_safety::lower(module, names);
885        if let Err(errors) = rucc_ir::verify(module, names) {
886            return Err(errors
887                .iter()
888                .map(|e| internal(&format!("invalid IR after check lowering, {e}")))
889                .collect());
890        }
891    }
892
893    // Worked out before the loop and not inside it, because it reads the whole module and the loop
894    // is holding one function of it. It has to be after the check lowering above, since that adds
895    // calls to the runtime and so can add a name this file does not define.
896    //
897    // The link that reads the object decides half of what is in it, and the command line is where
898    // that is said, which is why the flag reaches this far down. See #756. The format decides the
899    // other half, since a table only exists on a format that has one to reach through.
900    //
901    let elsewhere = Elsewhere::of(module, replaceable(target, opts), target.object_format);
902
903    let mut funcs = Vec::new();
904    let mut complaints = Vec::new();
905    for id in module.funcs() {
906        if module[id].is_declaration() {
907            continue;
908        }
909        match pipeline::compile_recording(
910            &mut module[id],
911            names,
912            &machine,
913            &elsewhere,
914            flags,
915            recording,
916        ) {
917            Ok(func) => funcs.push(func),
918            Err(why) => {
919                let name = names.resolve(module[id].name).to_owned();
920                // The function knows where the instruction came from, so the message lands on
921                // the line somebody wrote rather than on the file as a whole.
922                let span = why.inst().map_or(Span::DUMMY, |inst| module[id].span(inst));
923                let said = format!("cannot generate code for '{name}': {why}");
924                complaints.push(unsupported_at(&said, span));
925            }
926        }
927    }
928    if !complaints.is_empty() {
929        return Err(complaints);
930    }
931    // The variables the file defines, which go through the back end the way the functions did not:
932    // there is nothing in a variable to select instructions for, so the module is what says what
933    // one is right up to the point where it is written down.
934    // The second names go the same way and for the same reason, and they are neither a function
935    // nor a variable: an alias is an entry in the symbol table and no bytes of anything.
936    let (globals, aliases) = match opts.emit {
937        EmitKind::Asm | EmitKind::Object | EmitKind::Archive | EmitKind::Executable => (
938            rucc_asm::globals(module, names, target.object_format).map_err(refused)?,
939            rucc_asm::aliases(module, names).map_err(refused)?,
940        ),
941        _ => (rucc_asm::Globals::default(), Vec::new()),
942    };
943    // A failure in either of the last two is a bug here rather than a program this compiler is
944    // behind on, because every instruction in a function that got this far came out of the same
945    // description both of them read and every register in it has been allocated.
946    let unwind = opts.unwinds();
947    match opts.emit {
948        EmitKind::Asm => {
949            rucc_asm::print(&funcs, &globals, &aliases, names, target, unwind, output(opts, target))
950                .map(Artifact::Text)
951                .map_err(refused)
952        }
953        // An executable is an object as far as this gets: one is what each file of a link
954        // contributes, and the linker is what turns them into the other. An archive is the same
955        // again, with the archive writer in place of the linker.
956        EmitKind::Object | EmitKind::Archive | EmitKind::Executable => {
957            if opts.save_temps.wanted() {
958                let listing = rucc_asm::print(
959                    &funcs,
960                    &globals,
961                    &aliases,
962                    names,
963                    target,
964                    unwind,
965                    output(opts, target),
966                );
967                *assembly = Some(listing.map_err(refused)?);
968            }
969            // A template kept as text has no bytes until an assembler reads it, and it may jump to
970            // a label another statement's text defines or switch section halfway through. So a
971            // unit with one in it is assembled the way gcc assembles every unit: written out as a
972            // listing and read back. The listing carries no line table yet, so a build that asked
973            // for one is refused rather than handed an object without it.
974            if rucc_asm::kept(&funcs, names) {
975                if opts.debug_info {
976                    return Err(vec![unsupported(
977                        "debug information for a unit with an `asm` template kept as text",
978                    )]);
979                }
980                let listing = rucc_asm::print(
981                    &funcs,
982                    &globals,
983                    &aliases,
984                    names,
985                    target,
986                    unwind,
987                    output(opts, target),
988                )
989                .map_err(refused)?;
990                let read = rucc_asm::read(&listing).map_err(|trouble| {
991                    vec![unsupported(&format!(
992                        "an `asm` template kept as text, whose listing the assembler stopped at on \
993                         line {}: {}",
994                        trouble.line, trouble.why
995                    ))]
996                })?;
997                let defines = rucc_object::assembled_defines(&read);
998                let bytes =
999                    rucc_object::assembled(&read, &TargetInfo::new(opts.target)).map_err(wrote)?;
1000                return Ok(Artifact::Object { bytes, defines });
1001            }
1002            let assembled = rucc_asm::assemble(&funcs, names, target, unwind, opts.debug_info)
1003                .map_err(refused)?;
1004            let data = globals.image();
1005            // The line table, from the spans the assembler kept beside the bytes. Empty when the
1006            // build asked for no debug information, which is the case the rows above are not even
1007            // recorded in.
1008            let info = if opts.debug_info {
1009                describe(&assembled, &data, &funcs, origin, opts, target)
1010                    .map_err(|why| vec![internal(&why)])?
1011            } else {
1012                rucc_object::Info::default()
1013            };
1014            let text = assembled.text;
1015            // A format with no writer is a target this compiler is behind on and anything else
1016            // the writer refused is a bug here, and the two are not the same news to get.
1017            let bytes =
1018                rucc_object::write(&text, &data, &aliases, target, output(opts, target), &info)
1019                    .map_err(wrote)?;
1020            // Asked of the writer rather than worked out from the same three values here, so that
1021            // what the archive's index says and what is in the member cannot come apart. It is
1022            // wanted only by `--emit=archive` and is cheap enough that the other two kinds are not
1023            // worth a second path.
1024            let defines = rucc_object::defines(&text, &data, &aliases, target).map_err(wrote)?;
1025            Ok(Artifact::Object { bytes, defines })
1026        }
1027        _ => Ok(Artifact::Text(rucc_mir::print(&funcs, names, target.regs))),
1028    }
1029}
1030
1031/// The debug sections for what was just assembled, as bytes and relocations.
1032///
1033/// This is where a span becomes a file and a line, and it is here rather than anywhere further down
1034/// because the source map is the driver's and because the paths in it are still paths at this point.
1035/// [`rucc_session::PrefixMap::apply`] is run over every one of them, which is the whole of what
1036/// `-fdebug-prefix-map=` and `-ffile-prefix-map=` asked for: a build is only reproducible if all of
1037/// the paths in it are rewritten rather than most, so the file names, the name of the unit and the
1038/// directory it was compiled in all go through it.
1039///
1040/// A row whose span is [`Span::DUMMY`] is dropped rather than written at line zero. Those are the
1041/// instructions a pass invented, a prologue and a spill among them, and a debugger asking what a
1042/// program counter is in the middle of is better told the line before than told a line that is not
1043/// in the file. The row that follows covers those bytes, which is the same answer gcc gives.
1044///
1045/// # Errors
1046///
1047/// Whatever the DWARF writer refused, which is a bug here rather than a program this compiler is
1048/// behind on.
1049fn describe(
1050    assembled: &rucc_asm::Assembled,
1051    data: &rucc_object::Data,
1052    machine: &[rucc_mir::Func],
1053    origin: Origin<'_>,
1054    opts: &Options,
1055    target: &TargetInfo,
1056) -> Result<rucc_object::Info, String> {
1057    let rucc_asm::Assembled { text, lines, frames } = assembled;
1058    let rewrite = |path: &str| opts.prefix_map.debug.apply(path).into_owned();
1059    // The file table, built as the rows are walked rather than up front, because what belongs in it
1060    // is the files the code came from and not the files the preprocessor opened. A header that
1061    // contributed nothing but declarations is not one of them, and one that holds a definition is
1062    // in it twice over: once for the rows and once for the line the definition is declared on.
1063    let mut files: Vec<String> = Vec::new();
1064    let mut funcs = Vec::with_capacity(text.funcs.len());
1065    for ((extent, rows), built) in text.funcs.iter().zip(lines).zip(machine) {
1066        let mut out: Vec<rucc_debug::Row> = Vec::with_capacity(rows.len());
1067        for row in rows {
1068            if row.span.is_dummy() {
1069                continue;
1070            }
1071            let Some(at) = origin.map.presumed(row.span.lo) else {
1072                continue;
1073            };
1074            let which = interned(&mut files, rewrite(at.name));
1075            let place = rucc_debug::Row {
1076                at: row.at as u64,
1077                file: which,
1078                line: at.line,
1079                column: at.column,
1080            };
1081            // Two rows at one address is one row, and the first of the two wins. The only place it
1082            // happens is the front of a function, where the row the assembler writes for the
1083            // declaration and the row for the first instruction land on the same byte, which is
1084            // what a function this compiler built no prologue for looks like: two instructions
1085            // cannot start at one address, so nowhere else has the question. The declaration is the
1086            // better answer there because it is the answer gcc gives, which it gives because gcc
1087            // always builds a frame at -O0 and so always has a byte of prologue for the brace to be
1088            // about. A breakpoint on a function wants the line of the function rather than the line
1089            // of whatever its first statement happened to be.
1090            match out.last() {
1091                Some(last) if last.at == place.at => {}
1092                _ => out.push(place),
1093            }
1094        }
1095        // And the front of the function, for a function whose declaration had no span to give. The
1096        // assembler writes a row there from `Func::declared` and that is the usual way this is
1097        // covered, but a function that came from something other than a C source has no such span,
1098        // and the front of one is the one part of it no row would otherwise cover. A program
1099        // counter in there would get no answer at all rather than a slightly early one, and no
1100        // answer is the worse of the two for anybody reading a backtrace.
1101        if let Some(first) = out.first_mut() {
1102            first.at = 0;
1103        }
1104        // And what the function is, for the one this unit holds a definition of. A function the
1105        // walk above found and this did not is one whose name in the object is not the name the
1106        // declaration had, which `__asm__` on a declaration is the way to arrange, and one whose
1107        // signature could not be described. Both get rows and no entry, which leaves a debugger
1108        // where it is for every function today rather than anywhere worse.
1109        let known = origin.meaning.funcs.get(&extent.name);
1110        let decl = known.map(|known| rucc_debug::Place {
1111            file: interned(&mut files, rewrite(&known.file)),
1112            line: known.line,
1113        });
1114        // And where each of its locals is, for the ones the frame gave a slot. The back end hands
1115        // back the declaration each of them is and how far below the frame base it ended up, and
1116        // this is where a number turns back into a name, a type and a line, because this is the
1117        // last place the checker's declarations are still in hand.
1118        //
1119        // A parameter goes on the entry the signature already wrote for it rather than getting one
1120        // of its own, which is what the parameter numbers on the function are for. Two entries of
1121        // one name in one scope is a debugger's problem rather than a reader's.
1122        let mut sig = known.and_then(|known| known.sig.clone());
1123        let mut placed: Vec<(u32, i32)> = built.locals.clone();
1124        let mut spots = stretches(extent, rows, built, target);
1125        // And a local in the frame that shares its bytes and has no stretch at all, which still
1126        // gets its entry so that a debugger says it is not available rather than that there is no
1127        // such name. That is a function whose instructions were scheduled, where no stretch can be
1128        // given, and the whole of it is then somewhere the local may not be.
1129        for &decl in &built.sharing {
1130            if !spots.iter().any(|(at, _)| *at == decl) {
1131                spots.push((decl, Vec::new()));
1132            }
1133        }
1134        if let (Some(sig), Some(known)) = (sig.as_mut(), known) {
1135            for (param, decl) in sig.params.iter_mut().zip(&known.params) {
1136                let Some(decl) = *decl else { continue };
1137                if let Some(which) = placed.iter().position(|&(at, _)| at == decl) {
1138                    let at = rucc_debug::Held::Frame(i64::from(placed.remove(which).1));
1139                    param.spot = Some(rucc_debug::Spot::Always(at));
1140                    continue;
1141                }
1142                // Or the stretches, for a parameter the front end kept in a value rather than in
1143                // the frame, which is what a scalar parameter whose address is never taken is at
1144                // every optimization level including this one.
1145                let Some(which) = spots.iter().position(|(at, _)| *at == decl) else { continue };
1146                param.spot = Some(rucc_debug::Spot::Over(spots.remove(which).1));
1147            }
1148        }
1149        // Whatever is left, which is the locals that are not parameters, in the order the slots
1150        // were asked for. A number with nothing to look up is one whose declaration had no name,
1151        // which is a compound literal rather than anything the program can ask the value of.
1152        let mut locals = Vec::with_capacity(placed.len() + spots.len());
1153        // And which scope each of them was declared in, kept beside the list rather than on it,
1154        // because what goes on the entry is a place in this function's own table of scopes and that
1155        // table is not known until every local has been looked up.
1156        let mut wants: Vec<Option<usize>> = Vec::with_capacity(locals.capacity());
1157        for (decl, at) in placed {
1158            let Some(named) = origin.meaning.locals.get(&decl) else { continue };
1159            wants.push(named.scope);
1160            locals.push(rucc_debug::Local {
1161                name: named.name.clone(),
1162                ty: named.ty,
1163                decl: Some(rucc_debug::Place {
1164                    file: interned(&mut files, rewrite(&named.file)),
1165                    line: named.line,
1166                }),
1167                spot: rucc_debug::Spot::Always(rucc_debug::Held::Frame(i64::from(at))),
1168                scope: None,
1169            });
1170        }
1171        // And the ones with no slot at all, which are the locals the front end kept in a value.
1172        // Sorted by declaration, which is the order the program declared them in, so that what
1173        // comes out does not depend on the order the back end happened to hand registers out in.
1174        spots.sort_by_key(|(decl, _)| *decl);
1175        for (decl, spans) in spots {
1176            let Some(named) = origin.meaning.locals.get(&decl) else { continue };
1177            wants.push(named.scope);
1178            locals.push(rucc_debug::Local {
1179                name: named.name.clone(),
1180                ty: named.ty,
1181                decl: Some(rucc_debug::Place {
1182                    file: interned(&mut files, rewrite(&named.file)),
1183                    line: named.line,
1184                }),
1185                spot: rucc_debug::Spot::Over(spans),
1186                scope: None,
1187            });
1188        }
1189        // And the scopes the locals were declared in, which is where a name declared in an inner
1190        // block stops being one of the function's own. The numbers the walk over the tree handed out
1191        // are over the whole unit, and what goes on an entry is a place in this function's table, so
1192        // the two are joined here.
1193        let (scopes, at) = nests(&wants, &origin.meaning.scopes, extent, rows);
1194        for (local, want) in locals.iter_mut().zip(&wants) {
1195            local.scope = want.and_then(|want| at.get(&want).copied());
1196        }
1197        funcs.push(rucc_debug::Function {
1198            name: extent.name.clone(),
1199            len: extent.len as u64,
1200            rows: out,
1201            decl,
1202            sig,
1203            external: known.is_some_and(|known| known.external),
1204            locals,
1205            scopes,
1206        });
1207    }
1208    // And the file-scope variables, from the objects the back end laid out rather than from the
1209    // declarations, so that a name with an entry here is a name with a symbol to relocate against.
1210    // One the walk found and this did not is a `static` nothing read, and one this found and the
1211    // walk did not is a name the compiler made up rather than one the program wrote, a string
1212    // literal and a compound literal being the two: both are in the file and neither is a variable
1213    // anybody can ask the value of by name.
1214    let mut globals = Vec::new();
1215    for object in &data.objects {
1216        let Some(held) = origin.meaning.objects.get(&object.name) else { continue };
1217        globals.push(rucc_debug::Global {
1218            name: object.name.clone(),
1219            ty: held.ty,
1220            decl: Some(rucc_debug::Place {
1221                file: interned(&mut files, rewrite(&held.file)),
1222                line: held.line,
1223            }),
1224            external: held.external,
1225        });
1226    }
1227    let unit = rucc_debug::Unit {
1228        name: rewrite(origin.name),
1229        // A single dot when the process could not say where it was, which is a directory name every
1230        // debugger understands and which leaves a relative file name meaning what it already meant.
1231        dir: rewrite(opts.working_dir.as_deref().unwrap_or(".")),
1232        producer: format!("rucc {}", crate::VERSION),
1233        files,
1234        types: origin.meaning.types.clone(),
1235        funcs,
1236        globals,
1237        pointer: u8::try_from(target.pointer_width / 8).unwrap_or(8),
1238        // Whether a function can say where its frame base is, which it can when the build writes a
1239        // table that answers the question: the unwind table, or `.debug_frame` in its place. Read
1240        // off what was written rather than asked again, so the two cannot disagree about whether
1241        // the table a frame base is read through is there.
1242        frames: opts.unwinds() || frames.is_some(),
1243    };
1244    let mut info = rucc_debug::write(&unit).map_err(|why| why.to_string())?;
1245    info.chunks.extend(frames.clone());
1246    Ok(info)
1247}
1248
1249/// Where each local the back end kept in a register is, as stretches of the function's addresses.
1250///
1251/// The back end names a stretch by the instruction at either end of it, because a machine
1252/// instruction has no length until something encodes it. This is where it gets one: the assembler
1253/// writes a row per instruction for the line table and the row says how far into the function the
1254/// instruction begins, so the row after it is where it ends. The last instruction of a function
1255/// ends where the function does.
1256///
1257/// Grouped by declaration on the way out, since one local is in one place over one stretch and
1258/// somewhere else over the next, and that is the shape the debugging information wants.
1259fn stretches(
1260    extent: &rucc_object::Extent,
1261    rows: &[rucc_asm::Row],
1262    built: &rucc_mir::Func,
1263    target: &TargetInfo,
1264) -> Vec<(u32, Vec<rucc_debug::Span>)> {
1265    // A target nobody has written a calling convention down for has no DWARF numbering either, so
1266    // there is no way to name the register a local is in and nothing to say.
1267    let (false, Some(regs)) = (built.kept.is_empty(), target.call_regs) else {
1268        return Vec::new();
1269    };
1270    let ends = ends(extent, rows);
1271    let mut bounds = vec![None; built.inst_count()];
1272    for (which, row) in rows.iter().enumerate() {
1273        let Some(inst) = row.inst else { continue };
1274        bounds[inst.index()] = Some((row.at as u64, ends[which]));
1275    }
1276    let mut spots: Vec<(u32, Vec<rucc_debug::Span>)> = Vec::new();
1277    for kept in &built.kept {
1278        let (Some((from, _)), Some((_, to))) = (bounds[kept.from.index()], bounds[kept.to.index()])
1279        else {
1280            continue;
1281        };
1282        if to <= from {
1283            continue;
1284        }
1285        let held = match kept.at {
1286            // A register is named by the number this target's DWARF numbering gives it, which is a
1287            // fact about the class and the register together rather than about either alone.
1288            rucc_mir::Where::Reg { reg, class } => match regs.dwarf(class, reg) {
1289                Some(number) => rucc_debug::Held::Reg(number),
1290                None => continue,
1291            },
1292            rucc_mir::Where::Frame(at) => rucc_debug::Held::Frame(i64::from(at)),
1293        };
1294        let span = rucc_debug::Span { from, len: to - from, held };
1295        match spots.iter_mut().find(|(decl, _)| *decl == kept.decl) {
1296            Some((_, spans)) => spans.push(span),
1297            None => spots.push((kept.decl, vec![span])),
1298        }
1299    }
1300    for (_, spans) in &mut spots {
1301        *spans = settle(std::mem::take(spans));
1302    }
1303    spots.retain(|(_, spans)| !spans.is_empty());
1304    spots
1305}
1306
1307/// Where the instruction each of a function's line table rows was written for ends.
1308///
1309/// The row after it, which is where the next instruction begins, and the end of the function for the
1310/// last one. The row after it at a different address rather than simply the row after it, because an
1311/// instruction that encodes to nothing leaves two rows on one byte and the one in front of it is not
1312/// where anything ends.
1313///
1314/// Backwards, because that is one pass rather than a search from each row for the next address that
1315/// differs, and a function the size of `sqlite3VdbeExec` has tens of thousands of rows.
1316fn ends(extent: &rucc_object::Extent, rows: &[rucc_asm::Row]) -> Vec<u64> {
1317    let mut out = vec![extent.len as u64; rows.len()];
1318    let mut next = extent.len as u64;
1319    for which in (0..rows.len()).rev() {
1320        let at = rows[which].at as u64;
1321        // The answer the row behind got, for a row sharing an address with the one in front of it,
1322        // since the two end in the same place and the one in front has already been asked.
1323        out[which] = match next > at {
1324            true => next,
1325            false => out.get(which + 1).copied().unwrap_or(extent.len as u64),
1326        };
1327        next = next.min(at);
1328    }
1329    out
1330}
1331
1332/// The scopes one function's locals were declared in, as the debug writer wants them, and which of
1333/// its entries each of the unit's scopes became.
1334///
1335/// Only the ones a local of this function is in, and their ancestors. The unit's table holds every
1336/// scope in the translation unit, and a function reaches its own by walking up from the locals the
1337/// back end handed over, which is both the filter and the answer to which function a scope belongs
1338/// to. A scope no local of this function is in is not this function's business even if the numbers
1339/// happen to sit next to each other.
1340///
1341/// The addresses come from the source. A scope is a run of source bytes, every row of the line table
1342/// says which source bytes its instruction was built for, and the rows already say where each
1343/// instruction is, so the addresses of a scope are the addresses of the instructions whose bytes are
1344/// inside it. Nothing had to be carried down the compiler for this, and the nesting comes out right
1345/// on its own: a scope's bytes hold the bytes of every scope inside it, so its addresses hold
1346/// theirs.
1347fn nests(
1348    wants: &[Option<usize>],
1349    scopes: &[crate::shapes::Scope],
1350    extent: &rucc_object::Extent,
1351    rows: &[rucc_asm::Row],
1352) -> (Vec<rucc_debug::Scope>, HashMap<usize, usize>) {
1353    let mut needed: Vec<usize> = Vec::new();
1354    for &want in wants {
1355        let mut up = want;
1356        while let Some(which) = up {
1357            if needed.contains(&which) {
1358                break;
1359            }
1360            needed.push(which);
1361            up = scopes.get(which).and_then(|scope| scope.parent);
1362        }
1363    }
1364    // In the order the unit wrote them, which puts a scope after the one it is inside, because that
1365    // is the order the writer wants and is what lets a parent be named by an entry already made.
1366    needed.sort_unstable();
1367    let at: HashMap<usize, usize> =
1368        needed.iter().enumerate().map(|(which, &scope)| (scope, which)).collect();
1369    let ends = ends(extent, rows);
1370    let out = needed
1371        .iter()
1372        .map(|&which| {
1373            let scope = &scopes[which];
1374            rucc_debug::Scope {
1375                parent: scope.parent.and_then(|parent| at.get(&parent).copied()),
1376                over: spread(scope.span, &ends, rows),
1377            }
1378        })
1379        .collect();
1380    (out, at)
1381}
1382
1383/// Which of a function's addresses were built for a run of its source bytes.
1384///
1385/// A row whose own bytes are inside the run is code the run asked for, and the addresses of a scope
1386/// are the addresses of every such row joined up. Two rows that meet or overlap are one stretch,
1387/// which is what almost all of a scope is: the rows of a block are next to each other unless
1388/// something moved them, and a block the back end split into pieces is exactly the case a list is
1389/// for.
1390fn spread(span: Span, ends: &[u64], rows: &[rucc_asm::Row]) -> Vec<rucc_debug::Reach> {
1391    let mut out: Vec<rucc_debug::Reach> = Vec::new();
1392    for (which, row) in rows.iter().enumerate() {
1393        if row.span.is_dummy() || row.span.lo < span.lo || row.span.hi > span.hi {
1394            continue;
1395        }
1396        let (from, to) = (row.at as u64, ends[which]);
1397        if to <= from {
1398            continue;
1399        }
1400        match out.last_mut() {
1401            Some(last) if last.from + last.len >= from => {
1402                last.len = to.saturating_sub(last.from).max(last.len);
1403            }
1404            _ => out.push(rucc_debug::Reach { from, len: to - from }),
1405        }
1406    }
1407    out
1408}
1409
1410/// One declaration's stretches with the disagreements taken out and the neighbours joined up.
1411///
1412/// Two stretches of one declaration can cover the same address. That is what a program that assigns
1413/// to a local from something already live looks like: both values are live across the assignment
1414/// and nothing this far down knows which side of it an address is on, because what the back end was
1415/// handed is which values a declaration is behind and not where it started being behind each of
1416/// them. Where the two agree the answer is the same either way and they become one stretch, and
1417/// where they disagree the address is left out, so a debugger says the variable is unavailable
1418/// there rather than printing whichever register this walk reached first. A wrong answer is worse
1419/// than none.
1420fn settle(mut spans: Vec<rucc_debug::Span>) -> Vec<rucc_debug::Span> {
1421    spans.sort_by_key(|span| (span.from, span.len));
1422    // Every address a stretch begins or ends at, which cuts the function into pieces no stretch is
1423    // partly over: a piece is inside a stretch or outside it and never half of each.
1424    let mut edges: Vec<u64> =
1425        spans.iter().flat_map(|span| [span.from, span.from + span.len]).collect();
1426    edges.sort_unstable();
1427    edges.dedup();
1428    let mut out: Vec<rucc_debug::Span> = Vec::new();
1429    let mut first = 0;
1430    for pair in edges.windows(2) {
1431        let (from, to) = (pair[0], pair[1]);
1432        // Nothing before this can cover this piece or any piece after it, since the pieces only
1433        // ever move forward. The list is in the order the stretches start in, so the walk below
1434        // stops at the first one that starts too late as well.
1435        while spans.get(first).is_some_and(|span| span.from + span.len <= from) {
1436            first += 1;
1437        }
1438        let mut held = None;
1439        let mut agreed = true;
1440        for span in &spans[first..] {
1441            if span.from >= to {
1442                break;
1443            }
1444            if span.from > from || span.from + span.len < to {
1445                continue;
1446            }
1447            match held {
1448                None => held = Some(span.held),
1449                Some(seen) => agreed &= seen == span.held,
1450            }
1451        }
1452        let (Some(held), true) = (held, agreed) else { continue };
1453        match out.last_mut() {
1454            Some(last) if last.from + last.len == from && last.held == held => {
1455                last.len += to - from
1456            }
1457            _ => out.push(rucc_debug::Span { from, len: to - from, held }),
1458        }
1459    }
1460    out
1461}
1462
1463/// Where a file name is in the table, putting it there if it is not there yet.
1464///
1465/// A walk rather than a map because the table holds the files one object's code came from, which is
1466/// a handful even for an amalgamation: everything the preprocessor opened and nothing was generated
1467/// out of stays out of it.
1468fn interned(files: &mut Vec<String>, name: String) -> usize {
1469    match files.iter().position(|have| *have == name) {
1470        Some(which) => which,
1471        None => {
1472            files.push(name);
1473            files.len() - 1
1474        }
1475    }
1476}
1477
1478/// What the command line decided about the file being written, in the words the assembler and the
1479/// object writer use.
1480///
1481/// Two spellings of the same facts, because the flags are the command line's and the answer the two
1482/// writers want is the object format's. The conversion is here rather than in either of them so
1483/// that the two output paths are handed the same thing and cannot come to disagree about what is
1484/// in a file.
1485///
1486/// The feature word is empty on a machine whose bits these are not. It is the x86 one, and a target
1487/// that wanted its control flow checked would want a property of its own with a key of its own, so
1488/// writing this one there would be recording something untrue rather than recording nothing.
1489fn output(opts: &Options, target: &TargetInfo) -> rucc_object::Output {
1490    let mut features = 0;
1491    if target.tuple.arch() == Arch::X86_64 {
1492        if opts.control.branch() {
1493            features |= rucc_object::Property::IBT;
1494        }
1495        if opts.control.ret() {
1496            features |= rucc_object::Property::SHSTK;
1497        }
1498    }
1499    rucc_object::Output {
1500        sections: rucc_object::Sections {
1501            functions: opts.function_sections,
1502            data: opts.data_sections,
1503        },
1504        property: rucc_object::Property { features },
1505    }
1506}
1507
1508/// What the object writer said, as the kind of news it is.
1509///
1510/// A format with no writer is a target this compiler is behind on, which is a program nobody can
1511/// compile today and not a mistake in the one being compiled. Anything else it refused is a bug
1512/// here, because every value it was handed came out of this compiler.
1513fn wrote(why: rucc_object::Error) -> Vec<Diagnostic> {
1514    match why {
1515        rucc_object::Error::Format { .. } => vec![unsupported(&why.to_string())],
1516        rucc_object::Error::Refused { .. } => vec![internal(&why.to_string())],
1517    }
1518}
1519
1520/// What the assembler said, as the kind of news it is.
1521///
1522/// Three of these are about a program and the rest are about this compiler. A thread-local
1523/// variable, an ifunc and a prologue the target's unwind table cannot describe are all valid C that
1524/// the back end does not build yet, and everything else the assembler refuses is something that
1525/// should never have reached it.
1526fn refused(why: rucc_asm::Error) -> Vec<Diagnostic> {
1527    match why {
1528        rucc_asm::Error::Thread { .. }
1529        | rucc_asm::Error::IFunc { .. }
1530        | rucc_asm::Error::Frame { .. } => {
1531            vec![unsupported(&why.to_string())]
1532        }
1533        _ => vec![internal(&why.to_string())],
1534    }
1535}
1536
1537/// A diagnostic about a program this compiler is not finished enough to compile.
1538///
1539/// Not an internal error, because nothing here is wrong: the program is valid C and the part of
1540/// the back end that would handle it has not been written. The note says so, so that a report
1541/// about one of these is filed against the milestone rather than as a miscompilation.
1542fn unsupported(message: &str) -> Diagnostic {
1543    unsupported_at(message, Span::DUMMY)
1544}
1545
1546/// The same, about somewhere in the file rather than about the file.
1547///
1548/// The note names the issue tracker rather than `spec/17-milestones.md`, which is a document
1549/// about the plan: a reader who follows it wants to know whether the construct in front of them
1550/// is already written down as work, and the milestone list does not answer that.
1551fn unsupported_at(message: &str, span: Span) -> Diagnostic {
1552    Diagnostic::error(message.to_owned(), span)
1553        .with_code("E0653")
1554        .note("this construct is not lowered yet, see https://github.com/tamnd/rucc/issues", span)
1555}
1556
1557/// A diagnostic about IR that was handed to us rather than built by us.
1558fn invalid(message: &str) -> Diagnostic {
1559    Diagnostic::error(message.to_owned(), Span::DUMMY).with_code("E0661")
1560}
1561
1562/// A diagnostic about this compiler rather than about the program it was given.
1563fn internal(message: &str) -> Diagnostic {
1564    Diagnostic::error(format!("internal error: {message}"), Span::DUMMY)
1565        .with_code("E0652")
1566        .note("this is a bug in rucc rather than in the program, please report it", Span::DUMMY)
1567}
1568
1569/// A result that is nothing but one message, for the failures that happen before there is
1570/// anything to compile.
1571fn failure(message: String) -> Compiled {
1572    Compiled {
1573        artifact: Artifact::Nothing,
1574        messages: vec![format!("rucc: error: {message}")],
1575        errors: 1,
1576        fired: Fired::new(),
1577        pressure: Pressure::new(),
1578        lowerings: Lowerings::new(),
1579        dumps: Vec::new(),
1580        remarks: String::new(),
1581        deps: Vec::new(),
1582        temps: Temps::default(),
1583    }
1584}
1585
1586#[cfg(test)]
1587mod tests {
1588    use rucc_session::{MemoryFileSystem, Std};
1589    use rucc_target::Triple;
1590
1591    use super::*;
1592
1593    fn options() -> Options {
1594        let mut opts = Options::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
1595        opts.emit = EmitKind::Tast;
1596        opts
1597    }
1598
1599    fn run(opts: &Options, source: &str) -> Compiled {
1600        let mut fs = MemoryFileSystem::new();
1601        fs.insert("/main.c", source.to_owned().into_bytes());
1602        compile(opts, "/main.c", &fs)
1603    }
1604
1605    /// Options with the compiler's own headers on the search path and nothing else, which is
1606    /// what a freestanding compilation is. There is no file system underneath these tests,
1607    /// so a header that reached for one would fail to resolve and say so.
1608    fn freestanding() -> Options {
1609        let mut opts = options();
1610        opts.hosted = false;
1611        opts.search.push_system(rucc_session::runtime::DIR);
1612        opts
1613    }
1614
1615    /// The typed tree of a freestanding `source`, insisting that it compiled cleanly.
1616    fn shipped(source: &str) -> String {
1617        let result = run(&freestanding(), source);
1618        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
1619        result.text().to_owned()
1620    }
1621
1622    /// The typed tree of `source`, insisting that it compiled cleanly.
1623    fn tast(source: &str) -> String {
1624        let result = run(&options(), source);
1625        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
1626        result.text().to_owned()
1627    }
1628
1629    #[test]
1630    fn the_shipped_stdarg_declares_a_list_and_the_four_operators() {
1631        let text = shipped(concat!(
1632            "#include <stdarg.h>\n",
1633            "int sum(int n, ...) {\n",
1634            "  va_list ap, copy;\n",
1635            "  va_start(ap, n);\n",
1636            "  va_copy(copy, ap);\n",
1637            "  int total = va_arg(ap, int) + va_arg(copy, int);\n",
1638            "  va_end(ap);\n",
1639            "  va_end(copy);\n",
1640            "  return total;\n",
1641            "}\n",
1642        ));
1643        assert!(text.contains("va-start"), "{text}");
1644        assert!(text.contains("va-copy"), "{text}");
1645        assert!(text.contains("va-arg"), "{text}");
1646        assert!(text.contains("va-end"), "{text}");
1647    }
1648
1649    /// glibc includes `<stdarg.h>` this way from every header that declares a `vprintf`, and
1650    /// what it wants is the type without the four macro names. Answering the whole header
1651    /// would put `va_start` in the way of a program that has its own.
1652    #[test]
1653    fn stdarg_hands_out_the_type_alone_when_that_is_all_that_was_asked_for() {
1654        let text = shipped(concat!(
1655            "#define __need___va_list\n",
1656            "#include <stdarg.h>\n",
1657            "int vprint(const char *f, __gnuc_va_list ap);\n",
1658            "#ifdef va_start\n",
1659            "#error va_start should not be defined\n",
1660            "#endif\n",
1661            "#ifdef _VA_LIST_DEFINED\n",
1662            "#error va_list should not have been made\n",
1663            "#endif\n",
1664        ));
1665        assert!(text.contains("vprint"), "{text}");
1666    }
1667
1668    /// The same protocol on `<stddef.h>`, which glibc uses far more heavily: `<stdio.h>` asks
1669    /// for `size_t` and `NULL` and would be wrong to receive `offsetof` as well.
1670    #[test]
1671    fn stddef_answers_one_piece_at_a_time_and_the_next_request_still_gets_through() {
1672        let text = shipped(concat!(
1673            "#define __need_size_t\n",
1674            "#include <stddef.h>\n",
1675            "#ifdef offsetof\n",
1676            "#error offsetof should not be defined yet\n",
1677            "#endif\n",
1678            "#define __need_ptrdiff_t\n",
1679            "#include <stddef.h>\n",
1680            "#include <stddef.h>\n",
1681            "size_t a;\n",
1682            "ptrdiff_t b;\n",
1683            "wchar_t c;\n",
1684            "max_align_t d;\n",
1685            "void *e = NULL;\n",
1686            "struct P { int x; long y; };\n",
1687            "size_t f = offsetof(struct P, y);\n",
1688        ));
1689        assert!(text.contains("decl #0 a : unsigned long"), "{text}");
1690        assert!(text.contains("decl #1 b : long"), "{text}");
1691    }
1692
1693    #[test]
1694    fn the_shipped_limits_and_float_are_the_targets_own_answers() {
1695        let text = shipped(concat!(
1696            "#include <limits.h>\n",
1697            "#include <float.h>\n",
1698            "int bits = CHAR_BIT;\n",
1699            "long big = LONG_MAX;\n",
1700            "int low = INT_MIN;\n",
1701            "int radix = FLT_RADIX;\n",
1702            "int digits = DBL_MANT_DIG;\n",
1703        ));
1704        assert!(text.contains("const 8 : int"), "{text}");
1705        assert!(text.contains("const 9223372036854775807 : long"), "{text}");
1706        assert!(text.contains("const 2 : int"), "{text}");
1707        assert!(text.contains("const 53 : int"), "{text}");
1708    }
1709
1710    /// Freestanding, so there is no library header to chain to and `<stdint.h>` writes the
1711    /// whole set out itself. The widths are the ones the target picked, which is the only
1712    /// reason this header is the compiler's.
1713    #[test]
1714    fn the_shipped_stdint_writes_the_whole_set_when_there_is_no_library_to_defer_to() {
1715        let text = shipped(concat!(
1716            "#include <stdint.h>\n",
1717            "int64_t a = INT64_C(1);\n",
1718            "uint_least16_t b;\n",
1719            "intptr_t c;\n",
1720            "uintmax_t d = UINTMAX_MAX;\n",
1721            "int wide = sizeof(int_fast64_t);\n",
1722        ));
1723        assert!(text.contains("decl #0 a : long"), "{text}");
1724        assert!(text.contains("decl #1 b : unsigned short"), "{text}");
1725        assert!(text.contains("decl #2 c : long"), "{text}");
1726    }
1727
1728    /// `<mmintrin.h>` is the base of the vector header chain and the first one whose contents
1729    /// are C rather than declarations, so what this checks is that the C in it compiles: a
1730    /// header that is nothing but definitions fails as a whole or not at all.
1731    ///
1732    /// What the intrinsics answer is not checked here and cannot be, because the answer is
1733    /// only interesting next to another compiler's. Every intrinsic in the header was built
1734    /// and run against GCC 16.2.0 on the same inputs, at `-O0`, `-O1`, `-O2` and `-Os`, and
1735    /// gave the same bytes in all four. Carrying that comparison rather than repeating it by
1736    /// hand needs a facet in `tamnd/rucc-corpus` that works out the expected bytes itself,
1737    /// which is a second implementation of MMX and is `tamnd/rucc#1150`.
1738    #[test]
1739    fn the_shipped_mmintrin_defines_the_mmx_type_and_the_operations_over_it() {
1740        let text = shipped(concat!(
1741            "#include <mmintrin.h>\n",
1742            "__m64 add(__m64 a, __m64 b) { return _mm_add_pi16(a, b); }\n",
1743            "__m64 pack(__m64 a, __m64 b) { return _m_packsswb(a, b); }\n",
1744            "__m64 shift(__m64 a) { return _mm_srai_pi32(a, 3); }\n",
1745            "int low(__m64 a) { return _mm_cvtsi64_si32(a); }\n",
1746            "void done(void) { _mm_empty(); }\n",
1747        ));
1748        assert!(text.contains("add"), "{text}");
1749        assert!(text.contains("pack"), "{text}");
1750        assert!(text.contains("shift"), "{text}");
1751    }
1752
1753    /// The allocator beside the vector headers, which is the one piece of the family that is
1754    /// not a vector operation. It reaches for `<stddef.h>` and for three names out of the
1755    /// library, and the point of the test is that the reach resolves with nothing on the
1756    /// search path but the compiler's own directory.
1757    #[test]
1758    fn the_shipped_mm_malloc_asks_for_aligned_memory_and_gives_it_back() {
1759        let text = shipped(concat!(
1760            "#include <mm_malloc.h>\n",
1761            "void *get(void) { return _mm_malloc(64, 16); }\n",
1762            "void put(void *p) { _mm_free(p); }\n",
1763        ));
1764        assert!(text.contains("get"), "{text}");
1765        assert!(text.contains("put"), "{text}");
1766    }
1767
1768    /// `<xmmintrin.h>` is the next rung of the chain and pulls the other two in behind it, so a
1769    /// program that includes this one alone has to get all three. What the intrinsics answer is
1770    /// checked the same way `<mmintrin.h>` next door is checked and for the same reason: a
1771    /// hundred and forty eight lines of answers over nans, infinities, both zeros and values
1772    /// that do not fit in the integer they convert to, identical to GCC 16.2.0 at `-O0`, `-O1`,
1773    /// `-O2` and `-Os`.
1774    ///
1775    /// `_mm_rcp_ps` is the one answer in that run that is not identical, and is not meant to be.
1776    /// The instruction approximates a reciprocal and this computes one exactly, so the bits
1777    /// differ while both sit inside the relative error Intel documents, which the same program
1778    /// checks directly rather than by comparing bits.
1779    #[test]
1780    fn the_shipped_xmmintrin_defines_the_sse_type_and_the_operations_over_it() {
1781        let text = shipped(concat!(
1782            "#include <xmmintrin.h>\n",
1783            "__m128 add(__m128 a, __m128 b) { return _mm_add_ps(a, b); }\n",
1784            "__m128 one(__m128 a, __m128 b) { return _mm_max_ss(a, b); }\n",
1785            "__m128 mask(__m128 a, __m128 b) { return _mm_cmpnle_ps(a, b); }\n",
1786            "__m128 pick(__m128 a, __m128 b) { return _mm_shuffle_ps(a, b, _MM_SHUFFLE(0,1,2,3)); }\n",
1787            "int bits(__m128 a) { return _mm_movemask_ps(a); }\n",
1788            "int near(__m128 a) { return _mm_cvtss_si32(a); }\n",
1789            "__m128 wide(__m64 a) { return _mm_cvtpi16_ps(a); }\n",
1790            "void *room(void) { return _mm_malloc(64, 16); }\n",
1791            "void hint(const float *p) { _mm_prefetch(p, _MM_HINT_T0); _mm_sfence(); }\n",
1792        ));
1793        assert!(text.contains("add"), "{text}");
1794        assert!(text.contains("mask"), "{text}");
1795        assert!(text.contains("pick"), "{text}");
1796        assert!(text.contains("wide"), "{text}");
1797    }
1798
1799    /// The six names of gcc's header this one leaves out, each of which is an instruction whose
1800    /// answer no plain C reproduces exactly. Leaving them out is what turns a program that wants
1801    /// one into a diagnostic naming the function it called, rather than into a wrong answer, and
1802    /// this is what notices if one is ever quietly defined to something close.
1803    ///
1804    /// `tamnd/rucc#1157` is the square root, which brings the first four back.
1805    #[test]
1806    fn the_shipped_xmmintrin_leaves_out_the_names_that_need_an_instruction() {
1807        let text = rucc_session::runtime::header("xmmintrin.h").expect("xmmintrin.h is shipped");
1808        for absent in [
1809            "_mm_sqrt_ps",
1810            "_mm_sqrt_ss",
1811            "_mm_rsqrt_ps",
1812            "_mm_rsqrt_ss",
1813            "_mm_getcsr",
1814            "_mm_setcsr",
1815        ] {
1816            let defined = text.contains(&format!("{absent}("));
1817            assert!(!defined, "{absent} is defined and the header says it is not");
1818            assert!(text.contains(absent), "{absent} is absent and unexplained");
1819        }
1820    }
1821
1822    #[test]
1823    fn the_shipped_emmintrin_defines_both_sse2_types_and_the_operations_over_them() {
1824        let text = shipped(concat!(
1825            "#include <emmintrin.h>\n",
1826            "__m128i add(__m128i a, __m128i b) { return _mm_add_epi64(a, b); }\n",
1827            "__m128i wide(__m128i a, __m128i b) { return _mm_mul_epu32(a, b); }\n",
1828            "__m128i pick(__m128i a) { return _mm_shuffle_epi32(a, _MM_SHUFFLE(0,1,2,3)); }\n",
1829            "__m128i up(__m128i a) { return _mm_slli_epi64(a, 13); }\n",
1830            "__m128i down(__m128i a) { return _mm_srli_si128(a, 3); }\n",
1831            "__m128i pack(__m128i a, __m128i b) { return _mm_packus_epi16(a, b); }\n",
1832            "int bits(__m128i a) { return _mm_movemask_epi8(a); }\n",
1833            "__m128d sum(__m128d a, __m128d b) { return _mm_add_sd(a, b); }\n",
1834            "__m128d mask(__m128d a, __m128d b) { return _mm_cmpunord_pd(a, b); }\n",
1835            "__m128i near(__m128d a) { return _mm_cvtpd_epi32(a); }\n",
1836            "__m128d over(__m128 a) { return _mm_cvtps_pd(a); }\n",
1837            "__m128i half(__m64 a) { return _mm_movpi64_epi64(a); }\n",
1838            "__m128i grab(void const *p) { return _mm_loadu_si128(p); }\n",
1839            "void wall(void) { _mm_lfence(); _mm_mfence(); }\n",
1840        ));
1841        assert!(text.contains("wide"), "{text}");
1842        assert!(text.contains("pack"), "{text}");
1843        assert!(text.contains("near"), "{text}");
1844        assert!(text.contains("half"), "{text}");
1845    }
1846
1847    /// The umbrella header reaches the three underneath it. This is brotli's use of it, from
1848    /// `c/enc/matching_tag_mask.h`, which is the whole of what `tamnd/rucc#1236` was about: four
1849    /// SSE2 names that were already shipped and no way to get at them by the name gcc uses.
1850    #[test]
1851    fn the_shipped_immintrin_reaches_the_names_the_headers_under_it_define() {
1852        let text = shipped(concat!(
1853            "#include <immintrin.h>\n",
1854            "unsigned long long matching(unsigned char tag, unsigned char const *bucket) {\n",
1855            "  __m128i const want = _mm_set1_epi8((char)tag);\n",
1856            "  __m128i const chunk = _mm_loadu_si128((__m128i const *)(void const *)bucket);\n",
1857            "  __m128i const same = _mm_cmpeq_epi8(chunk, want);\n",
1858            "  return (unsigned long long)_mm_movemask_epi8(same);\n",
1859            "}\n",
1860            "__m64 narrow(__m64 a, __m64 b) { return _mm_add_pi32(a, b); }\n",
1861            "__m128 single(__m128 a, __m128 b) { return _mm_add_ps(a, b); }\n",
1862        ));
1863        assert!(text.contains("matching"), "{text}");
1864        assert!(text.contains("narrow"), "the MMX header is not reached: {text}");
1865        assert!(text.contains("single"), "the SSE header is not reached: {text}");
1866    }
1867
1868    /// The wider umbrella reaches everything the narrower one does, and the fence family with it.
1869    /// This is what mingw-w64's `<winnt.h>` includes and what it then uses, so a Windows program
1870    /// that has never heard of an intrinsic gets here through `<windows.h>`.
1871    #[test]
1872    fn the_shipped_x86intrin_reaches_the_fences_windows_headers_ask_it_for() {
1873        let text = shipped(concat!(
1874            "#include <x86intrin.h>\n",
1875            "void barriers(void *p) {\n",
1876            "  _mm_lfence();\n",
1877            "  _mm_sfence();\n",
1878            "  _mm_mfence();\n",
1879            "  _mm_pause();\n",
1880            "  _mm_clflush(p);\n",
1881            "}\n",
1882            "__m128i wide(__m128i a, __m128i b) { return _mm_add_epi32(a, b); }\n",
1883        ));
1884        assert!(text.contains("barriers"), "{text}");
1885        assert!(text.contains("wide"), "the SSE2 header is not reached: {text}");
1886    }
1887
1888    /// Including it twice is the same as including it once, and so is including it beside the
1889    /// header it reaches. A program that includes both spellings is the usual case rather than an
1890    /// odd one, because one of its own headers includes the umbrella and another includes SSE2.
1891    #[test]
1892    fn the_umbrella_and_the_header_under_it_can_both_be_included() {
1893        let text = shipped(concat!(
1894            "#include <immintrin.h>\n",
1895            "#include <emmintrin.h>\n",
1896            "#include <immintrin.h>\n",
1897            "#include <x86intrin.h>\n",
1898            "__m128i twice(__m128i a, __m128i b) { return _mm_add_epi32(a, b); }\n",
1899        ));
1900        assert!(text.contains("twice"), "{text}");
1901    }
1902
1903    /// The float header omits four square roots and SSE2 omits the matching two, for the reason
1904    /// both headers write down. A later change that quietly defines one as an approximation
1905    /// would be a wrong answer nobody sees, so the absence is held in place here.
1906    #[test]
1907    fn the_shipped_emmintrin_leaves_out_the_two_square_roots() {
1908        let text = rucc_session::runtime::header("emmintrin.h").expect("emmintrin.h is shipped");
1909        for absent in ["_mm_sqrt_pd", "_mm_sqrt_sd"] {
1910            let defined = text.contains(&format!("{absent}("));
1911            assert!(!defined, "{absent} is defined and the header says it is not");
1912            assert!(text.contains(absent), "{absent} is absent and unexplained");
1913        }
1914    }
1915
1916    #[test]
1917    fn the_three_formality_headers_still_have_to_work() {
1918        let text = shipped(concat!(
1919            "#include <stdbool.h>\n",
1920            "#include <stdalign.h>\n",
1921            "#include <iso646.h>\n",
1922            "#include <stdnoreturn.h>\n",
1923            "int t = true and not false;\n",
1924            "_Alignas(16) char buf[16];\n",
1925            "int a = alignof(long);\n",
1926        ));
1927        assert!(text.contains("decl #0 t : int"), "{text}");
1928        assert!(text.contains("const 8 : unsigned long"), "{text}");
1929    }
1930
1931    /// Including everything twice has to change nothing, because that is what happens in any
1932    /// program large enough to matter and a guard that is wrong shows up nowhere else.
1933    ///
1934    /// Stated as the two trees being the same rather than as a fact about what is in either
1935    /// one. A header that carries definitions puts them in the tree and moves everything
1936    /// after them along, so an assertion about where the program's own declaration landed is
1937    /// an assertion about how much `<mmintrin.h>` defines, which is not what is being asked.
1938    #[test]
1939    fn every_shipped_header_can_be_included_twice() {
1940        let once: String = rucc_session::runtime::names()
1941            .iter()
1942            .map(|name| format!("#include <{name}>\n"))
1943            .collect();
1944        let twice = once.repeat(2);
1945        assert_eq!(shipped(&format!("{once}int x;\n")), shipped(&format!("{twice}int x;\n")));
1946    }
1947
1948    #[test]
1949    fn a_file_that_is_not_there_says_so_and_produces_nothing() {
1950        let fs = MemoryFileSystem::new();
1951        let result = compile(&options(), "/nope.c", &fs);
1952        assert!(result.failed());
1953        assert!(result.messages[0].contains("/nope.c"), "{:?}", result.messages);
1954        assert!(result.text().is_empty());
1955    }
1956
1957    #[test]
1958    fn an_object_comes_out_with_its_type_its_linkage_and_how_much_of_a_definition_it_is() {
1959        let text = tast("int x = 1;\n");
1960        let expected = "\
1961decl #0 x : int object external static defined
1962  init
1963    +0
1964      const 1 : int
1965";
1966        assert_eq!(text, expected);
1967    }
1968
1969    #[test]
1970    fn the_macros_are_expanded_before_anything_is_parsed() {
1971        // The whole pipeline in one line. The bound came out of a macro, so it was expanded,
1972        // converted from a preprocessing number to a constant of a type, parsed as an
1973        // expression, and folded to the number the array type carries.
1974        let text = tast("#define N 2\nint a[N];\n");
1975        assert!(text.starts_with("decl #0 a : int[2] object external static tentative"), "{text}");
1976    }
1977
1978    /// A pragma survives the preprocessor on purpose, since what one means is not its
1979    /// business, and nothing after it has a place for a `#` in the grammar. `pack` is the one
1980    /// the parser reads and every other line is walked past. Both spellings are here because
1981    /// they arrive by different routes and only one of them was ever on a line of its own in
1982    /// the source.
1983    #[test]
1984    fn a_pragma_is_not_a_declaration_and_the_parse_walks_past_the_ones_it_does_not_read() {
1985        let text = tast(concat!(
1986            "#pragma pack(4)\n",
1987            "struct s { int a; };\n",
1988            "#pragma pack()\n",
1989            "int b;\n",
1990            "_Pragma(\"GCC visibility push(default)\") int c;\n",
1991        ));
1992        assert!(text.contains("decl #0 b : int"), "{text}");
1993        assert!(text.contains("decl #1 c : int"), "{text}");
1994    }
1995
1996    /// Every number in these two tests was read off gcc 16 on x86-64 under `-std=gnu23`
1997    /// rather than reasoned about, which is why they are written as assertions the program
1998    /// makes about itself: a compilation with no messages is every one of them holding.
1999    ///
2000    /// This half is the attributes. `packed` takes the padding out, on the record or on one
2001    /// member, `aligned` raises and never lowers, and the two written together are the
2002    /// combination that packs and then aligns the whole thing.
2003    #[test]
2004    fn the_layout_attributes_move_the_members_and_the_record_the_way_gcc_lays_them_out() {
2005        tast(concat!(
2006            "struct A { char c; int i; } __attribute__((packed));\n",
2007            "_Static_assert(sizeof(struct A) == 5 && _Alignof(struct A) == 1, \"A\");\n",
2008            "_Static_assert(__builtin_offsetof(struct A, i) == 1, \"A.i\");\n",
2009            // `aligned` with nothing in the parentheses is the largest alignment the target
2010            // has, which gcc calls BIGGEST_ALIGNMENT and which is sixteen everywhere here.
2011            "struct B { char c; int i; } __attribute__((aligned));\n",
2012            "_Static_assert(sizeof(struct B) == 16 && _Alignof(struct B) == 16, \"B\");\n",
2013            "struct C { char c; int i __attribute__((packed)); };\n",
2014            "_Static_assert(sizeof(struct C) == 5 && _Alignof(struct C) == 1, \"C\");\n",
2015            "_Static_assert(__builtin_offsetof(struct C, i) == 1, \"C.i\");\n",
2016            "struct D { char c; int i; } __attribute__((packed, aligned(4)));\n",
2017            "_Static_assert(sizeof(struct D) == 8 && _Alignof(struct D) == 4, \"D\");\n",
2018            "_Static_assert(__builtin_offsetof(struct D, i) == 1, \"D.i\");\n",
2019            "struct E { char c; _Alignas(8) int i; };\n",
2020            "_Static_assert(sizeof(struct E) == 16 && _Alignof(struct E) == 8, \"E\");\n",
2021            "_Static_assert(__builtin_offsetof(struct E, i) == 8, \"E.i\");\n",
2022            "struct F { char c; int i __attribute__((aligned(8))); };\n",
2023            "_Static_assert(sizeof(struct F) == 16 && _Alignof(struct F) == 8, \"F\");\n",
2024            // Two the record already had, so the attribute asks for nothing new, and two
2025            // where four was already there, so the attribute is ignored rather than obeyed.
2026            "struct G { char c; short s; } __attribute__((aligned(2)));\n",
2027            "_Static_assert(sizeof(struct G) == 4 && _Alignof(struct G) == 2, \"G\");\n",
2028            "struct H { char c; int i; } __attribute__((aligned(2)));\n",
2029            "_Static_assert(sizeof(struct H) == 8 && _Alignof(struct H) == 4, \"H\");\n",
2030            // `packed` on a member takes the padding out in front of that member alone, so on
2031            // the first one it does nothing and on the second one it does all of it.
2032            "struct I { [[gnu::packed]] char c; int i; };\n",
2033            "_Static_assert(sizeof(struct I) == 8 && _Alignof(struct I) == 4, \"I\");\n",
2034            "struct J { char c; [[gnu::packed]] int i; };\n",
2035            "_Static_assert(sizeof(struct J) == 5 && _Alignof(struct J) == 1, \"J\");\n",
2036            "struct M { char c; int i : 5; int j : 20; } __attribute__((packed));\n",
2037            "_Static_assert(sizeof(struct M) == 5 && _Alignof(struct M) == 1, \"M\");\n",
2038            "struct N { char c; long long l; } __attribute__((aligned(32)));\n",
2039            "_Static_assert(sizeof(struct N) == 32 && _Alignof(struct N) == 32, \"N\");\n",
2040            "union L { char c; int i; } __attribute__((packed));\n",
2041            "_Static_assert(sizeof(union L) == 4 && _Alignof(union L) == 1, \"L\");\n",
2042            // The armoured spellings, which are the ones a system header writes, since a
2043            // program is entitled to a macro called `packed` and is not entitled to one called
2044            // `__packed__`. The two names are one attribute and the layout is the same one.
2045            "struct O { char c; int i; } __attribute__((__packed__));\n",
2046            "_Static_assert(sizeof(struct O) == 5 && _Alignof(struct O) == 1, \"O\");\n",
2047            "struct P { char c; int i; } __attribute__((__aligned__(8)));\n",
2048            "_Static_assert(sizeof(struct P) == 8 && _Alignof(struct P) == 8, \"P\");\n",
2049        ));
2050    }
2051
2052    /// The attribute that changes what a call means rather than what a record lays out.
2053    ///
2054    /// Both halves are here. A call hands a value to a parameter of the union type and the value
2055    /// goes into the member that takes it, which is a compound literal of the union and is the
2056    /// same object the GNU cast to a union builds. And a declaration written with a member's type
2057    /// declares the same function as one written with the union, which is what lets a pointer to
2058    /// either be assigned from the other, and is what gnulib's signature checks do.
2059    ///
2060    /// The `void *` member is last on purpose: the search takes a member whose type the value
2061    /// already has wherever it sits, and falls back to a pointer member that would take the value
2062    /// silently only when there is no such member, so `char *` reaches the catch-all past two
2063    /// members that are not it.
2064    #[test]
2065    fn a_transparent_union_takes_the_member_a_value_fits_and_is_declared_either_way() {
2066        let text = tast(concat!(
2067            "struct one { int x; };\n",
2068            "struct two { long y; };\n",
2069            "typedef union { struct one *a; struct two *b; void *any; }\n",
2070            "  __attribute__((__transparent_union__)) arg;\n",
2071            "int takes(arg v);\n",
2072            "int f(struct one *p, struct two *q, char *c) {\n",
2073            "  return takes(p) + takes(q) + takes(c) + takes(0);\n",
2074            "}\n",
2075            // The other half, which is about declarations and not about values.
2076            "int takes(struct one *p);\n",
2077            "int (*as_a_member)(struct one *) = takes;\n",
2078            "int (*as_the_union)(arg) = takes;\n",
2079        ));
2080        assert!(text.contains("compound-literal"), "{text}");
2081    }
2082
2083    /// The other place glibc writes it, which is the one that matters.
2084    ///
2085    /// `sys/socket.h` puts the attribute on the declarator of the typedef rather than after the
2086    /// closing brace, so a compiler that reads only the second position reads nothing at all of
2087    /// the eleven pointer union that `bind` and `connect` and five others take.
2088    #[test]
2089    fn the_attribute_on_the_declarator_of_a_typedef_is_the_one_glibc_writes() {
2090        let text = tast(concat!(
2091            "struct sockaddr { int family; };\n",
2092            "struct sockaddr_in { int family; int addr; };\n",
2093            "typedef union { struct sockaddr *plain; struct sockaddr_in *inet; }\n",
2094            "  addr_arg __attribute__((__transparent_union__));\n",
2095            "int bind_to(int fd, addr_arg where);\n",
2096            "int f(struct sockaddr_in *where) { return bind_to(0, where); }\n",
2097        ));
2098        assert!(text.contains("compound-literal"), "{text}");
2099    }
2100
2101    /// What the attribute promises has to be a promise this can keep, and is checked rather than
2102    /// believed.
2103    ///
2104    /// A union wider than its first member is not passed the way that member is, and a structure
2105    /// has no members that are alternatives to each other at all. gcc drops the attribute in both
2106    /// cases with a warning and compiles the program, because the type is still a perfectly good
2107    /// type and only the extra rule is gone.
2108    #[test]
2109    fn a_transparent_union_that_cannot_keep_the_promise_is_dropped_with_a_word_about_it() {
2110        let result = run(
2111            &options(),
2112            concat!(
2113                "union wider { int small; double large; } __attribute__((transparent_union));\n",
2114                "struct plain { int x; } __attribute__((transparent_union));\n",
2115            ),
2116        );
2117        assert_eq!(result.messages.len(), 2, "{:?}", result.messages);
2118        assert!(!result.failed(), "{:?}", result.messages);
2119        for message in &result.messages {
2120            assert!(message.contains("'transparent_union' attribute ignored"), "{message}");
2121        }
2122        assert!(result.messages[0].contains("first member"), "{:?}", result.messages);
2123        assert!(result.messages[1].contains("only a union"), "{:?}", result.messages);
2124    }
2125
2126    /// What an access to a packed member is allowed to assume about where it starts.
2127    ///
2128    /// C 6.2.8 gives an object of type `int` four byte alignment and `packed` takes it away: the
2129    /// member goes wherever the members in front of it ended, and an `int` one byte into a record
2130    /// is aligned to one. The number on the access has to say so, because it is what the back end
2131    /// picks instructions from and what judgement J1 of `spec/safe-memory/04-safety-model.md`
2132    /// tests at run time. Four on an address that is a multiple of one is the compiler refusing a
2133    /// program that is doing nothing wrong.
2134    #[test]
2135    fn an_access_to_a_packed_member_says_the_alignment_the_layout_left_it() {
2136        let packed = body(concat!(
2137            "struct P { char c; int v; } __attribute__((packed));\n",
2138            "int f(struct P *p) { return p->v; }\n",
2139        ));
2140        assert!(packed.contains("load.i32 %2, align 1,"), "{packed}");
2141        // The same record without the attribute, which is where the type's own answer is right.
2142        let plain = body(concat!(
2143            "struct P { char c; int v; };\n",
2144            "int f(struct P *p) { return p->v; }\n",
2145        ));
2146        assert!(plain.contains("load.i32 %2, align 4,"), "{plain}");
2147    }
2148
2149    /// The same, for the two ways of being further in than the member itself.
2150    ///
2151    /// An array member is stepped through rather than offset to, and a record member is offset to
2152    /// twice, and both have to carry the outer record's alignment with them. A step of a whole
2153    /// number of elements leaves what the element width and the address had in common, which for
2154    /// a one byte aligned base is one byte however wide the elements are.
2155    #[test]
2156    fn what_is_inside_a_packed_member_is_no_more_aligned_than_the_member_is() {
2157        let stepped = body(concat!(
2158            "struct P { char c; int v[4]; } __attribute__((packed));\n",
2159            "int f(struct P *p, int i) { return p->v[i]; }\n",
2160        ));
2161        assert!(stepped.contains(", align 1,"), "{stepped}");
2162        assert!(!stepped.contains(", align 4,"), "{stepped}");
2163        let nested = body(concat!(
2164            "struct Inner { int v; };\n",
2165            "struct P { char c; struct Inner in; } __attribute__((packed));\n",
2166            "int f(struct P *p) { return p->in.v; }\n",
2167        ));
2168        assert!(nested.contains(", align 1,"), "{nested}");
2169        assert!(!nested.contains(", align 4,"), "{nested}");
2170    }
2171
2172    /// The other way an access gets an alignment its type would not have given it, which is a
2173    /// typedef that lowered one.
2174    ///
2175    /// `aligned` raises on a declaration and replaces on a typedef, so `typedef aligned(1) U32
2176    /// unalign32` really is a four byte integer that may sit anywhere. Reading a word out of a
2177    /// buffer nothing aligned is what every compression library does and this is how they write
2178    /// it: zstd's `lib/common/mem.h` is four typedefs of exactly this shape and `MEM_read32` is
2179    /// `*(const unalign32 *)ptr`.
2180    ///
2181    /// What made this worth a test is where it went wrong. `__alignof__` was right the whole time,
2182    /// because that asks about the type and the type knew. The access was wrong, because the type
2183    /// of `*p` was worked out by resolving every typedef in `p`'s type rather than only the one on
2184    /// the pointer, so the thing being read came back as the `unsigned int` the typedef stands for
2185    /// and the alignment came off that. The number on the access is what judgement J1 tests, so
2186    /// the monitor refused fifty six of zstd's reads, all of them correct.
2187    #[test]
2188    fn an_access_through_a_typedef_that_lowered_its_alignment_says_the_one_the_typedef_asked_for() {
2189        let through = body(concat!(
2190            "typedef __attribute__((aligned(1))) unsigned int unalign32;\n",
2191            "unsigned int f(const void *p) { return *(const unalign32 *)p; }\n",
2192        ));
2193        assert!(through.contains("load.i32 %0, align 1,"), "{through}");
2194        // A subscript is `*(p + i)` and a member through an arrow is a dereference and then an
2195        // offset, so both read the pointee the same way and both have to come out the same.
2196        let stepped = body(concat!(
2197            "typedef __attribute__((aligned(1))) unsigned int unalign32;\n",
2198            "unsigned int f(unalign32 *p, int i) { return p[i]; }\n",
2199        ));
2200        assert!(stepped.contains(", align 1,"), "{stepped}");
2201        assert!(!stepped.contains(", align 4,"), "{stepped}");
2202        // And the same typedef without the attribute, which is where the type's own answer is the
2203        // right one and nothing above should have changed it.
2204        let plain = body(concat!(
2205            "typedef unsigned int word;\n",
2206            "unsigned int f(const void *p) { return *(const word *)p; }\n",
2207        ));
2208        assert!(plain.contains("load.i32 %0, align 4,"), "{plain}");
2209    }
2210
2211    /// The same thing where the object does not fit in a register, which is what `_mm_loadu_si128`
2212    /// is and is the reason the intrinsic header exists at all.
2213    ///
2214    /// `__m128i_u` is `__m128i` with `aligned(1)` on it and `_mm_loadu_si128` is one line,
2215    /// `return *(const __m128i_u *)__p;`. Two things had to be right for that to come out as the
2216    /// unaligned read it is. The dereference has to keep the typedef, which is what the test above
2217    /// covers, and then the return has to read the object as aligned as the object is rather than
2218    /// as aligned as the type it is being returned as: a vector comes back in registers on this
2219    /// ABI, so the sixteen bytes are read as two pieces of eight and the ABI's own alignment is
2220    /// what lays the two pieces out rather than what either read may claim.
2221    #[test]
2222    fn a_vector_read_through_a_typedef_that_lowered_its_alignment_comes_back_a_piece_at_a_time() {
2223        let prefix = concat!(
2224            "typedef long long v2di __attribute__((__vector_size__(16)));\n",
2225            "typedef long long v2di_u __attribute__((__vector_size__(16), __aligned__(1)));\n",
2226        );
2227        let loaded =
2228            body(&format!("{prefix}v2di f(const void *p) {{ return *(const v2di_u *)p; }}"));
2229        assert_eq!(loaded.matches("align 1\n").count(), 2, "{loaded}");
2230        assert!(!loaded.contains("align 16"), "{loaded}");
2231        // The store side, which travels as a copy into whatever the pointer names and so carries
2232        // one number for both ends of it.
2233        let stored = body(&format!("{prefix}void f(void *p, v2di b) {{ *(v2di_u *)p = b; }}"));
2234        assert!(stored.contains("memcpy %0, %3, size 16, align 1"), "{stored}");
2235        // And the aligned spelling of the same two, which is where sixteen is the right answer.
2236        let aligned =
2237            body(&format!("{prefix}v2di f(const void *p) {{ return *(const v2di *)p; }}"));
2238        assert!(aligned.contains("align 16"), "{aligned}");
2239    }
2240
2241    /// The same attribute on a declaration rather than on a type, which asks that this object or
2242    /// this function be at a multiple of that, and which is where a program that has to hand a
2243    /// buffer to hardware or keep two counters off one cache line writes it.
2244    ///
2245    /// A raise and never a lower, which is the one place it does not agree with `_Alignas`: below
2246    /// what the type already has, `_Alignas` is a constraint violation and this is ignored without
2247    /// a word. `__alignof__` of the object answers what the object got and not what its type has,
2248    /// because that is the question a program asking it is asking.
2249    #[test]
2250    fn the_aligned_attribute_on_a_declaration_raises_what_that_one_object_is_aligned_to() {
2251        tast(concat!(
2252            "int v __attribute__((aligned(64)));\n",
2253            "_Static_assert(__alignof__(v) == 64, \"v\");\n",
2254            // Written on the specifiers rather than after the declarator, which asks the same
2255            // thing and is the spelling a header is more likely to use.
2256            "__attribute__((aligned(32))) int w;\n",
2257            "_Static_assert(__alignof__(w) == 32, \"w\");\n",
2258            "[[gnu::aligned(16)]] int x;\n",
2259            "_Static_assert(__alignof__(x) == 16, \"x\");\n",
2260            // Two below the four an `int` already has, so nothing is asked for and nothing is
2261            // said, and the type still answers for the object.
2262            "int y __attribute__((aligned(2)));\n",
2263            "_Static_assert(__alignof__(y) == 4, \"y\");\n",
2264            // A local, which is the same question one scope down.
2265            "void f(void) { int a __attribute__((aligned(128)));\n",
2266            "_Static_assert(__alignof__(a) == 128, \"a\"); (void)a; }\n",
2267            // The type is untouched by any of it: `aligned` on a declaration says where that
2268            // declaration goes and says nothing about every other `int` in the program.
2269            "_Static_assert(__alignof__(int) == 4, \"int\");\n",
2270            // A function, which has no alignment of its own for this to be measured against and
2271            // takes whatever was asked for.
2272            "void g(void) __attribute__((aligned(256)));\n",
2273            "void g(void) {}\n",
2274            "_Static_assert(__alignof__(g) == 256, \"g\");\n",
2275        ));
2276    }
2277
2278    /// And what the object file says, which is the half that makes the answer above true. A
2279    /// function is at a fixed offset inside the text section, so it is at a multiple of two
2280    /// hundred and fifty six only if the section is at one too.
2281    #[test]
2282    fn what_a_declaration_asked_to_be_aligned_to_is_what_the_assembler_is_told() {
2283        let text = asm(concat!(
2284            "int v __attribute__((aligned(64)));\n",
2285            "void g(void) __attribute__((aligned(256)));\n",
2286            "void g(void) {}\n",
2287            "void plain(void) {}\n",
2288        ));
2289        assert!(text.contains("\t.p2align\t6\n\t.type\tv, @object\n"), "{text}");
2290        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "{text}");
2291        assert!(text.contains("\t.p2align\t4, 0x90\n\t.globl\tplain\n"), "{text}");
2292    }
2293
2294    /// The same question asked by the command line instead of by a declaration, which is
2295    /// `-falign-functions` and is what femtolisp's Makefile writes on every compile. The flag is a
2296    /// floor: a function that named a larger boundary itself keeps it, and one that named a
2297    /// smaller one is moved up, because the attribute is a requirement about one function and the
2298    /// flag is a preference about all of them.
2299    #[test]
2300    fn the_alignment_the_command_line_asked_of_every_function_is_a_floor_under_all_of_them() {
2301        let source = concat!(
2302            "void g(void) __attribute__((aligned(256)));\n",
2303            "void g(void) {}\n",
2304            "void small(void) __attribute__((aligned(4)));\n",
2305            "void small(void) {}\n",
2306            "void plain(void) {}\n",
2307        );
2308        let listing = |align: Option<u32>| {
2309            let mut opts = options();
2310            opts.emit = EmitKind::Asm;
2311            opts.align_functions = align;
2312            let result = run(&opts, source);
2313            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
2314            result.text().to_owned()
2315        };
2316
2317        let text = listing(Some(32));
2318        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "the larger one wins: {text}");
2319        assert!(text.contains("\t.p2align\t5, 0x90\n\t.globl\tsmall\n"), "{text}");
2320        assert!(text.contains("\t.p2align\t5, 0x90\n\t.globl\tplain\n"), "{text}");
2321
2322        // And the negative form, which asks for the smallest boundary the target has and is the
2323        // one spelling that takes a function below the sixteen bytes it would get anyway.
2324        let text = listing(Some(8));
2325        assert!(text.contains("\t.p2align\t3, 0x90\n\t.globl\tplain\n"), "{text}");
2326        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "{text}");
2327    }
2328
2329    /// And the one position where the attribute means something else. On a declaration it raises
2330    /// what that one object is aligned to, and on a typedef it says what the type is aligned to,
2331    /// which gcc lets it lower as well: `typedef int L __attribute__((aligned(2)))` really is an
2332    /// `int` at a multiple of two and a record with one in it really is smaller for it.
2333    ///
2334    /// The size is left alone, which is gcc's answer rather than an omission here. An aligned
2335    /// typedef whose alignment is larger than what it stands for keeps the size it stands for,
2336    /// and gcc refuses an array of one rather than padding the elements out to fit.
2337    #[test]
2338    fn an_aligned_typedef_says_what_an_object_of_it_is_aligned_to_and_may_lower_it() {
2339        tast(concat!(
2340            "typedef int L __attribute__((aligned(2)));\n",
2341            "_Static_assert(__alignof__(L) == 2, \"L\");\n",
2342            "_Static_assert(_Alignof(L) == 2, \"L alignof\");\n",
2343            // Below what an `int` has, which is the half a declaration cannot ask for.
2344            "_Static_assert(sizeof(L) == 4, \"L size\");\n",
2345            "struct T { char c; L x; };\n",
2346            "_Static_assert(sizeof(struct T) == 6, \"T\");\n",
2347            "_Static_assert(__builtin_offsetof(struct T, x) == 2, \"T.x\");\n",
2348            // And upwards, which is the ordinary direction and the one a header writes.
2349            "typedef int H __attribute__((aligned(16)));\n",
2350            "_Static_assert(__alignof__(H) == 16, \"H\");\n",
2351            "_Static_assert(sizeof(H) == 4, \"H size\");\n",
2352            "struct U { char c; H x; };\n",
2353            "_Static_assert(sizeof(struct U) == 32, \"U\");\n",
2354            "_Static_assert(__builtin_offsetof(struct U, x) == 16, \"U.x\");\n",
2355            // A typedef of a typedef, where the nearer one is the one the declaration was
2356            // written with and is the one that answers.
2357            "typedef L M __attribute__((aligned(8)));\n",
2358            "_Static_assert(__alignof__(M) == 8, \"M\");\n",
2359            // And one that asked for nothing, which still has whatever the one behind it asked
2360            // for because it is the same type spelled again.
2361            "typedef L N;\n",
2362            "_Static_assert(__alignof__(N) == 2, \"N\");\n",
2363            // The type it stands for is untouched by any of it.
2364            "_Static_assert(__alignof__(int) == 4, \"int\");\n",
2365        ));
2366        let text = asm(concat!(
2367            "typedef int L __attribute__((aligned(2)));\n",
2368            "typedef int H __attribute__((aligned(16)));\n",
2369            "L low;\n",
2370            "H high;\n",
2371        ));
2372        assert!(text.contains("\t.p2align\t1\n\t.type\tlow, @object\n"), "{text}");
2373        assert!(text.contains("\t.p2align\t4\n\t.type\thigh, @object\n"), "{text}");
2374    }
2375
2376    /// The attribute that builds a type rather than changing a layout. `vector_size(n)` says the
2377    /// declared type is `n` bytes of what was written, taken as lanes, and every operator over
2378    /// one is that operator over each lane.
2379    ///
2380    /// The size is in bytes and not in lanes, which is the part a reader gets backwards: sixteen
2381    /// of `int` is four lanes and sixteen of `char` is sixteen. A vector is aligned to its own
2382    /// size, which is what a machine that has the registers wants and what gcc gives one here.
2383    #[test]
2384    fn the_vector_size_attribute_builds_a_type_of_lanes_and_measures_it_in_bytes() {
2385        tast(concat!(
2386            "typedef int __attribute__((vector_size(16))) v4si;\n",
2387            "_Static_assert(sizeof(v4si) == 16 && _Alignof(v4si) == 16, \"v4si\");\n",
2388            "typedef char __attribute__((vector_size(16))) v16qi;\n",
2389            "_Static_assert(sizeof(v16qi) == 16, \"v16qi\");\n",
2390            // One lane, which is a power of two and is a vector rather than the type it was
2391            // written on: the operators it takes are the vector's and not the scalar's.
2392            "typedef int __attribute__((vector_size(4))) v1si;\n",
2393            "_Static_assert(sizeof(v1si) == 4, \"v1si\");\n",
2394            // The armoured spelling and the bracket one, which are the same attribute.
2395            "typedef float __attribute__((__vector_size__(8))) v2sf;\n",
2396            "_Static_assert(sizeof(v2sf) == 8, \"v2sf\");\n",
2397            "typedef short [[gnu::vector_size(8)]] v4hi;\n",
2398            "_Static_assert(sizeof(v4hi) == 8, \"v4hi\");\n",
2399            // A lane is what a subscript answers with, and a vector is not a pointer: there is
2400            // nothing to decay and the lane type is the one the arithmetic happens in.
2401            "v4si g;\n",
2402            "_Static_assert(sizeof(g[0]) == 4, \"lane\");\n",
2403            "_Static_assert(sizeof(g + g) == 16, \"whole\");\n",
2404            // A scalar beside a vector stands for itself in every lane, so the answer is still
2405            // the vector and not the wider of the two types.
2406            "_Static_assert(sizeof(g + 1) == 16, \"broadcast\");\n",
2407            // An array of them, which is the ordinary way a program holds several.
2408            "_Static_assert(sizeof(v4si[3]) == 48, \"array\");\n",
2409        ));
2410    }
2411
2412    /// A whole vector written into an array of them, and a vector named by a type name rather
2413    /// than by a typedef.
2414    ///
2415    /// Both are the same question asked twice. A vector is filled like an array of its lanes when
2416    /// a list is written into it, so a braced element that is itself a vector has to be taken
2417    /// whole rather than started as the first lane, and the type of what was written is the only
2418    /// thing that says which was meant. And a type name is where a compound literal and a cast
2419    /// spell the type out, which a macro taking a lane type and a lane count does, so the
2420    /// attribute has to be read there and not only on a declaration.
2421    #[test]
2422    fn a_vector_is_written_whole_into_an_array_of_them_and_named_by_a_type_name() {
2423        tast(concat!(
2424            "typedef int __attribute__((vector_size(8))) v2si;\n",
2425            "v2si table[] = { (v2si){ 1, 2 }, (v2si){ 3, 4 } };\n",
2426            "_Static_assert(sizeof(table) == 16, \"two of them and not eight lanes\");\n",
2427            // The size written out rather than named, which is the spelling a macro expands to.
2428            "v2si written = (int __attribute__((vector_size(8)))){ 5, 6 };\n",
2429            "_Static_assert(sizeof((int __attribute__((vector_size(16)))){ 0 }) == 16, \"named\");\n",
2430            // A lane is still a lane, so a list of them fills the vector the way it always did
2431            // and the rule above did not turn brace elision off.
2432            "v2si lanes[2] = { 1, 2, 3, 4 };\n",
2433            "_Static_assert(sizeof(lanes) == 16, \"still elided\");\n",
2434        ));
2435    }
2436
2437    /// A lane written rather than read, and a shift whose two vectors are not the same type.
2438    ///
2439    /// Both are places where a vector is not the aggregate it looks like. A subscript of one is
2440    /// an lvalue because the vector it came from is an object, so a lane can be assigned to and
2441    /// has an address, and a qualifier written on the vector reaches every lane the way it does
2442    /// on an array. And a shift is the one lanewise operator whose sides are not brought to a
2443    /// single type, since the right side counts rather than computes.
2444    #[test]
2445    fn a_lane_is_assignable_and_a_shift_takes_a_count_of_its_own_lane() {
2446        let result = run(
2447            &options(),
2448            concat!(
2449                "typedef int __attribute__((vector_size(16))) v4si;\n",
2450                "typedef unsigned __attribute__((vector_size(16))) v4ui;\n",
2451                "void write(v4si *out, v4ui a, v4si b, int n) {\n",
2452                "  v4si v = { 1, 2, 3, 4 };\n",
2453                "  v[0] = n;\n",
2454                "  v[1] += n;\n",
2455                "  v[2]++;\n",
2456                "  *&v[3] = n;\n",
2457                // The count is signed and the value is not, which no other operator allows.
2458                "  v4ui shifted = a >> b;\n",
2459                "  shifted <<= b;\n",
2460                // A scalar stands in every lane on either side of a shift, which is the half
2461                // that looks wrong: the shape of the answer comes off the count here.
2462                "  *out = v + (v4si)shifted + (1 << b);\n",
2463                "}\n",
2464                // A qualifier on the vector is a qualifier on the lane, so there is nothing here
2465                // to write to.
2466                "void refused(const v4si c) {\n",
2467                "  c[0] = 1;\n",
2468                "}\n",
2469            ),
2470        );
2471        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
2472        assert!(result.messages[0].contains("assignment of read-only"), "{:?}", result.messages);
2473    }
2474
2475    /// The third layout attribute, and the one that moves nothing. It says the scalars in the
2476    /// record are stored in the byte order it names, so on a target whose order is the other one
2477    /// every load through a member swaps its bytes and so does every store. The record is the size
2478    /// and the alignment it would be without it and every member is where it would be, which is
2479    /// what gcc 16.2.0 does and what was measured before any of this was written.
2480    ///
2481    /// All four spellings are here because a header writes the armoured one, the attribute may be
2482    /// written in front of the body as well as behind it, and the C23 spelling in gcc's namespace
2483    /// is the same attribute a fourth way. The order the target already has is the fifth case and
2484    /// asks for nothing, since a program saying what would have happened anyway is entitled to be
2485    /// compiled as though it had said nothing.
2486    #[test]
2487    fn a_record_that_asks_for_the_other_byte_order_swaps_every_scalar_it_holds() {
2488        let read = "int f(struct s *p) { return p->i; }\n";
2489        let big = "struct s { int i; } __attribute__((scalar_storage_order(\"big-endian\")));\n";
2490        assert!(body(&format!("{big}{read}")).contains("bswap"), "{big}");
2491
2492        let armoured =
2493            "struct s { int i; } __attribute__((__scalar_storage_order__(\"big-endian\")));\n";
2494        assert!(body(&format!("{armoured}{read}")).contains("bswap"), "{armoured}");
2495
2496        let front = "struct __attribute__((scalar_storage_order(\"big-endian\"))) s { int i; };\n";
2497        assert!(body(&format!("{front}{read}")).contains("bswap"), "{front}");
2498
2499        let standard = "struct s { int i; } [[gnu::scalar_storage_order(\"big-endian\")]];\n";
2500        assert!(body(&format!("{standard}{read}")).contains("bswap"), "{standard}");
2501
2502        let same =
2503            "struct s { int i; } __attribute__((scalar_storage_order(\"little-endian\")));\n";
2504        assert!(!body(&format!("{same}{read}")).contains("bswap"), "{same}");
2505
2506        // A member one byte wide has only one order, and neither has the record itself.
2507        let byte = "struct s { char c; } __attribute__((scalar_storage_order(\"big-endian\")));\n";
2508        let source = format!("{byte}int f(struct s *p) {{ return p->c; }}\n");
2509        assert!(!body(&source).contains("bswap"), "{byte}");
2510
2511        tast(concat!(
2512            "struct s { int i; short h; char c; }",
2513            " __attribute__((scalar_storage_order(\"big-endian\")));\n",
2514            "_Static_assert(sizeof(struct s) == 8 && _Alignof(struct s) == 4, \"s\");\n",
2515            "_Static_assert(__builtin_offsetof(struct s, h) == 4, \"s.h\");\n",
2516            "_Static_assert(__builtin_offsetof(struct s, c) == 6, \"s.c\");\n",
2517        ));
2518    }
2519
2520    /// A bit-field in one of these records lies in the same bytes and is counted from the top of
2521    /// them rather than from the bottom. `execute/20230630-2.c` is the program that says so:
2522    /// `short i : 12` in front of four one bit fields holds 341 in the two bytes `15 5f`, so the
2523    /// twelve bits are the top twelve and reading them is a shift right by four rather than a mask
2524    /// alone. The plain record shifts nothing, since there the field is already at the bottom.
2525    #[test]
2526    fn a_bit_field_in_one_of_those_records_is_counted_from_the_top_of_its_bytes() {
2527        let members = "short i : 12; char c1 : 1; char c2 : 1; char c3 : 1; char c4 : 1;";
2528        let read = "int f(struct s *p) { return p->i; }\n";
2529        let plain = format!("struct s {{ {members} }};\n{read}");
2530        let reversed = format!(
2531            "struct s {{ {members} }} __attribute__((scalar_storage_order(\"big-endian\")));\n\
2532             {read}"
2533        );
2534        assert!(body(&plain).contains("shl"), "{}", body(&plain));
2535        assert!(!body(&plain).contains("bswap"), "{}", body(&plain));
2536        // The two loaded bytes the other way round and then the top twelve bits of them, which
2537        // is the arithmetic shift right on its own with nothing to move the field up to the top.
2538        let built = body(&reversed);
2539        assert!(built.contains("bswap"), "{built}");
2540        assert!(!built.contains("shl"), "{built}");
2541        assert!(built.contains("ashr"), "{built}");
2542    }
2543
2544    /// The one thing a program may not do with a member of one of these records. The bytes are
2545    /// there and they are the other way round, so a pointer to them is a pointer to a value of
2546    /// that type which is not the value the member holds. gcc refuses it in these words, and it
2547    /// refuses only the scalars: the address of a nested record or of an array member is an
2548    /// address of the bytes as they lie, and an access through it asks its own type which order
2549    /// it is in.
2550    #[test]
2551    fn the_address_of_a_scalar_stored_the_other_way_round_is_refused() {
2552        let opts = options();
2553        let record = "struct s { int i; int a[2]; struct in { int n; } w; }\n\
2554                      __attribute__((scalar_storage_order(\"big-endian\")));\n";
2555        let taken = format!("{record}int *f(struct s *p) {{ return &p->i; }}\n");
2556        assert_eq!(
2557            run(&opts, &taken).messages,
2558            ["/main.c:3:30: error: cannot take address of scalar with reverse storage order \
2559              [E0712]"]
2560        );
2561        let element = format!("{record}int *f(struct s *p) {{ return &p->a[0]; }}\n");
2562        let messages = run(&opts, &element).messages;
2563        assert!(messages[0].contains("[E0712]"), "{messages:?}");
2564
2565        let whole = format!("{record}int *f(struct s *p) {{ return (int *) &p->w; }}\n");
2566        assert_eq!(run(&opts, &whole).messages, Vec::<String>::new(), "{whole}");
2567    }
2568
2569    /// An argument that names neither order, which gcc answers with the two words it does take.
2570    /// A program that writes one of these is reading a wire format and would rather be told the
2571    /// spelling it got wrong than be handed a record laid out in the order it did not ask for.
2572    #[test]
2573    fn a_storage_order_that_names_neither_end_is_refused_with_the_two_words_that_are_taken() {
2574        let opts = options();
2575        let wrong = "struct s { int i; } __attribute__((scalar_storage_order(\"middle\")));\n";
2576        assert_eq!(
2577            run(&opts, wrong).messages,
2578            ["/main.c:1:36: error: 'scalar_storage_order' argument must be one of \"big-endian\" \
2579              or \"little-endian\" [E0688]"]
2580        );
2581        let bare = "struct s { int i; } __attribute__((scalar_storage_order));\n";
2582        let messages = run(&opts, bare).messages;
2583        assert!(messages[0].contains("[E0688]"), "{messages:?}");
2584    }
2585
2586    /// Where a bit-field goes, which packing decides and which is the part of all this that
2587    /// is not what the names suggest. A bit-field goes at the next free bit unless that would
2588    /// make it span more storage than its own type occupies, and then it moves to the next
2589    /// boundary of its alignment. Any packing at all takes that rule out, and `#pragma pack`
2590    /// counts even where it lowers nothing, which is the fourth and seventh cases here.
2591    ///
2592    /// Nothing in the language can be asked where a bit-field is, since `offsetof` refuses one
2593    /// and every size below comes out the same either way, so what is asked is the byte a read
2594    /// of the field loads from.
2595    #[test]
2596    fn packing_is_what_decides_whether_a_bit_field_may_straddle_its_own_storage() {
2597        // A `char` field after twelve bits, which will not straddle unpacked and does packed.
2598        assert_eq!(bit_field_byte("struct s { int x : 12; char y : 6; };"), 2);
2599        assert_eq!(
2600            bit_field_byte("struct s { int x : 12; char y : 6; } __attribute__((packed));"),
2601            1
2602        );
2603        assert_eq!(
2604            bit_field_byte("struct s { int x : 12; __attribute__((packed)) char y : 6; };"),
2605            1
2606        );
2607        assert_eq!(bit_field_byte("#pragma pack(4)\nstruct s { int x : 12; char y : 6; };"), 1);
2608        // A thirty bit field after a byte, which is the case the rule was written for.
2609        assert_eq!(bit_field_byte("struct s { char x; int y : 30; };"), 4);
2610        assert_eq!(bit_field_byte("struct s { char x; int y : 30; } __attribute__((packed));"), 1);
2611        // Four is what an `int` asked for anyway, so this caps nothing and still counts.
2612        assert_eq!(bit_field_byte("#pragma pack(4)\nstruct s { char x; int y : 30; };"), 1);
2613        assert_eq!(bit_field_byte("#pragma pack(2)\nstruct s { char x; int y : 30; };"), 1);
2614    }
2615
2616    /// The byte a read of `s.y` loads from, which is where the bit-field was placed.
2617    fn bit_field_byte(record: &str) -> u64 {
2618        let source = format!("{record}\nint f(struct s *p) {{ return p->y; }}\n");
2619        let body = body(&source);
2620        let Some((before, _)) = body.split_once("ptr_add") else { return 0 };
2621        let (_, constant) = before.rsplit_once("iconst.i64 ").expect("an offset constant");
2622        constant.lines().next().expect("a line").trim().parse().expect("a byte offset")
2623    }
2624
2625    /// An attribute in the middle of a specifier list, which is where a member usually carries
2626    /// one and which was read and then thrown away. The `[[...]]` spelling and whatever was
2627    /// written in front of the declaration are collected as the list is walked and the
2628    /// `__attribute__` spelling is put straight on the specifiers, and the two were assigned
2629    /// over each other rather than joined.
2630    #[test]
2631    fn an_attribute_among_the_specifiers_is_kept_beside_the_ones_written_in_front() {
2632        tast(concat!(
2633            "struct a { char c; __attribute__((aligned(8))) int i; };\n",
2634            "_Static_assert(sizeof(struct a) == 16 && _Alignof(struct a) == 8, \"a\");\n",
2635            "_Static_assert(__builtin_offsetof(struct a, i) == 8, \"a.i\");\n",
2636            "struct b { char c; __attribute__((packed)) int i; };\n",
2637            "_Static_assert(sizeof(struct b) == 5 && _Alignof(struct b) == 1, \"b\");\n",
2638            "_Static_assert(__builtin_offsetof(struct b, i) == 1, \"b.i\");\n",
2639            "typedef struct { char c; int i; } __attribute__((packed)) c;\n",
2640            "_Static_assert(sizeof(c) == 5 && _Alignof(c) == 1, \"c\");\n",
2641        ));
2642    }
2643
2644    /// The other half, which is `#pragma pack`. It caps a member's alignment where `packed`
2645    /// drops it, so `pack(2)` leaves a `short` where it was and moves an `int`, and it caps a
2646    /// member the program asked to align as well, which is where the two differ. It is read
2647    /// at the closing brace of the body, so a line written in the middle of one settles the
2648    /// whole record rather than the members after it, and `push` and `pop` nest.
2649    #[test]
2650    fn pragma_pack_caps_every_member_and_is_read_where_the_body_closes() {
2651        tast(concat!(
2652            "#pragma pack(1)\n",
2653            "struct A { char c; int i; };\n",
2654            "_Static_assert(sizeof(struct A) == 5 && _Alignof(struct A) == 1, \"A\");\n",
2655            "_Static_assert(__builtin_offsetof(struct A, i) == 1, \"A.i\");\n",
2656            "#pragma pack()\n",
2657            "struct B { char c; int i; };\n",
2658            "_Static_assert(sizeof(struct B) == 8 && _Alignof(struct B) == 4, \"B\");\n",
2659            "#pragma pack(2)\n",
2660            "struct C { char c; int i; double d; };\n",
2661            "_Static_assert(sizeof(struct C) == 14 && _Alignof(struct C) == 2, \"C\");\n",
2662            "_Static_assert(__builtin_offsetof(struct C, d) == 6, \"C.d\");\n",
2663            // A member the program aligned, which `pack` caps and `packed` would not.
2664            "struct K { char c; int i __attribute__((aligned(8))); };\n",
2665            "_Static_assert(sizeof(struct K) == 6 && _Alignof(struct K) == 2, \"K\");\n",
2666            "_Static_assert(__builtin_offsetof(struct K, i) == 2, \"K.i\");\n",
2667            // The record's own `aligned` is not a member's, so it is not capped.
2668            "struct J { char c; int i; } __attribute__((aligned(8)));\n",
2669            "_Static_assert(sizeof(struct J) == 8 && _Alignof(struct J) == 8, \"J\");\n",
2670            "#pragma pack()\n",
2671            "#pragma pack(push, 1)\n",
2672            "struct D { char c; short s; };\n",
2673            "_Static_assert(sizeof(struct D) == 3 && _Alignof(struct D) == 1, \"D\");\n",
2674            "#pragma pack(pop)\n",
2675            "struct E { char c; short s; };\n",
2676            "_Static_assert(sizeof(struct E) == 4 && _Alignof(struct E) == 2, \"E\");\n",
2677            // Written in the middle of a body, and it still settles the whole record.
2678            "struct H { char c;\n",
2679            "#pragma pack(1)\n",
2680            "  int i; };\n",
2681            "_Static_assert(sizeof(struct H) == 5 && _Alignof(struct H) == 1, \"H\");\n",
2682            "#pragma pack(1)\n",
2683            "struct I { char c;\n",
2684            "#pragma pack()\n",
2685            "  int i; };\n",
2686            "_Static_assert(sizeof(struct I) == 8 && _Alignof(struct I) == 4, \"I\");\n",
2687            "#pragma pack()\n",
2688            // Nested pushes, each one giving back what the one under it had.
2689            "#pragma pack(push, 8)\n",
2690            "#pragma pack(push, 1)\n",
2691            "struct P { char c; int i; };\n",
2692            "_Static_assert(sizeof(struct P) == 5 && _Alignof(struct P) == 1, \"P\");\n",
2693            "#pragma pack(pop)\n",
2694            "struct Q { char c; int i; };\n",
2695            "_Static_assert(sizeof(struct Q) == 8 && _Alignof(struct Q) == 4, \"Q\");\n",
2696            "#pragma pack(pop)\n",
2697            // A cap above what every member already asks for changes nothing at all.
2698            "#pragma pack(16)\n",
2699            "struct R { char c; int i; };\n",
2700            "_Static_assert(sizeof(struct R) == 8 && _Alignof(struct R) == 4, \"R\");\n",
2701            "#pragma pack()\n",
2702            "#pragma pack(1)\n",
2703            "struct S { char c; int i : 5; int j : 20; };\n",
2704            "_Static_assert(sizeof(struct S) == 5 && _Alignof(struct S) == 1, \"S\");\n",
2705            "union T { char c; int i; };\n",
2706            "_Static_assert(sizeof(union T) == 4 && _Alignof(union T) == 1, \"T\");\n",
2707            "#pragma pack()\n",
2708        ));
2709    }
2710
2711    /// A line the reader cannot make sense of is a warning and the line is dropped, which is
2712    /// what GCC does with one, and these are its words for each of them. The last line is the
2713    /// one nothing else would reach, since it stands after every record in the file.
2714    #[test]
2715    fn a_pack_line_that_is_not_one_is_reported_in_the_words_gcc_uses() {
2716        let result = run(
2717            &options(),
2718            concat!(
2719                "#pragma pack 4\n",
2720                "#pragma pack(pop)\n",
2721                "#pragma pack(3)\n",
2722                "#pragma pack(1) junk\n",
2723                "#pragma pack(push, 1\n",
2724                "#pragma pack(x)\n",
2725                // These two are well formed and say nothing. Zero is how a line asks for the
2726                // target's own alignments back without writing empty parentheses.
2727                "#pragma pack(0)\n",
2728                "#pragma pack(push)\n",
2729                "struct s { char c; int i; };\n",
2730                "#pragma pack(pop)\n",
2731                "#pragma pack(pop, foo)\n",
2732            ),
2733        );
2734        let expected = [
2735            "missing `(` after `#pragma pack` - ignored",
2736            "`#pragma pack (pop)` encountered without matching `#pragma pack (push)`",
2737            "alignment must be a small power of two, not 3",
2738            "junk at end of `#pragma pack`",
2739            "malformed `#pragma pack(push[, id][, <n>])` - ignored",
2740            "unknown action `x` for `#pragma pack` - ignored",
2741            "`#pragma pack(pop, foo)` encountered without matching `#pragma pack(push, foo)`",
2742        ];
2743        assert_eq!(result.messages.len(), expected.len(), "{:?}", result.messages);
2744        for (message, want) in result.messages.iter().zip(expected) {
2745            assert!(message.contains(want), "expected {want:?} in {message:?}");
2746        }
2747    }
2748
2749    /// A pragma line ends where the next line starts, so a macro that comes to nothing and was
2750    /// written first on that next line has to hand the line on rather than take it away. This
2751    /// is SQLite through mingw-w64's headers: `<stdarg.h>` leaves a `#pragma pack(pop)` behind
2752    /// it and `sqlite3.h` writes every declaration with `SQLITE_API` in front, which is empty.
2753    /// Without it the pragma swallows the declaration, the program is left without it, and the
2754    /// only thing said about any of it is that there was junk on the pragma.
2755    #[test]
2756    fn a_declaration_behind_an_empty_macro_is_not_eaten_by_the_pragma_above_it() {
2757        let result = run(
2758            &options(),
2759            concat!(
2760                "#pragma pack(push, 1)\n",
2761                "#pragma pack(pop)\n",
2762                "#define API\n",
2763                "API const char version[] = \"3.53.4\";\n",
2764                "const char *get(void) { return version; }\n",
2765            ),
2766        );
2767        assert!(result.messages.is_empty(), "{:?}", result.messages);
2768    }
2769
2770    /// The two typedef spellings of the 128 bit types. gcc offers them as keywords rather
2771    /// than as typedefs in a header, which is the only way a program that includes nothing at
2772    /// all can still use them, and Apple's `<mach/arm/_structs.h>` is one such program.
2773    #[test]
2774    fn the_wide_integer_answers_to_all_three_of_its_names() {
2775        let text = tast("__uint128_t a; __int128_t b; unsigned __int128 c;\n");
2776        assert!(text.contains("decl #0 a : unsigned __int128"), "{text}");
2777        assert!(text.contains("decl #1 b : __int128"), "{text}");
2778        assert!(text.contains("decl #2 c : unsigned __int128"), "{text}");
2779    }
2780
2781    #[test]
2782    fn every_conversion_the_language_performs_is_a_node_in_the_output() {
2783        // The point of a typed tree. The source has one operator and the output has the
2784        // widening that operator asked for, spelled out, so that nothing downstream has to
2785        // work out the conversion rules a second time.
2786        let text = tast("long f(int a, long b) { return a + b; }\n");
2787        assert!(text.contains("convert arithmetic"), "{text}");
2788    }
2789
2790    #[test]
2791    fn a_mistake_in_each_phase_reaches_the_caller_and_writes_no_tree() {
2792        for source in [
2793            "#error stop\n",
2794            "int f(void) { return 1 + ; }\n",
2795            "int f(void) { return undeclared; }\n",
2796        ] {
2797            let result = run(&options(), source);
2798            assert!(result.failed(), "expected this to fail:\n{source}");
2799            assert!(
2800                result.text().is_empty(),
2801                "a file that did not compile wrote a tree:\n{source}"
2802            );
2803        }
2804    }
2805
2806    #[test]
2807    fn one_undeclared_name_is_one_message_and_not_one_per_use() {
2808        // The poisoning rule from `spec/06-lexer-and-parser.md` section 6.8, seen from the
2809        // outside. Three uses of a name that was never declared, and the operators over them
2810        // say nothing at all.
2811        let result = run(&options(), "int f(void) { return nope + nope * nope; }\n");
2812        assert_eq!(result.errors, 1, "{:?}", result.messages);
2813    }
2814
2815    #[test]
2816    fn a_declaration_the_parser_skipped_does_not_become_an_undeclared_name_as_well() {
2817        // The reason the checking is skipped after a failed parse. The parser gave up on the
2818        // first line and there is no `x` in the tree, so a checker run over it would report
2819        // every use of `x` below as undeclared, which is a second message about one mistake.
2820        let result = run(&options(), "int x = ;\nint f(void) { return x; }\n");
2821        assert_eq!(result.errors, 1, "{:?}", result.messages);
2822    }
2823
2824    #[test]
2825    fn werror_turns_a_warning_into_an_error_in_the_count_and_in_the_word() {
2826        let source = "int f(void) { char c = 300; return c; }\n";
2827        let plain = run(&options(), source);
2828        assert_eq!(plain.errors, 0, "{:?}", plain.messages);
2829        assert_eq!(plain.messages.len(), 1, "expected a warning about the narrowed constant");
2830        assert!(!plain.text().is_empty(), "a warning is not a reason to write nothing");
2831
2832        let mut opts = options();
2833        opts.warnings_are_errors = true;
2834        let strict = run(&opts, source);
2835        assert!(strict.failed());
2836        assert!(strict.text().is_empty(), "and under -Werror it is a reason to write nothing");
2837        for message in &strict.messages {
2838            assert!(!message.contains("warning:"), "{message}");
2839        }
2840    }
2841
2842    #[test]
2843    fn w_drops_the_warning_before_werror_can_promote_it() {
2844        let source = "int f(void) { char c = 300; return c; }\n";
2845        let mut opts = options();
2846        opts.warnings = false;
2847        let quiet = run(&opts, source);
2848        assert_eq!(quiet.messages, Vec::<String>::new());
2849        assert_eq!(quiet.errors, 0);
2850        assert!(!quiet.text().is_empty(), "and the file still compiles");
2851
2852        // A build that passes both means it wants neither, and the order it wrote them in is not
2853        // something to make it think about.
2854        opts.warnings_are_errors = true;
2855        let both = run(&opts, source);
2856        assert_eq!(both.messages, Vec::<String>::new());
2857        assert!(!both.failed(), "-w -Werror is not an error about a warning nobody saw");
2858    }
2859
2860    #[test]
2861    fn the_dialect_reaches_the_keywords_and_the_checking() {
2862        // `typeof` is C23's and GNU's, so the same source is a declaration under one dialect
2863        // and a mistake under the other, which is the keyword table being built per dialect.
2864        let source = "typeof(1) x;\n";
2865        let mut opts = options();
2866        opts.std = Std::C23;
2867        opts.gnu_extensions = false;
2868        assert!(!run(&opts, source).failed(), "{:?}", run(&opts, source).messages);
2869
2870        opts.std = Std::C17;
2871        assert!(run(&opts, source).failed());
2872    }
2873
2874    #[test]
2875    fn asking_for_a_kind_that_is_not_written_yet_runs_the_front_end_and_writes_nothing() {
2876        let mut opts = options();
2877        opts.emit = EmitKind::Object;
2878        let result = run(&opts, "int x = 1;\n");
2879        assert!(!result.failed(), "{:?}", result.messages);
2880        assert!(result.text().is_empty());
2881        // And it still finds what the checking finds, so a later kind on a broken file is not
2882        // a silent success.
2883        assert!(run(&opts, "int f(void) { return undeclared; }\n").failed());
2884    }
2885
2886    /// The machine code of `source`, insisting that it compiled cleanly.
2887    fn mir(source: &str) -> String {
2888        let mut opts = options();
2889        opts.emit = EmitKind::MirFinal;
2890        let result = run(&opts, source);
2891        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
2892        result.text().to_owned()
2893    }
2894
2895    /// The whole compiler in one assertion, which is what this emit kind is for.
2896    ///
2897    /// C in, machine instructions out, every register a real one and every frame offset a
2898    /// number. Everything between the two is checked somewhere else, one pass at a time. What is
2899    /// checked here is that the passes are joined up and that the driver runs them.
2900    #[test]
2901    fn a_function_goes_from_c_to_instructions_with_real_registers_in_them() {
2902        let text = mir("int add(int a, int b) { return a + b; }\n");
2903        assert!(text.starts_with("mfunc @add {"), "{text}");
2904        assert!(text.contains("x64.add_rr_32"), "{text}");
2905        assert!(text.contains("x64.ret"), "{text}");
2906        // A virtual register is what the allocator was there to remove, so one left in the
2907        // output is the difference between code and something that looks like code.
2908        assert!(!text.contains('%'), "{text}");
2909    }
2910
2911    /// A declaration has no body, so there is nothing to generate for one and nothing is.
2912    #[test]
2913    fn a_function_with_no_body_produces_no_machine_function() {
2914        let text = mir("int g(int);\nint f(int a) { return g(a); }\n");
2915        assert_eq!(text.matches("mfunc @").count(), 1, "{text}");
2916        assert!(text.contains("mfunc @f {"), "{text}");
2917        assert!(text.contains("x64.call"), "{text}");
2918    }
2919
2920    /// Two functions come out in the order the module holds them, which is source order.
2921    #[test]
2922    fn every_definition_in_the_file_is_generated_and_they_keep_their_order() {
2923        let text = mir("int a(int x) { return x; }\nint b(int x) { return x; }\n");
2924        let first = text.find("mfunc @a").expect("the first function");
2925        let second = text.find("mfunc @b").expect("the second function");
2926        assert!(first < second, "{text}");
2927    }
2928
2929    /// The target reaches the back end, so the same C is different instructions on Windows.
2930    #[test]
2931    fn the_target_decides_which_convention_the_generated_code_follows() {
2932        let mut opts = options();
2933        opts.emit = EmitKind::MirFinal;
2934        let linux = run(&opts, "int f(int a) { return a; }\n").text().to_owned();
2935        assert!(linux.contains("$rdi"), "{linux}");
2936
2937        opts.target = "x86_64-pc-windows-msvc".parse::<Triple>().unwrap();
2938        let windows = run(&opts, "int f(int a) { return a; }\n").text().to_owned();
2939        assert!(windows.contains("$rcx"), "{windows}");
2940        assert!(!windows.contains("$rdi"), "{windows}");
2941    }
2942
2943    /// And it reaches the front end, where it decides what an anonymous member is.
2944    ///
2945    /// This is the shape `<objidl.h>` writes and the Windows headers are full of: the union inside
2946    /// `STGMEDIUM` closes with `} DUMMYUNIONNAME;`, and the macro expands to nothing unless the
2947    /// program defined `NONAMELESSUNION`, so what is left is a union with a tag and no name. On a
2948    /// Windows target that is an anonymous member, and reading it as a declaration of nothing
2949    /// drops it, which loses the names and the eight bytes the member takes up both.
2950    #[test]
2951    fn a_tagged_member_with_no_name_is_a_member_on_windows_and_nothing_on_linux() {
2952        let source = concat!(
2953            "struct S { union U { int i; void *p; }; unsigned long tymed; };\n",
2954            "int size(void) { return sizeof(struct S); }\n",
2955            "int f(struct S *s) { s->i = 1; return s->i; }\n",
2956        );
2957
2958        let mut opts = options();
2959        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
2960        let windows = run(&opts, source);
2961        assert!(windows.messages.is_empty(), "{:?}", windows.messages);
2962
2963        let linux = run(&options(), source);
2964        assert_eq!(linux.messages.len(), 3, "{:?}", linux.messages);
2965        assert!(linux.messages[0].contains("does not declare anything"), "{:?}", linux.messages);
2966
2967        // And the flag answers for either of them, so a program built for Linux against a header
2968        // written for Windows can be read the way the header meant it.
2969        let mut opts = options();
2970        opts.ms_extensions = Some(true);
2971        let asked = run(&opts, source);
2972        assert!(asked.messages.is_empty(), "{:?}", asked.messages);
2973    }
2974
2975    /// A target with no back end says so rather than generating something for another machine.
2976    #[test]
2977    fn a_target_this_has_no_back_end_for_is_reported_rather_than_generated() {
2978        let mut opts = options();
2979        opts.emit = EmitKind::MirFinal;
2980        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2981        let result = run(&opts, "int f(int a) { return a; }\n");
2982        assert!(result.failed());
2983        assert!(result.messages[0].contains("no back end for aarch64"), "{:?}", result.messages);
2984        assert!(result.text().is_empty());
2985    }
2986
2987    /// A construct the rule set does not reach yet is named, along with the function it is in.
2988    ///
2989    /// The message is about this compiler being unfinished rather than about the program, which
2990    /// is valid C either way, so it carries the note that says where the work is tracked. Both
2991    /// functions are attempted, so a file that is ahead of the back end in three places says so
2992    /// three times rather than one recompilation at a time.
2993    ///
2994    /// The construct is a local of a fixed size wanting more alignment than a call leaves the
2995    /// stack pointer on, in a function whose frame also grows. The prologue would force the
2996    /// alignment and the array would move the stack pointer afterwards, and those are two frames
2997    /// that each want the one register the rest of the frame is counted from.
2998    #[test]
2999    fn a_construct_the_back_end_cannot_reach_yet_is_reported_against_its_function() {
3000        let mut opts = options();
3001        opts.emit = EmitKind::MirFinal;
3002        let source = "void a(int n) { int v[n]; struct __attribute__((aligned(32))) S { int x; } \
3003                      s; s.x = 1; v[0] = s.x; }\n\
3004                      void b(int n) { int v[n]; struct __attribute__((aligned(32))) S { int x; } \
3005                      s; s.x = 1; v[0] = s.x; }\n";
3006        let result = run(&opts, source);
3007        assert!(result.failed());
3008        assert_eq!(result.messages.len(), 2, "{:?}", result.messages);
3009        assert!(result.messages[0].contains("cannot generate code for 'a'"), "{:?}", result);
3010        assert!(result.messages[0].contains("wants more alignment"), "{:?}", result);
3011        assert!(result.messages[1].contains("cannot generate code for 'b'"), "{:?}", result);
3012        assert!(result.text().is_empty());
3013    }
3014
3015    /// A variable length array walks its pages under the flag that says every page is touched.
3016    ///
3017    /// The pages the prologue takes are touched by the prologue. The pages the array takes are
3018    /// however many the size worked out to, so touching them is a loop written around the
3019    /// declaration rather than anything a prologue can do. What says the loop is there is the
3020    /// ordered comparison it ends each step with, which nothing else in a function writes, and the
3021    /// touch behind it. Without the flag the declaration is still the one subtraction it always was.
3022    #[test]
3023    fn a_variable_length_array_walks_its_pages_where_every_page_of_the_frame_is_to_be_touched() {
3024        let mut opts = options();
3025        opts.emit = EmitKind::MirFinal;
3026        let source = "void a(int n) { int v[n]; v[0] = 1; }\n";
3027        let plain = run(&opts, source);
3028        assert!(!plain.failed(), "{:?}", plain.messages);
3029        assert!(!plain.text().contains("cmp_set_a_64"), "{}", plain.text());
3030
3031        opts.stack_clash = true;
3032        let result = run(&opts, source);
3033        assert!(!result.failed(), "{:?}", result.messages);
3034        assert!(result.text().contains("cmp_set_a_64"), "{}", result.text());
3035        assert!(result.text().contains("or_mi_8"), "{}", result.text());
3036    }
3037
3038    /// A function that keeps a frame pointer on Windows now has an unwind record and an object.
3039    ///
3040    /// The record that platform carries counts every slot in it from where the stack pointer ends
3041    /// the prologue, and it gets to that place by taking a constant off the frame pointer, so a
3042    /// register pushed after the pointer was established has no row the format can write. The order
3043    /// that does have one is the pushes, then the frame, and only then the pointer, which is what
3044    /// the back end writes there and only there. A variable length array and an `alloca` keep a
3045    /// pointer whatever the flags asked for, so before this they were the two shapes of C that
3046    /// could not be compiled for that target at all. See tamnd/rucc#1403.
3047    #[test]
3048    fn a_function_that_keeps_a_frame_pointer_on_windows_reaches_an_object_file() {
3049        let mut opts = options();
3050        opts.emit = EmitKind::Object;
3051        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
3052        let source = concat!(
3053            "void use(void *p);\n",
3054            "void array(int n) { int v[n]; v[0] = 1; use(v); }\n",
3055            "void taken(unsigned long n) { use(__builtin_alloca(n)); }\n",
3056        );
3057        let result = run(&opts, source);
3058        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3059        let bytes = match result.artifact {
3060            Artifact::Object { bytes, .. } => bytes,
3061            other => panic!("expected an object, got {other:?}"),
3062        };
3063        assert_eq!(&bytes[..2], b"\x64\x86", "an object that says which machine it is for");
3064
3065        // And the same two functions for Linux, so that what the test is measuring is the target
3066        // rather than the program being one this compiler cannot reach yet.
3067        let mut opts = options();
3068        opts.emit = EmitKind::Object;
3069        assert_eq!(run(&opts, source).messages, Vec::<String>::new());
3070    }
3071
3072    /// The address of a name this file only declares, on the format with no table to read it out
3073    /// of.
3074    ///
3075    /// Every such name went into the table on every target, and COFF has no table, so the object
3076    /// writer was handed a relocation it has no way to write and refused the whole file. What the
3077    /// name stands for on this format is an address in the image whichever way the link supplies
3078    /// it, so the instruction pointer reaches it and gcc writes the same. Three shapes here, since
3079    /// the one that found it was a callback stored in a table of its own: a function passed as an
3080    /// argument, one put in a variable that lives past the call, and one called outright, which
3081    /// never needed the table and is here so the test says which of the three changed.
3082    #[test]
3083    fn the_address_of_a_function_this_file_only_declares_reaches_a_windows_object() {
3084        let source = concat!(
3085            "void other(void *p);\n",
3086            "void takes(void (*f)(void *));\n",
3087            "void (*held)(void *);\n",
3088            "void pass(void) { takes(other); }\n",
3089            "void keep(void) { held = other; }\n",
3090            "void call(void) { other(0); }\n",
3091        );
3092        let mut opts = options();
3093        opts.emit = EmitKind::Object;
3094        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
3095        let result = run(&opts, source);
3096        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
3097        let bytes = match result.artifact {
3098            Artifact::Object { bytes, .. } => bytes,
3099            other => panic!("expected an object, got {other:?}"),
3100        };
3101        assert_eq!(&bytes[..2], b"\x64\x86", "an object that says which machine it is for");
3102
3103        // And the same source for Linux, which does have a table and still uses it, so what this
3104        // measures is the format rather than the program.
3105        let mut opts = options();
3106        opts.emit = EmitKind::Object;
3107        assert_eq!(run(&opts, source).messages, Vec::<String>::new());
3108    }
3109
3110    /// An opcode the rule language has no word for is named anyway, and pointed at.
3111    ///
3112    /// The rule language's spelling is the better name when there is one, but an opcode it has
3113    /// no word for is exactly the opcode no rule lowers, so falling back to the opcode and the
3114    /// type is what makes the message say anything at all in the cases that happen. The span is
3115    /// the instruction's own, so the message lands on the line rather than on the file.
3116    ///
3117    /// The width of the float is what keeps the program refused. Everything else here is split into
3118    /// halves by `rucc_codegen::wide`, including the divisions and the conversions to a `float` and
3119    /// a `double`, which became calls into the compiler runtime. A `long double` is the eighty bit
3120    /// float on this target, the runtime has no conversion at that width because the back end has no
3121    /// register that holds one, which is tamnd/rucc#326, so a function converting to it is left with
3122    /// its wide values and reaches the selector the way every function of this width used to.
3123    #[test]
3124    fn an_opcode_with_no_name_in_the_rule_language_is_named_by_its_own_spelling() {
3125        let mut opts = options();
3126        opts.emit = EmitKind::MirFinal;
3127        let source =
3128            "long double f(int a) {\n  __int128 wide = a;\n  return (long double) wide;\n}\n";
3129        let result = run(&opts, source);
3130        assert!(result.failed());
3131        assert!(
3132            result.messages[0].contains("no rule lowers a `sext` producing a `i128`"),
3133            "{result:?}"
3134        );
3135        assert!(result.messages[0].contains(":2:"), "the line the widening is on: {result:?}");
3136        assert!(!result.messages[0].contains("this instruction"), "{result:?}");
3137    }
3138
3139    /// The note names the issue tracker, which is where a reader finds out whether it is known.
3140    #[test]
3141    fn the_note_on_unfinished_work_points_at_the_issues_rather_than_at_the_plan() {
3142        let mut opts = options();
3143        opts.emit = EmitKind::MirFinal;
3144        let source = "long double f(int a) { __int128 wide = a; return (long double) wide; }\n";
3145        let result = run(&opts, source);
3146        assert!(result.failed());
3147        let note = result.messages.iter().find(|line| line.contains("note:")).expect("a note");
3148        assert!(note.contains("https://github.com/tamnd/rucc/issues"), "{note}");
3149        assert!(!note.contains("spec/17-milestones.md"), "{note}");
3150    }
3151
3152    /// The two frame flags reach the frame, which is the only thing either of them does.
3153    #[test]
3154    fn the_frame_flags_on_the_command_line_reach_the_generated_frame() {
3155        let source = "int f(int a) { return a; }\n";
3156        assert!(!mir(source).contains("$rbp"), "a leaf needs no frame pointer by default");
3157
3158        let mut opts = options();
3159        opts.emit = EmitKind::MirFinal;
3160        opts.frame_pointer = true;
3161        let kept = run(&opts, source).text().to_owned();
3162        assert!(kept.contains("x64.push_64 $rbp"), "{kept}");
3163    }
3164
3165    /// The assembly of `source`, insisting that it compiled cleanly.
3166    fn asm(source: &str) -> String {
3167        let mut opts = options();
3168        opts.emit = EmitKind::Asm;
3169        let result = run(&opts, source);
3170        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3171        result.text().to_owned()
3172    }
3173
3174    /// `-S`, which is the same compiler as the kind above it with a different last step.
3175    ///
3176    /// What the assembly says is checked in `rucc-asm`, one instruction at a time and against the
3177    /// target's own description of what an instruction is. What is checked here is that a C file
3178    /// goes all the way to a listing an assembler would take, which means the directives around
3179    /// the function as well as the instructions in it.
3180    #[test]
3181    fn a_function_goes_from_c_to_assembly_an_assembler_would_take() {
3182        let text = asm("int add(int a, int b) { return a + b; }\n");
3183        assert!(text.contains("\t.globl\tadd\n"), "{text}");
3184        assert!(text.contains("\t.type\tadd, @function\n"), "{text}");
3185        assert!(text.contains("\nadd:\n"), "{text}");
3186        assert!(text.contains("\taddl\t"), "{text}");
3187        assert!(text.contains("\tret\n"), "{text}");
3188        assert!(text.contains("\t.size\tadd, .-add\n"), "{text}");
3189        // Without this the stack the program runs on is executable, which is not a default
3190        // anybody chose and is not a thing a reader would notice missing.
3191        assert!(text.contains(".note.GNU-stack"), "{text}");
3192    }
3193
3194    /// A call through a function pointer, which is a different instruction from a call to a name.
3195    ///
3196    /// Both are in the one function on purpose. What is being read is that the two calls are told
3197    /// apart all the way down: one carries a name the linker resolves and one carries a register,
3198    /// and neither turns into the other on the way.
3199    #[test]
3200    fn a_call_through_a_function_pointer_goes_through_the_register_it_is_in() {
3201        let text = asm("int g(int);\nint f(int (*p)(int), int a) { return p(a) + g(a); }\n");
3202        assert!(text.contains("\tcall\t*%"), "{text}");
3203        assert!(text.contains("\tcall\tg\n"), "{text}");
3204        // The address arrived in the first argument register and the argument the call passes has
3205        // to end up there, so the two cannot be the same register and the compiler has to have
3206        // moved one of them.
3207        assert!(text.contains("%rdi"), "{text}");
3208    }
3209
3210    /// A name at file scope, which is the one address a function cannot compute for itself. The
3211    /// `lea` that computes it is folded into the load that reads through it, so what is left to
3212    /// read is the addressing mode, which is where the instruction pointer shows up.
3213    #[test]
3214    fn the_address_of_a_global_is_read_from_the_instruction_pointer() {
3215        let text = asm("extern int counter;\nint f(void) { return counter; }\n");
3216        assert!(text.contains("\tmovl\tcounter(%rip), %eax\n"), "{text}");
3217    }
3218
3219    /// Every comparison a branch can be on, which the machine jumps on without keeping a byte.
3220    ///
3221    /// Ten conditions, and each of them comes out as its opposite because the block falls into the
3222    /// arm the comparison is true for and jumps to the other one. That is the half of this most
3223    /// worth pinning: a jump on the condition rather than on its opposite compiles, encodes and
3224    /// runs, and gets every one of these ten functions backwards. The unsigned four and the signed
3225    /// four are separate for the same reason, since `jl` where `jb` was meant is a program that
3226    /// works until an address is above two gigabytes.
3227    #[test]
3228    fn a_branch_on_a_comparison_jumps_on_the_opposite_of_what_it_compared() {
3229        let arms = "return 1; return 2;";
3230        let signed = [("==", "jne"), ("!=", "je"), ("<", "jge"), ("<=", "jg"), (">", "jle")];
3231        for (operator, jump) in signed.into_iter().chain([(">=", "jl")]) {
3232            let text = asm(&format!("int f(int a, int b) {{ if (a {operator} b) {arms} }}\n"));
3233            assert!(
3234                text.contains(&format!("\tcmpl\t%esi, %edi\n\t{jump}\t")),
3235                "{operator}: {text}"
3236            );
3237            assert!(!text.contains("\tset"), "{operator}: {text}");
3238            assert!(!text.contains("\ttest"), "{operator}: {text}");
3239        }
3240        let unsigned = [("<", "jae"), ("<=", "ja"), (">", "jbe"), (">=", "jb")];
3241        for (operator, jump) in unsigned {
3242            let source =
3243                format!("int f(unsigned a, unsigned b) {{ if (a {operator} b) {arms} }}\n");
3244            let text = asm(&source);
3245            assert!(
3246                text.contains(&format!("\tcmpl\t%esi, %edi\n\t{jump}\t")),
3247                "{operator}: {text}"
3248            );
3249        }
3250
3251        // And against a constant, which is four comparisons in five and is where the saving
3252        // mostly is, since the byte that goes was the only reason the constant was in a register.
3253        let text = asm("int f(int a) { if (a < 7) return 1; return 2; }\n");
3254        assert!(text.contains("\tcmpl\t$7, %edi\n\tjge\t"), "{text}");
3255    }
3256
3257    /// The comparison whose answer is a value rather than a branch, which keeps its byte.
3258    ///
3259    /// The one that goes is the byte nothing but the branch reads. A comparison the program asked
3260    /// for the answer of is not that, and there is no branch behind it to fold into in any case,
3261    /// so this is here to say that what was taken out was taken out of one place and not two.
3262    #[test]
3263    fn a_comparison_whose_answer_the_program_wanted_still_writes_a_byte() {
3264        let text = asm("int f(int a, int b) { return a < b; }\n");
3265        assert!(text.contains("\tsetl\t"), "{text}");
3266    }
3267
3268    /// The same source at `-O2`, which is where the optimizer's passes are in the list.
3269    fn optimized(source: &str) -> String {
3270        let mut opts = options();
3271        opts.emit = EmitKind::Asm;
3272        opts.opt_level = rucc_session::OptLevel::O2;
3273        let result = run(&opts, source);
3274        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3275        result.text().to_owned()
3276    }
3277
3278    /// A dense `switch` whose arms are a function of the label, which is arithmetic.
3279    ///
3280    /// Sixteen labels, and the arm for label `k` gives `k + 1`. What came out of this was a
3281    /// comparison and a jump for every one of them, which is tamnd/rucc#728. What comes out now is
3282    /// one comparison and one addition, and the count is the whole of the claim: it does not grow
3283    /// with the number of labels, so sixteen and a hundred and sixty compile to the same thing.
3284    ///
3285    /// The comparison is unsigned because the range check is the label minus the lowest one, which
3286    /// is a count and not a number the program wrote.
3287    #[test]
3288    fn a_switch_whose_arms_are_a_function_of_the_label_is_a_range_check_and_arithmetic() {
3289        let arms: String =
3290            (0..16).map(|k| format!("case {k}: return {};", k + 1)).collect::<Vec<_>>().join(" ");
3291        let text = optimized(&format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
3292        assert!(text.contains("\tcmpl\t$15, %edi\n\tja\t"), "{text}");
3293        assert!(text.contains("\taddl\t$1, %edi"), "{text}");
3294        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
3295    }
3296
3297    /// The same `switch` with one arm off the line, which is a table and not arithmetic.
3298    ///
3299    /// The answers being a line is what licenses the addition, since it answers for every label in
3300    /// the range at once. One label whose arm disagrees is a label it would answer wrongly, so this
3301    /// is here to say that the pass is reading the arms and not counting the labels. What it does
3302    /// instead is look the answer up: one comparison, no jump through a jump table, and the arm off
3303    /// the line is a cell of a constant array in `.rodata`, which is gcc's `CSWTCH` and its shape.
3304    #[test]
3305    fn a_dense_switch_whose_arms_are_not_a_line_is_a_load_from_a_table() {
3306        let arms: String = (0..16)
3307            .map(|k| format!("case {k}: return {};", if k == 9 { 100 } else { k + 1 }))
3308            .collect::<Vec<_>>()
3309            .join(" ");
3310        let text = optimized(&format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
3311        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
3312        assert!(!text.contains("\tjmp\t*"), "{text}");
3313        assert!(text.contains("leaq\tCSWTCH.0(%rip)"), "{text}");
3314        let table = &text[text.find("CSWTCH.0:").expect("the table is in the output")..];
3315        let section = text[..text.find("CSWTCH.0:").unwrap_or(0)].rfind("\t.section\t.rodata");
3316        assert!(section.is_some(), "{text}");
3317        assert_eq!(table.matches("\t.long\t").count(), 16, "{text}");
3318        assert!(table.contains("\t.long\t100\n"), "{text}");
3319    }
3320
3321    /// The same table at `-Os`, where a cell is a byte because every answer fits in one.
3322    ///
3323    /// gcc 16 narrows the cells at `-Os` and not at `-O2`, and so does rucc: sixteen answers under a
3324    /// hundred and twenty eight are sixteen bytes rather than sixty four, and the byte is widened
3325    /// back with its sign.
3326    #[test]
3327    fn a_table_at_os_has_cells_as_narrow_as_its_answers() {
3328        let arms: String = (0..16)
3329            .map(|k| format!("case {k}: return {};", if k == 9 { 100 } else { k + 1 }))
3330            .collect::<Vec<_>>()
3331            .join(" ");
3332        let mut opts = options();
3333        opts.emit = EmitKind::Asm;
3334        opts.opt_level = rucc_session::OptLevel::Os;
3335        let result = run(&opts, &format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
3336        assert_eq!(result.messages, Vec::<String>::new());
3337        let text = result.text();
3338        let table = &text[text.find("CSWTCH.0:").expect("the table is in the output")..];
3339        assert_eq!(table.matches("\t.byte\t").count(), 16, "{text}");
3340        assert!(text.contains("\tmovsbl\t"), "{text}");
3341    }
3342
3343    /// A table whose labels are every value the switched value can hold, which is the range check
3344    /// `rucc_opt::prune` takes out.
3345    ///
3346    /// The operand is `x & 3` and all four values are cases, so the `return -1` is dead. With the
3347    /// default out of the switch every case goes to the load, the switch is a jump, and what is
3348    /// left is the mask and the load with no compare in front of it.
3349    #[test]
3350    fn a_table_that_covers_its_operand_has_no_range_check() {
3351        let text = optimized(
3352            "int f(unsigned x) { switch (x & 3) { case 0: return 5; case 1: return 9; \
3353             case 2: return 2; case 3: return 7; } return -1; }\n",
3354        );
3355        assert!(text.contains("leaq\tCSWTCH.0(%rip)"), "{text}");
3356        assert!(!text.contains("\tcmp"), "{text}");
3357        assert!(!text.contains("$-1"), "{text}");
3358    }
3359
3360    /// A conversion whose operand the optimizer turned into a constant, which is the whole of what
3361    /// `rucc_opt::fold` does with floating point.
3362    ///
3363    /// The cast is not a constant expression, so the front end leaves it alone and the pipeline is
3364    /// what has to see it. Load forwarding turns the local back into the constant that was stored
3365    /// into it, and the conversion then has an `fconst` in front of it. What came out before was
3366    /// the sixty four bit pattern moved into a register, moved into an `xmm`, and a `cvttsd2si`.
3367    #[test]
3368    fn a_conversion_from_a_constant_double_is_the_number_it_converts_to() {
3369        let text = optimized("int f(void) { double d = 2.75; return (int) d; }\n");
3370        assert!(text.contains("movl\t$2, %eax"), "{text}");
3371        assert!(!text.contains("cvttsd2si"), "{text}");
3372    }
3373
3374    /// A slot of a `const` table read at an index the optimizer works out, which is what
3375    /// `rucc_opt::image` is for.
3376    ///
3377    /// The subscript is not a constant expression and the front end does not fold it. What it
3378    /// writes is the index sign extended, multiplied by four and added to the address of the
3379    /// table, so the offset only exists once `fold` has run and the load only folds after that.
3380    /// What came out before was a `movl t+8(%rip), %eax`.
3381    #[test]
3382    fn a_slot_of_a_read_only_table_is_the_value_the_table_holds() {
3383        let text =
3384            optimized("static const int t[4] = {10, 20, 30, 40};\nint f(void) { return t[2]; }\n");
3385        assert!(text.contains("movl\t$30, %eax"), "{text}");
3386        assert!(!text.contains("t(%rip)"), "{text}");
3387    }
3388
3389    /// A byte of a string literal, which is the same fold reading literal bytes rather than the
3390    /// scalars an `int` array is written as.
3391    #[test]
3392    fn a_byte_of_a_read_only_string_is_the_byte_the_string_spells() {
3393        let text = optimized("static const char s[] = \"abc\";\nint f(void) { return s[1]; }\n");
3394        assert!(text.contains("movl\t$98, %eax"), "{text}");
3395    }
3396
3397    /// A global something can write to, which is the condition the fold turns on and therefore
3398    /// the one worth a test of its own. Nothing here is `const`, so the store in `g` could be the
3399    /// store that ran last and the load has to happen.
3400    #[test]
3401    fn a_table_that_is_not_read_only_keeps_its_load() {
3402        let text = optimized(
3403            "static int t[4] = {10, 20, 30, 40};\nvoid g(int x) { t[2] = x; }\nint f(void) { return t[2]; }\n",
3404        );
3405        assert!(!text.contains("movl\t$30, %eax"), "{text}");
3406    }
3407
3408    /// `gcc.c-torture/execute/20030216-1.c`, which is the program the whole of this is for.
3409    ///
3410    /// It calls a function nothing defines, guarded by a condition the optimizer is meant to prove
3411    /// false, so the program links exactly when the call has been folded away. Getting there is
3412    /// three folds standing on each other: the load of the `const double`, the conversion of it to
3413    /// an `int`, and the comparison against one.
3414    #[test]
3415    fn a_call_guarded_by_a_condition_a_read_only_object_settles_is_not_emitted() {
3416        let text = optimized(
3417            "void link_error(void);\nconst double one = 1.0;\nint main(void) { if ((int) one != 1) link_error(); return 0; }\n",
3418        );
3419        assert!(!text.contains("call\tlink_error"), "{text}");
3420    }
3421
3422    /// A cast between a pointer and an integer as wide as one, which is every one C writes here.
3423    #[test]
3424    fn a_cast_between_a_pointer_and_an_integer_leaves_the_value_where_it_is() {
3425        let text = asm("long f(void *p) { return (long)p; }\n");
3426        // Every instruction in the body is a full width move or the return. The copies are the
3427        // allocator taking no hints, and what matters here is what is not among them: nothing
3428        // narrows the value and nothing widens it again, which is what a cast that did something
3429        // would look like.
3430        for line in text.lines().filter(|line| line.starts_with('\t') && !line.contains('.')) {
3431            let mnemonic = line.split_whitespace().next().unwrap_or("");
3432            assert!(matches!(mnemonic, "movq" | "ret"), "{line} in\n{text}");
3433        }
3434    }
3435
3436    /// The arguments past the sixth arrive in the caller's memory rather than in a register, and
3437    /// where that memory is depends on what the prologue did, so this is checked at the end of the
3438    /// pipeline rather than in the middle of it.
3439    #[test]
3440    fn an_argument_past_the_last_register_is_read_out_of_the_caller_s_stack() {
3441        let six = "long a, long b, long c, long d, long e, long f";
3442        let text = asm(&format!("long f({six}, long g, long h) {{ return g + h; }}\n"));
3443
3444        // Nothing is pushed and no frame is taken, so the only thing between the stack pointer and
3445        // the caller's arguments is the return address the call pushed. Which is where gcc 16.2.0
3446        // reads them from too, at `-O0`, though it reads them in three instructions where this
3447        // reads them in two: the second read is the addition's own memory operand, which is
3448        // `rucc_codegen::combine`, and the offset in it is the one the frame layout wrote into the
3449        // load before the two were put together.
3450        assert!(text.contains("\tmovq\t8(%rsp), "), "{text}");
3451        assert!(text.contains("\taddq\t16(%rsp), "), "{text}");
3452
3453        // A narrower one is read at its own width, because the bits above it are bits the
3454        // convention says nothing about, and one in the other register file with the other file's
3455        // instruction.
3456        let narrow = asm(&format!("int f({six}, int g) {{ return g; }}\n"));
3457        assert!(narrow.contains("\tmovl\t8(%rsp), "), "{narrow}");
3458        let eight =
3459            "double a, double b, double c, double d, double e, double f, double g, double h";
3460        let float = asm(&format!("double f({eight}, double i) {{ return i; }}\n"));
3461        assert!(float.contains("\tmovsd\t8(%rsp), "), "{float}");
3462    }
3463
3464    /// The other end of the same thing. What the caller writes is at the stack pointer, because
3465    /// that is the bottom of its frame and the bottom of its frame is where the callee looks.
3466    #[test]
3467    fn a_call_writes_the_arguments_with_no_register_left_at_the_stack_pointer() {
3468        let six = "1, 2, 3, 4, 5, 6";
3469        let decl = "long g(long, long, long, long, long, long, long, long);\n";
3470        let text = asm(&format!("{decl}long f(void) {{ return g({six}, 7, 8); }}\n"));
3471
3472        assert!(text.contains("\tmovq\t%"), "{text}");
3473        assert!(text.contains(", (%rsp)\n"), "{text}");
3474        assert!(text.contains(", 8(%rsp)\n"), "{text}");
3475        // And it reserved the bytes it wrote into, so nothing else in the frame is on top of them.
3476        assert!(text.contains("\tsubq\t$"), "{text}");
3477
3478        // A narrower one is written at its own width, matching what the callee reads it back with.
3479        let narrow = "int g(int, int, int, int, int, int, int);\n";
3480        let text = asm(&format!("{narrow}int f(void) {{ return g({six}, 7); }}\n"));
3481        assert!(text.contains("\tmovl\t%"), "{text}");
3482        assert!(text.contains(", (%rsp)\n"), "{text}");
3483    }
3484
3485    /// The count a variadic callee on this convention reads is a count of vector registers, so a
3486    /// float that ran out of them and went to memory is not in it.
3487    #[test]
3488    fn a_variadic_call_counts_registers_and_not_arguments() {
3489        let nine = "1., 2., 3., 4., 5., 6., 7., 8., 9.";
3490        let decl = "int g(int, ...);\n";
3491        let text = asm(&format!("{decl}int f(void) {{ return g(0, {nine}); }}\n"));
3492
3493        assert!(text.contains("\tmovl\t$8, "), "eight registers, not nine: {text}");
3494        assert!(text.contains("\tmovsd\t%"), "{text}");
3495        assert!(text.contains(", (%rsp)\n"), "{text}");
3496    }
3497
3498    /// The callee's half of the same convention. Every argument register it was handed is written
3499    /// into its frame on the way in, because which of them hold anything is a thing only the caller
3500    /// knew, and the ones the signature does name are left out because `va_start` sets the offsets
3501    /// past them and nothing ever reads their slots.
3502    #[test]
3503    fn a_variadic_function_writes_the_argument_registers_it_was_handed_into_its_frame() {
3504        let body =
3505            "__builtin_va_list ap; __builtin_va_start(ap, n); __builtin_va_end(ap); return n;";
3506        let text = asm(&format!("int f(int n, ...) {{ {body} }}\n"));
3507
3508        // Five general purpose registers and eight vector ones, since the one parameter the
3509        // signature names took the first of the six.
3510        let stores = |mnemonic: &str| text.matches(&format!("\t{mnemonic}\t%")).count();
3511        assert!(text.contains(", 8(%r"), "the second slot, not the first: {text}");
3512        assert!(!text.contains(", 0(%r"), "{text}");
3513        // All sixteen bytes of each vector register, which is what gcc writes and what a `va_arg`
3514        // of a `_Float128` reads back, so the mnemonic is the one that moves a whole register.
3515        assert_eq!(stores("movaps"), 8, "every vector register: {text}");
3516        assert_eq!(stores("movsd"), 0, "and the whole of each one: {text}");
3517
3518        // And the area is one of the function's own stack objects, so the frame holds it.
3519        assert!(text.contains("\tsubq\t$"), "{text}");
3520    }
3521
3522    /// What `va_start` writes is the four fields of the list, and the two numbers among them are
3523    /// where the arguments the signature names left the walk over each file's registers.
3524    #[test]
3525    fn va_start_writes_the_four_fields_the_psabi_describes() {
3526        let start = "__builtin_va_list ap; __builtin_va_start(ap, d);";
3527        let params = "int a, int b, int c, double d";
3528        let text = asm(&format!("int f({params}, ...) {{ {start} return a; }}\n"));
3529
3530        // Three integers took three of the six general purpose registers, and one double took one
3531        // of the eight vector ones, so the walk starts at twenty four bytes into the first half and
3532        // sixteen bytes into the second, which begins at forty eight.
3533        assert!(text.contains("	movl	$24, "), "{text}");
3534        assert!(text.contains("	movl	$64, "), "{text}");
3535        // The other two fields are addresses rather than numbers, so each is stored as a word and
3536        // each is a `lea` away. One of them reaches above the frame, which is where the caller's
3537        // arguments are and is the only thing in this function that is not below the stack pointer.
3538        assert!(text.contains(", 8(%r"), "{text}");
3539        assert!(text.contains(", 16(%r"), "{text}");
3540        let frame: u32 = text
3541            .lines()
3542            .find_map(|line| line.trim().strip_prefix("subq	$")?.split(',').next()?.parse().ok())
3543            .expect("a variadic function takes a frame for the save area");
3544        let above = |line: &str| {
3545            let at: u32 = line.trim().strip_prefix("leaq	")?.split('(').next()?.parse().ok()?;
3546            Some(at > frame)
3547        };
3548        assert!(text.lines().filter_map(above).any(|it| it), "{frame}: {text}");
3549    }
3550
3551    /// A `va_arg` is a branch on whether the argument it wants is still in the save area, and which
3552    /// of the two halves it walks is the type's answer.
3553    #[test]
3554    fn va_arg_branches_on_whether_the_argument_is_still_in_the_save_area() {
3555        let read = "__builtin_va_list ap; __builtin_va_start(ap, n);";
3556        let ints = format!("int f(int n, ...) {{ {read} return __builtin_va_arg(ap, int); }}\n");
3557        let text = asm(&ints);
3558
3559        // The last general purpose slot begins at forty, so an offset above it is an argument the
3560        // caller left in its own memory instead.
3561        assert!(text.contains("$40, "), "{text}");
3562        assert!(text.contains("	cmpl	"), "{text}");
3563        // The jump is the unsigned one, since an offset is a count of bytes. It is the opposite
3564        // of the comparison the front end wrote, because the block falls into the half taken when
3565        // the argument is still in the save area and jumps to the other one.
3566        assert!(text.contains("	ja	"), "{text}");
3567
3568        let arg = "__builtin_va_arg(ap, double)";
3569        let text = asm(&format!("double f(int n, ...) {{ {read} return {arg}; }}\n"));
3570        assert!(text.contains("$160, "), "the last vector slot: {text}");
3571    }
3572
3573    /// A structure assigned is a copy of a known size, and a copy of a known size is a run of
3574    /// moves rather than a call to a library this compiler has no way to reach yet.
3575    #[test]
3576    fn a_structure_assignment_is_a_move_for_each_word_of_it() {
3577        let decl = "struct pair { long a, b; };\n";
3578        let body = "struct pair p = *q; return p.a + p.b;";
3579        let text = asm(&format!("{decl}long f(struct pair *q) {{ {body} }}\n"));
3580
3581        assert!(!text.contains("memcpy"), "nothing calls the library: {text}");
3582        assert!(!text.contains("\tcall"), "{text}");
3583        // Sixteen bytes aligned to eight is two words, and each is a load and a store.
3584        assert!(text.matches("\tmovq\t").count() >= 4, "two words each way: {text}");
3585    }
3586
3587    /// A word is as wide as the object is aligned to and no wider, so a character array is copied
3588    /// a byte at a time and a structure of longs eight bytes at a time.
3589    #[test]
3590    fn how_wide_a_word_of_a_copy_is_follows_the_alignment() {
3591        let decl = "struct bytes { char a[8]; };\n";
3592        let body = "struct bytes p = *q; return p.a[0];";
3593        let text = asm(&format!("{decl}int f(struct bytes *q) {{ {body} }}\n"));
3594
3595        // Eight bytes aligned to one is eight words, and each is a load and a store.
3596        assert!(text.matches("\tmovb\t").count() >= 16, "a byte at a time: {text}");
3597    }
3598
3599    /// What an initialiser does not name is zero, which the front end writes as a fill and this
3600    /// writes as the byte spread across each word.
3601    #[test]
3602    fn the_part_of_an_initialiser_that_names_nothing_is_stored_as_zero() {
3603        let decl = "struct wide { long a, b, c; };\n";
3604        let text = asm(&format!("{decl}long f(void) {{ struct wide w = {{ 7 }}; return w.c; }}\n"));
3605
3606        assert!(!text.contains("memset"), "nothing calls the library: {text}");
3607        // Either spelling of a zero in a register, the move of one or the exclusive or of the
3608        // register with itself that `rucc_codegen::shorten` writes instead where it is free. The
3609        // exclusive or is the thirty-two bit one whatever the width of the word, since the half of
3610        // the register it does not write is cleared rather than left alone.
3611        assert!(text.contains("\tmovq\t$0, ") || text.contains("\txorl\t"), "the zero: {text}");
3612    }
3613
3614    /// A copy too large to be worth unrolling is a call to the runtime, which is the C library on
3615    /// a hosted target and `rucc-builtins` on a freestanding one.
3616    #[test]
3617    fn a_copy_too_large_to_unroll_calls_the_runtime() {
3618        let decl = "struct huge { char a[4096]; };\n";
3619        let mut opts = options();
3620        opts.emit = EmitKind::Asm;
3621        let source = format!("{decl}void f(struct huge *p, struct huge *q) {{ *p = *q; }}\n");
3622        let result = run(&opts, &source);
3623        assert!(!result.failed(), "{:?}", result.messages);
3624        let text = result.text();
3625        assert!(text.contains("call") && text.contains("memcpy"), "{text}");
3626        // The size in the register the convention passes the third argument in, which is what
3627        // says the call was built from the convention and not from the shape of the IR.
3628        assert!(text.contains("4096"), "the size travels: {text}");
3629    }
3630
3631    /// And an object passed by value with more words in it than that is the same call again,
3632    /// written in front of the call the object is an argument of.
3633    ///
3634    /// The copy is one the caller owes the callee, since the callee is free to write to what it
3635    /// was handed, so it is not an optimization that the size decides but the only way the call
3636    /// can be made at all.
3637    #[test]
3638    fn a_structure_too_large_to_unroll_is_copied_into_the_argument_area_by_the_runtime() {
3639        let decl = "struct huge { char a[4096]; };\nint take(struct huge);\n";
3640        let text = asm(&format!("{decl}int f(struct huge *p) {{ return take(*p); }}\n"));
3641
3642        let copy = text.find("call\tmemcpy").expect("the copy");
3643        let call = text.find("call\ttake").expect("the call");
3644        assert!(copy < call, "the copy comes first: {text}");
3645        // Into the bottom of the outgoing area, which is where the stack pointer already is, and
3646        // with the size in the register the convention passes the third argument in. The address
3647        // of the bottom of the frame is the stack pointer itself, so what carries it is the move
3648        // rather than the address computation the selector wrote. See `rucc_codegen::shorten`.
3649        assert!(text.contains("movq\t%rsp, %rdi"), "the destination: {text}");
3650        assert!(text.contains("$4096, %edx"), "the size: {text}");
3651    }
3652
3653    /// A frame that had to force its own alignment cannot say how far away the caller's stack
3654    /// pointer was, so it reaches back through the frame pointer instead.
3655    #[test]
3656    fn a_realigned_frame_reads_them_through_the_frame_pointer() {
3657        let six = "long a, long b, long c, long d, long e, long f";
3658        let body = "_Alignas(32) long wide[4]; wide[0] = g; return wide[0];";
3659        let text = asm(&format!("long f({six}, long g) {{ {body} }}\n"));
3660
3661        // The frame pointer is saved and pointed at where it was saved before the alignment is
3662        // forced, so the caller's arguments stay a constant distance from it: one word for the
3663        // saved frame pointer and one for the return address.
3664        assert!(text.contains("\tandq\t$-32, %rsp"), "{text}");
3665        assert!(text.contains("\tmovq\t16(%rbp), "), "{text}");
3666        assert!(!text.contains("\tmovq\t16(%rsp), "), "{text}");
3667    }
3668
3669    /// The object format decides the directives, and the target decides the object format.
3670    #[test]
3671    fn the_target_decides_how_the_assembly_is_spelled() {
3672        let mut opts = options();
3673        opts.emit = EmitKind::Asm;
3674        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
3675        let text = run(&opts, "int f(void) { return 0; }\n").text().to_owned();
3676        assert!(text.contains("__TEXT,__text"), "{text}");
3677        assert!(text.contains("\n_f:\n"), "{text}");
3678        assert!(!text.contains(".note.GNU-stack"), "{text}");
3679    }
3680
3681    /// The object file of `source`, insisting that it compiled cleanly.
3682    fn obj(source: &str) -> Vec<u8> {
3683        let mut opts = options();
3684        opts.emit = EmitKind::Object;
3685        let result = run(&opts, source);
3686        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3687        match result.artifact {
3688            Artifact::Object { bytes, .. } => bytes,
3689            other => panic!("expected an object, got {other:?}"),
3690        }
3691    }
3692
3693    /// `-c`, which is the last step of the three the back end can end with.
3694    ///
3695    /// What is in the file is checked in `rucc-object`, a field at a time. What is checked here is
3696    /// that a C file goes all the way to one, which is the whole compiler in one line and the
3697    /// thing that stops working when a layer between them changes its mind about something.
3698    #[test]
3699    fn a_function_goes_from_c_to_an_object_a_linker_would_take() {
3700        let bytes = obj("int add(int a, int b) { return a + b; }\n");
3701        assert_eq!(&bytes[..4], b"\x7fELF", "an object file starts by saying it is one");
3702        let text = asm("int add(int a, int b) { return a + b; }\n");
3703        assert!(
3704            text.contains("\taddl\t"),
3705            "and the listing of it is the same instructions:\n{text}"
3706        );
3707    }
3708
3709    /// A variable this file defines, which is what a reference to one has to resolve against.
3710    #[test]
3711    fn a_variable_goes_from_c_to_the_section_it_belongs_in() {
3712        let text = asm("int counter = 42;\nstatic int hidden;\nconst int fixed = 7;\n");
3713        assert!(text.contains("\t.data\n\t.globl\tcounter\n"), "{text}");
3714        assert!(text.contains("\ncounter:\n\t.long\t42\n"), "{text}");
3715        assert!(text.contains("\t.size\tcounter, .-counter\n"), "{text}");
3716        // A zeroed variable carries its size and none of its bytes, and a `static` one is not
3717        // announced to the linker at all, which is the whole of what `static` means here.
3718        assert!(text.contains("\t.bss\n\t.p2align\t2\n"), "{text}");
3719        assert!(text.contains("\nhidden:\n\t.space\t4\n"), "{text}");
3720        assert!(!text.contains(".globl\thidden"), "{text}");
3721        // Nothing writes through it, so it goes in a page the loader can map read only and every
3722        // process running the program can share.
3723        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
3724    }
3725
3726    /// A bit-field with a value in it, which is written as the bytes the value lands in.
3727    ///
3728    /// The interesting one is the field whose lowest byte is zero. The bytes a bit-field
3729    /// initializer makes are put together first and then taken back out as the run they make,
3730    /// and taking them out starts at the byte the field starts at, so a zero byte at the front
3731    /// used to end the object up in `.bss` with the rest of its value thrown away.
3732    #[test]
3733    fn a_bit_field_initializer_writes_every_byte_of_the_value_and_not_only_the_ones_that_are_set() {
3734        let text = asm("struct s { unsigned f : 20; } x = { 0x12300 };\n");
3735        assert!(text.contains("\t.data\n"), "there is something to write: {text}");
3736        assert!(text.contains("\nx:\n\t.ascii\t\"\\000#\\001\"\n"), "and it is the value: {text}");
3737
3738        // Two fields, the first of them zero, which is the same thing said with the zero byte
3739        // inside the run rather than at the front of it.
3740        let text = asm("struct s { unsigned a : 8; unsigned b : 8; } x = { 0, 3 };\n");
3741        assert!(text.contains("\nx:\n\t.ascii\t\"\\000\\003\"\n"), "{text}");
3742
3743        // Wider than an `int`, which is the same code and is worth saying because the value no
3744        // longer fits in the thirty two bits a bit-field used to be read at.
3745        let text = asm("struct s { unsigned long long f : 40; } x = { 0x100000 };\n");
3746        assert!(text.contains("\nx:\n\t.ascii\t\"\\000\\000\\020\"\n\t.space\t5\n"), "{text}");
3747
3748        // Nothing in it, which still costs no bytes in the file.
3749        let text = asm("struct s { unsigned f : 20; } x = { 0 };\n");
3750        assert!(text.contains("\t.bss\n"), "an object of zeroes is zeroes: {text}");
3751        assert!(text.contains("\nx:\n\t.space\t4\n"), "{text}");
3752    }
3753
3754    /// A string literal, which is a variable the program never named.
3755    #[test]
3756    fn a_string_literal_is_a_variable_with_a_name_no_program_could_write() {
3757        let text = asm("const char *f(void) { return \"hi\"; }\n");
3758        assert!(text.contains("\t.ascii\t\"hi\\000\"\n"), "{text}");
3759        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
3760        let label = text
3761            .lines()
3762            .find(|line| line.starts_with(".Lstr"))
3763            .unwrap_or_else(|| panic!("a label for the literal in\n{text}"));
3764        assert!(!text.contains(&format!(".globl\t{}", label.trim_end_matches(':'))), "{text}");
3765    }
3766
3767    /// A variable holding the address of another one, which is the only hole an image has in it.
3768    #[test]
3769    fn an_address_in_an_initializer_is_left_to_the_linker() {
3770        let source = "int counter;\nint *p = &counter;\n";
3771        let text = asm(source);
3772        assert!(text.contains("\np:\n\t.quad\tcounter\n"), "{text}");
3773        // And in the object it is eight zero bytes and a relocation, which is what the two paths
3774        // being one description is for.
3775        let bytes = obj(source);
3776        assert!(bytes.windows(8).any(|w| w == b"counter\0"), "the object has to name it");
3777    }
3778
3779    /// A const table of function pointers, which is the shape that made SQLite link with a warning.
3780    ///
3781    /// The table is const so nothing in the program writes it, but the addresses in it are not
3782    /// numbers a link knows, so the loader writes it once at startup. Putting it in `.rodata`
3783    /// leaves a relocation in a section that is never writable, and what the linker does about
3784    /// that is set `DT_TEXTREL` on the whole image and say so. `.data.rel.ro` is writable for
3785    /// exactly as long as the loader is writing it and read only afterwards, which is what the
3786    /// program asked for in the first place.
3787    #[test]
3788    fn a_constant_holding_an_address_goes_in_the_section_the_loader_may_write_once() {
3789        // Both names are `static` and both are defined here, so nothing else can be the one that
3790        // defines them and the linker may lay the table out in the first pages of the segment.
3791        let text = asm("static void a(void) {}\nstatic void b(void) {}\n\
3792             struct m { void (*x)(void); void (*y)(void); };\n\
3793             const struct m t = { a, b };\n");
3794        assert!(text.contains("\t.section\t.data.rel.ro.local,\"aw\",@progbits\n"), "{text}");
3795        assert!(text.contains("\nt:\n\t.quad\ta\n\t.quad\tb\n"), "{text}");
3796
3797        // One name this file only declares is enough to lose the `.local` half, because a name the
3798        // link resolves from somewhere else is one another object may turn out to define.
3799        let text =
3800            asm("void a(void);\nstruct m { void (*x)(void); };\nconst struct m t = { a };\n");
3801        assert!(text.contains("\t.section\t.data.rel.ro,\"aw\",@progbits\n"), "{text}");
3802
3803        // And a constant with no address in it stays exactly where it was.
3804        let text = asm("const int fixed = 7;\n");
3805        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
3806    }
3807
3808    /// A thread-local variable, which is the whole of one: the storage and the way to reach it.
3809    ///
3810    /// The two halves are in one test on purpose. Either one alone is worse than neither: a
3811    /// definition with no way to reach it is a variable nothing can read, and a reference with no
3812    /// definition behind it is the bug this pair was written to prevent, where a thread-local is
3813    /// read as though it were an ordinary global and every thread quietly shares one copy.
3814    #[test]
3815    fn a_thread_local_variable_is_storage_a_thread_gets_a_copy_of_and_an_offset_into_it() {
3816        let text = asm("_Thread_local int x = 1;\nint read(void) { return x; }\n");
3817        // The storage: the section the loader makes a copy of for every thread, and the symbol
3818        // type that makes a linker refuse an ordinary relocation aimed at it.
3819        assert!(text.contains("\t.section\t.tdata,\"awT\",@progbits\n"), "{text}");
3820        assert!(text.contains("\t.type\tx, @tls_object\n"), "{text}");
3821        // The way to reach it: how far into a thread's block it sits, out of the table, plus where
3822        // this thread's block is, out of the segment register.
3823        assert!(text.contains("x@GOTTPOFF(%rip)"), "{text}");
3824        assert!(text.contains("%fs:0"), "{text}");
3825    }
3826
3827    /// The second half of that on its own, which is what a program asks for when the number it
3828    /// wants is the thread rather than anything in it.
3829    ///
3830    /// rpmalloc writes this to find its per thread cache, and it is the whole of what stood
3831    /// between that library and a build. gcc 16 writes the same one instruction.
3832    #[test]
3833    fn the_address_of_this_thread_s_own_storage_is_read_out_of_the_segment_register() {
3834        let text = asm("void *here(void) { return __builtin_thread_pointer(); }\n");
3835        assert!(text.contains("movq\t%fs:0, "), "{text}");
3836        // No table slot and no addition, because there is no variable to find inside the block.
3837        assert!(!text.contains("GOTTPOFF"), "{text}");
3838    }
3839
3840    /// The four hints and the one thing that decides between them, which is the locality.
3841    ///
3842    /// A prefetch promises nothing, so what is checked here is the instruction rather than any
3843    /// effect: the program runs the same whichever of the four it gets, and the whole point of
3844    /// writing one is which. The four spellings are what gcc 16.2.0 writes for the same four
3845    /// programs, measured on x86-64 rather than read off a manual.
3846    ///
3847    /// The write hint is not one of them. `prefetchw` is not in the base instruction set and gcc
3848    /// writes it only when the command line says the part has it, so a prefetch for a write is the
3849    /// same instruction as a prefetch for a read, which is the fourth line here.
3850    #[test]
3851    fn a_prefetch_is_one_of_four_instructions_and_the_locality_is_what_picks() {
3852        for (locality, wanted) in
3853            [(0, "prefetchnta"), (1, "prefetcht2"), (2, "prefetcht1"), (3, "prefetcht0")]
3854        {
3855            let source =
3856                format!("void warm(void *p) {{ __builtin_prefetch(p, 0, {locality}); }}\n");
3857            let text = asm(&source);
3858            assert!(text.contains(&format!("\t{wanted}\t")), "locality {locality}: {text}");
3859        }
3860        // The one argument form, which means a read that wants all of the data afterwards.
3861        let text = asm("void warm(void *p) { __builtin_prefetch(p); }\n");
3862        assert!(text.contains("\tprefetcht0\t"), "{text}");
3863        // A prefetch for a write, which on a part nobody said has `prefetchw` is the same
3864        // instruction as the read above.
3865        let text = asm("void warm(void *p) { __builtin_prefetch(p, 1); }\n");
3866        assert!(text.contains("\tprefetcht0\t"), "{text}");
3867        assert!(!text.contains("prefetchw"), "{text}");
3868    }
3869
3870    /// The stop, which is the one instruction the machine is promised never to have a meaning for.
3871    ///
3872    /// What is checked is the instruction and not any effect, because the effect is a fault and a
3873    /// unit test has nowhere to take one. gcc 16.2.0 writes the same instruction for the same
3874    /// program, and it is not a call, which is the half that matters in a kernel and in a
3875    /// freestanding program: neither has an `abort` for a call to reach.
3876    ///
3877    /// The second half is the block going on after it. A statement written under a stop is
3878    /// compiled the way it would have been without one, so the addition is still there, and that
3879    /// is the front end declining to treat a stop as the end of a path.
3880    #[test]
3881    fn a_trap_is_the_instruction_the_machine_has_no_meaning_for() {
3882        let text = asm("void stop(void) { __builtin_trap(); }\n");
3883        assert!(text.contains("\tud2\n"), "{text}");
3884        assert!(!text.contains("\tcall"), "a stop is not a call to anything: {text}");
3885
3886        let text = asm("int stop(int a) { __builtin_trap(); return a + 1; }\n");
3887        assert!(text.contains("\tud2\n"), "{text}");
3888        assert!(text.contains("\taddl\t"), "the block goes on after a stop: {text}");
3889    }
3890
3891    /// The promise about the low bits of an address, whose value is the address.
3892    ///
3893    /// Nothing here reads an alignment fact about a value yet, so what the call leaves behind is
3894    /// its first argument and no instruction at all. The claim worth checking end to end is that
3895    /// the name is gone: a builtin nothing lowers reaches the assembler as a call to a name no
3896    /// object file defines, which is how this one used to fail to link out of glibc's string
3897    /// headers.
3898    ///
3899    /// The arguments behind the address are still evaluated, because gcc 16.2.0 evaluates them at
3900    /// every optimization level even though it has folded the call away. A constant has nothing to
3901    /// run and is dropped, and a call does, so the second half asks for the callee by name.
3902    #[test]
3903    fn assume_aligned_is_its_first_argument_and_keeps_the_rest() {
3904        let text = asm("void *aligned(char *p) { return __builtin_assume_aligned(p, 16); }\n");
3905        assert!(!text.contains("assume_aligned"), "{text}");
3906        assert!(!text.contains("\tcall"), "nothing is called for an alignment fact: {text}");
3907
3908        let source = "unsigned long width(void);\n\
3909                      void *aligned(char *p) { return __builtin_assume_aligned(p, width()); }\n";
3910        let text = asm(source);
3911        assert!(!text.contains("assume_aligned"), "{text}");
3912        assert!(text.contains("width"), "the argument that is not the answer still runs: {text}");
3913    }
3914
3915    /// Where a frame is, which on this machine is what the frame pointer holds.
3916    ///
3917    /// The first half is a function that would have kept no frame pointer at all, since it is a
3918    /// leaf with no locals, and keeps one because it asked where its frame is. The answer being
3919    /// `%rbp` rather than an offset off `%rsp` is the whole of the builtin at a depth of zero.
3920    ///
3921    /// The second half is the walk. Each link above zero is one load through the register the last
3922    /// one wrote, so a depth of two is two loads and a depth of three is three, which is what gcc
3923    /// 16.2.0 writes for the same programs at `-O2`.
3924    #[test]
3925    fn the_frame_address_is_the_frame_pointer_after_walking_that_many_links() {
3926        let text = asm("void *here(void) { return __builtin_frame_address(0); }\n");
3927        assert!(text.contains("pushq\t%rbp"), "a function that asks keeps a frame pointer: {text}");
3928        assert!(text.contains("movq\t%rbp, %rax"), "{text}");
3929        assert!(!text.contains("\tcall"), "a frame address is not a call to anything: {text}");
3930
3931        let walk = |depth: u32| {
3932            let source = format!("void *up(void) {{ return __builtin_frame_address({depth}); }}\n");
3933            asm(&source).matches("movq\t(%r").count()
3934        };
3935        assert_eq!(walk(1), 1, "one link is one load");
3936        assert_eq!(walk(3), 3, "three links are three loads");
3937    }
3938
3939    /// The address a frame returns to, which is one word above the frame the walk ended at.
3940    ///
3941    /// A word is eight bytes here and the `8(...)` is the whole claim: the call instruction pushed
3942    /// the return address and the prologue pushed the caller's frame pointer under it, so what the
3943    /// frame pointer points at is the link and what is above it is where control goes back to.
3944    /// gcc 16.2.0 writes `movq 8(%rbp), %rax` for the first of these, measured at `-O2`.
3945    ///
3946    /// The second half is the same walk the frame address does, with the load at the end of it
3947    /// reading one word further along rather than the register itself being the answer.
3948    #[test]
3949    fn the_return_address_is_one_word_above_the_frame_the_walk_ended_at() {
3950        let text = asm("void *back(void) { return __builtin_return_address(0); }\n");
3951        assert!(text.contains("pushq\t%rbp"), "a function that asks keeps a frame pointer: {text}");
3952        assert!(text.contains("movq\t8(%rbp), %rax"), "{text}");
3953        assert!(!text.contains("\tcall"), "a return address is not a call to anything: {text}");
3954
3955        let text = asm("void *back(void) { return __builtin_return_address(2); }\n");
3956        assert_eq!(text.matches("movq\t(%r").count(), 2, "two links are two loads: {text}");
3957        assert!(text.contains("movq\t8(%r"), "and the answer is above the last of them: {text}");
3958    }
3959
3960    /// A depth that is not a constant is refused, and so is one past the limit.
3961    ///
3962    /// The first is gcc's rule and not a convenience: what the call becomes is a walk that many
3963    /// links long, written out, so a number that is not known until the program runs has nothing
3964    /// to walk. gcc 16.2.0 says `invalid argument to '__builtin_return_address'` for the same
3965    /// program.
3966    ///
3967    /// The second is where this and gcc part company. gcc writes the walk however long it is, and
3968    /// this refuses a depth no program has a use for rather than filling an object file with loads
3969    /// that fault part way up.
3970    #[test]
3971    fn a_depth_that_is_not_a_small_constant_is_refused() {
3972        let mut opts = options();
3973        opts.emit = EmitKind::Ir;
3974        for source in [
3975            "void *up(int n) { return __builtin_return_address(n); }\n",
3976            "void *up(void) { return __builtin_frame_address(1000); }\n",
3977        ] {
3978            let messages = run(&opts, source).messages;
3979            let named = messages.iter().any(|m| m.contains("E0705"));
3980            assert!(named, "expected a refusal in {messages:?}");
3981        }
3982    }
3983
3984    /// Bytes off the frame, which is the stack pointer moving down and the answer being where it
3985    /// moved to.
3986    ///
3987    /// The rounding is the alignment: the size is taken up to the next sixteen before it is
3988    /// subtracted, so the pointer suits anything the program puts behind it. gcc 16.2.0 rounds the
3989    /// same way at `-O0` and spends a division doing it, which is the one place the two differ and
3990    /// is about how the rounding is written rather than about what it answers.
3991    ///
3992    /// There is no call anywhere in either program. An alloca that had reached the linker would
3993    /// have found the C library's, which is a real function with a real frame and is not what a
3994    /// program writing the builtin asked for.
3995    #[test]
3996    fn an_alloca_takes_the_bytes_off_the_stack_pointer_and_answers_where_they_are() {
3997        let text =
3998            asm("void use(void *p); void f(unsigned long n) { use(__builtin_alloca(n)); }\n");
3999        assert!(text.contains("andq\t$-16"), "the size is rounded up to sixteen: {text}");
4000        assert!(text.contains("subq\t%rdi, %rsp"), "and taken off the stack pointer: {text}");
4001        assert_eq!(text.matches("\tcall").count(), 1, "the only call is the one written: {text}");
4002
4003        // The plain name, which a program that declares it the way the C library does means the
4004        // same thing by. `gcc.c-torture/execute/20010122-1.c` is exactly this program.
4005        let plain = concat!(
4006            "extern void *alloca(__SIZE_TYPE__);\n",
4007            "void use(void *p);\n",
4008            "void f(unsigned long n) { use(alloca(n)); }\n",
4009        );
4010        let text = asm(plain);
4011        assert!(text.contains("subq\t%rdi, %rsp"), "the plain name is the same bytes: {text}");
4012        assert_eq!(text.matches("\tcall").count(), 1, "and is not a call either: {text}");
4013
4014        // And a program that means something of its own by the name keeps it, which is what the
4015        // declaration is looked at for.
4016        let own = concat!(
4017            "static void *alloca(unsigned long n) { return 0; }\n",
4018            "void *f(unsigned long n) { return alloca(n); }\n",
4019        );
4020        assert!(asm(own).contains("\tcall"), "a name the program took back is a call");
4021    }
4022
4023    /// A name nothing declared that the implementation knows the type of is declared with that
4024    /// type rather than with the `extern int f()` C89 6.3.2.2 writes down.
4025    ///
4026    /// That is gcc's rule and it is measurable: gcc 16.2.0 compiles an undeclared `alloca` with
4027    /// no call in it at all, and says `incompatible implicit declaration of built-in function`
4028    /// beside the implicit declaration warning. A C89 declaration would have made the call return
4029    /// an `int` and reach a function no C library defines, since every header that offers
4030    /// `alloca` offers it as a macro for the builtin. Four torture programs turn on it,
4031    /// `execute/20020314-1.c`, `20040223-1.c`, `941202-1.c` and `pr22061-1.c`, each of which
4032    /// calls `alloca` with nothing above it.
4033    ///
4034    /// The rule is the builtin table's rather than this one name's, so an undeclared `strlen` is
4035    /// the builtin too. What it is not is a declaration the program wrote that disagrees with the
4036    /// builtin's type, which gcc keeps and calls, and that was measured as well.
4037    #[test]
4038    fn a_builtin_the_program_never_declared_is_the_builtin_rather_than_the_one_c89_wrote_down() {
4039        // `-fpermissive`, because the implicit declaration itself is an error in every dialect
4040        // after C89 and the program would never get as far as a type without it. Each of the four
4041        // torture programs asks for either that or `-std=gnu89` on its own options line.
4042        let mut opts = options();
4043        opts.permissive = true;
4044        let undeclared = "void use(void *p);
4045void f(unsigned long n) { use(alloca(n)); }
4046";
4047        assert_eq!(
4048            run(&opts, undeclared).messages,
4049            [
4050                "/main.c:2:31: warning: implicit declaration of function 'alloca' [E0521]",
4051                "/main.c:2:31: warning: incompatible implicit declaration of built-in function \
4052                 'alloca' [E0713]",
4053            ]
4054        );
4055
4056        opts.emit = EmitKind::Asm;
4057        let text = run(&opts, undeclared).text().to_owned();
4058        assert!(text.contains("subq\t%rdi, %rsp"), "the bytes come off the stack: {text}");
4059        assert_eq!(text.matches("\tcall").count(), 1, "the only call is the one written: {text}");
4060
4061        // The table's rule and not this one name's, so a name whose whole answer is the library
4062        // function of the same name gets that function's type and still reaches it.
4063        let string = "unsigned long f(void) { return strlen(\"abc\"); }\n";
4064        let text = run(&opts, string).text().to_owned();
4065        assert!(text.contains("call\tstrlen"), "strlen is still a call: {text}");
4066
4067        // A declaration the program wrote is the program's, whatever the table says. gcc keeps
4068        // this one and writes the call, which is what makes the type worth looking at.
4069        let own = concat!(
4070            "static void *alloca(unsigned long n) { return 0; }\n",
4071            "void *f(unsigned long n) { return alloca(n); }\n",
4072        );
4073        assert!(asm(own).contains("\tcall"), "a name the program took back is a call");
4074    }
4075
4076    /// The bytes an alloca took live until the function returns and not until the end of the block
4077    /// the call was written in.
4078    ///
4079    /// That is what makes it different from a variable length array, and the way it is kept is that
4080    /// every scope open where the call was written stops giving the stack back. The second program
4081    /// is the mixed case: an array in the outer block and an alloca in the inner one, where the
4082    /// inner block gives nothing back either even though an array is in scope that ordinarily
4083    /// would. gcc 16.2.0 at `-O0` writes no restore at the end of either block, measured rather
4084    /// than read off the manual.
4085    #[test]
4086    fn the_bytes_an_alloca_took_are_still_there_at_the_end_of_the_block_that_took_them() {
4087        let inner = "{ use(__builtin_alloca(n)); }";
4088        for body in [inner.to_owned(), format!("int a[n]; {inner} use(a);")] {
4089            let source = format!("void use(void *p);\nvoid f(unsigned long n) {{ {body} }}\n");
4090            let text = asm(&source);
4091            // Every instruction that writes the stack pointer, which in a function that gives
4092            // nothing back is the alloca taking bytes and the epilogue putting the frame pointer
4093            // there. A restore would be a third kind, a move out of a register the save wrote.
4094            for line in text.lines().filter(|line| line.trim_end().ends_with(", %rsp")) {
4095                let taking = line.contains("subq");
4096                let leaving = line.contains("%rbp");
4097                assert!(taking || leaving, "nothing puts the stack back: {line} in {text}");
4098            }
4099        }
4100    }
4101
4102    /// Not a rewording of the check above: what the two paths agree about is the point.
4103    #[test]
4104    fn the_object_and_the_listing_are_two_spellings_of_one_compilation() {
4105        // A call, because it is the one thing whose spelling in the two differs completely: the
4106        // listing writes a name and the object writes four zero bytes and a relocation asking the
4107        // linker for the same name. If either path had lost the callee, one of these would fail.
4108        let source = "int callee(void); int g(void) { return callee(); }\n";
4109        let bytes = obj(source);
4110        assert!(
4111            bytes.windows(7).any(|w| w == b"callee\0"),
4112            "the object has to name the callee for the linker to find it"
4113        );
4114        let text = asm(source);
4115        assert!(text.contains("\tcall\tcallee\n"), "{text}");
4116    }
4117
4118    /// What a file of a link contributes is an object, and the default emit is a link.
4119    ///
4120    /// This is here because getting it wrong is silent in the worst way: an empty file is a valid
4121    /// empty linker script, so a link fed one gets as far as reporting every symbol of the file as
4122    /// undefined and says nothing about the compilation that produced nothing.
4123    #[test]
4124    fn compiling_for_an_executable_produces_an_object_and_not_a_dump() {
4125        let mut opts = options();
4126        // What a command line with no `-c` and no `-S` on it asks for.
4127        opts.emit = EmitKind::Executable;
4128        let result = run(&opts, "int main(void) { return 0; }\n");
4129        assert_eq!(result.messages, Vec::<String>::new());
4130        match result.artifact {
4131            Artifact::Object { bytes, .. } => assert_eq!(&bytes[..4], b"\x7fELF"),
4132            other => panic!("expected an object, got {other:?}"),
4133        }
4134    }
4135
4136    /// A target with a back end but no object writer says so rather than writing the wrong file.
4137    #[test]
4138    fn a_platform_with_no_object_writer_is_said_so_rather_than_written_as_elf() {
4139        let mut opts = options();
4140        opts.emit = EmitKind::Object;
4141        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
4142        let result = run(&opts, "int f(void) { return 0; }\n");
4143        assert!(result.failed(), "an object nobody can read is worse than a message");
4144        assert!(
4145            result.messages.iter().any(|m| m.contains("no object writer")),
4146            "{:?}",
4147            result.messages
4148        );
4149    }
4150
4151    /// The IR of `source`, insisting that it compiled cleanly.
4152    fn ir(source: &str) -> String {
4153        let mut opts = options();
4154        opts.emit = EmitKind::Ir;
4155        let result = run(&opts, source);
4156        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4157        result.text().to_owned()
4158    }
4159
4160    /// What was said about `source`, insisting that something was.
4161    fn errors(source: &str) -> Vec<String> {
4162        let mut opts = options();
4163        opts.emit = EmitKind::Ir;
4164        let result = run(&opts, source);
4165        assert!(result.failed(), "expected this to be refused:\n{source}");
4166        result.messages
4167    }
4168
4169    /// The body of the one function in `source`, which is what most of these are about.
4170    fn body(source: &str) -> String {
4171        let text = ir(source);
4172        let (_, rest) = text.split_once("{\n").expect("a function definition");
4173        let (body, _) = rest.rsplit_once("}\n").expect("a function definition");
4174        body.to_owned()
4175    }
4176
4177    /// What `-fgnu89-inline` is for, seen at the only place it shows: whether a body reached the
4178    /// module or only a declaration did.
4179    ///
4180    /// The C99 reading is the one an inline definition is written for and is not being changed
4181    /// here. What the flag is for is a program written before C99 swapped the two, which relies on
4182    /// `inline` alone leaving something behind for another unit to call, and there are twelve of
4183    /// those in the GCC torture suite alone.
4184    #[test]
4185    fn gnu89_inline_is_what_decides_whether_a_bare_inline_definition_reaches_the_module() {
4186        let source = "inline int f(int x) { return x + 1; }\n";
4187        let with = |flag: bool| {
4188            let mut opts = options();
4189            opts.emit = EmitKind::Ir;
4190            opts.gnu89_inline = flag;
4191            let result = run(&opts, source);
4192            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
4193            result.text().to_owned()
4194        };
4195
4196        // Under C's reading the module holds the declaration and the calls in this unit go to
4197        // whatever definition another unit has, which is C 6.7.4p7 and is what gcc does too.
4198        assert!(!with(false).contains("block0"), "no body: {}", with(false));
4199
4200        // Under GNU's it is an ordinary external definition, so the body is there and the symbol
4201        // is one the linker can resolve against.
4202        assert!(with(true).contains("block0"), "a body: {}", with(true));
4203    }
4204
4205    /// Every shape that reads or writes through a C type names that type.
4206    ///
4207    /// The tree itself is `rucc_lower::aliasing`'s and is tested there. What this is about is that
4208    /// the walk reaches it from every shape a program actually writes, since a node on the scalar
4209    /// load and nothing on the member load would be a layer that answers for a third of the
4210    /// accesses in a program and is not worth having.
4211    #[test]
4212    fn an_access_through_a_type_names_the_type_it_went_through() {
4213        let source = "\
4214struct s { int a; float b; };\n\
4215union u { int i; float f; };\n\
4216int scalar(int *p) { return *p; }\n\
4217float member(struct s *p) { p->a = 1; return p->b; }\n\
4218int element(int *a, long i) { return a[i]; }\n\
4219float through_a_union(union u *p) { p->i = 1; return p->f; }\n";
4220        let text = ir(source);
4221        assert!(text.contains(r#"!0 = tbaa "char""#), "the root: {text}");
4222        assert!(text.contains(r#"tbaa "int", parent !0"#), "int under it: {text}");
4223        assert!(text.contains(r#"tbaa "float", parent !0"#), "float under it: {text}");
4224        // One per access, and a function whose accesses all go through one type says so once per
4225        // access rather than once per function.
4226        let named = text.lines().filter(|line| line.contains(", tbaa !")).count();
4227        assert_eq!(named, 6, "six accesses: {text}");
4228    }
4229
4230    /// `-fno-strict-aliasing` is the front end leaving the name off.
4231    ///
4232    /// Nothing asks the alias analysis anything yet, so no program compiles differently for having
4233    /// passed this today. What this test is for is the day one does: the flag has to be the
4234    /// absence of the names rather than a condition somewhere downstream, since that is the only
4235    /// version of it that a pass added later cannot forget about.
4236    #[test]
4237    fn turning_strict_aliasing_off_leaves_the_type_off_every_access() {
4238        let source = "int punned(float *f, int *i) { *i = 1; *f = 2.0f; return *i; }\n";
4239        let mut opts = options();
4240        opts.emit = EmitKind::Ir;
4241        opts.strict_aliasing = false;
4242        let result = run(&opts, source);
4243        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
4244        let text = result.text().to_owned();
4245        assert!(!text.contains("tbaa"), "not even the root: {text}");
4246    }
4247
4248    /// `return;` from a function that promised a value, which only C89 lets through and which
4249    /// therefore only reaches the IR builder under that dialect.
4250    ///
4251    /// Zero goes back. The alternatives are worse: an empty return list builds a `ret` the
4252    /// verifier refuses, which is what a torture case found, and `unreachable` would be a claim
4253    /// that the branch reaching this never runs, which is a claim about the program rather than
4254    /// about the value and lets the optimizer delete the path that led here.
4255    #[test]
4256    fn a_bare_return_from_a_function_that_promised_a_value_gives_back_a_zero() {
4257        let mut opts = options();
4258        opts.emit = EmitKind::Ir;
4259        opts.std = Std::C89;
4260        let compiled = |source: &str| {
4261            let result = run(&opts, source);
4262            assert_eq!(result.messages, Vec::<String>::new(), "C89 has nothing to say about this");
4263            result.text().to_owned()
4264        };
4265
4266        let text = compiled("int f(int x) { if (x) return; return 3; }\n");
4267        assert!(text.contains("iconst.i32 0\n    return"), "zero goes back: {text}");
4268        assert!(!text.contains("unreachable"), "the branch that reached it is kept: {text}");
4269
4270        // A floating point return needs the constant of its own kind rather than an integer one.
4271        let text = compiled("double f(int x) { if (x) return; return 1.0; }\n");
4272        assert!(text.contains("fconst.f64 0x0\n    return"), "a float zero goes back: {text}");
4273    }
4274
4275    /// What C89 6.3.2.2 declares for a call to a name nothing declared, seen in the IR rather than
4276    /// in what was said about it.
4277    ///
4278    /// `extern int f();`, so the call gives back an `int` and its arguments are promoted rather
4279    /// than converted to parameters there are none of. The declaration lasts for the file, which
4280    /// is what makes a second call to the same name ordinary and is why gcc says this once per
4281    /// file rather than once per call.
4282    #[test]
4283    fn a_call_to_a_name_nothing_declared_declares_it_as_c89_said_to() {
4284        let mut opts = options();
4285        opts.emit = EmitKind::Ir;
4286        opts.std = Std::C89;
4287        let compiled = |source: &str| {
4288            let result = run(&opts, source);
4289            assert_eq!(result.messages, Vec::<String>::new(), "C89 has nothing to say about this");
4290            result.text().to_owned()
4291        };
4292
4293        // An `int` back, which is the whole of what the implicit declaration says.
4294        let text = compiled("int f(void) { return g(); }\n");
4295        assert!(text.contains("call @g"), "the call is to the name that was written: {text}");
4296        assert!(text.contains("i32"), "and it gives back an int: {text}");
4297
4298        // No prototype, so a `char` argument arrives promoted to `int` the way an argument to a
4299        // function whose parameters are unspecified does.
4300        let text = compiled("int f(char c) { return g(c); }\n");
4301        assert!(text.contains("sext.i32"), "the argument is promoted: {text}");
4302
4303        // A name written as a value rather than called is still undeclared, since the rule is
4304        // about a call and nothing else.
4305        let mut opts = options();
4306        opts.std = Std::C89;
4307        let said = run(&opts, "int f(void) { return h; }\n").messages.join("\n");
4308        assert!(said.contains("'h' undeclared"), "not a call, so not declared: {said}");
4309    }
4310
4311    /// A file that calls a name above the definition of it, which is the shape the implicit
4312    /// declaration has to survive rather than swallow.
4313    ///
4314    /// The definition merges into the declaration the call already made rather than making a
4315    /// second one, so a declaration the tree does not carry at the top level takes the definition
4316    /// down with it: the body is attached to a node nothing walks and no function comes out.
4317    /// Nothing about the call itself looks wrong when that happens, and the program gets to the
4318    /// linker before anyone finds out, which is where `execute/cmpsi-1.c` in the torture suite
4319    /// found it, as an undefined reference to a name defined eleven lines further down.
4320    #[test]
4321    fn a_name_called_before_it_is_defined_still_gets_its_definition() {
4322        let mut opts = options();
4323        opts.emit = EmitKind::Ir;
4324        opts.std = Std::C89;
4325        let text = run(&opts, "int f(void) { return dummy(); }\ndummy () { return 7; }\n")
4326            .text()
4327            .to_owned();
4328        assert!(text.contains("func @f()"), "the caller is there: {text}");
4329        assert!(text.contains("func @dummy"), "and so is what it calls: {text}");
4330        assert!(text.contains("iconst.i32 7"), "with the body it was given: {text}");
4331    }
4332
4333    /// An old style definition whose parameter is narrower than what a call passes it.
4334    ///
4335    /// There is no prototype for a call to convert its argument to, so the argument is promoted
4336    /// and an `int` arrives for a parameter the body reads as an `unsigned char`. The entry block
4337    /// is where the two meet, and gcc writes the same pair of instructions there: store the low
4338    /// byte, read it back widened. `execute/950605-1.c` in the torture suite calls `f(-1)` and
4339    /// checks the parameter against `0xFF`, which is the difference between converting and not.
4340    #[test]
4341    fn an_old_style_parameter_is_converted_from_what_the_call_promoted_it_to() {
4342        let mut opts = options();
4343        opts.emit = EmitKind::Ir;
4344        opts.std = Std::C89;
4345        let compiled = |source: &str| run(&opts, source).text().to_owned();
4346
4347        let text = compiled("f (c) unsigned char c; { return c; }\n");
4348        assert!(text.contains("func @f(i32"), "an int arrives: {text}");
4349        assert!(text.contains("trunc.i8"), "and is cut down to what was declared: {text}");
4350        assert!(text.contains("zext.i32"), "then read back unsigned: {text}");
4351
4352        // A `short` is the same shape and signed, so it comes back the other way.
4353        let text = compiled("f (s) short s; { return s; }\n");
4354        assert!(text.contains("trunc.i16"), "cut down: {text}");
4355        assert!(text.contains("sext.i32"), "and read back signed: {text}");
4356
4357        // A `float` parameter is promoted to `double`, and without the conversion the multiply
4358        // below has one f64 operand and one f32, which the verifier refuses as invalid IR.
4359        let text = compiled("f (x) float x; { return x * 2; }\n");
4360        assert!(text.contains("func @f(f64"), "a double arrives: {text}");
4361        assert!(text.contains("fptrunc.f32"), "and is narrowed to the float: {text}");
4362
4363        // A parameter a prototype named arrives as itself and nothing is converted, which is the
4364        // case this must not have changed.
4365        let text = compiled("int f(unsigned char c) { return c; }\n");
4366        assert!(text.contains("func @f(i8)"), "the declared type arrives: {text}");
4367        assert!(!text.contains("trunc"), "so there is nothing to cut down: {text}");
4368    }
4369
4370    /// The six rules gcc 14 turned from a warning into an error, and the three answers each one
4371    /// gets depending on the dialect and on `-fpermissive`.
4372    ///
4373    /// The table is a measurement rather than a reading of the release notes. Six files, one per
4374    /// rule, put through gcc 16.2.0 on x86-64 Linux under each of the four command lines below
4375    /// with no `-W` flags on any of them, and what came back is what is written here. The three
4376    /// rules that say nothing under C89 are the three C89 did not have, and the three that warn
4377    /// there were constraint violations then as well.
4378    #[test]
4379    fn the_rules_gcc_promoted_are_decided_by_the_dialect_and_by_fpermissive() {
4380        // `-std=gnu89`, `-std=gnu17`, `-std=gnu17 -fpermissive`, and `-std=gnu23`.
4381        let modes = [(Std::C89, false), (Std::C17, false), (Std::C17, true), (Std::C23, false)];
4382        let cases = [
4383            ("static counted;\n", ["", "error", "warning", "error"]),
4384            ("int f(void) { return g(); }\n", ["", "error", "warning", "error"]),
4385            ("int f(x) { return x; }\n", ["", "error", "warning", "error"]),
4386            ("int *p;\nvoid h(void) { p = 1; }\n", ["warning", "error", "warning", "error"]),
4387            (
4388                "char *q;\nint *r;\nvoid k(void) { r = q; }\n",
4389                ["warning", "error", "warning", "error"],
4390            ),
4391            ("int f(void) { return; }\n", ["", "error", "warning", "error"]),
4392            ("void g(void) { return 1; }\n", ["warning", "error", "warning", "error"]),
4393        ];
4394
4395        for (source, wanted) in cases {
4396            for (&(std, permissive), wanted) in modes.iter().zip(wanted) {
4397                let mut opts = options();
4398                opts.std = std;
4399                opts.permissive = permissive;
4400                let said = run(&opts, source).messages.join("\n");
4401                let severity = if said.contains(": error: ") {
4402                    "error"
4403                } else if said.contains(": warning: ") {
4404                    "warning"
4405                } else {
4406                    ""
4407                };
4408                let how = if permissive { " -fpermissive" } else { "" };
4409                assert_eq!(
4410                    severity,
4411                    wanted,
4412                    "under -std={}{how}, {source} was answered with `{said}`",
4413                    std.as_str()
4414                );
4415                if wanted.is_empty() {
4416                    assert!(said.is_empty(), "nothing to say, but said `{said}`");
4417                }
4418            }
4419        }
4420    }
4421
4422    /// A first argument that is not a list, which the four variadic operators answer in two ways.
4423    ///
4424    /// gcc has `va_arg` as an operator, since it takes a type name and no function can, and the
4425    /// other three as builtin functions taking the address of a list. The difference is not a
4426    /// naming one: the operator's complaint is its own and is an error under every dialect, and
4427    /// the three functions go through the ordinary rule about an argument of the wrong type,
4428    /// which is one of the rules the table above is about. The same four command lines through
4429    /// gcc 16.2.0 on x86-64 Linux is where these came from.
4430    #[test]
4431    fn the_three_variadic_builtins_answer_a_bad_list_the_way_a_call_answers_a_bad_argument() {
4432        let modes = [(Std::C89, false), (Std::C17, false), (Std::C17, true), (Std::C23, false)];
4433        let cases = [
4434            (
4435                "int f(int n, ...) { char *p; return __builtin_va_arg(p, int); }\n",
4436                "first argument to 'va_arg' not of type 'va_list'",
4437                ["error", "error", "error", "error"],
4438            ),
4439            (
4440                "void f(int n, ...) { char *p; __builtin_va_start(p, n); }\n",
4441                "passing argument 1 of '__builtin_va_start' from incompatible pointer type",
4442                ["warning", "error", "warning", "error"],
4443            ),
4444            (
4445                "void f(int n, ...) { int x; __builtin_va_end(x); }\n",
4446                "passing argument 1 of '__builtin_va_end' makes pointer from integer without a \
4447                 cast",
4448                ["warning", "error", "warning", "error"],
4449            ),
4450            (
4451                "void f(int n, ...) { __builtin_va_list a; char *p; __builtin_va_copy(a, p); }\n",
4452                "passing argument 2 of '__builtin_va_copy' from incompatible pointer type",
4453                ["warning", "error", "warning", "error"],
4454            ),
4455        ];
4456
4457        for (source, message, wanted) in cases {
4458            for (&(std, permissive), wanted) in modes.iter().zip(wanted) {
4459                let mut opts = options();
4460                opts.std = std;
4461                opts.permissive = permissive;
4462                let said = run(&opts, source).messages.join("\n");
4463                let how = if permissive { " -fpermissive" } else { "" };
4464                assert!(
4465                    said.contains(&format!(": {wanted}: {message}")),
4466                    "under -std={}{how}, {source} was answered with `{said}`",
4467                    std.as_str()
4468                );
4469            }
4470        }
4471    }
4472
4473    /// The IR of `source` at one safety tier, insisting that it compiled cleanly.
4474    fn safe_ir(tier: rucc_session::Safety, source: &str) -> String {
4475        let mut opts = options();
4476        opts.emit = EmitKind::Ir;
4477        opts.safety = tier;
4478        let result = run(&opts, source);
4479        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4480        result.text().to_owned()
4481    }
4482
4483    const READS_THROUGH_A_POINTER: &str = "int read(int *p) { return p[1]; }\n";
4484
4485    /// The IR for a source built with a tier and a padding mode.
4486    fn padded_ir(padding: Padding, source: &str) -> String {
4487        let mut opts = options();
4488        opts.emit = EmitKind::Ir;
4489        opts.safety = rucc_session::Safety::Detect;
4490        opts.padding = padding;
4491        let result = run(&opts, source);
4492        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4493        result.text().to_owned()
4494    }
4495
4496    const FILLS_A_RECORD_A_MEMBER_AT_A_TIME: &str = "struct padded { char tag; int value; };\n\
4497         void fill(struct padded *p) { p->tag = 1; p->value = 2; }\n";
4498
4499    #[test]
4500    fn a_record_filled_a_member_at_a_time_comes_out_whole_when_padding_does_not_participate() {
4501        // Section 9.3 of document 09, and the reason the default is the one it gives library code.
4502        // Four bytes from the `char` and four from the `int` is the whole of an eight byte record,
4503        // so the `memcmp` or the hash or the `write` that reads it back is not refused.
4504        let text = padded_ir(Padding::Ignored, FILLS_A_RECORD_A_MEMBER_AT_A_TIME);
4505        assert_eq!(text.matches("owns 4").count(), 2, "{text}");
4506    }
4507
4508    #[test]
4509    fn a_store_says_only_what_it_wrote_when_padding_does_participate() {
4510        // The kernel profile's default, which is section 9.3's actual rule: the padding stays
4511        // unwritten and the read of the record that would leak it is the one that reports.
4512        let text = padded_ir(Padding::Tracked, FILLS_A_RECORD_A_MEMBER_AT_A_TIME);
4513        assert!(!text.contains("owns"), "{text}");
4514    }
4515
4516    #[test]
4517    fn a_member_of_a_union_owns_nothing_after_it() {
4518        // The bytes after a short member of a union belong to a longer member rather than to
4519        // padding, and saying a store through the short one wrote them would be saying the longer
4520        // one holds a value nobody put there.
4521        let text = padded_ir(
4522            Padding::Ignored,
4523            "union u { char tag; long wide; };\nvoid fill(union u *p) { p->tag = 1; }\n",
4524        );
4525        assert!(!text.contains("owns"), "{text}");
4526    }
4527
4528    #[test]
4529    fn an_inner_records_trailing_padding_reaches_the_outer_records() {
4530        // The composition. `in` owns four bytes of `outer` because `x` starts there, and `c` is
4531        // the last member of `in`, so what it owns is what `in` owns rather than its own one byte.
4532        // Without that the three bytes between them would stay unwritten and a read of the whole
4533        // thing would report.
4534        let text = padded_ir(
4535            Padding::Ignored,
4536            "struct inner { char c; };\n\
4537             struct outer { struct inner in; int x; };\n\
4538             void fill(struct outer *p) { p->in.c = 1; p->x = 2; }\n",
4539        );
4540        assert_eq!(text.matches("owns 4").count(), 2, "{text}");
4541    }
4542
4543    #[test]
4544    fn a_build_that_did_not_ask_for_the_monitor_is_compiled_the_way_it_always_was() {
4545        // This is the load bearing test of the whole flag. The monitor is being built in the open
4546        // and every build in the world is compiled by this compiler with the flag absent, so a
4547        // check that leaked into that path would be a regression for everybody.
4548        let text = ir(READS_THROUGH_A_POINTER);
4549        assert!(!text.contains("check_"), "{text}");
4550        assert!(!text.contains("cap_of"), "{text}");
4551    }
4552
4553    #[test]
4554    fn asking_for_a_tier_puts_the_checks_in_before_the_optimizer_sees_them() {
4555        let text = safe_ir(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
4556        assert!(text.contains("cap_of"), "{text}");
4557        assert!(text.contains("check_bounds"), "{text}");
4558        assert!(text.contains("check_live"), "{text}");
4559        // The subscript is address arithmetic, so J2 applies to it as well as J1.
4560        assert!(text.contains("check_deriv"), "{text}");
4561        // And the read names a type, so it asks the type plane about the bytes as well.
4562        assert!(text.contains("check_type"), "{text}");
4563    }
4564
4565    #[test]
4566    fn the_three_tiers_that_are_not_off_all_check_the_same_accesses_so_far() {
4567        // What separates them is the reporter and the boundary, which are milestones S2 and S3.
4568        // Pinning it here means the day they stop agreeing, this test says so rather than the
4569        // difference going unnoticed.
4570        let detect = safe_ir(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
4571        for tier in [rucc_session::Safety::Enforce, rucc_session::Safety::Kernel] {
4572            assert_eq!(safe_ir(tier, READS_THROUGH_A_POINTER), detect, "{tier}");
4573        }
4574    }
4575
4576    /// The safety summary of `source` at one tier, insisting that it compiled cleanly.
4577    fn summary(tier: rucc_session::Safety, source: &str) -> String {
4578        let mut opts = options();
4579        opts.emit = EmitKind::SafetySummary;
4580        opts.safety = tier;
4581        let result = run(&opts, source);
4582        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4583        result.text().to_owned()
4584    }
4585
4586    #[test]
4587    fn the_summary_counts_the_checks_that_went_in_and_the_ones_still_standing() {
4588        let text = summary(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
4589        assert!(text.contains("\"tier\": \"detect\""), "{text}");
4590        // One load, so one of each of the two access checks, and the subscript is a derivation.
4591        assert!(
4592            text.contains("\"bounds\": { \"emitted\": 1, \"remaining\": 1, \"discharged\": 0 }"),
4593            "{text}"
4594        );
4595        assert!(
4596            text.contains(
4597                "\"derivation\": { \"emitted\": 1, \"remaining\": 1, \"discharged\": 0 }"
4598            ),
4599            "{text}"
4600        );
4601    }
4602
4603    #[test]
4604    fn a_build_without_the_monitor_summarises_as_a_build_with_no_checks_in_it() {
4605        // Which is the honest summary rather than an error. A build system that emits a summary
4606        // for every unit should get one for the units nobody asked to instrument too, and the
4607        // zeroes are what say that the guarantee over that file is nothing at all.
4608        let text = summary(rucc_session::Safety::Off, READS_THROUGH_A_POINTER);
4609        assert!(text.contains("\"tier\": \"off\""), "{text}");
4610        assert!(
4611            text.contains("\"bounds\": { \"emitted\": 0, \"remaining\": 0, \"discharged\": 0 }"),
4612            "{text}"
4613        );
4614    }
4615
4616    #[test]
4617    fn a_call_the_boundary_models_is_counted_apart_from_one_it_does_not() {
4618        let text = summary(
4619            rucc_session::Safety::Detect,
4620            "void *memcpy(void *, const void *, unsigned long);\n\
4621             int puts(const char *);\n\
4622             void f(char *d, char *s) { memcpy(d, s, 4); puts(d); }\n",
4623        );
4624        assert!(text.contains("\"interposed\": 1"), "{text}");
4625        assert!(text.contains("\"puts\""), "{text}");
4626        // The wrapper it was pointed at is ours, so it is not on the list of things this build
4627        // failed to model. Counting it there would make instrumenting a file look worse than
4628        // leaving it alone.
4629        assert!(!text.contains("__rucc_wrap_memcpy\""), "{text}");
4630    }
4631
4632    #[test]
4633    fn an_address_taken_of_a_library_function_is_counted_the_way_a_call_to_one_is() {
4634        // The shape SQLite's syscall table has, cut down to two rows. `memcpy` has a wrapper so the
4635        // table holds the wrapper's address and the build modelled it; `puts` has none, so what the
4636        // table holds is the real function and the build did not, and section 10.1 says the one it
4637        // did not is named rather than passed over.
4638        let text = summary(
4639            rucc_session::Safety::Detect,
4640            "void *memcpy(void *, const void *, unsigned long);\n\
4641             int puts(const char *);\n\
4642             void *table[2] = { (void *)memcpy, (void *)puts };\n\
4643             void *f(int i) { return table[i]; }\n",
4644        );
4645        assert!(text.contains("\"interposed\": 1"), "{text}");
4646        assert!(text.contains("\"puts\""), "{text}");
4647        assert!(!text.contains("\"memcpy\""), "{text}");
4648    }
4649
4650    #[test]
4651    fn the_two_directions_a_pointer_crosses_the_boundary_are_counted_apart() {
4652        // `f` is a name the linker can bind to and takes a pointer, so a pointer arrives there.
4653        // `notes_open` is a library this build did not instrument, so a pointer comes back from
4654        // it. Both are crossings and neither is the other, which is why there are two numbers.
4655        let text = summary(
4656            rucc_session::Safety::Detect,
4657            "void *notes_open(void);\n\
4658             char *f(char *p) { char *q = notes_open(); return q ? q : p; }\n",
4659        );
4660        assert!(text.contains("\"crossings\": { \"entered\": 1, \"returned\": 1 }"), "{text}");
4661        assert!(text.contains("\"notes_open\""), "{text}");
4662    }
4663
4664    #[test]
4665    fn a_static_function_nobody_takes_the_address_of_is_not_a_crossing() {
4666        // Nothing outside the file can reach it, so a witness on its parameters would be counting
4667        // a crossing that does not happen.
4668        let text = summary(
4669            rucc_session::Safety::Detect,
4670            "static int len(const char *p) { return p ? 1 : 0; }\n\
4671             int f(void) { return len(\"x\"); }\n",
4672        );
4673        assert!(text.contains("\"crossings\": { \"entered\": 0, \"returned\": 0 }"), "{text}");
4674    }
4675
4676    /// The granule report for `source`, insisting that it compiled cleanly.
4677    fn granules(source: &str) -> String {
4678        let mut opts = options();
4679        opts.emit = EmitKind::TypeGranules;
4680        let result = run(&opts, source);
4681        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4682        result.text().to_owned()
4683    }
4684
4685    #[test]
4686    fn the_granule_report_names_every_record_and_both_keyings() {
4687        let text = granules(
4688            "struct hot { char *p; int a; int b; };\n\
4689             int f(struct hot *h) { return h->a; }\n",
4690        );
4691        assert!(text.contains("struct hot"), "{text}");
4692        // Both keyings are reported because which types count as one is a decision the design
4693        // has not made yet, and a report that picked one would be hiding the cost of the other.
4694        assert!(text.contains("every type distinct"), "{text}");
4695        assert!(text.contains("every pointer one type"), "{text}");
4696        assert!(text.contains("budget"), "{text}");
4697    }
4698
4699    #[test]
4700    fn a_record_nothing_uses_is_still_measured() {
4701        // The measurement is about what a program declares, not about what it runs, so a type
4702        // that is only ever declared still costs the plane whatever its layout costs.
4703        let text = granules("struct unused { long a; double b; };\nint f(void) { return 0; }\n");
4704        assert!(text.contains("struct unused"), "{text}");
4705    }
4706
4707    #[test]
4708    fn the_granule_report_stops_before_anything_is_lowered() {
4709        // A layout is settled at the closing brace, so lowering the function bodies would take
4710        // minutes on an amalgamation and answer nothing. The evidence that it stops is that a
4711        // body the back end has no way to compile still produces a report.
4712        let text = granules(
4713            "struct wide { long double d; };\n\
4714             long double f(long double x) { return x * x; }\n",
4715        );
4716        assert!(text.contains("struct wide"), "{text}");
4717    }
4718
4719    #[test]
4720    fn a_witness_reaches_the_assembler_as_a_call_to_the_runtime() {
4721        // The count only means anything if the call is really there, and a summary saying one is
4722        // there is not evidence that the back end emitted it.
4723        let text = safe_asm(rucc_session::Safety::Detect, "char *f(char *p) { return p; }\n");
4724        assert!(text.contains("\tcall\t__rucc_cap_witness\n"), "{text}");
4725    }
4726
4727    #[test]
4728    fn a_pointer_turned_into_an_integer_is_on_the_trust_set() {
4729        let text = summary(
4730            rucc_session::Safety::Detect,
4731            "unsigned long f(int *p) { return (unsigned long) p; }\n",
4732        );
4733        assert!(text.contains("\"exposed\": 1"), "{text}");
4734    }
4735
4736    /// The assembly of `source` at one safety tier, insisting that it compiled cleanly.
4737    fn safe_asm(tier: rucc_session::Safety, source: &str) -> String {
4738        let mut opts = options();
4739        opts.emit = EmitKind::Asm;
4740        opts.safety = tier;
4741        let result = run(&opts, source);
4742        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4743        result.text().to_owned()
4744    }
4745
4746    #[test]
4747    fn a_check_reaches_the_assembler_as_a_call_to_the_runtime() {
4748        let text = safe_asm(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
4749        assert!(text.contains("\tcall\t__rucc_check_bounds\n"), "{text}");
4750        assert!(text.contains("\tcall\t__rucc_check_live\n"), "{text}");
4751        assert!(text.contains("\tcall\t__rucc_check_deriv\n"), "{text}");
4752        // The type check and the init check of one read reach the assembler as the one call that
4753        // asks both planes about it. `rucc_safety::lower::partner` is what recognises the pair.
4754        assert!(text.contains("\tcall\t__rucc_check_typed_init\n"), "{text}");
4755    }
4756
4757    #[test]
4758    fn every_check_that_reached_the_assembler_has_a_row_describing_it() {
4759        // Four calls and four descriptors, each in the section the runtime's reporter reads. The
4760        // width is `rucc_safety::lower::WIDTH` and the row is `rucc_safe_rt::fail::Descriptor`, and
4761        // the two agreeing is what makes the address a check is handed mean anything. Four rather
4762        // than five because the read's two plane questions are one call carrying one row, which the
4763        // two of them can share because a type check's row and an init check's row are identical.
4764        let text = safe_asm(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
4765        let section = format!("\t.section\t{},", rucc_safety::SECTION);
4766        assert_eq!(text.matches(&section).count(), 4, "{text}");
4767        for index in 0..4 {
4768            let name = format!("__rucc_safety_desc_{index}");
4769            // Defined once and referenced once, because a descriptor nothing points at describes
4770            // nothing and a reference with no definition does not link.
4771            assert!(text.contains(&format!("{name}:\n")), "{text}");
4772            assert!(text.contains(&format!("{name}(%rip)")), "{text}");
4773        }
4774        assert!(!text.contains("__rucc_safety_desc_4"), "{text}");
4775    }
4776
4777    /// `__builtin_constant_p` is answered in the front end and never reaches the IR.
4778    ///
4779    /// gcc folds it after optimization, so its answer for an argument that is not written as a
4780    /// constant can differ between `-O0` and `-O2`. What is checked here is the front end's
4781    /// answer, which is the same at every level, and the four cases where gcc gives the same
4782    /// answer at both levels are the ones measured on gcc 16: a literal is one, a variable is
4783    /// zero, a string literal is one and the address of an object is zero.
4784    #[test]
4785    fn builtin_constant_p_is_folded_where_it_is_written_rather_than_called() {
4786        let text = ir(concat!(
4787            "int g;\n",
4788            "int a = __builtin_constant_p(1);\n",
4789            "int b = __builtin_constant_p(g);\n",
4790            "int c = __builtin_constant_p(\"abc\");\n",
4791            "int d = __builtin_constant_p(&g);\n",
4792            "int e = __builtin_constant_p(1.5);\n",
4793            "int h = __builtin_choose_expr(__builtin_constant_p(3), 11, 22);\n",
4794        ));
4795        assert!(text.contains("global @a : i32 = 1,"), "{text}");
4796        assert!(text.contains("global @b : i32 = 0,"), "{text}");
4797        assert!(text.contains("global @c : i32 = 1,"), "{text}");
4798        assert!(text.contains("global @d : i32 = 0,"), "{text}");
4799        assert!(text.contains("global @e : i32 = 1,"), "{text}");
4800        assert!(text.contains("global @h : i32 = 11,"), "{text}");
4801        assert!(!text.contains("__builtin_constant_p"), "it is not a call to anything:\n{text}");
4802
4803        // The argument is not evaluated, which is what gcc does with it as well, so `i` is
4804        // still zero. The second constant is the answer, which nothing reads and which the
4805        // first pass that looks for dead code will take out.
4806        let text = body("int f(void) { int i = 0; __builtin_constant_p(i++); return i; }\n");
4807        assert_eq!(text, "block0:\n    %0 = iconst.i32 0\n    %1 = iconst.i32 0\n    return %0\n");
4808    }
4809
4810    /// A library builtin is the library function of the same name, and the call says so.
4811    ///
4812    /// A program writes `__builtin_strlen` rather than `strlen` to reach the function the C
4813    /// library promises where its own name has been taken by a macro, and to say that the usual
4814    /// meaning is the one intended. So the name in the program and the name in the object file
4815    /// are two different names and the call carries the second one. gcc folds several of these
4816    /// when the arguments allow it, which is an optimization on top of a call that is already
4817    /// right rather than instead of it, so nothing here depends on any folding happening.
4818    #[test]
4819    fn a_call_to_a_library_builtin_reaches_the_library_function() {
4820        let text = body("void f(void) { __builtin_abort(); }\n");
4821        assert_eq!(text, "block0:\n    call @abort() : ()\n    return\n");
4822
4823        // Nothing declared either of these and nothing had to: the prefix is what says the name
4824        // belongs to the implementation, and the type comes out of `features.toml`.
4825        let text = ir("int f(const char *s) { return __builtin_puts(s) + __builtin_strlen(s); }\n");
4826        assert!(text.contains("call @puts(%0) : (ptr) -> i32"), "{text}");
4827        assert!(text.contains("call @strlen(%0) : (ptr) -> i64"), "{text}");
4828        assert!(!text.contains("__builtin_"), "the prefix is not part of any name here:\n{text}");
4829    }
4830
4831    /// A `_chk` builtin reaches the checking function in the library with the object size still
4832    /// on the end of it.
4833    ///
4834    /// This is what a fortified `string.h` turns every copy into, so it is what a program built
4835    /// the way a distribution builds one is full of, and the whole of what makes the call right
4836    /// is that the size goes with it. The checking function takes `(size_t) -1` to mean nothing
4837    /// is known and does no check, which is what the header passes when the destination's object
4838    /// is not in sight, so the unconditional call means the same thing in both cases and costs a
4839    /// call gcc would have folded away in the second.
4840    ///
4841    /// The name is the one place this family reads like an exception and is not one:
4842    /// `__builtin___memcpy_chk` with `__builtin_` taken off is `__memcpy_chk`.
4843    #[test]
4844    fn a_chk_builtin_reaches_the_checking_function_and_keeps_the_size() {
4845        let text = ir(concat!(
4846            "char d[8];\n",
4847            "void f(const char *s, unsigned long n) {\n",
4848            "  __builtin___memcpy_chk(d, s, n, __builtin_object_size(d, 0));\n",
4849            "  __builtin___strcpy_chk(d, s, __builtin_object_size(d, 1));\n",
4850            "  __builtin___memset_chk(d, 0, n, 8);\n",
4851            "}\n",
4852        ));
4853        assert!(text.contains("call @__memcpy_chk("), "{text}");
4854        assert!(text.contains("call @__strcpy_chk("), "{text}");
4855        assert!(text.contains("call @__memset_chk("), "{text}");
4856        assert!(text.contains("iconst.i64 8"), "the object size reaches the call: {text}");
4857        assert!(!text.contains("__builtin_"), "the prefix is not part of any name here:\n{text}");
4858    }
4859
4860    /// A checking call whose object size says nothing is known is the plain library call.
4861    ///
4862    /// That is the whole of the folding half of the family. The checking function reads the all
4863    /// ones value as do not check, so the call it was going to make is the function it guards with
4864    /// an argument nobody reads on the end of it, and gcc drops the argument and calls the plain
4865    /// function at every level including `-O0`. Where the size is a real number the checking call
4866    /// stands, because the check is the point.
4867    #[test]
4868    fn a_checking_call_whose_size_says_nothing_is_known_is_the_plain_library_call() {
4869        let text = ir(concat!(
4870            "extern char *p;\n",
4871            "char d[8];\n",
4872            "void f(const char *s, unsigned long n) {\n",
4873            "  __builtin___memcpy_chk(d, s, n, __builtin_object_size(d, 0));\n",
4874            "  __builtin___memcpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
4875            "  __builtin___strcpy_chk(p, s, __builtin_object_size(p, 0));\n",
4876            "  __builtin___stpncpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
4877            "  __builtin___sprintf_chk(p, 1, __builtin_object_size(p, 0), s);\n",
4878            "}\n",
4879        ));
4880
4881        // The destination whose object is in sight keeps its check, size and all.
4882        assert!(
4883            text.contains("call @__memcpy_chk(%2, %0, %1, %3) : (ptr, ptr, i64, i64)"),
4884            "{text}"
4885        );
4886
4887        // The three whose object is not lose the argument and the name along with it. The type of
4888        // the call goes with them, which is what says the argument is gone rather than ignored.
4889        assert!(text.contains("call @memcpy(%6, %0, %1) : (ptr, ptr, i64) -> ptr"), "{text}");
4890        assert!(text.contains("call @strcpy(%10, %0) : (ptr, ptr) -> ptr"), "{text}");
4891        assert!(text.contains("call @stpncpy(%14, %0, %1) : (ptr, ptr, i64) -> ptr"), "{text}");
4892
4893        // The formatted one never folds, whatever the size says, because refusing a `%n` in a
4894        // writable format is the other half of what it was asked to do.
4895        assert!(text.contains("call @__sprintf_chk("), "{text}");
4896
4897        // Nothing is left behind in the instructions either. The size the folded calls no longer
4898        // take is a constant nobody reads, and no instruction is written for one.
4899        let asm = asm(concat!(
4900            "void f(char *p, const char *s, unsigned long n) {\n",
4901            "  __builtin___memcpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
4902            "}\n",
4903        ));
4904        assert!(asm.contains("call\tmemcpy"), "{asm}");
4905        assert!(!asm.contains("$-1"), "the size that went away leaves no instruction:\n{asm}");
4906    }
4907
4908    /// The `v` spellings take a `__builtin_va_list`, which is the first type in the table the
4909    /// target chooses the shape of rather than the width of.
4910    ///
4911    /// On x86-64 it is an array of one, so what the prototype has to say is the pointer that
4912    /// array decays to, which is the same adjustment C makes to any parameter written as an array
4913    /// and is what a `va_list` parameter already holds. A prototype that kept the array would be
4914    /// one no argument could ever match.
4915    #[test]
4916    fn the_v_spellings_of_the_chk_family_take_the_list_a_va_list_parameter_holds() {
4917        let text = ir(concat!(
4918            "char d[64];\n",
4919            "int f(const char *fmt, ...) {\n",
4920            "  __builtin_va_list ap;\n",
4921            "  __builtin_va_start(ap, fmt);\n",
4922            "  int n = __builtin___vsprintf_chk(d, 1, __builtin_object_size(d, 0), fmt, ap);\n",
4923            "  __builtin_va_end(ap);\n",
4924            "  return n;\n",
4925            "}\n",
4926        ));
4927        assert!(text.contains("call @__vsprintf_chk("), "{text}");
4928        assert!(text.contains("iconst.i64 64"), "the object size reaches the call: {text}");
4929    }
4930
4931    /// The absolute value family is four instructions and not a call, whoever declared the name.
4932    ///
4933    /// `abs`, `labs` and `llabs` are reserved to the implementation, so a program that writes one
4934    /// means the one the C library promises and the compiler is allowed to know what it does. The
4935    /// program in `gcc.c-torture/execute/20021127-1.c` is the one that insists: it defines `llabs`
4936    /// to abort and expects the call not to reach it. Measured against gcc 16.2.0, which writes a
4937    /// `neg` and a `cmovns` and never calls the definition either.
4938    ///
4939    /// The most negative value comes back as itself, which is what the arithmetic gives and what
4940    /// gcc's pair of instructions gives, and C says the answer is undefined there.
4941    #[test]
4942    fn the_absolute_value_family_is_the_magnitude_and_not_a_call() {
4943        let text = body(concat!(
4944            "long long llabs(long long);\n",
4945            "long long f(long long x) { return llabs(x); }\n",
4946        ));
4947        assert!(text.contains("%1 = iconst.i64 63"), "{text}");
4948        assert!(text.contains("%2 = ashr %0, %1"), "{text}");
4949        assert!(text.contains("%3 = xor %0, %2"), "{text}");
4950        assert!(text.contains("%4 = sub %3, %2"), "{text}");
4951        assert!(!text.contains("call"), "the call does not happen:\n{text}");
4952
4953        // The narrower two, whose width comes from the type the library gives the name and not
4954        // from anything at the call.
4955        let text = body("int abs(int);\nint f(int x) { return abs(x); }\n");
4956        assert!(text.contains("iconst.i32 31"), "{text}");
4957        let text = body("long labs(long);\nlong f(long x) { return labs(x); }\n");
4958        assert!(text.contains("iconst.i64 63"), "{text}");
4959
4960        // The prefixed spelling is the same node, and it is what a program writes to reach the
4961        // library's meaning where the plain name has been taken.
4962        let text = body("long long f(long long x) { return __builtin_llabs(x); }\n");
4963        assert!(!text.contains("call"), "{text}");
4964
4965        // A definition of the name in the same file changes nothing, which is the whole point.
4966        let text = ir(concat!(
4967            "long long llabs(long long b);\n",
4968            "long long g(long long x) { return llabs(x); }\n",
4969            "long long llabs(long long b) { return 7; }\n",
4970        ));
4971        assert!(!text.contains("call @llabs"), "{text}");
4972    }
4973
4974    /// A byte swap is one instruction and not a call, and nothing had to declare it.
4975    ///
4976    /// SQLite writes these for its page headers and glibc's `<endian.h>` defines `htobe32` and its
4977    /// neighbours as exactly these, so a program that reads a file format reaches one without ever
4978    /// naming it. There is no object file anywhere that defines `__builtin_bswap32`, so a call left
4979    /// standing here would not link.
4980    #[test]
4981    fn a_byte_swap_is_arithmetic_and_not_a_call() {
4982        let text = body("unsigned f(unsigned x) { return __builtin_bswap32(x); }\n");
4983        assert_eq!(text, "block0(%0: i32):\n    %1 = bswap %0\n    return %1\n");
4984
4985        // The argument is converted by the prototype the way any other call's would be, so the
4986        // swap happens at the width the name says and not at the width the program wrote.
4987        let text = body("unsigned f(unsigned char c) { return __builtin_bswap32(c); }\n");
4988        assert!(text.contains("zext.i32 %0"), "widened first: {text}");
4989        assert!(text.contains("bswap %1"), "and swapped at four bytes: {text}");
4990    }
4991
4992    /// Each of the three reverses in the width its name says, which is the type of the node.
4993    ///
4994    /// The width matters more here than it looks. `__builtin_bswap16` is the two bytes of a
4995    /// `uint16_t` exchanged, and if the node came out at the machine's width instead then the bits
4996    /// above the value would be dragged into the answer and the result would be zero.
4997    #[test]
4998    fn the_byte_swaps_reverse_at_the_width_their_name_says() {
4999        for (name, ty, width) in [
5000            ("__builtin_bswap16", "unsigned short", "i16"),
5001            ("__builtin_bswap32", "unsigned", "i32"),
5002            ("__builtin_bswap64", "unsigned long long", "i64"),
5003        ] {
5004            let source = format!("{ty} f({ty} x) {{ return {name}(x); }}\n");
5005            let text = body(&source);
5006            assert_eq!(
5007                text,
5008                format!("block0(%0: {width}):\n    %1 = bswap %0\n    return %1\n"),
5009                "{name}"
5010            );
5011        }
5012    }
5013
5014    /// The three bit counts the IR has an instruction for are that instruction and not a call.
5015    ///
5016    /// Eighteen rows of `features.toml` come out of six questions, and three of the six are one
5017    /// instruction each. The kernel's bitmap search is built on them, ffmpeg counts leading zeroes
5018    /// in its bitstream reader and SQLite uses one to size a page, so a call left standing here
5019    /// would not link against anything and would be slow if it did.
5020    #[test]
5021    fn the_bit_counts_are_instructions_and_not_calls() {
5022        let text = body("int f(unsigned x) { return __builtin_clz(x); }\n");
5023        assert_eq!(text, "block0(%0: i32):\n    %1 = ctlz %0\n    return %1\n");
5024
5025        let text = body("int f(unsigned x) { return __builtin_ctz(x); }\n");
5026        assert_eq!(text, "block0(%0: i32):\n    %1 = cttz %0\n    return %1\n");
5027
5028        let text = body("int f(unsigned x) { return __builtin_popcount(x); }\n");
5029        assert_eq!(text, "block0(%0: i32):\n    %1 = ctpop %0\n    return %1\n");
5030    }
5031
5032    /// The width counted is the operand's and the width answered is `int`, which are two different
5033    /// things at every spelling but the narrowest.
5034    ///
5035    /// This is the mistake the family invites. `__builtin_clz` of a value counts the leading zeroes
5036    /// of it narrowed to `unsigned int` and `__builtin_clzll` counts them at sixty four bits, and
5037    /// those are different numbers for the same value. What decides it is the prototype the row
5038    /// carries, so the count happens after the conversion and the narrowing back to `int` happens
5039    /// after the count.
5040    #[test]
5041    fn the_bit_counts_ask_about_the_width_their_name_says() {
5042        let text = body("int f(unsigned long long x) { return __builtin_clzll(x); }\n");
5043        assert!(text.starts_with("block0(%0: i64):"), "counted at eight bytes: {text}");
5044        assert!(text.contains("%1 = ctlz %0"), "{text}");
5045        assert!(text.contains("trunc.i32 %1"), "and answered in an int: {text}");
5046
5047        // The same value asked about at the narrower width, which converts first and so counts
5048        // something else.
5049        let text = body("int f(unsigned long long x) { return __builtin_clz(x); }\n");
5050        assert!(text.contains("trunc.i32 %0"), "narrowed to what was asked about: {text}");
5051        assert!(text.contains("ctlz %1"), "and counted there: {text}");
5052
5053        let text = body("int f(unsigned long x) { return __builtin_popcountl(x); }\n");
5054        assert!(text.contains("%1 = ctpop %0"), "{text}");
5055        assert!(!text.contains("call"), "{text}");
5056    }
5057
5058    /// A parity is whether the count of set bits is odd, which is that count and its low bit.
5059    ///
5060    /// Not the machine's parity flag, which on x86-64 is over the low byte of a result and so is a
5061    /// different question, and not the count itself, since C says the answer is zero or one.
5062    #[test]
5063    fn a_parity_is_the_low_bit_of_the_set_bit_count() {
5064        let text = body("int f(unsigned x) { return __builtin_parity(x); }\n");
5065        assert!(text.contains("%1 = ctpop %0"), "{text}");
5066        assert!(text.contains("iconst.i32 1"), "{text}");
5067        assert!(text.contains("and %1, %2"), "the low bit of it: {text}");
5068    }
5069
5070    /// `__builtin_ffs` is the trailing zero count and one, kept only when there was a bit to find.
5071    ///
5072    /// The one in the family defined at zero, where it answers zero. Written as a mask rather than
5073    /// as a branch: the count and the comparison do not depend on each other and both are cheap, so
5074    /// a branch would buy nothing and cost two blocks and a join.
5075    #[test]
5076    fn the_first_set_bit_is_one_based_and_zero_for_a_zero() {
5077        let text = body("int f(int x) { return __builtin_ffs(x); }\n");
5078        assert!(text.contains("%1 = cttz %0"), "{text}");
5079        assert!(text.contains("%4 = add %1, %2"), "one more than the count: {text}");
5080        assert!(text.contains("%5 = icmp ne %0, %3"), "whether there was a bit at all: {text}");
5081        assert!(text.contains("%7 = sub %3, %6"), "spread to a mask: {text}");
5082        assert!(text.contains("%8 = and %4, %7"), "and kept only then: {text}");
5083        assert!(!text.contains("br_if"), "no branch: {text}");
5084    }
5085
5086    /// `__builtin_clrsb` is how many bits below the sign bit repeat it, which is a leading zero
5087    /// count of the value folded onto its own sign.
5088    ///
5089    /// Exclusive or with the sign spread over every bit turns a negative value into its complement
5090    /// and leaves one that is not negative alone, so in both cases the top bit is clear and there
5091    /// is one zero above the highest bit that does not repeat the sign. The answer is one less
5092    /// than that count, and the shift left is what takes the one off, with the low bit set on the
5093    /// way so that zero and minus one have something to count: both of them fold to a word with no
5094    /// bits in it, which is the one input a leading zero count says nothing about.
5095    #[test]
5096    fn the_redundant_sign_bit_count_is_instructions_and_not_a_call() {
5097        let text = body("int f(int x) { return __builtin_clrsb(x); }\n");
5098        assert!(text.contains("%1 = iconst.i32 31"), "{text}");
5099        assert!(text.contains("%2 = ashr %0, %1"), "the sign over every bit: {text}");
5100        assert!(text.contains("%3 = xor %0, %2"), "folded onto it: {text}");
5101        assert!(text.contains("%5 = shl %3, %4"), "one less than the count: {text}");
5102        assert!(text.contains("%6 = or %5, %4"), "with something to count at zero: {text}");
5103        assert!(text.contains("%7 = ctlz %6"), "{text}");
5104        assert!(!text.contains("call"), "{text}");
5105        assert!(!text.contains("br_if"), "no branch: {text}");
5106    }
5107
5108    /// The unsigned four are the same four instructions answering in the unsigned type.
5109    ///
5110    /// Which on a two's complement machine is the same bits, so what this checks is that the type
5111    /// of the answer is the unsigned one. The reason the family exists is the most negative value,
5112    /// whose magnitude is not representable in the signed type and is representable in this one.
5113    #[test]
5114    fn the_unsigned_absolute_value_family_answers_in_the_unsigned_type() {
5115        let text = body("unsigned f(int x) { return __builtin_uabs(x); }\n");
5116        assert!(text.contains("%1 = iconst.i32 31"), "{text}");
5117        assert!(text.contains("%4 = sub %3, %2"), "{text}");
5118        assert!(!text.contains("call"), "nothing declares uabs, so a call would not link: {text}");
5119
5120        let text = body("unsigned long long f(long long x) { return __builtin_ullabs(x); }\n");
5121        assert!(text.contains("iconst.i64 63"), "at the width the name says: {text}");
5122
5123        // The answer is the unsigned type and not the signed one, which is what a comparison
5124        // against it is decided by.
5125        let text = body("int f(int x) { return __builtin_uabs(x) > 2147483647u; }\n");
5126        assert!(text.contains("icmp ugt"), "compared unsigned: {text}");
5127    }
5128
5129    /// `intmax_t` is not a fixed type, so the two widest spellings ask the target what it is.
5130    ///
5131    /// `long` where that is sixty four bits wide and `long long` where it is not, which is the rule
5132    /// `rucc_pp::predef` writes `__INTMAX_TYPE__` out of. The three targets here are all LP64, so
5133    /// the answer is `long` and the shift is sixty three, and the point of the test is that the
5134    /// signature was understood at all rather than refused for naming a type the table could not
5135    /// spell.
5136    #[test]
5137    fn the_widest_absolute_value_is_whichever_type_the_target_makes_intmax_t() {
5138        let text = body("long f(long x) { return __builtin_imaxabs(x); }\n");
5139        assert!(text.contains("iconst.i64 63"), "{text}");
5140        assert!(text.contains("%4 = sub %3, %2"), "{text}");
5141        assert!(!text.contains("call"), "{text}");
5142
5143        let text = body("unsigned long f(long x) { return __builtin_umaxabs(x); }\n");
5144        assert!(text.contains("iconst.i64 63"), "{text}");
5145        assert!(!text.contains("call"), "{text}");
5146    }
5147
5148    /// The `_p` spellings ask the same question, write nothing, and do not evaluate the third
5149    /// argument.
5150    ///
5151    /// gcc says the third argument is there for its type alone, so a call is two operands and a
5152    /// type by the time it reaches the IR. What the type decides is the same thing it decides for
5153    /// the three that write: whether the exact answer would have fit there, which is why the
5154    /// second call below is done at a wider width than the first.
5155    #[test]
5156    fn an_overflow_predicate_writes_nothing_and_answers_the_bit_the_check_would() {
5157        let text =
5158            body("int f(int a, int b) { return __builtin_add_overflow_p(a, b, (int) 0); }\n");
5159        assert!(text.contains("%2, %3 = sadd_overflow.(i32, i1) %0, %1"), "{text}");
5160        assert!(!text.contains("store"), "nothing is written: {text}");
5161        assert!(!text.contains("call"), "{text}");
5162
5163        // A wider destination is a wider arithmetic, and the narrowing test that goes with it is
5164        // what says whether the answer got there, exactly as for the spelling that stores.
5165        let text =
5166            body("int f(int a, int b) { return __builtin_mul_overflow_p(a, b, (long long) 0); }\n");
5167        assert!(text.contains("smul_overflow.(i64, i1)"), "{text}");
5168        assert!(!text.contains("store"), "{text}");
5169
5170        // The third argument is a value and not a pointer, and a side effect written in it does
5171        // not happen, because what the argument is there for is its type.
5172        let text = body(concat!(
5173            "int g(void);\n",
5174            "int f(int a, int b) { return __builtin_sub_overflow_p(a, b, g()); }\n",
5175        ));
5176        assert!(!text.contains("call @g"), "the third argument is not evaluated: {text}");
5177    }
5178
5179    /// The three overflow checks are arithmetic and a flag, and not a call to anything.
5180    ///
5181    /// gcc has emitted these since 5.0 and there is no object file that defines one, so a call left
5182    /// standing here would not link. SQLite reaches all three within twenty lines of each other, in
5183    /// `sqlite3AddInt64` and its two neighbours, which is the reason they were done now.
5184    ///
5185    /// The IR instruction answers two things at once, the wrapped value and whether it wrapped,
5186    /// which is a shape nothing else in the IR has. The store is the builtin writing the answer
5187    /// through the pointer it was handed.
5188    #[test]
5189    fn an_overflow_check_is_arithmetic_and_not_a_call() {
5190        let text =
5191            body("int f(int a, int b, int *r) { return __builtin_add_overflow(a, b, r); }\n");
5192        assert!(text.contains("%3, %4 = sadd_overflow.(i32, i1) %0, %1"), "{text}");
5193        assert!(text.contains("store %3 -> %2"), "{text}");
5194        assert!(!text.contains("call"), "{text}");
5195
5196        let text =
5197            body("int f(int a, int b, int *r) { return __builtin_sub_overflow(a, b, r); }\n");
5198        assert!(text.contains("ssub_overflow.(i32, i1) %0, %1"), "{text}");
5199
5200        let text =
5201            body("int f(int a, int b, int *r) { return __builtin_mul_overflow(a, b, r); }\n");
5202        assert!(text.contains("smul_overflow.(i32, i1) %0, %1"), "{text}");
5203
5204        // Unsigned operands get the unsigned form, which is a different question about the same
5205        // arithmetic: an unsigned sum wraps where a signed one of the same bits does not.
5206        let text = body(
5207            "int f(unsigned a, unsigned b, unsigned *r) { return __builtin_add_overflow(a, b, r); }\n",
5208        );
5209        assert!(text.contains("uadd_overflow.(i32, i1) %0, %1"), "{text}");
5210    }
5211
5212    /// The arithmetic happens at a type that holds every value all three written types can hold.
5213    ///
5214    /// That is what makes the check exact. `unsigned int` and `int` in one call need thirty three
5215    /// bits between them, so the add is done at sixty four with each operand extended the way its
5216    /// own signedness says: the unsigned one zero extended, the signed one sign extended. Sign
5217    /// extending the unsigned one would turn three billion into a negative number before the
5218    /// addition ever saw it.
5219    #[test]
5220    fn an_overflow_check_is_done_at_a_type_that_holds_every_operand() {
5221        let text = body(
5222            "int f(unsigned a, int b, long long *r) { return __builtin_add_overflow(a, b, r); }\n",
5223        );
5224        assert!(text.contains("%3 = zext.i64 %0"), "the unsigned operand keeps its value: {text}");
5225        assert!(text.contains("%4 = sext.i64 %1"), "and so does the signed one: {text}");
5226        assert!(text.contains("sadd_overflow.(i64, i1) %3, %4"), "{text}");
5227
5228        // Three types that agree need no extension at all, which is what nearly every real call
5229        // is written as.
5230        let text = body(
5231            "int f(long long a, long long b, long long *r) { return __builtin_mul_overflow(a, b, r); }\n",
5232        );
5233        assert!(text.contains("smul_overflow.(i64, i1) %0, %1"), "{text}");
5234        assert!(!text.contains("sext."), "{text}");
5235        // The one widening left is the answer, which is a bit becoming the `int` C says it is.
5236        assert!(!text.contains("zext.i64"), "{text}");
5237    }
5238
5239    /// The wrapped answer is written through the pointer whether or not it fit.
5240    ///
5241    /// That is gcc's rule and it is what makes the builtin usable as a wrapping add with a flag on
5242    /// the side. A destination narrower than the arithmetic is narrowed and widened back, and the
5243    /// answer being different is the second half of the test: the instruction says whether the
5244    /// arithmetic itself needed more room, and the round trip says whether what came out survived
5245    /// the trip down to where it was going.
5246    #[test]
5247    fn an_overflow_check_writes_the_wrapped_answer_whether_or_not_it_fit() {
5248        let text =
5249            body("int f(int a, int b, char *r) { return __builtin_sub_overflow(a, b, r); }\n");
5250        assert!(text.contains("%3, %4 = ssub_overflow.(i32, i1) %0, %1"), "{text}");
5251        assert!(text.contains("%5 = trunc.i8 %3"), "narrowed to where it goes: {text}");
5252        assert!(text.contains("%6 = sext.i32 %5"), "and back: {text}");
5253        assert!(text.contains("%7 = icmp ne %6, %3"), "which is whether it fit: {text}");
5254        assert!(text.contains("store %5 -> %2"), "the narrowed value is stored either way: {text}");
5255        assert!(text.contains("%8 = or %4, %7"), "and either bit is an overflow: {text}");
5256    }
5257
5258    /// A call needing more than the widest type there is compiles, by not asking for such a type.
5259    ///
5260    /// One way to reach it: an unsigned `__int128` mixed with a signed type, which needs a hundred
5261    /// and twenty nine bits to represent both and so has nowhere left to go. That used to be refused
5262    /// by name. It is done now by carrying the sign of each operand alongside its value rather than
5263    /// inside it, which is what gcc does, so all three of the family compile for that mix.
5264    #[test]
5265    fn a_call_needing_more_than_the_widest_type_still_compiles() {
5266        for name in ["add", "sub", "mul"] {
5267            let source = format!(
5268                "int f(unsigned __int128 a, long long b, __int128 *r) {{\n    \
5269                 return __builtin_{name}_overflow(a, b, r);\n}}\n"
5270            );
5271            let mut opts = options();
5272            opts.emit = EmitKind::MirFinal;
5273            assert!(!run(&opts, &source).failed(), "{name} was refused or stopped the back end");
5274        }
5275    }
5276
5277    /// An operand that is not an integer at all is the older message, from the type checking every
5278    /// type generic builtin shares.
5279    #[test]
5280    fn an_overflow_check_over_something_that_is_not_an_integer_says_so() {
5281        let messages =
5282            errors("int f(double a, int b, int *r) { return __builtin_add_overflow(a, b, r); }\n");
5283        assert!(messages.iter().any(|line| line.contains("E0671")), "{messages:?}");
5284
5285        let messages =
5286            errors("int f(int a, int b, double *r) { return __builtin_add_overflow(a, b, r); }\n");
5287        assert!(messages.iter().any(|line| line.contains("E0671")), "{messages:?}");
5288    }
5289
5290    /// An ordered access is an ordered access in the IR, with the ordering the program wrote.
5291    ///
5292    /// Which is the point of the node existing at all. An ordering is not an argument anything is
5293    /// passed, it is a thing the IR says about an access, so the number in the source is read once
5294    /// in the front end and after that the ordering travels on the instruction where every pass
5295    /// that moves code can see it.
5296    ///
5297    /// SQLite is why these are done: `AtomicLoad` and `AtomicStore` in `sqlite3.c` are
5298    /// `__atomic_load_n` and `__atomic_store_n` at the relaxed ordering, and there are thirty five
5299    /// calls to the pair.
5300    #[test]
5301    fn an_ordered_access_is_ordered_in_the_ir() {
5302        let text = body("int f(int *p) { return __atomic_load_n(p, 0); }\n");
5303        assert!(text.contains("atomic_load.i32 %0, align 4, relaxed"), "{text}");
5304
5305        let text = body("long f(long *p) { return __atomic_load_n(p, 2); }\n");
5306        assert!(text.contains("atomic_load.i64 %0, align 8, acquire"), "{text}");
5307
5308        let text = body("void f(int *p, int v) { __atomic_store_n(p, v, 3); }\n");
5309        assert!(text.contains("atomic_store %1 -> %0, align 4, release"), "{text}");
5310
5311        let text = body("void f(int *p, int v) { __atomic_store_n(p, v, 5); }\n");
5312        assert!(text.contains("atomic_store %1 -> %0, align 4, seq_cst"), "{text}");
5313
5314        // The value is converted to what the pointer points at before it is stored, which is what
5315        // the call would have done if it had a prototype to convert against.
5316        let text = body("void f(char *p, int v) { __atomic_store_n(p, v, 0); }\n");
5317        assert!(text.contains("trunc.i8 %1"), "{text}");
5318        assert!(text.contains("atomic_store %2 -> %0, align 1, relaxed"), "{text}");
5319    }
5320
5321    /// On this machine the ordered access is the plain instruction, except at the strongest
5322    /// ordering of a store.
5323    ///
5324    /// x86-64 is total store order: every load is already an acquire and every store is already a
5325    /// release, and an aligned access no wider than a word is indivisible whether or not anybody
5326    /// asked. So the whole family is `mov` and the one thing the machine does not give away is a
5327    /// store staying in front of a later load, which is `mfence` behind the store. Every line below
5328    /// is what gcc 16.2.0 writes for the same function.
5329    #[test]
5330    fn an_ordered_access_is_the_plain_instruction_on_this_machine() {
5331        let text = asm("int f(int *p) { return __atomic_load_n(p, 5); }\n");
5332        assert!(text.contains("movl\t(%rdi), %eax"), "{text}");
5333        assert!(!text.contains("mfence"), "a load needs no barrier here: {text}");
5334
5335        let text = asm("void f(int *p, int v) { __atomic_store_n(p, v, 3); }\n");
5336        assert!(text.contains("movl\t%esi, (%rdi)"), "{text}");
5337        assert!(!text.contains("mfence"), "a release store needs no barrier here: {text}");
5338
5339        let text = asm("void f(int *p, int v) { __atomic_store_n(p, v, 5); }\n");
5340        let (before, after) = text.split_once("mfence").expect("a barrier: {text}");
5341        assert!(before.contains("movl\t%esi, (%rdi)"), "the store comes first: {text}");
5342        assert!(!after.contains("movl"), "and nothing else is between them: {text}");
5343    }
5344
5345    /// A barrier is one instruction at the strongest ordering and no instruction below it.
5346    ///
5347    /// The same reasoning the other way round. An acquire, a release and an acquire release fence
5348    /// are already true of every program running on this machine, and what a program wanted from
5349    /// one is that the compiler not move accesses across it, which is already so by the time any
5350    /// instruction is picked. Sequential consistency is the one that costs something.
5351    ///
5352    /// `__sync_synchronize` is the older family's spelling of the strongest one and compiles to
5353    /// exactly the same instruction, which is what SQLite calls twice in `sqlite3.c`.
5354    #[test]
5355    fn a_barrier_is_one_instruction_at_the_strongest_ordering_and_none_below_it() {
5356        assert!(asm("void f(void) { __atomic_thread_fence(5); }\n").contains("mfence"));
5357        assert!(asm("void f(void) { __sync_synchronize(); }\n").contains("mfence"));
5358
5359        for weaker in ["1", "2", "3", "4"] {
5360            let source = format!("void f(void) {{ __atomic_thread_fence({weaker}); }}\n");
5361            assert!(!asm(&source).contains("mfence"), "{weaker} costs nothing here");
5362        }
5363    }
5364
5365    /// The three x86 fences under gcc's names are that same barrier at that same ordering.
5366    ///
5367    /// Exact for `mfence` and stronger than asked for the other two, which is a safe answer: a
5368    /// program that wanted its stores ordered gets that and more. Narrowing the two is worth doing
5369    /// once an instruction can be named from there, which is the note the shipped `xmmintrin.h`
5370    /// already carries at `_mm_sfence`.
5371    ///
5372    /// Each carries a signature, so an argument written on one is reported like an argument
5373    /// written on any other call, which is the whole reason they have one.
5374    #[test]
5375    fn the_three_x86_fences_are_the_barrier_the_strongest_ordering_gives() {
5376        for name in ["__builtin_ia32_sfence", "__builtin_ia32_lfence", "__builtin_ia32_mfence"] {
5377            let source = format!("void f(void) {{ {name}(); }}\n");
5378            assert!(asm(&source).contains("mfence"), "{name} is a barrier");
5379            let text = body(&source);
5380            assert!(text.contains("fence seq_cst"), "{name}: {text}");
5381        }
5382
5383        let result = run(&options(), "void f(void) { __builtin_ia32_sfence(1); }\n");
5384        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
5385        assert!(result.messages[0].contains("too many arguments"), "{:?}", result.messages);
5386    }
5387
5388    /// The four compare and exchange names are one IR instruction producing two values.
5389    ///
5390    /// Which of the two the expression answers is the difference between three of the four names,
5391    /// and the fourth difference is the C11 pair writing what they found back through the pointer
5392    /// they were handed, which is the branch after the instruction.
5393    #[test]
5394    fn a_compare_and_exchange_is_one_instruction_answering_two_things() {
5395        // The older family, whose two names are the same instruction read two ways. Neither has a
5396        // memory order argument and both are a full barrier, which is what `seq_cst` says.
5397        let text =
5398            body("int f(int *p, int e, int d) { return __sync_val_compare_and_swap(p, e, d); }\n");
5399        assert!(text.contains("%3, %4 = cmpxchg.(i32, i1) %0, %1, %2, align 4, seq_cst"), "{text}");
5400        assert!(text.contains("return %3"), "the value it found: {text}");
5401
5402        let text =
5403            body("int f(int *p, int e, int d) { return __sync_bool_compare_and_swap(p, e, d); }\n");
5404        assert!(text.contains("%3, %4 = cmpxchg.(i32, i1) %0, %1, %2, align 4, seq_cst"), "{text}");
5405        assert!(text.contains("zext.i32 %4"), "whether it happened: {text}");
5406
5407        // The C11 form, whose value expected arrives by pointer and is read before the exchange,
5408        // and whose answer is whether it happened. The write back is on the path where it did not.
5409        let text = body(
5410            "int f(int *p, int *e, int d) { return __atomic_compare_exchange_n(p, e, d, 0, 4, 2); }\n",
5411        );
5412        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
5413        assert!(text.contains("%4, %5 = cmpxchg.(i32, i1) %0, %3, %2, align 4, acq_rel"), "{text}");
5414        assert!(text.contains("br_if %5, block2, block1"), "{text}");
5415        assert!(text.contains("store %4 -> %1, align 4"), "{text}");
5416
5417        // And the form that takes the value to put there by pointer as well, which is one more
5418        // read and is otherwise the same node.
5419        let text = body(
5420            "int f(int *p, int *e, int *d) { return __atomic_compare_exchange(p, e, d, 0, 5, 5); }\n",
5421        );
5422        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
5423        assert!(text.contains("%4 = load.i32 %2, align 4"), "{text}");
5424        assert!(text.contains("%5, %6 = cmpxchg.(i32, i1) %0, %3, %4, align 4, seq_cst"), "{text}");
5425    }
5426
5427    /// On this machine it is `lock cmpxchg`, at the width of the object and at every ordering.
5428    ///
5429    /// The `lock` is what makes the whole of it one step as far as every other processor is
5430    /// concerned, and it is also what makes the instruction a full barrier, which is why the
5431    /// ordering the program wrote changes nothing in what is written here. Every line below is what
5432    /// gcc 16.2.0 writes for the same function.
5433    #[test]
5434    fn a_compare_and_exchange_is_a_locked_instruction_at_the_width_of_the_object() {
5435        let widths = [("char", "b", "%dl"), ("short", "w", "%dx"), ("int", "l", "%edx")];
5436        for (ty, suffix, reg) in widths {
5437            let source = format!(
5438                "int f({ty} *p, {ty} e, {ty} d) {{ return __sync_bool_compare_and_swap(p, e, d); }}\n"
5439            );
5440            let text = asm(&source);
5441            assert!(text.contains("\tlock\n"), "{ty}: {text}");
5442            assert!(text.contains(&format!("cmpxchg{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
5443            assert!(text.contains("sete\t"), "{ty}: {text}");
5444        }
5445        let source =
5446            "int f(long *p, long e, long d) { return __sync_bool_compare_and_swap(p, e, d); }\n";
5447        assert!(asm(source).contains("cmpxchgq\t%rdx, (%rdi)"), "{}", asm(source));
5448
5449        // The ordering the program asked for changes nothing, because a locked instruction on this
5450        // machine orders everything whatever it was asked for, so there is never a barrier beside
5451        // it either.
5452        for order in ["0", "2", "3", "4", "5"] {
5453            let call = format!("__atomic_compare_exchange_n(p, e, d, 0, {order}, 0)");
5454            let source = format!("int f(int *p, int *e, int d) {{ return {call}; }}\n");
5455            let text = asm(&source);
5456            assert!(text.contains("cmpxchgl\t"), "{order}: {text}");
5457            assert!(!text.contains("mfence"), "{order} needs no barrier here: {text}");
5458        }
5459    }
5460
5461    /// A read modify write is one IR instruction, and a name that asks for the value afterwards is
5462    /// that instruction and one more operation.
5463    ///
5464    /// The instruction answers what was there before, which is the convention every machine and
5465    /// every language in this area uses. Half the names in the family ask for the value afterwards
5466    /// instead, and that is the answer and the operand put together again, which is arithmetic on
5467    /// two values already in registers rather than a second flavour of the instruction.
5468    ///
5469    /// The two lock names are here too. They are not read modify writes in the same sense: one is
5470    /// an exchange and the other is a store of a zero, and what makes them a pair is the ordering,
5471    /// which is the one place in the older family that is not sequential consistency.
5472    #[test]
5473    fn a_read_modify_write_is_one_instruction_and_the_arithmetic_a_name_asks_for() {
5474        let text = body("int f(int *p, int v) { return __atomic_fetch_add(p, v, 5); }\n");
5475        assert!(text.contains("%2 = atomic_rmw.i32 add %0, %1, align 4, seq_cst"), "{text}");
5476        assert!(text.contains("return %2"), "the value that was there: {text}");
5477
5478        let text = body("int f(int *p, int v) { return __atomic_add_fetch(p, v, 5); }\n");
5479        assert!(text.contains("%2 = atomic_rmw.i32 add %0, %1, align 4, seq_cst"), "{text}");
5480        assert!(text.contains("%3 = add %2, %1"), "and the value afterwards: {text}");
5481
5482        let text = body("int f(int *p, int v) { return __atomic_sub_fetch(p, v, 5); }\n");
5483        assert!(text.contains("%2 = atomic_rmw.i32 sub %0, %1, align 4, seq_cst"), "{text}");
5484        assert!(text.contains("%3 = sub %2, %1"), "{text}");
5485
5486        // The older family, which passes no ordering and is a full barrier.
5487        let text = body("int f(int *p, int v) { return __sync_fetch_and_sub(p, v); }\n");
5488        assert!(text.contains("%2 = atomic_rmw.i32 sub %0, %1, align 4, seq_cst"), "{text}");
5489
5490        // The exchange, and the older family's spelling of it, which is taking a lock and so is an
5491        // acquire rather than the full barrier the rest of that family is.
5492        let text = body("int f(int *p, int v) { return __atomic_exchange_n(p, v, 5); }\n");
5493        assert!(text.contains("%2 = atomic_rmw.i32 xchg %0, %1, align 4, seq_cst"), "{text}");
5494
5495        let text = body("int f(int *p, int v) { return __sync_lock_test_and_set(p, v); }\n");
5496        assert!(text.contains("%2 = atomic_rmw.i32 xchg %0, %1, align 4, acquire"), "{text}");
5497
5498        // Giving the lock back, which is one of the two names in the family that is handed no value
5499        // to put there, because what it puts there is a zero.
5500        let text = body("void f(int *p) { __sync_lock_release(p); }\n");
5501        assert!(text.contains("release"), "{text}");
5502        assert!(text.contains("%1 = iconst.i32 0"), "{text}");
5503
5504        // And with something after the pointer, which is the list of variables the call promises to
5505        // protect rather than a value to write. Reading it as a value would store whatever the
5506        // caller happened to name there, which is the one thing giving a lock back must not do.
5507        let text = body("void f(int *p, int guard) { __sync_lock_release(p, guard); }\n");
5508        assert!(text.contains("%2 = iconst.i32 0"), "{text}");
5509        assert!(text.contains("atomic_store %2 -> %0, align 4, release"), "{text}");
5510
5511        // The bitwise four, which look no different here from the arithmetic ones: what the machine
5512        // has an instruction for is a question further down and this level does not ask it.
5513        let text = body("int f(int *p, int v) { return __atomic_fetch_and(p, v, 5); }\n");
5514        assert!(text.contains("%2 = atomic_rmw.i32 and %0, %1, align 4, seq_cst"), "{text}");
5515
5516        let text = body("int f(int *p, int v) { return __sync_or_and_fetch(p, v); }\n");
5517        assert!(text.contains("%2 = atomic_rmw.i32 or %0, %1, align 4, seq_cst"), "{text}");
5518        assert!(text.contains("%3 = or %2, %1"), "and the value afterwards: {text}");
5519
5520        // The nand, which is the one of the six that is two operations. The flip is an exclusive or
5521        // against every bit set because the IR has no not and that is what one is.
5522        let text = body("int f(int *p, int v) { return __atomic_nand_fetch(p, v, 5); }\n");
5523        assert!(text.contains("%2 = atomic_rmw.i32 nand %0, %1, align 4, seq_cst"), "{text}");
5524        assert!(text.contains("%3 = and %2, %1"), "{text}");
5525        assert!(text.contains("%4 = iconst.i32 -1"), "{text}");
5526        assert!(text.contains("%5 = xor %3, %4"), "{text}");
5527    }
5528
5529    /// The four operations with no instruction on this machine are a loop around `lock cmpxchg`.
5530    ///
5531    /// The shape is the one every architecture manual writes out by hand: read the word, work out
5532    /// what should be there instead, put it back if nothing else got in first, and go round again
5533    /// when something did. What is checked is that the loop is there at every width, that the
5534    /// operation is inside it, and that no `xchg` or `xadd` got used for something neither of them
5535    /// does.
5536    ///
5537    /// gcc 16.2.0 writes the same loop for the same functions, down to which register holds the
5538    /// value that was read.
5539    #[test]
5540    fn a_bitwise_read_modify_write_is_a_loop_around_the_compare_and_exchange() {
5541        let widths = [("char", "b", "%dl"), ("short", "w", "%dx"), ("int", "l", "%edx")];
5542        for (ty, suffix, reg) in widths {
5543            for (name, call, insn) in [
5544                ("and", "__atomic_fetch_and(p, v, 5)", "and"),
5545                ("or", "__sync_fetch_and_or(p, v)", "or"),
5546                ("xor", "__atomic_xor_fetch(p, v, 5)", "xor"),
5547            ] {
5548                let source = format!("{ty} f({ty} *p, {ty} v) {{ return {call}; }}\n");
5549                let text = asm(&source);
5550                assert!(text.contains("\tlock\n"), "{ty} {name}: {text}");
5551                assert!(
5552                    text.contains(&format!("cmpxchg{suffix}\t{reg}, (%rdi)")),
5553                    "{ty} {name}: {text}"
5554                );
5555                assert!(text.contains(&format!("{insn}{suffix}\t")), "{ty} {name}: {text}");
5556                // The tab matters on the second of these, since `cmpxchg` ends in the other name.
5557                assert!(!text.contains("\txadd"), "{ty} {name} is not an add: {text}");
5558                assert!(!text.contains("\txchg"), "{ty} {name} is not an exchange: {text}");
5559            }
5560        }
5561        let source = "long f(long *p, long v) { return __atomic_fetch_or(p, v, 5); }\n";
5562        assert!(asm(source).contains("cmpxchgq\t%rdx, (%rdi)"), "{}", asm(source));
5563
5564        // The nand, which puts two instructions inside the loop rather than one. The flip is an
5565        // exclusive or against every bit set in the IR and the folder turns that into the `not` the
5566        // machine has, which is what gcc writes here too.
5567        let text = asm("int f(int *p, int v) { return __sync_fetch_and_nand(p, v); }\n");
5568        assert!(text.contains("cmpxchgl\t"), "{text}");
5569        assert!(text.contains("andl\t"), "{text}");
5570        assert!(text.contains("notl\t"), "{text}");
5571    }
5572
5573    /// The three names that pass a value through a pointer are the same access and one plain one.
5574    ///
5575    /// They exist for an object too big to come back in a register, and the front end takes them at
5576    /// their word rather than folding them into the `_n` spellings, because the extra access is real:
5577    /// the caller handed over somewhere to read from or write into and that is where the value has
5578    /// to come from or go. Both of those accesses are plain. The object at the end of the caller's
5579    /// pointer is the caller's own and no other thread has its address, which is what the whole
5580    /// shape is for.
5581    #[test]
5582    fn an_access_through_a_second_pointer_is_the_same_access_and_one_more() {
5583        let text = body("void f(int *p, int *r) { __atomic_load(p, r, 5); }\n");
5584        assert!(text.contains("%2 = atomic_load.i32 %0, align 4, seq_cst"), "{text}");
5585        assert!(text.contains("store %2 -> %1, align 4"), "and out through the place: {text}");
5586
5587        let text = body("void f(int *p, int *v) { __atomic_store(p, v, 3); }\n");
5588        assert!(text.contains("%2 = load.i32 %1, align 4"), "in through the place: {text}");
5589        assert!(text.contains("atomic_store %2 -> %0, align 4, release"), "{text}");
5590
5591        // The exchange, which reads through one pointer and writes through another and is the same
5592        // instruction in between as the spelling that takes and answers values.
5593        let text = body("void f(int *p, int *v, int *r) { __atomic_exchange(p, v, r, 5); }\n");
5594        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
5595        assert!(text.contains("%4 = atomic_rmw.i32 xchg %0, %3, align 4, seq_cst"), "{text}");
5596        assert!(text.contains("store %4 -> %2, align 4"), "{text}");
5597    }
5598
5599    /// The flag pair is an exchange of one byte and a store of a zero over the same byte.
5600    ///
5601    /// One byte whatever the pointer was written as, which is the standard's reading rather than a
5602    /// liberty: the object is an `atomic_flag`, there is no other way to read or write one, so the
5603    /// type the pointer carries says nothing about the access and the width is the implementation's
5604    /// to fix. gcc 16.2.0 writes `xchgb` here through an `int *` too.
5605    ///
5606    /// The answer is a comparison against zero rather than the byte itself, because the type of the
5607    /// call is `_Bool` and a byte that is neither zero nor one is not one. gcc answers the raw byte,
5608    /// and the two agree wherever the flag is only ever touched through this pair.
5609    #[test]
5610    fn a_flag_is_an_exchange_of_one_byte_and_a_store_of_a_zero_over_the_same_byte() {
5611        for pointer in ["char", "int", "void"] {
5612            let source = format!("int f({pointer} *p) {{ return __atomic_test_and_set(p, 5); }}\n");
5613            let text = body(&source);
5614            assert!(text.contains("%1 = iconst.i8 1"), "{pointer}: {text}");
5615            assert!(
5616                text.contains("%2 = atomic_rmw.i8 xchg %0, %1, align 1, seq_cst"),
5617                "{pointer}: {text}"
5618            );
5619            assert!(text.contains("%4 = icmp ne %2, %3"), "{pointer}: {text}");
5620
5621            let source = format!("void f({pointer} *p) {{ __atomic_clear(p, 3); }}\n");
5622            let text = body(&source);
5623            assert!(text.contains("atomic_store %2 -> %0, align 1, release"), "{pointer}: {text}");
5624        }
5625
5626        // And on this machine, where the exchange carries no `lock` because one with memory locks
5627        // the bus whether it was asked to or not. Both lines are what gcc 16.2.0 writes.
5628        let text = asm("int f(int *p) { return __atomic_test_and_set(p, 5); }\n");
5629        assert!(text.contains("xchgb\t%al, (%rdi)"), "{text}");
5630        assert!(text.contains("setne\t"), "{text}");
5631    }
5632
5633    /// On this machine it is `xchg` where the machine has an exchange and `lock xadd` where it has
5634    /// an add, at the width of the object.
5635    ///
5636    /// The exchange carries no prefix and the add carries one, which is the machine rather than an
5637    /// oversight: an exchange with memory locks the bus whether it is asked to or not. Both are
5638    /// therefore full barriers whatever ordering the program wrote, so no ordering costs an
5639    /// `mfence` beside them. Every line below is what gcc 16.2.0 writes for the same function.
5640    #[test]
5641    fn a_read_modify_write_is_an_exchange_or_a_locked_add_at_the_width_of_the_object() {
5642        let widths = [("char", "b", "%sil"), ("short", "w", "%si"), ("int", "l", "%esi")];
5643        for (ty, suffix, reg) in widths {
5644            let source =
5645                format!("{ty} f({ty} *p, {ty} v) {{ return __atomic_fetch_add(p, v, 5); }}\n");
5646            let text = asm(&source);
5647            assert!(text.contains("\tlock\n"), "{ty}: {text}");
5648            assert!(text.contains(&format!("xadd{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
5649
5650            let source =
5651                format!("{ty} f({ty} *p, {ty} v) {{ return __atomic_exchange_n(p, v, 5); }}\n");
5652            let text = asm(&source);
5653            assert!(text.contains(&format!("xchg{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
5654            assert!(!text.contains("\tlock\n"), "an exchange is locked already: {ty}: {text}");
5655        }
5656        let source = "long f(long *p, long v) { return __atomic_fetch_add(p, v, 5); }\n";
5657        assert!(asm(source).contains("xaddq\t%rsi, (%rdi)"), "{}", asm(source));
5658
5659        // A subtraction is the same instruction over the negated operand, which is right at every
5660        // width because the machine's arithmetic wraps.
5661        let source = "int f(int *p, int v) { return __atomic_fetch_sub(p, v, 5); }\n";
5662        let text = asm(source);
5663        assert!(text.contains("negl\t"), "{text}");
5664        assert!(text.contains("xaddl\t"), "{text}");
5665
5666        // The ordering changes nothing, for the reason it changes nothing for a compare and
5667        // exchange: a locked instruction on this machine orders everything whatever it was asked.
5668        for order in ["0", "2", "3", "4", "5"] {
5669            let source =
5670                format!("int f(int *p, int v) {{ return __atomic_fetch_add(p, v, {order}); }}\n");
5671            let text = asm(&source);
5672            assert!(text.contains("xaddl\t"), "{order}: {text}");
5673            assert!(!text.contains("mfence"), "{order} needs no barrier here: {text}");
5674        }
5675
5676        // And the lock pair, which is the exchange and a store of a zero. Neither is a barrier
5677        // instruction: the exchange is one already and the store is a release, which this machine
5678        // gives away.
5679        let text = asm("int f(int *p, int v) { return __sync_lock_test_and_set(p, v); }\n");
5680        assert!(text.contains("xchgl\t%esi, (%rdi)"), "{text}");
5681        // The zero goes through a register on the way, which is where every constant this
5682        // compiler stores goes: gcc writes the one instruction because it has a store that takes an
5683        // immediate and no rule here does. That is a rule this rule set is missing rather than
5684        // anything about the builtin, and it is the same two instructions a plain `*p = 0` makes.
5685        // The register gets its zero from an exclusive or with itself rather than from a move of a
5686        // zero, which is `rucc_codegen::shorten` writing the shorter of the two spellings.
5687        let text = asm("void f(int *p) { __sync_lock_release(p); }\n");
5688        assert!(text.contains("xorl\t%eax, %eax"), "{text}");
5689        assert!(text.contains("movl\t%eax, (%rdi)"), "{text}");
5690        assert!(!text.contains("mfence"), "a release store needs no barrier here: {text}");
5691    }
5692
5693    /// The two lock free questions are numbers in the program rather than calls to anything.
5694    ///
5695    /// Both answer from the size, which has to be a power of two no wider than the widest access
5696    /// this compiler writes, and from what the pointer says about the alignment. Sixteen bytes is
5697    /// no here and is no in gcc without `-mcx16`, because `cmpxchg16b` is not in the baseline and
5698    /// nothing here writes it. Three bytes is no because there is no three byte access at all.
5699    ///
5700    /// The whole point of both names is that the answer is available before the program runs, so
5701    /// what is checked is that a `mov` of a constant is the whole function and that no call was
5702    /// left behind. A call would be to `__atomic_is_lock_free` in libatomic, which is not a library
5703    /// this links against.
5704    #[test]
5705    fn the_lock_free_questions_are_answered_as_constants() {
5706        for size in ["1", "2", "4", "8"] {
5707            let source =
5708                format!("int f(void) {{ return __atomic_always_lock_free({size}, 0); }}\n");
5709            let text = asm(&source);
5710            assert!(text.contains("movb\t$1, %al"), "{size} bytes is lock free: {text}");
5711            assert!(!text.contains("call"), "and is not a call: {text}");
5712        }
5713        for size in ["3", "16", "sizeof(long double)"] {
5714            let source = format!("int f(void) {{ return __atomic_is_lock_free({size}, 0); }}\n");
5715            let text = asm(&source);
5716            assert!(text.contains("movb\t$0, %al"), "{size} bytes is not: {text}");
5717            assert!(!text.contains("call"), "and is not a call either: {text}");
5718        }
5719
5720        // A size the compiler cannot work out, which is no rather than a refusal, and an object
5721        // whose type is aligned under the size asked about, which is the whole of what the second
5722        // argument is for.
5723        let text = asm("int f(int n) { return __atomic_is_lock_free(n, 0); }\n");
5724        assert!(text.contains("movb\t$0, %al"), "a size nobody knows is not lock free: {text}");
5725        let text = asm("int f(int *p) { return __atomic_always_lock_free(8, p); }\n");
5726        assert!(text.contains("movb\t$0, %al"), "eight bytes at four is not: {text}");
5727        let text = asm("int f(long *p) { return __atomic_always_lock_free(8, p); }\n");
5728        assert!(text.contains("movb\t$1, %al"), "and at eight it is: {text}");
5729    }
5730
5731    /// A memory order an operation cannot carry is read as the strongest one, and said so about.
5732    ///
5733    /// There are three ways the number is not one the operation can take: it is not a constant at
5734    /// all, it is not one of the six the headers define, or it is one of them and means nothing for
5735    /// this operation, which is a release load or an acquire store. All three become sequential
5736    /// consistency, which is stronger than anything the program could have meant, so a program that
5737    /// wrote nonsense gets a correct answer rather than a fast one. gcc does the same.
5738    ///
5739    /// The last two also warn, because the number was written down and is wrong. The first does
5740    /// not: gcc takes a computed order, and so does the C11 spelling, so a warning there would fire
5741    /// on correct programs.
5742    #[test]
5743    fn a_memory_order_an_operation_cannot_carry_is_read_as_the_strongest() {
5744        let mut opts = options();
5745        opts.emit = EmitKind::Ir;
5746
5747        let acquire_store = run(&opts, "void f(int *p, int v) { __atomic_store_n(p, v, 2); }\n");
5748        assert!(acquire_store.text().contains("seq_cst"), "{:?}", acquire_store.text());
5749        assert!(acquire_store.messages[0].contains("[W0333]"), "{:?}", acquire_store.messages);
5750
5751        let nonsense = run(&opts, "int f(int *p) { return __atomic_load_n(p, 99); }\n");
5752        assert!(nonsense.text().contains("seq_cst"), "{:?}", nonsense.text());
5753        assert!(nonsense.messages[0].contains("[W0333]"), "{:?}", nonsense.messages);
5754
5755        let computed = run(&opts, "int f(int *p, int n) { return __atomic_load_n(p, n); }\n");
5756        assert!(computed.text().contains("seq_cst"), "{:?}", computed.text());
5757        assert_eq!(computed.messages, Vec::<String>::new(), "a computed order is not a mistake");
5758    }
5759
5760    /// A conversion between a float and the widest unsigned integer, which the machine has not got.
5761    ///
5762    /// Every other conversion between a float and an integer is the signed one at some width with a
5763    /// widening in front or a narrowing behind. These two are not, because there is no signed width
5764    /// that holds every value of an unsigned sixty four bit integer, so each is the signed
5765    /// conversion with arithmetic around it that brings the value into range and puts it back.
5766    ///
5767    /// What is checked here is that the conversion happens at all and that it happens without a
5768    /// branch. gcc writes a branch for both; this writes the choice as a mask, because every rewrite
5769    /// in that pass stays inside the block it started in. The arithmetic itself is checked in
5770    /// `rucc-codegen`, where it can be run against the answer rather than read in the assembly.
5771    #[test]
5772    fn a_conversion_between_a_float_and_the_widest_unsigned_integer_is_written_without_a_branch() {
5773        let text = asm("double f(unsigned long long x) { return (double)x; }\n");
5774        assert!(text.contains("cvtsi2sdq"), "the signed conversion is what runs: {text}");
5775        assert!(text.contains("shrq"), "with the value halved first: {text}");
5776        assert!(text.contains("addsd"), "and doubled after: {text}");
5777        assert!(!text.contains("\tj"), "and no branch anywhere: {text}");
5778
5779        let text = asm("unsigned long long f(double d) { return (unsigned long long)d; }\n");
5780        assert!(text.contains("cvttsd2siq"), "the signed conversion is what runs: {text}");
5781        assert!(text.contains("subsd"), "with half the range taken off first: {text}");
5782        assert!(text.contains("shlq\t$63"), "and the top bit put back: {text}");
5783        assert!(!text.contains("\tj"), "and no branch anywhere: {text}");
5784    }
5785
5786    /// The plain names are the library's only where nothing else has taken them.
5787    ///
5788    /// Four ways a program says it means something else. A `static` definition is its own
5789    /// function and the name outside the file is somebody else's. A declaration of another type
5790    /// is another function. `-fno-builtin` and `-fno-builtin-<name>` say so outright, and
5791    /// `-ffreestanding` says there is no C library for the name to be the name of. Every one of
5792    /// these was measured against gcc 16.2.0, which calls the program's function in all of them.
5793    ///
5794    /// The `__builtin_` spelling goes on meaning the library's function through all of it, which
5795    /// is what the prefix is for and what lets a freestanding build reach one deliberately.
5796    #[test]
5797    fn a_plain_name_the_program_took_is_the_programs_own_function() {
5798        let taken = concat!(
5799            "static long long llabs(long long b) { return 7; }\n",
5800            "long long f(long long x) { return llabs(x); }\n",
5801        );
5802        assert!(ir(taken).contains("call @llabs"), "a static definition is the program's own");
5803
5804        let retyped = concat!("int llabs(int b);\n", "int f(int x) { return llabs(x); }\n",);
5805        assert!(ir(retyped).contains("call @llabs"), "another type is another function");
5806
5807        let plain = concat!(
5808            "long long llabs(long long b);\n",
5809            "long long f(long long x) { return llabs(x); }\n",
5810        );
5811        let mut opts = options();
5812        opts.emit = EmitKind::Ir;
5813        assert!(!run(&opts, plain).text().contains("call @llabs"), "the library's by default");
5814
5815        opts.builtins = false;
5816        assert!(run(&opts, plain).text().contains("call @llabs"), "-fno-builtin");
5817
5818        opts.builtins = true;
5819        opts.no_builtin = vec!["llabs".to_owned()];
5820        assert!(run(&opts, plain).text().contains("call @llabs"), "-fno-builtin-llabs");
5821        let one = "long labs(long b);\nlong f(long x) { return labs(x); }\n";
5822        assert!(!run(&opts, one).text().contains("call @labs"), "one name and not the family");
5823
5824        // `-ffreestanding` reaches the front end as the same answer, which is what the driver
5825        // does with it in `compile`, and the prefixed spelling is untouched by any of it.
5826        opts.no_builtin = Vec::new();
5827        opts.builtins = false;
5828        let prefixed = "long long f(long long x) { return __builtin_llabs(x); }\n";
5829        assert!(!run(&opts, prefixed).text().contains("call @llabs"), "the prefix is a promise");
5830    }
5831
5832    /// The hint builtins are their first argument, and nothing is left of the hint.
5833    ///
5834    /// Which way a branch is expected to go is the whole of what they say, and there is nothing
5835    /// here that reads a branch weight yet, so what reaches the IR is the value and the hint is
5836    /// gone. The one thing the prototype has to keep doing is converting: gcc gives both of them
5837    /// a `long` result, so `sizeof(__builtin_expect((char)1, 1))` is eight and a narrower argument
5838    /// widens before it is answered with.
5839    ///
5840    /// Whether a side effect in the hint happens depends on the first argument, which is gcc's
5841    /// answer rather than a rule anybody designed. A constant first argument folds the whole call
5842    /// where it is written and the hint goes with it, and a first argument that is not a constant
5843    /// leaves the hint standing. Both halves are below and both were measured on gcc 16.2.0.
5844    #[test]
5845    fn the_hint_builtins_are_their_first_argument_and_the_hint_leaves_no_trace() {
5846        let text = ir(concat!(
5847            "long a = __builtin_expect(7, 1);\n",
5848            "long b = __builtin_expect_with_probability(9, 1, 0.9);\n",
5849            "unsigned long c = sizeof(__builtin_expect((char)1, 1));\n",
5850        ));
5851        assert!(text.contains("global @a : i64 = 7,"), "{text}");
5852        assert!(text.contains("global @b : i64 = 9,"), "{text}");
5853        assert!(text.contains("global @c : i64 = 8,"), "{text}");
5854        assert!(!text.contains("__builtin_expect"), "it is not a call to anything:\n{text}");
5855
5856        // A narrower argument is widened by the prototype before it is handed back, and it is
5857        // widened with its sign, since the parameter is a signed `long`.
5858        let text = body("long f(char c) { return __builtin_expect(c, 1); }\n");
5859        assert!(text.contains("sext"), "{text}");
5860
5861        // The first argument is a constant, so the second is not evaluated and `i` is still zero,
5862        // and neither is the third. What is left of each statement is the first argument widened,
5863        // which nothing reads and which the first pass that looks for dead code will take out.
5864        let one = "block0:\n    %0 = iconst.i32 0\n    %1 = iconst.i32 1\n    %2 = sext.i64 %1\n    return %0\n";
5865        assert_eq!(body("int f(void) { int i = 0; __builtin_expect(1, i++); return i; }\n"), one);
5866        let source = "int g(void) { int i = 0; __builtin_expect_with_probability(1, i++, 0.5); return i; }\n";
5867        assert_eq!(body(source), one);
5868
5869        // The first argument is not a constant, so the hint runs and `i` comes back one. There is
5870        // an increment in the body and the value it returns is the load after it, which is what
5871        // gcc gives for the same program, and the whole of tamnd/rucc#584 is that this used to
5872        // come out the same as the pair above.
5873        let kept = body("int f(int n) { int i = 0; __builtin_expect(n, i++); return i; }\n");
5874        assert!(kept.contains("add.nsw"), "the hint still runs: {kept}");
5875        assert!(kept.ends_with("return %3\n"), "and the answer is what it left behind: {kept}");
5876        let both = "int g(int n) { int i = 0; __builtin_expect_with_probability(n, i++, 0.5); return i; }\n";
5877        assert!(body(both).contains("add.nsw"), "and so does the one with three arguments");
5878    }
5879
5880    /// A point control does not arrive at, in both of the ways the compiler has one.
5881    ///
5882    /// `__builtin_unreachable()` is the promise written down, and a function whose body can run
5883    /// off the bottom is the walk arriving at the same place on its own. Neither writes an
5884    /// instruction, which is what gcc 16.2.0 does at `-O0`: it emits the epilogue and the `ret`
5885    /// for both of the functions below and nothing else, and the two of them come out byte for
5886    /// byte the same there.
5887    ///
5888    /// The `ret` is the part worth holding on to. It is not there because anything runs it, it is
5889    /// there because a function whose last instruction is not a return is one that falls into
5890    /// whatever the assembler puts after it.
5891    #[test]
5892    fn a_promise_that_control_does_not_arrive_writes_no_instruction() {
5893        let promised = "int f(int x) { if (x) return 1; __builtin_unreachable(); }\n";
5894        let text = ir(promised);
5895        assert!(text.contains("    unreachable_hint\n"), "{text}");
5896        assert!(!text.contains("call"), "it is not a call to anything:\n{text}");
5897
5898        // The statement after it is still lowered. Continuing to translate a path the program
5899        // promised is dead is one of the things a compiler may do with undefined behaviour, and
5900        // it is the one that keeps a program built at `-O0` behaving the way it was watched to.
5901        let after = body("int g(int x) { __builtin_unreachable(); return x; }\n");
5902        assert!(after.contains("return"), "{after}");
5903
5904        // Both functions are the same instructions, because the hint writes none of them and the
5905        // terminator underneath it writes none either.
5906        let text = asm(promised);
5907        let mine = text.split_once("\nf:\n").expect("a definition").1;
5908        let mine = mine.split_once("\t.size").expect("a definition").0;
5909        let plain = asm("int f(int x) { if (x) return 1; }\n");
5910        let plain = plain.split_once("\nf:\n").expect("a definition").1;
5911        let plain = plain.split_once("\t.size").expect("a definition").0;
5912        assert_eq!(mine, plain);
5913        // The last instruction, rather than the last line, because the unwind record is closed
5914        // after it and a directive is not something the machine runs.
5915        let last = mine.lines().rfind(|line| !line.trim_start().starts_with('.'));
5916        assert_eq!(last.map(str::trim), Some("ret"), "{mine}");
5917        assert!(!mine.contains("ud2"), "{mine}");
5918    }
5919
5920    /// The two names stay apart, which is what having both of them is for.
5921    ///
5922    /// The one the program wrote is what the call is checked against and what a diagnostic about
5923    /// it says, and the one the library defines is what the call ends up carrying. A compiler
5924    /// that kept only the second would report this against `abort`, which is a function the
5925    /// program never mentions.
5926    #[test]
5927    fn a_library_builtin_is_diagnosed_under_the_name_the_program_wrote() {
5928        let mut opts = options();
5929        opts.emit = EmitKind::Ir;
5930        let messages = run(&opts, "void f(void) { __builtin_abort(1); }\n").messages;
5931        assert!(
5932            messages.iter().any(|m| m.contains("__builtin_abort")),
5933            "expected the written name in {messages:?}"
5934        );
5935    }
5936
5937    /// A builtin nothing lowers is refused where it is written, rather than at the link.
5938    ///
5939    /// One name is left, which is the last of the atomic family that is refused and is also the
5940    /// one whose prefix is not `__builtin_`; its older half has nothing left in it at all, and so
5941    /// does the half of the family that carries a prototype. What the message has to carry is the
5942    /// name, because the whole complaint about the link error this replaces is that the name in it
5943    /// was one the compiler chose.
5944    #[test]
5945    fn a_builtin_nothing_lowers_is_refused_by_name() {
5946        let mut opts = options();
5947        opts.emit = EmitKind::Ir;
5948        let builtin = "__atomic_signal_fence";
5949        let source = format!("int counter;\nint f(void) {{ return ({builtin}(5), 0); }}\n");
5950        let messages = run(&opts, &source).messages;
5951        let named = messages.iter().any(|m| m.contains(builtin) && m.contains("E0686"));
5952        assert!(named, "expected {builtin} to be refused by name in {messages:?}");
5953    }
5954
5955    /// The refusal is about a call and not about the name, so a program that defines the name
5956    /// itself gets the function it wrote.
5957    ///
5958    /// That is not the reason the refusal exists, but a definition in front of us is a definition
5959    /// and the call to it links. It works here because the name is one with no prototype and no
5960    /// meaning the front end knows, which is what is left once the rest of the family is
5961    /// implemented: a `__builtin_` name the front end does answer is answered whatever the program
5962    /// declares, the way gcc answers one.
5963    #[test]
5964    fn what_is_refused_is_the_call_and_not_the_name() {
5965        let text = ir(concat!(
5966            "void __atomic_signal_fence(int order) { (void)order; }\n",
5967            "void f(void) { __atomic_signal_fence(5); }\n",
5968        ));
5969        assert!(text.contains("call @__atomic_signal_fence"), "{text}");
5970    }
5971
5972    /// How many bytes are behind an address is read off the layout, for every shape the walk
5973    /// covers.
5974    ///
5975    /// This is what `_FORTIFY_SOURCE` runs on, so the numbers matter one at a time rather than in
5976    /// aggregate: a size too small turns a correct copy into an abort, and a size too large turns
5977    /// a checked copy back into an unchecked one. Every answer here was measured against gcc
5978    /// 16.2.0 first. They are written as initializers so that each one is a constant in the
5979    /// output and the test reads as the table it is.
5980    #[test]
5981    fn the_object_size_of_an_address_is_what_the_layout_leaves_in_front_of_it() {
5982        let text = ir(concat!(
5983            "struct S { char a[8]; int n; char b[12]; };\n",
5984            "char g[32];\n",
5985            "struct S gs;\n",
5986            "unsigned long whole = __builtin_object_size(g, 0);\n",
5987            "unsigned long moved = __builtin_object_size(g + 4, 0);\n",
5988            "unsigned long back = __builtin_object_size(g + 30 - 2, 0);\n",
5989            "unsigned long outer = __builtin_object_size(gs.a, 0);\n",
5990            "unsigned long inner = __builtin_object_size(gs.a, 1);\n",
5991            "unsigned long scalar = __builtin_object_size(&gs.n, 1);\n",
5992            "unsigned long after = __builtin_object_size(&gs.n, 0);\n",
5993            "unsigned long into = __builtin_object_size(&gs.b[2], 1);\n",
5994            "unsigned long text = __builtin_object_size(\"hello\", 0);\n",
5995            "unsigned long dyn = __builtin_dynamic_object_size(gs.b, 1);\n",
5996        ));
5997        for (name, size) in [
5998            ("whole", 32),
5999            ("moved", 28),
6000            ("back", 4),
6001            ("outer", 24),
6002            ("inner", 8),
6003            ("scalar", 4),
6004            ("after", 16),
6005            ("into", 10),
6006            ("text", 6),
6007            ("dyn", 12),
6008        ] {
6009            let said = format!("global @{name} : i64 = {size},");
6010            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
6011        }
6012    }
6013
6014    /// A local is as knowable as a global, which is the whole point of asking on the way into a
6015    /// copy.
6016    ///
6017    /// A fortified header expands around the destination the caller wrote, and the destination a
6018    /// program most wants checked is the buffer on its own stack. Nothing in the answer depends on
6019    /// storage duration, unlike in a constant expression, where the address of a local is exactly
6020    /// what is not allowed.
6021    #[test]
6022    fn the_object_behind_an_address_can_be_one_with_automatic_storage() {
6023        let text = body(concat!(
6024            "struct S { char a[8]; int n; char b[12]; };\n",
6025            "unsigned long f(void) {\n",
6026            "  char loc[20];\n",
6027            "  struct S ls;\n",
6028            "  return __builtin_object_size(loc + 3, 0) + __builtin_object_size(ls.b + 2, 1);\n",
6029            "}\n",
6030        ));
6031        assert!(text.contains("iconst.i64 17"), "twenty bytes with three used: {text}");
6032        assert!(text.contains("iconst.i64 10"), "twelve bytes with two used: {text}");
6033    }
6034
6035    /// An address whose object the walk cannot see answers at whichever end of the range the kind
6036    /// asks for.
6037    ///
6038    /// The two bits are a question and the answer has to fit it. A kind wanting the largest object
6039    /// the address could be in has to name a size nothing is bigger than, and a kind wanting the
6040    /// smallest has to name a size nothing is smaller than, so the unknown answers are all ones
6041    /// and zero. That pair is what a fortified header compares against to decide whether to check
6042    /// at all, and getting either of them the wrong way round turns every unknown copy into an
6043    /// abort.
6044    #[test]
6045    fn an_address_with_no_object_in_sight_answers_at_the_end_of_the_range_its_kind_asks_for() {
6046        let text = ir(concat!(
6047            "struct T { int n; char f[]; };\n",
6048            "extern char *p;\n",
6049            "extern struct T *t;\n",
6050            "unsigned long largest = __builtin_object_size(p, 0);\n",
6051            "unsigned long nearest = __builtin_object_size(p, 1);\n",
6052            "unsigned long least = __builtin_object_size(p, 2);\n",
6053            "unsigned long tight = __builtin_object_size(p, 3);\n",
6054            "unsigned long flex = __builtin_object_size(t->f, 1);\n",
6055            "int says = __builtin_object_size(p, 0) == (unsigned long)-1;\n",
6056        ));
6057        for name in ["largest", "nearest", "flex"] {
6058            // All ones, printed as the signed rendering of the sixty four bits it is held in.
6059            // `says` is what pins the pattern itself, since it is the comparison a fortified
6060            // header writes and it folds only if every bit is set.
6061            let said = format!("global @{name} : i64 = -1,");
6062            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
6063        }
6064        for name in ["least", "tight"] {
6065            let said = format!("global @{name} : i64 = 0,");
6066            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
6067        }
6068        assert!(text.contains("global @says : i32 = 1,"), "{text}");
6069    }
6070
6071    /// The address is not evaluated, which is the rule `sizeof` follows and for the same reason.
6072    ///
6073    /// What the builtin reads is the shape of the expression rather than the value it would
6074    /// produce, so there is nothing to run. It matters because a fortified header writes the
6075    /// destination twice, once into the copy and once into the size, and a program whose
6076    /// destination is `*next()` would advance twice if this evaluated.
6077    #[test]
6078    fn the_address_an_object_size_is_asked_about_is_not_evaluated() {
6079        let text = body(concat!(
6080            "extern char *side(void);\n",
6081            "unsigned long f(void) { return __builtin_object_size(side(), 0); }\n",
6082        ));
6083        assert!(!text.contains("call"), "nothing is called: {text}");
6084    }
6085
6086    /// The kind has to be a constant in range, because it says which of four questions was asked.
6087    ///
6088    /// A number that is not known until the program runs decides nothing, and one outside the two
6089    /// bits names no question at all. gcc refuses both in one sentence and so does this.
6090    #[test]
6091    fn a_kind_that_is_not_one_of_the_four_is_refused() {
6092        for source in [
6093            "extern char *p;\nextern int k;\nunsigned long f(void) ".to_owned()
6094                + "{ return __builtin_object_size(p, k); }\n",
6095            "extern char *p;\nunsigned long f(void) { return __builtin_object_size(p, 4); }\n"
6096                .to_owned(),
6097            "extern char *p;\nunsigned long f(void) ".to_owned()
6098                + "{ return __builtin_dynamic_object_size(p, -1); }\n",
6099        ] {
6100            let messages = errors(&source);
6101            let named = messages.iter().any(|m| m.contains("E0709") && m.contains("0 to 3"));
6102            assert!(named, "expected a complaint about the kind in {messages:?}");
6103        }
6104    }
6105
6106    /// The pair that saves a place in a function and comes back to it, which is not a call.
6107    ///
6108    /// What the IR has to show is one instruction each and no call to anything: there is no
6109    /// function of either name for a call to reach, and a program that got one would fail to link.
6110    /// The save answers an `int`, which is the value that says how control got there.
6111    #[test]
6112    fn the_pair_that_saves_a_place_lowers_to_the_two_markers() {
6113        let text = ir(concat!(
6114            "void *buf[5];\n",
6115            "int f(void) {\n",
6116            "  if (__builtin_setjmp(buf)) return 2;\n",
6117            "  return 1;\n",
6118            "}\n",
6119            "void g(void) { __builtin_longjmp(buf, 1); }\n",
6120        ));
6121        assert!(text.contains("= setjmp_marker.i32 %0\n"), "the save answers a value: {text}");
6122        assert!(text.contains("    longjmp_marker %0\n"), "the restore answers nothing: {text}");
6123        assert!(!text.contains("call @"), "neither of them is a call: {text}");
6124    }
6125
6126    /// Every local of a function that saves a place lives in the frame, and not in a value.
6127    ///
6128    /// The edge a restore travels is not an edge of the graph, so a local the SSA construction
6129    /// renamed would answer the write that reached the read along the edges there are rather than
6130    /// the write that last ran. The second function here is the same code without the save, where
6131    /// the local is a value and there is no slot at all, which is what makes the first one a rule
6132    /// about the save and not about the shape of the code.
6133    #[test]
6134    fn a_local_of_a_function_that_saves_a_place_gets_a_slot() {
6135        let text = ir(concat!(
6136            "void *buf[5];\n",
6137            "int f(int x) { int a = 0; if (__builtin_setjmp(buf)) return a; a = 1; return x; }\n",
6138            "int g(int x) { int a = 0; if (x) return a; a = 1; return x; }\n",
6139        ));
6140        let (saves, plain) = text.split_once("func @g").expect("both functions");
6141        assert_eq!(saves.matches("= alloca").count(), 2, "the parameter and the local: {text}");
6142        assert!(saves.contains("store %9 -> %2"), "the local is written through: {text}");
6143        assert!(!plain.contains("alloca"), "nothing in the plain one needs a slot: {text}");
6144    }
6145
6146    /// What the save writes and where it leaves control, which is a new block.
6147    ///
6148    /// Four words: the frame pointer, the address to come back to, the stack pointer, and the
6149    /// address of the word the answer arrives in, which is this compiler's own and is why the
6150    /// block after the save opens with a load. The frame pointer is kept although the function
6151    /// asked for nothing and calls nothing, since the epilogue has to find the caller's frame
6152    /// after control has come back, and the frame is grown although there is one word in it,
6153    /// since a function control comes back into cannot use the red zone.
6154    #[test]
6155    fn the_save_writes_four_words_and_carries_on_in_a_new_block() {
6156        let text =
6157            asm(concat!("void *buf[5];\n", "int f(void) { return __builtin_setjmp(buf); }\n",));
6158        let body = text.split_once("\nf:\n").expect("the function").1;
6159        assert!(body.contains("\tmovq\t%rsp, %rbp\n"), "a frame pointer whatever: {text}");
6160        assert!(body.contains("\tsubq\t$8, %rsp\n"), "no red zone: {text}");
6161        assert!(body.contains("\tmovq\t%rbp, (%rax)\n"), "the frame pointer: {text}");
6162        assert!(body.contains("\tmovq\t%rsp, 16(%rax)\n"), "the stack pointer: {text}");
6163        assert!(body.contains("\tleaq\t.Lf_1(%rip), %rcx\n"), "where to come back to: {text}");
6164        assert!(body.contains("\tmovq\t%rcx, 8(%rax)\n"), "and that goes in the buffer: {text}");
6165        let back = body.split_once(".Lf_1:\n").expect("the block control comes back to").1;
6166        assert!(back.starts_with("\tmovq\t(%rsp), %rax\n"), "the answer is read back: {text}");
6167    }
6168
6169    /// Nothing stays in a register across the save, which is said with a write of every one of
6170    /// them and shows up as the callee-saved registers the function saves and restores.
6171    ///
6172    /// The restore puts back two registers and no others, so a function coming back through one
6173    /// finds every other register holding whatever the code between the two put there. The pushes
6174    /// are what makes the epilogue right on that path: the values popped are the caller's, off the
6175    /// stack the restore put back, rather than whatever is in the registers when control arrives.
6176    #[test]
6177    fn a_save_destroys_every_register_the_allocator_hands_out() {
6178        let text =
6179            asm(concat!("void *buf[5];\n", "int f(void) { return __builtin_setjmp(buf); }\n",));
6180        for reg in ["%rbx", "%r12", "%r13", "%r14", "%r15"] {
6181            assert!(text.contains(&format!("\tpushq\t{reg}\n")), "{reg} is saved: {text}");
6182            assert!(text.contains(&format!("\tpopq\t{reg}\n")), "{reg} is restored: {text}");
6183        }
6184    }
6185
6186    /// The restore puts both registers back before it goes, at every level.
6187    ///
6188    /// The jump reads the two of them as well as the address it goes through, which is what keeps
6189    /// it behind them. Without that the two instructions write registers nothing reads, and the
6190    /// scheduler at `-O2` puts the jump in front of both and the program comes back to a frame
6191    /// that is not there.
6192    #[test]
6193    fn the_restore_puts_the_frame_back_before_it_jumps() {
6194        for level in [rucc_session::OptLevel::O0, rucc_session::OptLevel::O2] {
6195            let mut opts = options();
6196            opts.emit = EmitKind::Asm;
6197            opts.opt_level = level;
6198            let source = "void *buf[5];\nvoid g(void) { __builtin_longjmp(buf, 1); }\n";
6199            let result = run(&opts, source);
6200            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
6201            let text = result.text().to_owned();
6202            let jump = text.find("\tjmp\t*%").unwrap_or_else(|| panic!("an indirect jump: {text}"));
6203            let stack = text.find(", %rsp\n").unwrap_or_else(|| panic!("the stack back: {text}"));
6204            let frame = text.find(", %rbp\n").unwrap_or_else(|| panic!("the frame back: {text}"));
6205            assert!(stack < jump, "the stack goes back first at {level:?}: {text}");
6206            assert!(frame < jump, "and so does the frame at {level:?}: {text}");
6207        }
6208    }
6209
6210    /// The second argument of the restore has one allowed value, which gcc 16.2.0 also insists on.
6211    ///
6212    /// This pair does not carry a value back the way the library's `longjmp` does, because what
6213    /// the matching save answers is decided by which way control reached it. So the argument is a
6214    /// place-holder, and a program that wrote anything else meant the library's function.
6215    #[test]
6216    fn a_longjmp_whose_second_argument_is_not_one_is_turned_down() {
6217        for source in [
6218            "void *buf[5];\nvoid f(void) { __builtin_longjmp(buf, 0); }\n",
6219            "void *buf[5];\nextern int v;\nvoid f(void) { __builtin_longjmp(buf, v); }\n",
6220        ] {
6221            let messages = errors(source);
6222            let named = messages.iter().any(|m| m.contains("E0710"));
6223            assert!(named, "expected a complaint about the value in {messages:?}");
6224        }
6225    }
6226
6227    /// A `static` function nothing refers to is not emitted, and one that is refered to is.
6228    ///
6229    /// The pair is written as one program so that the two answers come out of one walk. What
6230    /// makes the difference is the call in `main` and nothing else about either definition.
6231    #[test]
6232    fn a_static_function_nothing_refers_to_is_not_emitted() {
6233        let text = ir("static int dropped(void) { return 1; }\n\
6234                       static int kept(void) { return 2; }\n\
6235                       int main(void) { return kept(); }\n");
6236        assert!(text.contains("func @kept"), "{text}");
6237        assert!(!text.contains("dropped"), "{text}");
6238    }
6239
6240    /// The set is transitive, so two of them that only call each other are both dropped.
6241    ///
6242    /// Counting the references to a name would keep this pair, since each is named once, and
6243    /// that is the mistake this is here to catch: what decides it is whether a root reaches the
6244    /// definition, and a root is something the file has a reason to emit on its own.
6245    #[test]
6246    fn two_static_functions_that_only_call_each_other_are_both_dropped() {
6247        let text = ir("static int ping(void);\n\
6248                       static int pong(void) { return ping(); }\n\
6249                       static int ping(void) { return pong(); }\n\
6250                       int main(void) { return 0; }\n");
6251        assert!(!text.contains("ping"), "{text}");
6252        assert!(!text.contains("pong"), "{text}");
6253    }
6254
6255    /// Everything that names a function keeps it, whether or not the name is being called.
6256    ///
6257    /// An address taken in a body, an image that holds one, and a body that is only reached
6258    /// through another `static` function are three different ways for a definition to be needed
6259    /// and none of them is a call at the top level of a reachable function.
6260    #[test]
6261    fn naming_a_static_function_anywhere_keeps_it() {
6262        let text = ir("static int by_address(void) { return 1; }\n\
6263                       static int in_an_image(void) { return 2; }\n\
6264                       static int deeper(void) { return 3; }\n\
6265                       static int reaches_deeper(void) { return deeper(); }\n\
6266                       static int (*table[1])(void) = {in_an_image};\n\
6267                       int main(void) {\n\
6268                         int (*p)(void) = by_address;\n\
6269                         return p() + table[0]() + reaches_deeper();\n\
6270                       }\n");
6271        for kept in ["by_address", "in_an_image", "deeper", "reaches_deeper"] {
6272            assert!(text.contains(&format!("func @{kept}")), "expected {kept} in:\n{text}");
6273        }
6274    }
6275
6276    /// An attribute that says something outside the file reaches it keeps the definition.
6277    ///
6278    /// None of the five is implemented as anything else yet, and this is the part of each of
6279    /// them that a program notices first: a symbol a linker script names or a function the
6280    /// run-up to `main` calls is not written about anywhere a C file can see.
6281    #[test]
6282    fn an_attribute_keeps_a_static_function_nothing_refers_to() {
6283        for attribute in ["used", "retain", "constructor", "destructor", "__used__"] {
6284            let source = format!(
6285                "__attribute__(({attribute})) static int kept(void) {{ return 1; }}\n\
6286                 int main(void) {{ return 0; }}\n"
6287            );
6288            let text = ir(&source);
6289            assert!(text.contains("func @kept"), "for {attribute}:\n{text}");
6290        }
6291    }
6292
6293    /// A function with external linkage is emitted whatever this file does with it, because
6294    /// another one may call it, and that is what external linkage is.
6295    #[test]
6296    fn a_function_anything_could_call_is_emitted_without_being_called() {
6297        let text =
6298            ir("int nobody_here_calls_it(void) { return 1; }\nint main(void) { return 0; }\n");
6299        assert!(text.contains("func @nobody_here_calls_it"), "{text}");
6300    }
6301
6302    /// Four of the classification builtins are operators C already has, and become those.
6303    ///
6304    /// What the standard's macro promises over the operator is that it does not raise the
6305    /// invalid operation exception on a quiet NaN. This compiler does not model floating point
6306    /// exceptions, so there is nothing left for a node of its own to carry and a second way of
6307    /// spelling a comparison would be a second thing every pass has to know about.
6308    #[test]
6309    fn a_classification_c_has_an_operator_for_is_that_operator() {
6310        for (builtin, operator) in [
6311            ("__builtin_isgreater", "binary >"),
6312            ("__builtin_isgreaterequal", "binary >="),
6313            ("__builtin_isless", "binary <"),
6314            ("__builtin_islessequal", "binary <="),
6315        ] {
6316            let source = format!("int f(double x, double y) {{ return {builtin}(x, y); }}\n");
6317            let text = tast(&source);
6318            assert!(text.contains(&format!("{operator} : int")), "for {builtin}:\n{text}");
6319        }
6320    }
6321
6322    /// The rest of the family are comparisons in the IR and never a call to anything.
6323    ///
6324    /// `math.h` defines the macro of each of these names as the builtin of the same name, so
6325    /// there is no function under any of them for a call to reach. `isunordered` and
6326    /// `islessgreater` are predicates the IR's comparison already has, `isnan` is the value that
6327    /// is unordered with itself, and the two that ask about a magnitude are written against the
6328    /// infinities. `signbit` is the one that is not a question about the value, since a negative
6329    /// zero compares equal to a positive one, so its answer comes from the bits.
6330    #[test]
6331    fn the_classification_builtins_are_comparisons_and_not_calls() {
6332        let text = body("int f(double x, double y) { return __builtin_isunordered(x, y); }\n");
6333        assert_eq!(
6334            text,
6335            "block0(%0: f64, %1: f64):\n    %2 = fcmp uno %0, %1\n    %3 = zext.i32 \
6336                          %2\n    return %3\n"
6337        );
6338
6339        // Not `x != y`, which is true when the two are unordered and so is true of a NaN.
6340        let text = body("int f(double x, double y) { return __builtin_islessgreater(x, y); }\n");
6341        assert!(text.contains("fcmp one %0, %1"), "{text}");
6342
6343        let text = body("int f(double x) { return __builtin_isnan(x); }\n");
6344        assert!(text.contains("fcmp uno %0, %0"), "{text}");
6345
6346        let text = body("int f(double x) { return __builtin_isinf(x); }\n");
6347        assert!(text.contains("fconst.f64 0x7ff0000000000000"), "{text}");
6348        assert!(text.contains("fconst.f64 0xfff0000000000000"), "{text}");
6349        assert!(text.contains("%3 = fcmp oeq %0, %1"), "{text}");
6350        assert!(text.contains("%4 = fcmp oeq %0, %2"), "{text}");
6351        assert!(text.contains("%5 = or %3, %4"), "{text}");
6352
6353        // Strictly between the two infinities, which a NaN is not, because an ordered comparison
6354        // against either of them is false. That is what makes this one test rather than two.
6355        let text = body("int f(double x) { return __builtin_isfinite(x); }\n");
6356        assert!(text.contains("%3 = fcmp olt %2, %0"), "{text}");
6357        assert!(text.contains("%4 = fcmp olt %0, %1"), "{text}");
6358        assert!(text.contains("%5 = and %3, %4"), "{text}");
6359
6360        let text = body("int f(double x) { return __builtin_signbit(x); }\n");
6361        assert!(text.contains("%1 = bitcast.i64 %0"), "{text}");
6362        assert!(text.contains("icmp slt %1, %2"), "{text}");
6363
6364        // The same question of a value in the target's widest format, where the bits are eighty
6365        // and the object they sit in is sixteen bytes.
6366        let text = body("int f(long double x) { return __builtin_signbitl(x); }\n");
6367        assert!(text.contains("%1 = bitcast.i80 %0"), "{text}");
6368
6369        // The operand is evaluated once however many times it is compared, which is the whole
6370        // reason these are nodes rather than a rewriting into the operators.
6371        let text = body("double g(void);\nint f(void) { return __builtin_isnan(g()); }\n");
6372        assert_eq!(text.matches("call @g()").count(), 1, "{text}");
6373    }
6374
6375    /// A spelling that names a width converts its argument before it asks.
6376    ///
6377    /// gcc gives `__builtin_isinff` a `float` parameter and `__builtin_isinf` no parameter type
6378    /// at all, and the difference is visible rather than academic: `1e300` does not fit in a
6379    /// `float`, so converting it first is an infinity and not converting it is not. Both numbers
6380    /// here are what gcc 16 gives.
6381    #[test]
6382    fn a_classification_spelling_that_names_a_width_converts_before_it_asks() {
6383        let text = ir(concat!(
6384            "int a = __builtin_isinff(1e300);\n",
6385            "int b = __builtin_isinf(1e300);\n",
6386            // Folded here rather than compared at run time, because a question about a value has
6387            // an answer as soon as the value is a constant, and an initializer for an object
6388            // with static storage duration has to have one.
6389            "int c = __builtin_isnan(0.0);\n",
6390            "int d = __builtin_signbit(-0.0);\n",
6391            "int e = __builtin_islessgreater(1.0, 2.0);\n",
6392        ));
6393        assert!(text.contains("global @a : i32 = 1,"), "{text}");
6394        assert!(text.contains("global @b : i32 = 0,"), "{text}");
6395        assert!(text.contains("global @c : i32 = 0,"), "{text}");
6396        assert!(text.contains("global @d : i32 = 1,"), "{text}");
6397        assert!(text.contains("global @e : i32 = 1,"), "{text}");
6398    }
6399
6400    /// An argument that is not floating point is refused, in gcc's words.
6401    #[test]
6402    fn a_classification_builtin_refuses_an_argument_that_is_not_floating_point() {
6403        let mut opts = options();
6404        opts.emit = EmitKind::Ir;
6405        let source = concat!(
6406            "int a(int x) { return __builtin_isnan(x); }\n",
6407            "int b(int x, int y) { return __builtin_isunordered(x, y); }\n",
6408            "int c(double x) { return __builtin_isnan(x, x); }\n",
6409        );
6410        let messages = run(&opts, source).messages;
6411        assert_eq!(
6412            messages,
6413            [
6414                "/main.c:1:23: error: non-floating-point argument in call to function \
6415                 '__builtin_isnan' [E0685]",
6416                "/main.c:2:30: error: non-floating-point arguments in call to function \
6417                 '__builtin_isunordered' [E0685]",
6418                "/main.c:3:26: error: too many arguments to function '__builtin_isnan' [E0511]",
6419            ]
6420        );
6421    }
6422
6423    /// The three of the family that need a constant of the format other than an infinity.
6424    ///
6425    /// `isnormal` is the one that needs the smallest normal, and it is asked of the magnitude, so
6426    /// the sign comes off first and what is left is the same shape as `isfinite`. `isinf_sign` is
6427    /// the one whose answer is a number: the two comparisons `isinf` builds, subtracted rather
6428    /// than combined. `fpclassify` is four questions of one value and five answers to pick from,
6429    /// and the picking is a mask because all five are constants and neither of them can have an
6430    /// effect.
6431    #[test]
6432    fn the_last_three_classification_builtins_are_comparisons_and_not_calls() {
6433        let text = body("int f(double x) { return __builtin_isnormal(x); }\n");
6434        // The sign off, which is the magnitude, and then the range, asked of the bits rather than
6435        // of the number, since the encoding of a value whose sign bit is clear rises with the
6436        // value in every format this compiles for.
6437        assert!(text.contains("%1 = bitcast.i64 %0"), "{text}");
6438        assert!(text.contains("%2 = iconst.i64 9223372036854775807"), "{text}");
6439        assert!(text.contains("%3 = and %1, %2"), "{text}");
6440        assert!(text.contains("%4 = iconst.i64 4503599627370496"), "{text}");
6441        assert!(text.contains("%5 = iconst.i64 9218868437227405312"), "{text}");
6442        assert!(text.contains("%6 = icmp uge %3, %4"), "{text}");
6443        assert!(text.contains("%7 = icmp ult %3, %5"), "{text}");
6444        assert!(text.contains("%8 = and %6, %7"), "{text}");
6445
6446        // The same question in the target's widest format, where the smallest normal has the
6447        // leading significand bit stored rather than implied, so its encoding is two bits and not
6448        // one.
6449        let text = body("int f(long double x) { return __builtin_isnormal(x); }\n");
6450        assert!(text.contains("%4 = iconst.i80 27670116110564327424"), "{text}");
6451        assert!(text.contains("%5 = iconst.i80 604453686435277732577280"), "{text}");
6452
6453        let text = body("int f(double x) { return __builtin_isinf_sign(x); }\n");
6454        assert!(text.contains("%3 = fcmp oeq %0, %1"), "{text}");
6455        assert!(text.contains("%4 = fcmp oeq %0, %2"), "{text}");
6456        assert!(text.contains("%7 = sub %5, %6"), "{text}");
6457
6458        let text = body("int f(double x) { return __builtin_fpclassify(0, 1, 2, 3, 4, x); }\n");
6459        assert!(text.contains("fcmp uno %0, %0"), "{text}");
6460        assert!(text.contains("fcmp oeq %0, %6"), "{text}");
6461        // Four questions, each of them a bit widened into the type of the answer and then spread
6462        // into a mask that picks between the answer and whatever the questions after it settled
6463        // on. Nothing sign extends, because no rule lowers a sign extension out of one bit.
6464        assert_eq!(text.matches(" = zext.i32 ").count(), 4, "{text}");
6465        assert_eq!(text.matches(" = xor ").count(), 4, "{text}");
6466        assert!(!text.contains("call"), "{text}");
6467
6468        // The value is evaluated once however many questions are asked of it, which is the whole
6469        // reason `fpclassify` is a node rather than the chain of tests it turns into.
6470        let text = body(concat!(
6471            "double g(void);\n",
6472            "int f(void) { return __builtin_fpclassify(0, 1, 2, 3, 4, g()); }\n",
6473        ));
6474        assert_eq!(text.matches("call @g()").count(), 1, "{text}");
6475    }
6476
6477    /// Each of the three answers a constant where its operand is one.
6478    ///
6479    /// glibc's `fpclassify` macro is exactly this builtin, so a program that writes
6480    /// `fpclassify(0.0)` in a static initializer is writing this, and it has to have a value at
6481    /// translation time or the program is refused rather than merely compiled slowly. Every
6482    /// number here is what gcc 16 gives.
6483    #[test]
6484    fn the_last_three_classification_builtins_fold_where_their_operand_is_a_constant() {
6485        let text = ir(concat!(
6486            "int a = __builtin_isnormal(1.0);\n",
6487            "int b = __builtin_isnormal(0.0);\n",
6488            "int c = __builtin_isnormal(1.0 / 0.0);\n",
6489            "int d = __builtin_isinf_sign(-1.0 / 0.0);\n",
6490            "int e = __builtin_isinf_sign(1.0);\n",
6491            "int g = __builtin_fpclassify(0, 1, 2, 3, 4, 0.0);\n",
6492            "int h = __builtin_fpclassify(0, 1, 2, 3, 4, 1.0);\n",
6493            "int i = __builtin_fpclassify(0, 1, 2, 3, 4, 1.0 / 0.0);\n",
6494        ));
6495        assert!(text.contains("global @a : i32 = 1,"), "{text}");
6496        assert!(text.contains("global @b : i32 = 0,"), "{text}");
6497        assert!(text.contains("global @c : i32 = 0,"), "{text}");
6498        assert!(text.contains("global @d : i32 = -1,"), "{text}");
6499        assert!(text.contains("global @e : i32 = 0,"), "{text}");
6500        assert!(text.contains("global @g : i32 = 4,"), "{text}");
6501        assert!(text.contains("global @h : i32 = 2,"), "{text}");
6502        assert!(text.contains("global @i : i32 = 1,"), "{text}");
6503    }
6504
6505    /// `fpclassify` refuses what gcc refuses, in gcc's words.
6506    ///
6507    /// The five answers have to be integer constant expressions, because what the builtin does is
6508    /// pick one of them and a pick between values that are not known here would be a chain of
6509    /// conditionals over expressions the call has already evaluated.
6510    #[test]
6511    fn fpclassify_refuses_an_answer_that_is_not_an_integer_constant() {
6512        let mut opts = options();
6513        opts.emit = EmitKind::Ir;
6514        let source = concat!(
6515            "int a(double x, int n) { return __builtin_fpclassify(0, 1, n, 3, 4, x); }\n",
6516            "int b(double x) { return __builtin_fpclassify(0, 1, 2, 3, x); }\n",
6517            "int c(int x) { return __builtin_fpclassify(0, 1, 2, 3, 4, x); }\n",
6518        );
6519        let messages = run(&opts, source).messages;
6520        assert_eq!(
6521            messages,
6522            [
6523                "/main.c:1:60: error: non-const integer argument 3 in call to function \
6524                 '__builtin_fpclassify' [E0687]",
6525                "/main.c:2:26: error: too few arguments to function '__builtin_fpclassify' \
6526                 [E0511]",
6527                "/main.c:3:23: error: non-floating-point argument in call to function \
6528                 '__builtin_fpclassify' [E0685]",
6529            ]
6530        );
6531    }
6532
6533    /// A builtin whose answer is a constant is one, and is not a call to the library.
6534    ///
6535    /// This is the reason the family is answered in the front end at all. `double x =
6536    /// __builtin_inf();` at file scope initializes an object with static storage duration, so
6537    /// there is no point in the program at which a call could be made, and a compiler that
6538    /// lowered it to one would reject a program gcc accepts. Every number here is the encoding
6539    /// gcc 16 gives on x86-64.
6540    #[test]
6541    fn a_builtin_whose_answer_is_a_constant_is_one_and_not_a_call() {
6542        let text = ir(concat!(
6543            "double a = __builtin_inf();\n",
6544            "float b = __builtin_huge_valf();\n",
6545            "long double c = __builtin_infl();\n",
6546            "double d = __builtin_huge_val();\n",
6547        ));
6548        assert!(text.contains("global @a : f64 = 0x7ff0000000000000,"), "{text}");
6549        assert!(text.contains("global @b : f32 = 0x7f800000,"), "{text}");
6550        assert!(text.contains("f80 0x7fff8000000000000000"), "{text}");
6551        assert!(text.contains("global @d : f64 = 0x7ff0000000000000,"), "{text}");
6552        assert!(!text.contains("call"), "{text}");
6553    }
6554
6555    /// A nan is written with the payload the program asked for.
6556    ///
6557    /// The string is read the way `strtoull` reads a number, which is what the library function
6558    /// of the same name does with it, and a string that is not one at all leaves the call for the
6559    /// library to answer at run time. A quiet nan has the high fraction bit set and a signalling
6560    /// one does not, except that a signalling nan with nothing in it would be an infinity, so it
6561    /// gets the next bit down instead. Every encoding here was measured against gcc 16, the two
6562    /// `long double` ones on a machine with the x87 format.
6563    #[test]
6564    fn a_nan_is_written_with_the_payload_the_program_asked_for() {
6565        let text = ir(concat!(
6566            "double a = __builtin_nan(\"\");\n",
6567            "double b = __builtin_nan(\"0x1\");\n",
6568            // Octal, since there is a leading zero, so this is eight and not ten.
6569            "double c = __builtin_nan(\"010\");\n",
6570            "double d = __builtin_nans(\"\");\n",
6571            "double e = __builtin_nans(\"0x1\");\n",
6572            "float f = __builtin_nanf(\"0x1\");\n",
6573            "float g = __builtin_nansf(\"\");\n",
6574            "long double h = __builtin_nansl(\"\");\n",
6575        ));
6576        assert!(text.contains("global @a : f64 = 0x7ff8000000000000,"), "{text}");
6577        assert!(text.contains("global @b : f64 = 0x7ff8000000000001,"), "{text}");
6578        assert!(text.contains("global @c : f64 = 0x7ff8000000000008,"), "{text}");
6579        assert!(text.contains("global @d : f64 = 0x7ff4000000000000,"), "{text}");
6580        assert!(text.contains("global @e : f64 = 0x7ff0000000000001,"), "{text}");
6581        assert!(text.contains("global @f : f32 = 0x7fc00001,"), "{text}");
6582        assert!(text.contains("global @g : f32 = 0x7fa00000,"), "{text}");
6583        assert!(text.contains("f80 0x7fffa000000000000000"), "{text}");
6584
6585        // A payload that is not a number, and one that is not known until run time, are both
6586        // left to the library, which is the same thing gcc emits for either of them.
6587        let text = ir(concat!(
6588            "double f(const char *p) { return __builtin_nan(p); }\n",
6589            "double g(void) { return __builtin_nans(\"1x\"); }\n",
6590        ));
6591        assert_eq!(text.matches("call @nan(").count(), 1, "{text}");
6592        assert_eq!(text.matches("call @nans(").count(), 1, "{text}");
6593    }
6594
6595    /// The length and the order of a string literal are known here.
6596    ///
6597    /// A program that asks for either of them is asking about something the translation already
6598    /// has in front of it, and folding is not only an optimization: `execute/921007-1.c` in the
6599    /// torture suite calls `__builtin_strcmp` in a file that defines its own `strcmp` with a
6600    /// different signature, so leaving the call behind is a name collision that gcc does not
6601    /// have. The comparison is over `unsigned char`, which is why the second one is negative.
6602    #[test]
6603    fn the_length_and_the_order_of_a_string_literal_are_known_here() {
6604        let text = ir(concat!(
6605            "unsigned long a = __builtin_strlen(\"hello\");\n",
6606            "unsigned long b = __builtin_strlen(\"a\\0bc\");\n",
6607            "int c = __builtin_strcmp(\"X\", \"X\\376\") < 0;\n",
6608            "int d = __builtin_strcmp(\"abc\", \"abc\");\n",
6609            "int e = __builtin_strcmp(\"abc\", \"ab\") > 0;\n",
6610        ));
6611        assert!(text.contains("global @a : i64 = 5,"), "{text}");
6612        assert!(text.contains("global @b : i64 = 1,"), "{text}");
6613        assert!(text.contains("global @c : i32 = 1,"), "{text}");
6614        assert!(text.contains("global @d : i32 = 0,"), "{text}");
6615        assert!(text.contains("global @e : i32 = 1,"), "{text}");
6616        assert!(!text.contains("call"), "{text}");
6617
6618        // An argument that is not a literal is the library's to answer, as it has to be.
6619        let text = ir("unsigned long f(const char *p) { return __builtin_strlen(p); }\n");
6620        assert!(text.contains("call @strlen("), "{text}");
6621    }
6622
6623    /// A sign builtin is a mask over the bits, and is not a call.
6624    ///
6625    /// `fabs` and `copysign` are in the math library rather than the C one, so a program that
6626    /// only ever wrote the prefixed spelling never asked for `-lm` and a call left behind here
6627    /// would not link. Neither needs anything the library has: one clears the sign bit and the
6628    /// other takes it from the second operand, and every other bit goes through untouched.
6629    #[test]
6630    fn a_sign_builtin_is_a_mask_over_the_bits_and_not_a_call() {
6631        let text = body("double f(double x) { return __builtin_fabs(x); }\n");
6632        assert!(text.contains("bitcast.i64 %0"), "{text}");
6633        assert!(text.contains("iconst.i64 9223372036854775807"), "{text}");
6634        assert!(text.contains("and %1, %2"), "{text}");
6635        assert!(text.contains("bitcast.f64 %3"), "{text}");
6636        assert!(!text.contains("call"), "{text}");
6637
6638        let text = body("double f(double x, double y) { return __builtin_copysign(x, y); }\n");
6639        assert!(text.contains("iconst.i64 -9223372036854775808"), "{text}");
6640        assert!(text.contains("%8 = or %4, %7"), "{text}");
6641        assert!(!text.contains("call"), "{text}");
6642
6643        // The x87 format, whose value is eighty bits sitting in an object of sixteen. The mask is
6644        // as wide as the value and not as wide as the object, so the padding is not part of it.
6645        let text = body("long double f(long double x) { return __builtin_fabsl(x); }\n");
6646        assert!(text.contains("bitcast.i80 %0"), "{text}");
6647        assert!(text.contains("bitcast.f80"), "{text}");
6648
6649        // The width a name does not spell out is `double`, so a `float` argument widens first and
6650        // the answer is a `double`, which is what gcc's declaration of it says.
6651        let text = body("double f(float x) { return __builtin_fabs(x); }\n");
6652        assert!(text.contains("fpext.f64 %0"), "{text}");
6653        assert!(text.contains("bitcast.i64 %1"), "{text}");
6654    }
6655
6656    /// The plain math library names are the same mask, which is what makes a program link.
6657    ///
6658    /// `math.h` declares `fabs` and never spells `__builtin_fabs`, so the plain name is the one
6659    /// every program that includes the header reaches. Recognising only the prefixed spelling
6660    /// leaves a call to the math library behind, and the math library is not on the link line
6661    /// unless the program asked for `-lm`. parson is the project that shows it: its makefile has
6662    /// no `-lm`, it does not need one under gcc, and `undefined reference to 'fabs'` is where the
6663    /// build stopped. That is issue 630.
6664    #[test]
6665    fn the_plain_math_names_are_the_same_mask_and_not_a_call() {
6666        let text =
6667            body(concat!("double fabs(double x);\n", "double f(double x) { return fabs(x); }\n",));
6668        assert!(text.contains("iconst.i64 9223372036854775807"), "{text}");
6669        assert!(!text.contains("call"), "{text}");
6670
6671        let text =
6672            body(concat!("float fabsf(float x);\n", "float f(float x) { return fabsf(x); }\n",));
6673        assert!(text.contains("bitcast.i32 %0"), "{text}");
6674        assert!(!text.contains("call"), "{text}");
6675
6676        let text = body(concat!(
6677            "double copysign(double x, double y);\n",
6678            "double f(double x, double y) { return copysign(x, y); }\n",
6679        ));
6680        assert!(text.contains("iconst.i64 -9223372036854775808"), "{text}");
6681        assert!(!text.contains("call"), "{text}");
6682
6683        let text = body(concat!(
6684            "float copysignf(float x, float y);\n",
6685            "float f(float x, float y) { return copysignf(x, y); }\n",
6686        ));
6687        assert!(!text.contains("call"), "{text}");
6688
6689        // The `long double` pair is left alone on purpose. The prefixed spelling of both stops in
6690        // the back end with `no rule lowers a bitcast producing an i80`, so expanding the plain
6691        // name would trade a link error for a worse one. They go in with issue 540.
6692        let text = ir(concat!(
6693            "long double fabsl(long double x);\n",
6694            "long double f(long double x) { return fabsl(x); }\n",
6695        ));
6696        assert!(text.contains("call @fabsl"), "{text}");
6697    }
6698
6699    /// A plain math name the program took is the program's own function.
6700    ///
6701    /// The same four ways as the absolute value family next door, asked again here because these
6702    /// two go through a different path: the plain names of this family are taken after the call
6703    /// has been checked against the declaration, and the declaration is the whole reason the
6704    /// question can be answered at all. Measured against gcc 16.2.0, which calls the program's
6705    /// function in every one of them.
6706    #[test]
6707    fn a_plain_math_name_the_program_took_is_the_programs_own_function() {
6708        let taken = concat!(
6709            "static double fabs(double b) { return 7; }\n",
6710            "double f(double x) { return fabs(x); }\n",
6711        );
6712        assert!(ir(taken).contains("call @fabs"), "a static definition is the program's own");
6713
6714        let retyped = concat!("int fabs(int b);\n", "int f(int x) { return fabs(x); }\n");
6715        assert!(ir(retyped).contains("call @fabs"), "another type is another function");
6716
6717        let plain = concat!("double fabs(double b);\n", "double f(double x) { return fabs(x); }\n");
6718        let mut opts = options();
6719        opts.emit = EmitKind::Ir;
6720        assert!(!run(&opts, plain).text().contains("call @fabs"), "the library's by default");
6721
6722        opts.builtins = false;
6723        assert!(run(&opts, plain).text().contains("call @fabs"), "-fno-builtin");
6724
6725        opts.builtins = true;
6726        opts.no_builtin = vec!["fabs".to_owned()];
6727        assert!(run(&opts, plain).text().contains("call @fabs"), "-fno-builtin-fabs");
6728        let one = concat!(
6729            "double copysign(double a, double b);\n",
6730            "double f(double x) { return copysign(x, 1.0); }\n",
6731        );
6732        assert!(!run(&opts, one).text().contains("call @copysign"), "one name and not the family");
6733
6734        // The prefixed spelling is untouched by any of it, which is what the prefix is for.
6735        opts.no_builtin = Vec::new();
6736        opts.builtins = false;
6737        let prefixed = "double f(double x) { return __builtin_fabs(x); }\n";
6738        assert!(!run(&opts, prefixed).text().contains("call @fabs"), "the prefix is not a library");
6739    }
6740
6741    /// The sign builtins answer a zero and a nan the way the bits say.
6742    ///
6743    /// This is why they are described over the bits rather than written with comparisons and
6744    /// negation. A negative zero compares equal to a positive one and has a sign bit to clear,
6745    /// and a nan compares equal to nothing at all and keeps its payload through both operations.
6746    /// `execute/ieee/copysign1.c` in the torture suite is the test that notices, because it
6747    /// compares its answers with `memcmp`. Every number here is what gcc 16 gives, the two in the
6748    /// x87 format measured on a machine that has it.
6749    #[test]
6750    fn the_sign_builtins_answer_a_zero_and_a_nan_the_way_the_bits_say() {
6751        let text = ir(concat!(
6752            "double a = __builtin_fabs(-3.5);\n",
6753            "double b = __builtin_copysign(1.0, -0.0);\n",
6754            "double c = __builtin_copysign(0.0, -2.0);\n",
6755            // The payload survives both, and only the sign bit moves.
6756            "double d = __builtin_copysign(-__builtin_nan(\"\"), 1.0);\n",
6757            "double e = __builtin_fabs(-__builtin_nan(\"0x1\"));\n",
6758            "float g = __builtin_copysignf(-0.0f, 2.0f);\n",
6759            "long double h = __builtin_copysignl(1.0L, -1.0L);\n",
6760            "long double i = __builtin_fabsl(-__builtin_infl());\n",
6761        ));
6762        assert!(text.contains("global @a : f64 = 0x400c000000000000,"), "{text}");
6763        assert!(text.contains("global @b : f64 = 0xbff0000000000000,"), "{text}");
6764        assert!(text.contains("global @c : f64 = 0x8000000000000000,"), "{text}");
6765        assert!(text.contains("global @d : f64 = 0x7ff8000000000000,"), "{text}");
6766        assert!(text.contains("global @e : f64 = 0x7ff8000000000001,"), "{text}");
6767        assert!(text.contains("global @g : f32 = 0x0,"), "{text}");
6768        assert!(text.contains("f80 0xbfff8000000000000000"), "{text}");
6769        assert!(text.contains("f80 0x7fff8000000000000000"), "{text}");
6770    }
6771
6772    /// The complex builtins are the halves of the value, and are not a call.
6773    ///
6774    /// `conj`, `creal` and `cimag` are `~`, `__real__` and `__imag__` under the names `complex.h`
6775    /// gives them, so there is nothing for the math library to do that the translation cannot do
6776    /// with the object in front of it. Leaving the call behind would not link either, since all
6777    /// three are in the math library and a program that wrote one never had a reason to ask for
6778    /// `-lm`. Measured against gcc 16.2.0, which emits no call for any of them even at `-O0`.
6779    #[test]
6780    fn the_complex_builtins_are_the_halves_of_the_value_and_not_a_call() {
6781        let text = body("double f(_Complex double z) { return __builtin_creal(z); }\n");
6782        assert!(!text.contains("call"), "{text}");
6783        let text = body("double f(_Complex double z) { return __builtin_cimag(z); }\n");
6784        assert!(!text.contains("call"), "{text}");
6785
6786        // The conjugate is the imaginary half negated and the real half as it stands, so there is
6787        // one negation in it. A complex negation is the one with two.
6788        let text = body("_Complex double f(_Complex double z) { return __builtin_conj(z); }\n");
6789        assert_eq!(text.matches("fneg").count(), 1, "{text}");
6790        assert!(!text.contains("call"), "{text}");
6791        let negated = body("_Complex double f(_Complex double z) { return -z; }\n");
6792        assert_eq!(negated.matches("fneg").count(), 2, "{negated}");
6793
6794        // `~` on a complex operand is the same operator, which is the spelling the language has
6795        // had all along and the one a program that never included the header writes.
6796        let written = body("_Complex double f(_Complex double z) { return ~z; }\n");
6797        assert_eq!(written, text, "the name and the operator are the same thing");
6798
6799        // The plain names, which are the ones the header declares and so the ones programs write.
6800        let text = body(concat!(
6801            "double creal(_Complex double z);\n",
6802            "double f(_Complex double z) { return creal(z); }\n",
6803        ));
6804        assert!(!text.contains("call"), "{text}");
6805        let text = body(concat!(
6806            "_Complex float conjf(_Complex float z);\n",
6807            "_Complex float f(_Complex float z) { return conjf(z); }\n",
6808        ));
6809        assert_eq!(text.matches("fneg").count(), 1, "{text}");
6810        assert!(!text.contains("call"), "{text}");
6811
6812        // A program that took the name means its own function, the same four ways the absolute
6813        // value family next door asks it.
6814        let taken = concat!(
6815            "static double creal(_Complex double z) { return 7; }\n",
6816            "double f(_Complex double z) { return creal(z); }\n",
6817        );
6818        assert!(ir(taken).contains("call @creal"), "a static definition is the program's own");
6819        let retyped = concat!("int cimag(int z);\n", "int f(int z) { return cimag(z); }\n");
6820        assert!(ir(retyped).contains("call @cimag"), "another type is another function");
6821        let plain = concat!(
6822            "double cimag(_Complex double z);\n",
6823            "double f(_Complex double z) { return cimag(z); }\n",
6824        );
6825        let mut opts = options();
6826        opts.emit = EmitKind::Ir;
6827        opts.builtins = false;
6828        assert!(run(&opts, plain).text().contains("call @cimag"), "-fno-builtin");
6829        opts.builtins = true;
6830        opts.no_builtin = vec!["cimag".to_owned()];
6831        assert!(run(&opts, plain).text().contains("call @cimag"), "-fno-builtin-cimag");
6832
6833        // A constant folds, which is what a static initializer written with one needs.
6834        let text = ir(concat!(
6835            "double a = __builtin_creal(1.5 + 2.5i);\n",
6836            "double b = __builtin_cimag(1.5 + 2.5i);\n",
6837            "_Complex double c = __builtin_conj(1.5 + 2.5i);\n",
6838        ));
6839        assert!(text.contains("global @a : f64 = 0x3ff8000000000000,"), "{text}");
6840        assert!(text.contains("global @b : f64 = 0x4004000000000000,"), "{text}");
6841        assert!(
6842            text.contains("{ f64 0x3ff8000000000000, f64 0xc004000000000000 }"),
6843            "the conjugate of a constant is the constant with the second half negated: {text}"
6844        );
6845        assert!(!text.contains("call"), "{text}");
6846    }
6847
6848    /// A math library builtin handed a constant is the answer, and is not a call.
6849    ///
6850    /// This is the reason the family is answered in the front end at all. `double x =
6851    /// __builtin_ceil(1.5);` at file scope initializes an object with static storage duration, so
6852    /// there is no point in the program at which a call could be made, and a compiler that lowered
6853    /// it to one would refuse a program gcc accepts. Every number here is the encoding gcc 16.2.0
6854    /// gives on x86-64, read out of the object file one initializer at a time.
6855    #[test]
6856    fn a_math_library_builtin_of_a_constant_is_the_answer_and_not_a_call() {
6857        let text = ir(concat!(
6858            "double a = __builtin_ceil(1.5);\n",
6859            "double b = __builtin_floor(1.5);\n",
6860            "double c = __builtin_trunc(-1.5);\n",
6861            // A half goes away from zero and not to even, which is where C and the default
6862            // rounding of IEEE 754 part company.
6863            "double d = __builtin_round(2.5);\n",
6864            // The sign survives a number that rounds away to nothing, so this is a negative zero.
6865            "double e = __builtin_ceil(-0.5);\n",
6866            "double f = __builtin_fmax(1.0, 2.0);\n",
6867            "double g = __builtin_fmin(1.0, 2.0);\n",
6868            "float h = __builtin_ceilf(1.25f);\n",
6869            // The plain name is the same answer, which is what a program that included `math.h`
6870            // and never wrote a prefix reaches.
6871            "double ceil(double x);\n",
6872            "double i = ceil(2.25);\n",
6873        ));
6874        assert!(text.contains("global @a : f64 = 0x4000000000000000,"), "{text}");
6875        assert!(text.contains("global @b : f64 = 0x3ff0000000000000,"), "{text}");
6876        assert!(text.contains("global @c : f64 = 0xbff0000000000000,"), "{text}");
6877        assert!(text.contains("global @d : f64 = 0x4008000000000000,"), "{text}");
6878        assert!(text.contains("global @e : f64 = 0x8000000000000000,"), "{text}");
6879        assert!(text.contains("global @f : f64 = 0x4000000000000000,"), "{text}");
6880        assert!(text.contains("global @g : f64 = 0x3ff0000000000000,"), "{text}");
6881        assert!(text.contains("global @h : f32 = 0x40000000,"), "{text}");
6882        assert!(text.contains("global @i : f64 = 0x4008000000000000,"), "{text}");
6883        assert!(!text.contains("call"), "{text}");
6884    }
6885
6886    /// A math library builtin handed anything else is a call to the library function it is.
6887    ///
6888    /// gcc emits `jmp ceil` for `__builtin_ceil` on x86-64 at the default architecture, measured
6889    /// on gcc 16.2.0, and reaches the `roundsd` instruction only under `-msse4.1`. So the call is
6890    /// what a program gets from gcc too, and the name on it is the plain one, which is the whole
6891    /// point of the prefixed spelling: a program writing it reaches the library's function even
6892    /// where a macro or a definition of its own has taken the short name.
6893    #[test]
6894    fn a_math_library_builtin_of_anything_else_is_a_call_to_the_library() {
6895        let text = ir(concat!(
6896            "double f(double x) { return __builtin_ceil(x); }\n",
6897            "float g(float x) { return __builtin_floorf(x); }\n",
6898            "double h(double x, double y) { return __builtin_fmax(x, y); }\n",
6899        ));
6900        assert!(text.contains("call @ceil("), "{text}");
6901        assert!(text.contains("call @floorf("), "{text}");
6902        assert!(text.contains("call @fmax("), "{text}");
6903
6904        // The two the rounding mode decides are calls even when the argument is a constant, since
6905        // what they answer is not known until the program runs. gcc refuses a static initializer
6906        // written with one for that reason, so there is nothing to fold here either.
6907        let text = ir(concat!(
6908            "double f(void) { return __builtin_rint(2.5); }\n",
6909            "double g(void) { return __builtin_nearbyint(2.5); }\n",
6910        ));
6911        assert!(text.contains("call @rint("), "{text}");
6912        assert!(text.contains("call @nearbyint("), "{text}");
6913
6914        // A nan operand is the library's rule rather than the machine's, 7.12.12.2 saying the
6915        // answer is the other operand, and gcc will not fold that one either.
6916        let text = ir("double f(void) { return __builtin_fmin(__builtin_nan(\"\"), 1.0); }\n");
6917        assert!(text.contains("call @fmin("), "{text}");
6918
6919        // `-fno-builtin-ceil` is a program saying it means its own `ceil`, and it leaves the
6920        // prefixed spelling alone, which is what writing the prefix is for.
6921        let plain = concat!("double ceil(double x);\n", "double f(void) { return ceil(2.25); }\n");
6922        let mut opts = options();
6923        opts.emit = EmitKind::Ir;
6924        opts.no_builtin = vec!["ceil".to_owned()];
6925        assert!(run(&opts, plain).text().contains("call @ceil("), "-fno-builtin-ceil");
6926    }
6927
6928    /// A `constexpr` object is a named constant, which is the whole reason the keyword exists.
6929    ///
6930    /// C23 6.6p8 puts two of them on the list an integer constant expression is built from: one
6931    /// of an arithmetic type, and a member of one of a structure or union type. A subscript of
6932    /// one is not on the list and is a variably modified type in gcc 16 as well, and every
6933    /// number here is what gcc 16 gives on x86-64.
6934    #[test]
6935    fn a_constexpr_object_is_a_constant_wherever_one_is_required() {
6936        let text = ir(concat!(
6937            "constexpr int side = 4;\n",
6938            "constexpr int wider = side + 1;\n",
6939            "constexpr double half = 1.5;\n",
6940            "struct point { int x; int y; };\n",
6941            "constexpr struct point origin = { 5, 6 };\n",
6942            "int square[side * side];\n",
6943            "int rectangle[wider];\n",
6944            "int rounded[(int)half * 2];\n",
6945            "int across[origin.y];\n",
6946            "enum named { four = side };\n",
6947            "int e = four;\n",
6948        ));
6949        assert!(text.contains("global @square : bytes 64 ="), "{text}");
6950        assert!(text.contains("global @rectangle : bytes 20 ="), "{text}");
6951        assert!(text.contains("global @rounded : bytes 8 ="), "{text}");
6952        assert!(text.contains("global @across : bytes 24 ="), "{text}");
6953        assert!(text.contains("global @e : i32 = 4,"), "{text}");
6954
6955        // A `const` object is not one of them, which is what makes `int a[n];` a variable
6956        // length array in C and is the distinction the keyword was added to draw.
6957        let mut opts = options();
6958        opts.emit = EmitKind::Ir;
6959        let konst = "const int n = 1;\nint a[n];\n";
6960        let message = "/main.c:2:5: error: variably modified 'a' at file scope [E0538]";
6961        assert_eq!(run(&opts, konst).messages, [message]);
6962
6963        // Nor is a subscript of one, which gcc 16 refuses in the same words.
6964        let subscript = "constexpr int t[3] = { 1, 2, 3 };\nint a[t[1]];\n";
6965        assert_eq!(run(&opts, subscript).messages, [message]);
6966
6967        // And `constexpr` implies `const`, so the address of one is an address of a `const`.
6968        let address = "constexpr int c = 3;\nint *p = &c;\n";
6969        let warning = "/main.c:2:6: warning: initialization discards 'const' qualifier from \
6970             pointer target type [E0514]";
6971        assert_eq!(run(&opts, address).messages, [warning]);
6972    }
6973
6974    /// A member whose size was refused is not a flexible array member, whatever it looks like.
6975    ///
6976    /// The refusal leaves the member with no size, which is also how `int a[]` is written, so
6977    /// without the count that tells the two apart the rules about where a flexible array member
6978    /// may sit read the wreckage of the first error as a second mistake. gcc 16.2.0 says one
6979    /// thing about each of these and so does this, which is what the program can act on: adding
6980    /// a named member to `struct D` makes the message about `k` no clearer, and moving `a` to
6981    /// the end of `struct E` does not either.
6982    #[test]
6983    fn a_member_whose_size_was_refused_is_not_a_flexible_array_member() {
6984        let mut opts = options();
6985        opts.emit = EmitKind::Ir;
6986
6987        let alone = "int k;\nextern struct D { int a[k]; } ed;\n";
6988        let message = "/main.c:2:23: error: variably modified 'a' at file scope [E0538]";
6989        assert_eq!(run(&opts, alone).messages, [message]);
6990
6991        // And not one in the wrong place either, which is the other half of the same rule.
6992        let first = "int k;\nextern struct E { int a[k]; int b; } ee;\n";
6993        assert_eq!(run(&opts, first).messages, [message]);
6994
6995        // A size that is refused for a reason of its own, to show the count is about the
6996        // refusal rather than about the one message that happens to have been found first.
6997        let negative = "struct F { int a[-1]; };\n";
6998        let refused = "/main.c:1:18: error: size of array 'a' is negative [E0536]";
6999        assert_eq!(run(&opts, negative).messages, [refused]);
7000
7001        // The member that was written with no size at all is still a flexible array member, and
7002        // a structure with nothing else in it still has no named member to hang one off.
7003        let flexible = "struct G { int a[]; };\n";
7004        let named = "/main.c:1:16: error: flexible array member in a struct with no named \
7005             members [E0554]";
7006        assert_eq!(run(&opts, flexible).messages, [named]);
7007    }
7008
7009    /// A pointer to an array, where the qualifiers are on the element and the comparison is not.
7010    ///
7011    /// 6.7.3p10 says the qualifiers in an array declaration belong to the element, so `const int
7012    /// [4]` is an unqualified array of `const int` and not a qualified array of `int`. Compatibility
7013    /// then reads the element types, finds one `const` and one not, and calls the two arrays
7014    /// incompatible, which makes `const int (*)[4] = p` an incompatible pointer rather than a
7015    /// pointer that gained a qualifier. That is what the wording said before C23 and it is not what
7016    /// any compiler does: gcc and clang take it, C23 wrote the rule the way they read it, and the
7017    /// two directions are told apart the way they are everywhere else, which is that adding a
7018    /// qualifier is silent and dropping one is worth a word.
7019    ///
7020    /// Found in libwebp, where `src/enc/vp8l_enc.c` takes the address of a `HistogramBuckets` out of
7021    /// a structure into a `const HistogramBuckets *const`, and a whole file of a real library did
7022    /// not compile for it.
7023    #[test]
7024    fn a_pointer_to_an_array_gains_a_qualifier_the_same_way_a_pointer_to_anything_else_does() {
7025        let mut opts = options();
7026        opts.emit = EmitKind::Ir;
7027        let prefix = "typedef unsigned int B[4];\nstruct H { B category[2]; };\n";
7028
7029        // Adding it, which is the direction the library writes and the one nothing is owed for.
7030        let adding = format!("{prefix}const B *f(struct H *h) {{ return &h->category[0]; }}\n");
7031        assert_eq!(run(&opts, &adding).messages, [] as [String; 0]);
7032
7033        // And the same thing written out rather than through the typedef, since the typedef is a
7034        // spelling and the rule is about the array.
7035        let plain = concat!(
7036            "const unsigned int (*f(unsigned int (*p)[4]))[4] { return p; }\n",
7037            "const unsigned int (*g(unsigned int (*p)[2][3]))[2][3] { return p; }\n",
7038        );
7039        assert_eq!(run(&opts, plain).messages, [] as [String; 0]);
7040
7041        // Dropping it, which is the direction that is worth a word, and the word is the one every
7042        // other pointer target gets rather than a complaint about the types not matching.
7043        let dropping = format!("{prefix}B *f(const B *p) {{ return p; }}\n");
7044        let warning = "/main.c:3:27: warning: return discards 'const' qualifier from pointer target type \
7045             [E0514]";
7046        assert_eq!(run(&opts, &dropping).messages, [warning]);
7047
7048        // A pointer to an array of something else is still an incompatible pointer, because
7049        // nothing here is about the element being a different type.
7050        let wrong = "const unsigned int (*f(unsigned short (*p)[4]))[4] { return p; }\n";
7051        let error = "/main.c:1:61: error: returning 'unsigned short (*)[4]' from a function with \
7052             incompatible return type 'const unsigned int (*)[4]' [E0512]";
7053        assert_eq!(run(&opts, wrong).messages, [error]);
7054    }
7055
7056    /// A definition that names its parameters and then declares them under the list.
7057    ///
7058    /// The declarations say what the types are, 6.9.1p6, and what the function takes is those
7059    /// types with the default argument promotions over them, which is what a caller of an
7060    /// unprototyped function hands over. A prototype already in scope overrules the promoted
7061    /// types, since a header saying `int narrow(char);` over a definition written this way is
7062    /// the pairing all the code written this way relies on and 6.7.6.3p15 is read that way by
7063    /// every compiler.
7064    #[test]
7065    fn an_old_style_definition_takes_its_types_from_the_declarations_under_its_list() {
7066        // C17, since the default dialect is the one that warns about the form and this is
7067        // about what it means rather than about the warning.
7068        let mut opts = options();
7069        opts.std = Std::C17;
7070        let source = concat!(
7071            "int add(a, b)\n",
7072            "int a;\n",
7073            "int b;\n",
7074            "{ return a + b; }\n",
7075            "int promoted(c)\n",
7076            "char c;\n",
7077            "{ return c; }\n",
7078            "int narrow(char);\n",
7079            "int narrow(c)\n",
7080            "char c;\n",
7081            "{ return c; }\n",
7082            "int first(a)\n",
7083            "int a[4];\n",
7084            "{ return a[0]; }\n",
7085        );
7086        let result = run(&opts, source);
7087        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
7088        let text = result.text();
7089        assert!(text.contains("add : int(int, int) function external defined"), "{text}");
7090        assert!(text.contains("promoted : int(int) function external defined"), "{text}");
7091        // The body still sees the `char` it was declared as, whatever the caller hands over.
7092        assert!(text.contains("c : char object automatic defined"), "{text}");
7093        assert!(text.contains("narrow : int(char) function external defined"), "{text}");
7094        // An array parameter is a pointer here as much as it is in a prototype.
7095        assert!(text.contains("first : int(int *) function external defined"), "{text}");
7096    }
7097
7098    /// What the two halves of an old-style parameter list can disagree about.
7099    ///
7100    /// Each of these is a sentence gcc 16 has, and every message below is the one it prints,
7101    /// read off it on x86-64 rather than reasoned about. The last two are the dialect: a name
7102    /// with no declaration is an `int` in C89 and a diagnostic from C99 on, and the whole form
7103    /// left the language in C23, where gcc still takes it and warns.
7104    #[test]
7105    fn the_two_halves_of_an_old_style_parameter_list_have_to_agree() {
7106        let mut opts = options();
7107        opts.std = Std::C17;
7108        for (source, message) in [
7109            ("int f(a, a)\nint a;\n{ return a; }\n", "1:10: error: multiple parameters named 'a'"),
7110            (
7111                "int f(a)\nint a;\nint b;\n{ return a; }\n",
7112                "3:5: error: declaration for parameter 'b' but no such parameter",
7113            ),
7114            ("int f(a)\nint a;\nint a;\n{ return a; }\n", "3:5: error: redefinition of parameter"),
7115            ("int f(a)\nint a = 1;\n{ return a; }\n", "2:5: error: parameter 'a' is initialized"),
7116            (
7117                "int f(a)\nstatic int a;\n{ return a; }\n",
7118                "2:12: error: storage class specified for parameter 'a'",
7119            ),
7120            (
7121                "int f(char);\nint f(a)\nshort a;\n{ return a; }\n",
7122                "2:7: error: argument 'a' doesn't match prototype",
7123            ),
7124        ] {
7125            let result = run(&opts, source);
7126            assert!(result.failed(), "expected this to fail:\n{source}");
7127            assert!(result.messages[0].contains(message), "{:?}", result.messages);
7128        }
7129
7130        // A name the declarations never mention. C89 gave it an `int` and gcc still takes it
7131        // in that dialect, and every dialect after it made the same line a diagnostic.
7132        let implicit = "int f(a, b)\nint a;\n{ return a + b; }\n";
7133        let mut older = options();
7134        older.std = Std::C89;
7135        assert!(!run(&older, implicit).failed(), "{:?}", run(&older, implicit).messages);
7136        let result = run(&opts, implicit);
7137        assert!(
7138            result.messages[0].contains("1:10: error: type of 'b' defaults to 'int'"),
7139            "{:?}",
7140            result.messages
7141        );
7142
7143        // C23 took the form out of the language and gcc kept accepting it with a warning, and
7144        // a warning is what this is, because the code written this way is not going to be
7145        // rewritten and refusing it would put the compiler out of reach of it.
7146        let mut newer = options();
7147        newer.std = Std::C23;
7148        let plain = "int f(a)\nint a;\n{ return a; }\n";
7149        let result = run(&newer, plain);
7150        assert!(!result.failed(), "{:?}", result.messages);
7151        assert_eq!(
7152            result.messages,
7153            ["/main.c:1:5: warning: old-style function definition [E0412]"]
7154        );
7155        assert!(run(&opts, plain).messages.is_empty(), "and nothing to say in the dialects before");
7156    }
7157
7158    /// The two obsolete designators, which are silent until `-pedantic` asks about them.
7159    ///
7160    /// `[3] 7` is what GCC had for an array before C99 settled on `[3] = 7`, and `x: 7` is the
7161    /// same era's spelling for a member. Both are still in code written against a compiler of
7162    /// that era, and gcc 16 takes both without a word unless it is asked to be pedantic, which
7163    /// is where the columns below come from as well.
7164    #[test]
7165    fn the_obsolete_designators_are_taken_and_are_pedantic_warnings() {
7166        let array = "int a[8] = { [3] 7 };\n";
7167        let member = "struct s { int x; } v = { x: 7 };\n";
7168        for source in [array, member] {
7169            let result = run(&options(), source);
7170            assert!(!result.failed(), "{:?}", result.messages);
7171            assert!(result.messages.is_empty(), "nothing to say: {:?}", result.messages);
7172        }
7173
7174        let mut asked = options();
7175        asked.pedantic = true;
7176        assert_eq!(
7177            run(&asked, array).messages,
7178            ["/main.c:1:18: warning: obsolete designator, write `[i] =` instead [E0415]"]
7179        );
7180        assert_eq!(
7181            run(&asked, member).messages,
7182            ["/main.c:1:27: warning: obsolete designator, write `.field =` instead [E0413]"]
7183        );
7184    }
7185
7186    /// A type nothing is ever an object of is a type `sizeof` still has to answer about, which
7187    /// is what `991014-1.c` in the gcc.c-torture execution suite asks.
7188    ///
7189    /// The limit is `PTRDIFF_MAX` and it is the same one for an array and for a record, so a
7190    /// record of every byte an object may have is laid out and one byte more is refused. All
7191    /// four numbers are what gcc 16 gives on x86-64.
7192    #[test]
7193    fn a_type_is_refused_when_it_passes_the_largest_object_and_not_before() {
7194        let text = ir(concat!(
7195            "struct huge_struct { short buf[(1L << 62) - 256]; int a, b, c, d; };\n",
7196            "struct brim { char buf[9223372036854775807L]; };\n",
7197            "struct bitty { char buf[9223372036854775800L]; int x : 1; };\n",
7198            "unsigned long h = sizeof(struct huge_struct);\n",
7199            "unsigned long b = sizeof(struct brim);\n",
7200            "unsigned long y = sizeof(struct bitty);\n",
7201        ));
7202        assert!(text.contains("global @h : i64 = 9223372036854775312,"), "{text}");
7203        assert!(text.contains("global @b : i64 = 9223372036854775807,"), "{text}");
7204        assert!(text.contains("global @y : i64 = 9223372036854775804,"), "{text}");
7205
7206        let mut opts = options();
7207        opts.emit = EmitKind::Ir;
7208        let over = "struct over { char buf[9223372036854775800L]; char x[8]; };\n";
7209        let message = "/main.c:1:1: error: type 'struct over' is too large [E0560]";
7210        assert_eq!(run(&opts, over).messages, [message]);
7211        let array = "struct wide { short buf[1L << 62]; };\n";
7212        let message = "/main.c:1:25: error: size of array 'buf' exceeds \
7213             maximum object size '9223372036854775807' [E0537]";
7214        assert_eq!(run(&opts, array).messages[0], message);
7215    }
7216
7217    /// A byte in the source that is not part of a character, which only a literal may hold.
7218    ///
7219    /// The source cannot be a `&str` here, which is the whole point: a file is bytes and only
7220    /// mostly text.
7221    fn compile_bytes(source: &[u8]) -> Compiled {
7222        let mut opts = options();
7223        opts.emit = EmitKind::Ir;
7224        let mut fs = MemoryFileSystem::new();
7225        fs.insert("/main.c", source.to_vec());
7226        compile(&opts, "/main.c", &fs)
7227    }
7228
7229    /// A raw byte inside a string literal is that byte, which gcc has always taken and which is
7230    /// the only place in a source file where a byte does not have to be part of a character.
7231    /// Replacing it would give the object three bytes rather than one, since the replacement
7232    /// character is three bytes of UTF-8, so the object would not be the one that was written
7233    /// even where the diagnostic is ignored. Anywhere else the byte is still a mistake, which
7234    /// is where gcc draws the same line.
7235    #[test]
7236    fn a_byte_that_is_not_a_character_is_kept_in_a_literal_and_refused_outside_one() {
7237        let mut source = b"char s[] = \"a".to_vec();
7238        source.push(0xff);
7239        source.extend_from_slice(b"b\";\nchar c = '");
7240        source.push(0xff);
7241        source.extend_from_slice(b"';\n");
7242        let result = compile_bytes(&source);
7243        assert_eq!(result.messages, Vec::<String>::new(), "a raw byte in a literal is that byte");
7244        assert!(result.text().contains(r#"bytes "a\ffb\00""#), "{}", result.text());
7245        // Plain `char` is signed on this target, so the constant is minus one rather than 255.
7246        assert!(result.text().contains("global @c : i8 = -1,"), "{}", result.text());
7247
7248        let mut stray = b"int a".to_vec();
7249        stray.push(0xff);
7250        stray.extend_from_slice(b" = 1;\n");
7251        let result = compile_bytes(&stray);
7252        assert!(
7253            result.messages.iter().any(|m| m.contains("source is not valid UTF-8 here")),
7254            "{:?}",
7255            result.messages
7256        );
7257    }
7258
7259    #[test]
7260    fn an_object_becomes_a_global_with_an_image_and_a_function_becomes_a_func() {
7261        let text = ir("int x = 7;\nint add(int a, int b) { return a + b; }\n");
7262        assert!(text.contains("global @x : i32 = 7, align 4, linkage(external)\n"), "{text}");
7263        let expected = "\
7264func @add(i32, i32) -> i32, linkage(external) {
7265block0(%0: i32, %1: i32):
7266    %2 = add.nsw %0, %1
7267    return %2
7268}
7269";
7270        assert!(text.contains(expected), "{text}");
7271    }
7272
7273    #[test]
7274    fn a_local_nothing_takes_the_address_of_is_a_value_and_never_a_stack_slot() {
7275        let text = body("int f(int n) { int a = n + 1; int b = a * 2; return a + b; }\n");
7276        assert!(!text.contains("alloca"), "{text}");
7277        assert!(!text.contains("load"), "{text}");
7278        assert!(!text.contains("store"), "{text}");
7279    }
7280
7281    #[test]
7282    fn a_local_whose_address_is_taken_gets_a_slot_in_the_entry_block() {
7283        let text = body("int g(int *);\nint f(void) { int a = 1; return g(&a); }\n");
7284        let expected = "\
7285block0:
7286    %0 = alloca, size 4, align 4
7287    %1 = iconst.i32 1
7288    store %1 -> %0, align 4, tbaa !1
7289    %2 = call @g(%0) : (ptr) -> i32
7290    return %2
7291";
7292        assert_eq!(text, expected);
7293    }
7294
7295    #[test]
7296    fn a_loop_carries_what_it_changes_as_block_parameters() {
7297        // The whole point of building SSA during the walk rather than after it: `i` and
7298        // `total` are values that arrive on an edge, and neither has ever been in memory.
7299        let text = body(
7300            "int f(int n) {\n  int total = 0;\n  for (int i = 0; i < n; i++) total += i;\n  \
7301             return total;\n}\n",
7302        );
7303        assert!(!text.contains("alloca"), "{text}");
7304        assert!(text.contains("block1(%3: i32, %4: i32):"), "{text}");
7305        assert!(text.contains("jump block1("), "{text}");
7306    }
7307
7308    #[test]
7309    fn a_comparison_used_as_a_condition_is_not_widened_and_narrowed_again() {
7310        let text = body("int f(int a, int b) { if (a < b) return 1; return 0; }\n");
7311        assert!(text.contains("icmp slt %0, %1"), "{text}");
7312        assert!(!text.contains("zext"), "{text}");
7313    }
7314
7315    #[test]
7316    fn the_right_side_of_a_short_circuit_is_in_a_block_of_its_own() {
7317        let text = body("int f(int a, int b) { return a && b; }\n");
7318        let expected = "\
7319block0(%0: i32, %1: i32):
7320    %2 = iconst.i32 0
7321    %3 = icmp ne %0, %2
7322    %4 = iconst.i1 0
7323    br_if %3, block1, block2(%4)
7324
7325block1:
7326    %5 = iconst.i32 0
7327    %6 = icmp ne %1, %5
7328    jump block2(%6)
7329
7330block2(%7: i1):
7331    %8 = zext.i32 %7
7332    return %8
7333";
7334        assert_eq!(text, expected);
7335    }
7336
7337    #[test]
7338    fn code_after_a_return_is_not_built_and_does_not_leave_an_empty_block_behind() {
7339        let text = body("int f(int a) { if (a) return 1; else return 2; return 3; }\n");
7340        // Three blocks, the test and the two arms. The join the `return 3` would need is
7341        // never created, because a block nothing branches to is not a block.
7342        assert!(!text.contains("block3"), "{text}");
7343        assert!(!text.contains("iconst.i32 3"), "{text}");
7344    }
7345
7346    #[test]
7347    fn falling_off_the_end_returns_zero_from_main_and_nothing_from_a_void_function() {
7348        assert!(body("int main(void) { }\n").contains("iconst.i32 0\n    return"));
7349        assert_eq!(body("void f(void) { }\n"), "block0:\n    return\n");
7350        assert!(body("int f(void) { }\n").contains("unreachable"));
7351    }
7352
7353    #[test]
7354    fn a_structure_is_copied_rather_than_held_in_a_value() {
7355        let text = body(
7356            "struct point { int x, y; };\n\
7357             int f(void) { struct point p = { 1, 2 }; struct point q = p; return q.x; }\n",
7358        );
7359        assert!(text.contains("memcpy"), "{text}");
7360    }
7361
7362    #[test]
7363    fn an_initializer_that_leaves_part_of_an_object_unwritten_zeroes_it_first() {
7364        let text = body("int f(void) { int a[4] = { 1 }; return a[3]; }\n");
7365        assert!(text.contains("memset"), "{text}");
7366    }
7367
7368    #[test]
7369    fn a_switch_is_one_branch_and_a_case_that_falls_through_carries_what_it_wrote() {
7370        let text = body(
7371            "int f(int x) { int r = 0; switch (x) { case 1: r = 1; case 2: r += 2; break; \
7372             default: r = 4; } return r; }\n",
7373        );
7374        let expected = "\
7375block0(%0: i32):
7376    %1 = iconst.i32 0
7377    switch %0, block1, [1 => block2, 2 => block3(%1)]
7378
7379block1:
7380    %2 = iconst.i32 4
7381    jump block4(%2)
7382
7383block2:
7384    %3 = iconst.i32 1
7385    jump block3(%3)
7386
7387block3(%4: i32):
7388    %5 = iconst.i32 2
7389    %6 = add.nsw %4, %5
7390    jump block4(%6)
7391
7392block4(%7: i32):
7393    return %7
7394";
7395        assert_eq!(text, expected);
7396    }
7397
7398    #[test]
7399    fn a_case_range_is_tested_for_rather_than_put_in_the_table() {
7400        // GNU's `case 1 ... 9`. Nine table entries would be nine here and four billion for the
7401        // range a program is allowed to write, so it is a subtraction and one unsigned compare.
7402        let text = body("int f(int x) { switch (x) { case 1 ... 9: return 1; } return 0; }\n");
7403        assert!(text.contains("%2 = sub %0, %1"), "{text}");
7404        assert!(text.contains("icmp ule"), "{text}");
7405        assert!(!text.contains("switch"), "{text}");
7406    }
7407
7408    #[test]
7409    fn break_leaves_the_switch_and_continue_leaves_the_loop_around_it() {
7410        let text = body(
7411            "int f(int n) { int t = 0; for (int i = 0; i < n; i++) { switch (i) { \
7412             case 0: continue; case 1: break; default: t += i; } t++; } return t; }\n",
7413        );
7414        // The `continue` goes to the step and the `break` goes to the `t++` after the switch,
7415        // which is also where the default falls out to.
7416        assert!(text.contains("switch %3, block4, [0 => block5, 1 => block6]"), "{text}");
7417        assert!(text.contains("block5:\n    jump block7("), "{text}");
7418        assert!(text.contains("block6:\n    jump block8("), "{text}");
7419    }
7420
7421    #[test]
7422    fn a_switch_with_nothing_to_branch_on_still_runs_what_comes_after_it() {
7423        assert_eq!(body("void f(int x) { switch (x) { } }\n"), "block0(%0: i32):\n    return\n");
7424    }
7425
7426    #[test]
7427    fn a_label_a_loop_is_only_entered_through_builds_the_loop_around_it() {
7428        // A branch into the middle of a loop that nothing else reaches, the Duff's device shape.
7429        // The `while` is not reached in order, so the walk starts a block nothing branches to and
7430        // builds it from there. What comes out is the loop with an edge straight into its body,
7431        // and the header that nothing arrives at is pruned.
7432        let text = body(
7433            "int f(int x, int n) { switch (x) { case 1: break; while (n) { case 2: n--; } } \
7434             return n; }\n",
7435        );
7436        // `case 2` lands on the body, `case 1` and the default land on the return, and the test
7437        // at the bottom of the loop comes back round to the body.
7438        assert!(text.contains("switch %0, block1(%1), [1 => block2, 2 => block3(%1)]"), "{text}");
7439        assert!(text.contains("block3(%3: i32):\n    %4 = iconst.i32 1"), "{text}");
7440        assert!(text.contains("block4:\n    jump block3("), "{text}");
7441    }
7442
7443    #[test]
7444    fn a_goto_into_a_loop_body_enters_it_without_the_test() {
7445        // The same thing through a `goto`. The first pass through the body runs whatever the
7446        // label is on, and only then does the loop reach its own test.
7447        let text = body("int f(int x, int n) { goto in; while (n) { in: n--; } return n; }\n");
7448        assert!(text.starts_with("block0(%0: i32, %1: i32):\n    jump block1(%1)"), "{text}");
7449        assert!(text.contains("block1(%2: i32):\n    %3 = iconst.i32 1"), "{text}");
7450        assert!(text.contains("br_if %6, block2, block3"), "{text}");
7451    }
7452
7453    #[test]
7454    fn a_goto_is_a_jump_to_the_block_the_label_starts() {
7455        let text = body("int f(int x) { int r = 0; if (x) goto out; r = 1; out: return r; }\n");
7456        // Both edges into `out` carry what `r` holds on the way, and neither is a stack slot. The
7457        // block the `goto` jumps out of is empty and hands its edge on, which is what moves `out`
7458        // up the block list to second place.
7459        assert!(!text.contains("alloca"), "{text}");
7460        assert!(text.contains("block2(%4: i32):\n    return %4"), "{text}");
7461        assert_eq!(text.matches("jump block2(").count(), 2, "{text}");
7462    }
7463
7464    #[test]
7465    fn a_backward_goto_is_a_loop_and_carries_what_it_changes() {
7466        let text =
7467            body("int f(int n) { int i = 0; again: if (i < n) { i++; goto again; } return i; }\n");
7468        assert!(!text.contains("alloca"), "{text}");
7469        assert!(text.contains("block1(%2: i32):"), "{text}");
7470        assert!(text.contains("jump block1(%5)"), "{text}");
7471    }
7472
7473    #[test]
7474    fn a_label_nothing_reaches_is_taken_out_rather_than_left_for_the_verifier() {
7475        // A block nothing branches to is not a legal function, and which labels are dead is not
7476        // known until the last statement has been walked, since the `goto` is allowed to be it.
7477        assert_eq!(
7478            body("int f(int x) { return x; spare: return 0; }\n"),
7479            "block0(%0: i32):\n    return %0\n"
7480        );
7481    }
7482
7483    #[test]
7484    fn a_bit_field_is_read_by_loading_the_bytes_it_lies_in_and_shifting() {
7485        let text = body(
7486            "struct s { unsigned a : 3; signed b : 5; };\nint f(struct s *p) { return p->b; }\n",
7487        );
7488        // One byte holds both fields, and the signed one needs no mask: shifting it down
7489        // arithmetically is what says its top bit is a sign.
7490        assert_eq!(
7491            text,
7492            "\
7493block0(%0: ptr):
7494    %1 = load.i8 %0, align 1
7495    %2 = iconst.i8 3
7496    %3 = ashr %1, %2
7497    %4 = sext.i32 %3
7498    return %4
7499"
7500        );
7501    }
7502
7503    #[test]
7504    fn a_store_to_a_bit_field_does_not_write_a_byte_it_has_no_bit_in() {
7505        // C11 says an ordinary member beside a bit-field is a memory location of its own, so
7506        // the four byte store this would take is a data race in a program that has none. The
7507        // three bytes of `a` go in as two and one, and `c` is not touched.
7508        let text =
7509            body("struct s { int a : 24; char c; };\nvoid f(struct s *p, int v) { p->a = v; }\n");
7510        assert_eq!(
7511            text,
7512            "\
7513block0(%0: ptr, %1: i32):
7514    %2 = iconst.i32 16777215
7515    %3 = and %1, %2
7516    %4 = trunc.i16 %3
7517    store %4 -> %0, align 2
7518    %5 = iconst.i32 16
7519    %6 = lshr %3, %5
7520    %7 = trunc.i8 %6
7521    %8 = iconst.i64 2
7522    %9 = ptr_add %0, %8
7523    store %7 -> %9, align 1
7524    return
7525"
7526        );
7527    }
7528
7529    #[test]
7530    fn what_an_assignment_to_a_bit_field_is_worth_is_what_fits_in_it() {
7531        let text =
7532            body("struct s { unsigned b : 5; };\nunsigned f(struct s *p) { return p->b = 33; }\n");
7533        // 33 does not fit in five bits, and 1 is both what goes in the field and what the
7534        // assignment is worth.
7535        assert!(text.contains("%3 = iconst.i8 31\n    %4 = and %2, %3"), "{text}");
7536        assert!(text.ends_with("%9 = zext.i32 %4\n    return %9\n"), "{text}");
7537    }
7538
7539    #[test]
7540    fn an_assignment_a_statement_throws_away_builds_none_of_what_it_is_worth() {
7541        // The value of an assignment to a bit-field takes a shift to build, and a statement
7542        // has no use for it. Nothing here reads back what was stored.
7543        let text = body("struct s { signed b : 5; };\nvoid f(struct s *p) { p->b = 3; }\n");
7544        assert_eq!(text.matches("ashr").count(), 0, "{text}");
7545        assert!(text.ends_with("store %8 -> %0, align 1\n    return\n"), "{text}");
7546    }
7547
7548    #[test]
7549    fn a_bit_field_in_an_initializer_goes_in_over_bytes_that_were_zeroed_first() {
7550        // A bit-field writes part of a byte and leaves the rest of it alone, so the object has
7551        // to be zero before it goes in or what the initializer did not name is whatever the
7552        // stack held.
7553        let text = body(
7554            "struct s { int a : 3; int b; };\nint f(void) { struct s v = { 1 }; return v.b; }\n",
7555        );
7556        assert!(text.contains("memset %0, %1, size 8, align 4"), "{text}");
7557    }
7558
7559    #[test]
7560    fn the_image_of_a_static_bit_field_is_the_bytes_the_fields_share() {
7561        // Two fields in one byte are not two entries in the image, because an image is written
7562        // in bytes: they are the byte they are both in.
7563        let text = ir("struct s { unsigned a : 3; unsigned b : 5; } g = { 1, 2 };\n");
7564        assert!(
7565            text.contains("global @g : bytes 4 = { bytes \"\\11\", zero 3 }, align 4"),
7566            "{text}"
7567        );
7568    }
7569
7570    #[test]
7571    fn an_initialized_flexible_array_member_makes_the_object_larger_than_its_type() {
7572        // `sizeof` answers without the array and the definition has to hold what was written, so
7573        // the object is the size of its image. gcc 16 gives these four, three and two bytes and
7574        // so does this. The image used to be written at the size the type had, which left the
7575        // verifier looking at twenty bytes going into four.
7576        let text = ir(concat!(
7577            "struct a { int i; int j[]; } x = { 1, { 2, 0, 2, 3 } };\n",
7578            "struct b { char c; char p[]; } y = { 'o', \"wx\" };\n",
7579            "struct c { char c; char p[]; } z = { '9', { 'e', 'b' } };\n",
7580            "char s[2] = \"hi\";\n",
7581        ));
7582        assert!(
7583            text.contains("global @x : bytes 20 = { i32 1, i32 2, i32 0, i32 2, i32 3 }"),
7584            "{text}"
7585        );
7586        assert!(text.contains("global @y : bytes 4 = { i8 111, bytes \"wx\\00\" }"), "{text}");
7587        assert!(text.contains("global @z : bytes 3 = { i8 57, i8 101, i8 98 }"), "{text}");
7588        // The array with a length of its own still cuts the literal down to it, which is the
7589        // one case in C where a string initializer drops its terminator.
7590        assert!(text.contains("global @s : bytes 2 = { bytes \"hi\" }"), "{text}");
7591    }
7592
7593    #[test]
7594    fn a_definition_takes_a_parameter_it_left_unnamed() {
7595        // The entry block's parameters are the definition's, and one the front end dropped for
7596        // having no name left the two lists different lengths, which the walk read as an
7597        // old-style definition and refused. gcc has taken these for far longer than C23 has.
7598        let text = ir("int f(int a, int) { return a; }\n");
7599        assert!(text.contains("func @f(i32, i32) -> i32"), "{text}");
7600        assert!(text.contains("block0(%0: i32, %1: i32):"), "{text}");
7601
7602        // The unnamed one first, so that the named one is the second parameter of the entry
7603        // block and not the first: the list says the order and not only how many there are.
7604        let text = ir("int g(int, int n) { return n; }\n");
7605        assert!(text.contains("block0(%0: i32, %1: i32):\n    return %1\n"), "{text}");
7606    }
7607
7608    #[test]
7609    fn an_assignment_of_a_structure_is_the_object_it_wrote() {
7610        // `d = e = c` used to be refused, because the middle assignment is a value of structure
7611        // type and the walk had nowhere to read one from. What an assignment is worth is the
7612        // value it stored, so the object it stored into is the answer and the chain is three
7613        // copies out of the one source with no temporary in it.
7614        let text = body(concat!(
7615            "struct s { int f; int g; };\n",
7616            "void h(struct s *a, struct s *c, struct s *d, struct s *e)\n",
7617            "{ *d = *e = a[0] = *c; }\n",
7618        ));
7619        assert_eq!(text.matches("memcpy").count(), 3, "{text}");
7620        assert!(text.contains("memcpy %8, %1, size 8, align 4\n"), "{text}");
7621        assert!(text.contains("memcpy %3, %8, size 8, align 4\n"), "{text}");
7622        assert!(text.contains("memcpy %2, %3, size 8, align 4\n"), "{text}");
7623    }
7624
7625    #[test]
7626    fn a_string_literal_stops_at_the_end_of_the_array_it_is_filling() {
7627        // The excess used to be laid into the object anyway, so the row after was written over
7628        // and the image refused the entry that came to it. C 6.7.10p14 says the terminator goes
7629        // in only if there is room for it, and gcc discards the rest of a literal that is longer
7630        // still, which is what the first of these is and why it warns.
7631        let mut opts = options();
7632        opts.emit = EmitKind::Ir;
7633        let result = run(
7634            &opts,
7635            concat!(
7636                "const char a[2][3] = { \"1234\", \"xyz\" };\n",
7637                "static const char b[3][5] = { \"12345\", \"678\", \"9\" };\n",
7638                "union u { struct { char x[4]; char y[4]; }; struct { char z[8]; }; };\n",
7639                "const union u c = { { \"1234\", \"567\" } };\n",
7640            ),
7641        );
7642        let text = result.text();
7643        assert_eq!(
7644            result.messages,
7645            ["/main.c:1:24: warning: initializer-string for array of 'const char' is too long \
7646              (5 chars into 3 available) [E0637]"]
7647        );
7648        assert!(text.contains("global @a : bytes 6 = { bytes \"123\", bytes \"xyz\" }"), "{text}");
7649        assert!(
7650            text.contains(
7651                "global @b : bytes 15 = { bytes \"12345\", bytes \"678\\00\", zero 1, \
7652                 bytes \"9\\00\", zero 3 }"
7653            ),
7654            "{text}"
7655        );
7656        // The eight bytes are four, three and a terminator, and then the byte the shorter
7657        // literal left for the string in the other member of the union to end at.
7658        assert!(
7659            text.contains("global @c : bytes 8 = { bytes \"1234\", bytes \"567\\00\" }"),
7660            "{text}"
7661        );
7662    }
7663
7664    #[test]
7665    fn a_cast_of_a_record_to_its_own_type_is_the_object_that_was_cast() {
7666        // gcc accepts one and does nothing with it, which sema already had. Lowering asked for
7667        // the object under it and had no arm for a cast, so `(struct s)x` in an initializer was
7668        // refused with E0519. It is one copy out of the object named, not two.
7669        let text = body(concat!(
7670            "struct s { int a, b; };\nstruct v { struct s s; int t; };\n",
7671            "void g(struct v *);\n",
7672            "void f(struct s *p) { struct v w = { (struct s)*p, 5 }; g(&w); }\n",
7673        ));
7674        assert_eq!(text.matches("memcpy").count(), 1, "{text}");
7675    }
7676
7677    #[test]
7678    fn a_compound_literal_read_in_a_static_initializer_lays_its_bytes_into_the_image() {
7679        // C 6.7.11p4 says a compound literal at file scope has static storage duration, which
7680        // makes it a constant element, and tcc and c-testsuite both write one. Sema used to call
7681        // it a non constant because reading it is a node of its own and the read was what it
7682        // looked at, and lowering had no way to put an object where it wanted a number.
7683        let text = ir(concat!(
7684            "struct s { int x; };\n",
7685            "struct t { struct s s; int o; } a = { (struct s){ 2 }, 3 };\n",
7686            "int n = (int){ 7 };\n",
7687            "struct u { struct s p; struct s q; } b = { (struct s){ 1 }, (struct s){ } };\n",
7688        ));
7689        assert!(text.contains("global @a : bytes 8 = { i32 2, i32 3 }"), "{text}");
7690        assert!(text.contains("global @n : i32 = 7,"), "{text}");
7691        // The second literal names nothing, so what it puts in is the zeros of its own size and
7692        // not the tail of the object it went in, which would have been the same bytes by luck.
7693        assert!(text.contains("global @b : bytes 8 = { i32 1, zero 4 }"), "{text}");
7694    }
7695
7696    #[test]
7697    fn the_address_of_a_compound_literal_asks_for_the_object_it_points_at() {
7698        // Nothing declares a compound literal, so the reference is the only thing that can ask
7699        // for it to be emitted. The image named `.Lanon.0` and the module defined no such
7700        // symbol, which the link would have been the first to find out.
7701        let text = ir("struct s { int x; };\nstruct s *q = &(struct s){ 9 };\n");
7702        assert!(text.contains("global @.Lanon.0 : i32 = 9, align 4, linkage(internal)"), "{text}");
7703        assert!(text.contains("global @q : bytes 8 = { addr.8 @.Lanon.0 }"), "{text}");
7704    }
7705
7706    #[test]
7707    fn an_object_of_no_size_at_all_has_an_image_with_nothing_in_it() {
7708        // A zero length array, which gcc allows and real code uses as the tail of a structure.
7709        // The image is there and holds nothing, which is not the global that has no image at
7710        // all, and the IR reader used to stop on the empty one.
7711        let text = ir("unsigned char foo[1][0];\n");
7712        assert!(text.contains("global @foo : bytes 0 = {}, align 1"), "{text}");
7713    }
7714
7715    #[test]
7716    fn a_null_pointer_in_an_image_is_the_bits_an_address_has_room_for() {
7717        // `NULL` in a static initializer, which every program has. The IR type is `ptr` and a
7718        // `ptr` has no width of its own, so the width the bits are cut to is the target's.
7719        let text = ir("void *p = 0;\nchar *q = (char *) 4096;\n");
7720        assert!(text.contains("global @p : i64 = 0, align 8"), "{text}");
7721        assert!(text.contains("global @q : i64 = 4096, align 8"), "{text}");
7722    }
7723
7724    #[test]
7725    fn an_object_another_module_defines_may_be_one_that_cannot_be_written_through() {
7726        // Which the verifier used to refuse, having read a declaration as a definition with
7727        // nothing in it. `extern const` is how a program names something in the library's read
7728        // only data, and glibc and Darwin both have one in a header a real program includes.
7729        let text = ir("extern const int limit;\nint f(void) { return limit; }\n");
7730        assert!(
7731            text.contains("global @limit : bytes 4, align 4, linkage(external), constant"),
7732            "{text}"
7733        );
7734    }
7735
7736    #[test]
7737    fn a_conditional_whose_value_is_an_object_answers_where_the_object_is() {
7738        // A structure is not a value in the IR, so the two arms cannot be joined as one. The
7739        // addresses can, and the answer is the address of whichever arm was taken rather than
7740        // a copy of it into a third place: both arms outlive the expression, so a copy would
7741        // be one nothing could observe. SQLite's parser writes one of these.
7742        let text = body(
7743            "\
7744struct s { int a, b; };
7745struct s pick(int c, struct s x, struct s y) { return c ? x : y; }
7746",
7747        );
7748        // The join takes an address, each arm hands it the one it has, and nothing is copied.
7749        assert!(text.contains("block3(%7: ptr)"), "{text}");
7750        assert!(text.contains("jump block3(%3)") && text.contains("jump block3(%4)"), "{text}");
7751        assert!(!text.contains("memcpy"), "the arms are joined rather than copied: {text}");
7752    }
7753
7754    /// GNU's `a ?: b` evaluates `a` once, and the arm answers the value that was tested.
7755    ///
7756    /// The checking keeps one node for `a` and converts it in two directions, to the bit the
7757    /// branch is taken on and to the type the whole expression has. Walking into the arm used to
7758    /// reach that node a second time and build a second copy of whatever it says, so `++i ?: 10`
7759    /// incremented twice and `f() ?: 10` called twice. Measured against gcc 16.2.0, which
7760    /// increments once.
7761    #[test]
7762    fn the_left_side_of_a_conditional_with_no_middle_is_evaluated_once() {
7763        let text = body("int f(int i) { return ++i ?: 10; }\n");
7764        assert!(text.contains("jump block3(%2)"), "the arm is the value that was tested: {text}");
7765        assert_eq!(text.matches("add.nsw").count(), 1, "incremented once: {text}");
7766
7767        // The arm still converts, since what the whole expression is worth is a `long` here and
7768        // the node under it is an `int`. What it converts is the value in hand.
7769        let text = body("long f(int i) { return ++i ?: 10L; }\n");
7770        assert!(text.contains("%5 = sext.i64 %2"), "the arm widens what was tested: {text}");
7771        assert_eq!(text.matches("add.nsw").count(), 1, "incremented once: {text}");
7772
7773        // A call, which is where evaluating twice is a wrong answer rather than a slow one.
7774        let text = body("int g(void);\nint f(void) { return g() ?: 10; }\n");
7775        assert_eq!(text.matches("call @g").count(), 1, "called once: {text}");
7776
7777        // Written out in full it is two reads of `i`, which is what C says it is, so the middle
7778        // operand being absent is the whole of the difference.
7779        let text = body("int f(int i) { return ++i ? ++i : 10; }\n");
7780        assert_eq!(text.matches("add.nsw").count(), 2, "incremented twice: {text}");
7781    }
7782
7783    #[test]
7784    fn a_structure_that_fits_in_registers_travels_as_the_registers_it_fits_in() {
7785        // `struct pair` is two eightbytes on SysV, one of them integer, so the signature says
7786        // one `i64` in each direction and the body takes the object apart and puts it back
7787        // together around the call.
7788        let text = ir("\
7789struct pair { int a, b; };
7790struct pair make(int a, int b);
7791struct pair twice(struct pair p) { return make(p.a, p.b); }
7792");
7793        assert!(text.contains("func @make(i32, i32) -> i64"), "{text}");
7794        assert!(text.contains("func @twice(i64) -> i64"), "{text}");
7795    }
7796
7797    #[test]
7798    fn a_structure_too_large_for_the_registers_travels_as_where_its_bytes_are() {
7799        // Over two eightbytes the caller passes the bytes in the argument area, which is
7800        // `byval`, and passes somewhere to write the return value, which is `sret`. Neither is
7801        // a parameter the program wrote and both are parameters the function has.
7802        let text = ir("\
7803struct big { double v[8]; };
7804struct big grow(struct big b);
7805struct big twice(struct big b) { return grow(grow(b)); }
7806");
7807        assert!(
7808            text.contains("func @grow(ptr sret(64, align 8), ptr byval(64, align 8))"),
7809            "{text}"
7810        );
7811        assert!(text.contains("block0(%0: ptr, %1: ptr):"), "{text}");
7812        // The inner call writes into a slot and the outer one reads the same slot, so the
7813        // object between the two calls is never copied anywhere.
7814        assert_eq!(text.matches("call @grow").count(), 2, "{text}");
7815    }
7816
7817    #[test]
7818    fn a_structure_passed_to_a_variadic_function_says_so_at_the_call() {
7819        // The bytes travel in the argument area the same way they would for a parameter, and
7820        // `printf` has no parameter there to say it on, so the call says it instead. The one
7821        // that fits in registers says nothing, because travelling as the registers it fits in
7822        // is what an argument does when nothing says otherwise.
7823        let text = ir("\
7824struct big { double v[8]; };
7825struct pair { int a, b; };
7826int p(const char *, ...);
7827int f(struct big b, struct pair q) { return p(\"\", 1, b, q); }
7828");
7829        assert!(
7830            text.contains("call @p(%4, %5, %2 byval(64, align 8), %6) : (ptr, ...) -> i32"),
7831            "{text}"
7832        );
7833    }
7834
7835    #[test]
7836    fn what_a_call_produced_is_somewhere_before_anything_is_read_out_of_it() {
7837        // `make(1, 2).b` has no object to read a member of until one is made, and what makes it
7838        // is a slot the returned registers are written to.
7839        let body = body(
7840            "\
7841struct pair { int a, b; };
7842struct pair make(int a, int b);
7843int second(void) { return make(1, 2).b; }
7844",
7845        );
7846        assert!(body.starts_with("block0:\n    %0 = alloca, size 8, align 4\n"), "{body}");
7847        assert!(body.contains("store %3 -> %0, align 4\n"), "{body}");
7848    }
7849
7850    #[test]
7851    fn a_structure_of_floats_travels_in_floating_point_registers_on_aarch64() {
7852        // The same declaration, classified by a different ABI: three `float` members are an
7853        // eightbyte of two of them and a half eightbyte of the third on SysV, and three vector
7854        // registers on AAPCS64.
7855        let source = "\
7856struct hfa { float x, y, z; };
7857int take(struct hfa h);
7858int give(struct hfa h) { return take(h); }
7859";
7860        assert!(ir(source).contains("func @take(f64, f32) -> i32"), "{}", ir(source));
7861        let mut opts = options();
7862        opts.emit = EmitKind::Ir;
7863        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
7864        let result = run(&opts, source);
7865        assert_eq!(result.messages, Vec::<String>::new());
7866        assert!(result.text().contains("func @take(f32, f32, f32) -> i32"), "{}", result.text());
7867    }
7868
7869    #[test]
7870    fn an_array_whose_length_is_not_a_constant_is_a_slot_made_where_its_declaration_is() {
7871        // The size is a multiplication rather than a number, the slot is taken from the stack
7872        // where the declaration is, and the scope it was declared in gives it back.
7873        let source = "\
7874int use(int *);
7875void f(int n) {
7876  {
7877    int a[n];
7878    use(a);
7879  }
7880  use(0);
7881}
7882";
7883        let body = body(source);
7884        assert!(body.contains("mul.nsw"), "{body}");
7885        assert!(body.contains("stacksave"), "{body}");
7886        assert!(body.contains("alloca %"), "{body}");
7887        assert!(body.contains("stackrestore"), "{body}");
7888    }
7889
7890    #[test]
7891    fn a_goto_out_of_the_scope_of_one_gives_its_stack_back_on_the_way() {
7892        // The label is outside the block the array is in, so arriving there means the array is
7893        // gone, and the restore that says so goes in front of the branch. The `goto` is written
7894        // before the walk knows where the label is, which is why the restore is put there at
7895        // the end rather than built where the branch was.
7896        let source = "\
7897int use(int *);
7898int f(int n) {
7899  {
7900    int a[n];
7901    if (use(a)) goto out;
7902    use(0);
7903  }
7904out:
7905  return 0;
7906}
7907";
7908        let body = body(source);
7909        // Two ways out of the block and a restore on each: the jump and the end of the block.
7910        assert_eq!(body.matches("stackrestore").count(), 2, "{body}");
7911        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
7912        assert!(after.starts_with(" %4\n    jump block"), "{body}");
7913    }
7914
7915    #[test]
7916    fn a_goto_to_a_label_the_array_is_still_alive_at_leaves_the_stack_alone() {
7917        // The label is after the declaration and in the same block, so control that arrives
7918        // there arrives somewhere the array exists. Giving it back would be giving back an
7919        // object the next statement reads.
7920        let source = "\
7921int use(int *);
7922int f(int n) {
7923  int a[n];
7924again:
7925  if (use(a)) goto again;
7926  return 0;
7927}
7928";
7929        let body = body(source);
7930        assert!(body.contains("stacksave"), "{body}");
7931        assert!(!body.contains("stackrestore"), "{body}");
7932    }
7933
7934    #[test]
7935    fn a_goto_back_to_a_label_in_front_of_one_gives_it_back_every_time_round() {
7936        // A loop written out of a `goto`, with the array made inside it. The label is in the
7937        // same block as the declaration and before it, which is a place where the array does
7938        // not exist yet, so the jump there leaves its scope and has to give the stack back. A
7939        // compiler that skips this restore grows the stack once per iteration.
7940        let source = "\
7941int use(int *);
7942int f(int n) {
7943again:
7944  {
7945    int a[n];
7946    if (use(a)) goto again;
7947  }
7948  return 0;
7949}
7950";
7951        let body = body(source);
7952        assert_eq!(body.matches("stacksave").count(), 1, "{body}");
7953        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
7954        assert!(after.starts_with(" %4\n    jump block1\n"), "{body}");
7955    }
7956
7957    #[test]
7958    fn the_head_of_a_for_loop_is_a_scope_that_closes_where_the_loop_is_left() {
7959        // The scope opened for `for (int a[n];;)` used to stay open, and a scope left open is
7960        // not one mark nobody reads. The marks are a stack, so the next close took this one
7961        // instead of its own, and the body of the loop gave back nothing while the block after
7962        // the loop restored a pointer saved inside it. The verifier refused that, which is how
7963        // it was found.
7964        let source = "\
7965int f(void);
7966void t(void) {
7967  int count = 10;
7968  for (; count--;) {
7969    int b[f()];
7970    int i;
7971    for (i = 0; i < f(); i++) {
7972      b[i] = count;
7973    }
7974  }
7975}
7976";
7977        let body = body(source);
7978        // One save, in the body, and one restore for it, also in the body: the block the
7979        // restore is in is the one the inner loop leaves through, and it goes back round the
7980        // outer loop rather than out of it.
7981        assert_eq!(body.matches("stacksave").count(), 1, "{body}");
7982        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
7983        // The rest of the block the restore is in, which is the last block here, so there is not
7984        // always another one after it to split on.
7985        let next = after.split("\n\n").next().expect("the block the restore is in");
7986        assert!(next.contains("jump block1("), "{body}");
7987    }
7988
7989    #[test]
7990    fn how_long_one_of_those_is_was_decided_where_it_was_declared_and_not_where_it_is_asked() {
7991        // What C says about the length being evaluated once: `sizeof a` after `n` changed is
7992        // still as long as the array is, which is what `n` was when the array came into being.
7993        let source = "\
7994unsigned long f(int n) {
7995  int a[n];
7996  n = 0;
7997  return sizeof a;
7998}
7999";
8000        let body = body(source);
8001        // One read of the parameter, at the declaration, and the answer is built out of it.
8002        assert_eq!(body.matches("sext.i64 %0").count(), 2, "{body}");
8003    }
8004
8005    #[test]
8006    fn a_block_in_the_middle_of_an_expression_is_walked_where_the_expression_is() {
8007        // GNU's statement expression: the statements happen where they are written and the last
8008        // one is the value, so the temporary in it never becomes a slot and never is copied.
8009        let source = "\
8010int use(int);
8011int f(int x) {
8012  return ({
8013    int t = use(x);
8014    t * t;
8015  });
8016}
8017";
8018        let expected = "\
8019block0(%0: i32):
8020    %1 = call @use(%0) : (i32) -> i32
8021    %2 = mul.nsw %1, %1
8022    return %2
8023";
8024        assert_eq!(body(source), expected);
8025    }
8026
8027    #[test]
8028    fn a_comma_whose_value_is_an_object_names_the_object_the_right_side_named() {
8029        // What janet writes, which is a call that does not return and then a value after it so
8030        // that the arm is worth something. The left side happens for what it did and the answer
8031        // is where the right side is, so there is nothing to copy and no temporary for a copy.
8032        let source = "\
8033struct pair { int a, b; };
8034void bail(void);
8035int f(struct pair p) {
8036  return (bail(), p).b;
8037}
8038";
8039        let expected = "\
8040block0(%0: i64):
8041    %1 = alloca, size 8, align 4
8042    store %0 -> %1, align 4
8043    call @bail() : ()
8044    %2 = iconst.i64 4
8045    %3 = ptr_add %1, %2
8046    %4 = load.i32 %3, align 4, tbaa !1
8047    return %4
8048";
8049        assert_eq!(body(source), expected);
8050    }
8051
8052    #[test]
8053    fn one_of_those_that_control_never_leaves_is_lowered_and_what_follows_it_is_dropped() {
8054        // A macro that always jumps, which is what this shape is in real code. The value is
8055        // never taken, and the block the rest of the expression would have been built in is
8056        // one nothing branches to, so it goes with the other unreachable blocks.
8057        let source = "int f(int x) { return ({ return x; 0; }); }\n";
8058        assert_eq!(body(source), "block0(%0: i32):\n    return %0\n");
8059    }
8060
8061    #[test]
8062    fn one_argument_off_a_variable_argument_list_stays_an_intrinsic() {
8063        // What it becomes is the target's answer, and this is not where the target's answers
8064        // are, so the walk writes down which list and which type and leaves it at that. Two of
8065        // them are two instructions, since each moves the list on.
8066        let source = "double f(__builtin_va_list ap) { return __builtin_va_arg(ap, double) + __builtin_va_arg(ap, double); }\n";
8067        let expected = "\
8068block0(%0: ptr):
8069    %1 = va_arg.f64 %0
8070    %2 = va_arg.f64 %0
8071    %3 = fadd %1, %2
8072    return %3
8073";
8074        assert_eq!(body(source), expected);
8075    }
8076
8077    #[test]
8078    fn one_that_reads_a_structure_answers_where_the_object_is() {
8079        // An aggregate is not a value, so there is nothing for the result of `va_arg` to be and
8080        // the object form is a second instruction. What it answers is an address, so it is a
8081        // place already and the walk copies nothing out of it: the copy here is the one the
8082        // initializer asks for, into the variable being declared. The size and the alignment
8083        // travel with it because they are what steps the list on and what a target that has to
8084        // put registers somewhere needs to know. So does the classification, which says the two
8085        // halves of this one arrived in general purpose registers: that is an answer about a C
8086        // type, and this is the last place that still has one.
8087        //
8088        // The slot is aligned to sixteen and the copy into it to eight, which is not a
8089        // disagreement. Sixteen is what a local aggregate of sixteen bytes gets whatever its
8090        // members ask for, and eight is what the type asks for and so what the copy may assume
8091        // about the object it is reading from.
8092        let source = "\
8093struct s { int a; long b; };
8094long f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.b; }
8095";
8096        let expected = "\
8097block0(%0: ptr):
8098    %1 = alloca, size 16, align 16
8099    %2 = va_object %0, size 16, align 8, in(int 8 at 0, int 8 at 8)
8100    memcpy %1, %2, size 16, align 8
8101    %3 = iconst.i64 8
8102    %4 = ptr_add %1, %3
8103    %5 = load.i64 %4, align 8, tbaa !1
8104    return %5
8105";
8106        assert_eq!(body(source), expected);
8107    }
8108
8109    /// Which register file each eightbyte arrived in is the whole of what the classification adds,
8110    /// and an object with no slots at all is one it sent to the caller's argument area, which is
8111    /// what everything over two eightbytes is whatever its members are.
8112    #[test]
8113    fn the_classification_says_which_registers_the_object_arrived_in() {
8114        let source = "\
8115struct s { double a; double b; };
8116double f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.a; }
8117";
8118        assert!(
8119            body(source)
8120                .contains("va_object %0, size 16, align 8, in(float f64 at 0, float f64 at 8)"),
8121            "{}",
8122            body(source)
8123        );
8124
8125        let big = "\
8126struct s { long a[4]; };
8127long f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.a[0]; }
8128";
8129        assert!(body(big).contains("va_object %0, size 32, align 8\n"), "{}", body(big));
8130    }
8131
8132    #[test]
8133    fn a_jump_to_an_address_branches_to_every_label_the_function_takes_the_address_of() {
8134        // GNU's computed goto. Which label the address holds is not known here, so all of them
8135        // are listed, and the values arriving at one are passed on every edge the same way they
8136        // are on an ordinary branch.
8137        let source = "\
8138int f(int c) {
8139  void *p = c ? &&one : &&two;
8140  goto *p;
8141one:
8142  return 1;
8143two:
8144  return 2;
8145}
8146";
8147        let expected = "\
8148block0(%0: i32):
8149    %1 = iconst.i32 0
8150    %2 = icmp ne %0, %1
8151    br_if %2, block1, block2
8152
8153block1:
8154    %3 = block_addr block3
8155    jump block4(%3)
8156
8157block2:
8158    %4 = block_addr block5
8159    jump block4(%4)
8160
8161block3:
8162    %5 = iconst.i32 1
8163    return %5
8164
8165block4(%6: ptr):
8166    indirect_br %6, block3, block5
8167
8168block5:
8169    %7 = iconst.i32 2
8170    return %7
8171";
8172        assert_eq!(body(source), expected);
8173    }
8174
8175    /// An interpreter, cut down to the shape that matters: a table of labels, a few values the
8176    /// loop keeps in hand, and a jump through the table at the end of every one of them.
8177    fn dispatch(labels: usize) -> String {
8178        let mask = labels - 1;
8179        let mut source = String::from("int spin(int n)\n{\n\tstatic void *table[] = {");
8180        for index in 0..labels {
8181            source.push_str(&format!(" &&a{index},"));
8182        }
8183        source.push_str(" };\n\tint w = n, x = n + 1, y = n + 2, z = n + 3;\n");
8184        source.push_str(&format!("\tif (n < 0) return 0;\n\tgoto *table[n & {mask}];\n"));
8185        for index in 0..labels {
8186            let step = match index % 4 {
8187                0 => "w += x;",
8188                1 => "x += y;",
8189                2 => "y += z;",
8190                _ => "z += w;",
8191            };
8192            source.push_str(&format!("a{index}:\n\t{step}\n"));
8193            source.push_str("\tif (--n <= 0) return w + x + y + z;\n");
8194            source.push_str(&format!("\tgoto *table[n & {mask}];\n"));
8195        }
8196        source.push_str("}\n");
8197        source
8198    }
8199
8200    /// How many moves are written in front of the first jump through a register.
8201    fn in_front_of_the_jump(text: &str) -> usize {
8202        let (before, _) = text.split_once("\tjmp\t*%").expect("a jump through a register");
8203        before.lines().rev().take_while(|line| line.starts_with("\tmov")).count()
8204    }
8205
8206    /// What a branch writes in front of its jump is what it carries, not what every label it can
8207    /// reach would like to be handed.
8208    ///
8209    /// A label an indirect branch reaches is given its values in registers the branch writes
8210    /// before it goes, because the moves cannot go after a jump and cannot go across the register
8211    /// the jump reads. Writing a register for each parameter of each label costs the table's
8212    /// length on every dispatch, which is a few moves in a program with two labels and five
8213    /// hundred in an interpreter with seventy. The values are the same values, so the registers
8214    /// are the same registers, and the cost stays where the number of values puts it.
8215    #[test]
8216    fn a_jump_through_a_register_writes_what_it_carries_and_not_the_whole_table() {
8217        let small = in_front_of_the_jump(&asm(&dispatch(4)));
8218        let large = in_front_of_the_jump(&asm(&dispatch(32)));
8219        assert_eq!(small, large, "eight times the labels and the same values in hand");
8220        assert!(large <= 8, "the values the loop keeps, and not a set of them per label: {large}");
8221    }
8222
8223    /// The same interpreter with more values in hand than there are registers, which is what makes
8224    /// the allocator send some of them to the stack at every label.
8225    fn crowded(labels: usize) -> String {
8226        const VALUES: usize = 24;
8227        let mask = labels - 1;
8228        let mut source = String::from("int spin(int n)\n{\n\tstatic void *table[] = {");
8229        for index in 0..labels {
8230            source.push_str(&format!(" &&a{index},"));
8231        }
8232        source.push_str(" };\n\t");
8233        for value in 0..VALUES {
8234            source.push_str(&format!("int v{value} = n + {value}; "));
8235        }
8236        let sum: Vec<String> = (0..VALUES).map(|value| format!("v{value}")).collect();
8237        source.push_str(&format!("\n\tif (n < 0) return 0;\n\tgoto *table[n & {mask}];\n"));
8238        for index in 0..labels {
8239            let (to, from) = (index % VALUES, (index + 1) % VALUES);
8240            source.push_str(&format!("a{index}:\n\tv{to} += v{from};\n"));
8241            source.push_str(&format!("\tif (--n <= 0) return {};\n", sum.join(" + ")));
8242            source.push_str(&format!("\tgoto *table[n & {mask}];\n"));
8243        }
8244        source.push_str("}\n");
8245        source
8246    }
8247
8248    /// How many bytes of frame the first function in a listing opens.
8249    fn the_frame(text: &str) -> u64 {
8250        text.lines()
8251            .find_map(|line| {
8252                let (size, _) = line.strip_prefix("\tsubq\t$")?.split_once(", %rsp")?;
8253                size.parse().ok()
8254            })
8255            .expect("a function that opens a frame")
8256    }
8257
8258    /// A frame holds what a function wants at once, and an interpreter does not want the whole
8259    /// table at once.
8260    ///
8261    /// Every label a dispatch table reaches is handed the values the loop keeps, and what the
8262    /// allocator has no register for goes on the stack. They are the same few values one label at
8263    /// a time, so they are the same bytes. A slot each put forty kilobytes on the frame of lua's
8264    /// interpreter and ran the C stack out at a depth lua's own limit was supposed to catch,
8265    /// which is tamnd/rucc#1630.
8266    #[test]
8267    fn a_frame_holds_what_is_wanted_at_once_and_not_a_slot_for_every_label() {
8268        let small = the_frame(&asm(&crowded(16)));
8269        let large = the_frame(&asm(&crowded(64)));
8270        assert_eq!(small, large, "four times the labels and the same values: {small}, {large}");
8271    }
8272
8273    /// A template that saves the callee-saved registers by name, which is micropython's non local
8274    /// return and is tamnd/rucc#1583.
8275    ///
8276    /// Every register in it is one the template named rather than one the statement handed over,
8277    /// because the buffer is defined as holding those registers and there is no constraint letter
8278    /// that means `%rsp`. The instructions come out naming what the program named, and the
8279    /// allocator, which was told about the writes rather than left to find out, saves the ones the
8280    /// calling convention says belong to whoever called.
8281    #[test]
8282    fn a_template_that_names_its_own_registers_gets_the_ones_it_named() {
8283        let source = "void save(void *nlr) {
8284    __asm volatile (
8285        \"movq   %%rsp, 32(%%rdi)   \\n\"
8286        \"movq   %%rbx, 40(%%rdi)   \\n\"
8287        \"movq   %%r12, 48(%%rdi)   \\n\"
8288        : : \"D\" (nlr) : \"memory\");
8289}
8290";
8291        let text = asm(source);
8292        assert!(text.contains("\tmovq\t%rsp, 32(%rdi)\n"), "{text}");
8293        assert!(text.contains("\tmovq\t%rbx, 40(%rdi)\n"), "{text}");
8294        assert!(text.contains("\tmovq\t%r12, 48(%rdi)\n"), "{text}");
8295    }
8296
8297    #[test]
8298    fn a_jump_to_an_address_no_label_in_the_function_has_arrives_nowhere() {
8299        // The address came from outside the function, and a jump to a label in another function
8300        // is undefined. The expression is still evaluated, since a call in it has to happen.
8301        let source = "void **next(void);
8302void f(void) { goto *next(); }
8303";
8304        let expected = "\
8305block0:
8306    %0 = call @next() : () -> ptr
8307    unreachable
8308";
8309        assert_eq!(body(source), expected);
8310    }
8311
8312    #[test]
8313    fn an_asm_with_no_operands_is_volatile_and_the_clobbers_are_the_whole_of_what_it_says() {
8314        // Nothing reads a result, so the only thing that keeps it is that it is volatile, which
8315        // a basic asm implies.
8316        let source = "void f(void) { __asm__(\"mfence\" ::: \"memory\"); }\n";
8317        let expected = "\
8318block0:
8319    inline_asm.volatile \"mfence\", \"\", \"memory\"()
8320    return
8321";
8322        assert_eq!(body(source), expected);
8323    }
8324
8325    #[test]
8326    fn the_constraints_are_one_list_in_the_order_the_template_counts_the_operands() {
8327        // The outputs first and then the inputs, which is the numbering `%0` and `%1` use. An
8328        // output in a register is a result, and one that is read as well is an argument too.
8329        let source = "\
8330int f(int x, int y) {
8331  int r;
8332  __asm__(\"addl %2, %0\" : \"=r\"(r), \"+r\"(y) : \"r\"(x));
8333  return r + y;
8334}
8335";
8336        let expected = "\
8337block0(%0: i32, %1: i32):
8338    %2, %3 = inline_asm.(i32, i32) \"addl %2, %0\", \"=r,+r,r\", \"\"(%1, %0)
8339    %4 = add.nsw %2, %3
8340    return %4
8341";
8342        assert_eq!(body(source), expected);
8343    }
8344
8345    #[test]
8346    fn a_memory_operand_travels_as_the_address_of_an_object_that_is_given_a_slot() {
8347        // The assembly is handed a pointer, so the object cannot live in a value, and the scan
8348        // that runs before the walk has to have known that or there would be nothing to point
8349        // at. A structure travels this way whatever else its constraint allows, since there is
8350        // no register that holds one.
8351        let source = "\
8352struct pair { int a, b; };
8353int f(int x) {
8354  int slot = x;
8355  struct pair p = { x, x };
8356  __asm__(\"incl %0\" : \"+m\"(slot), \"=m\"(p));
8357  return slot + p.a;
8358}
8359";
8360        let text = body(source);
8361        assert!(text.contains("inline_asm \"incl %0\", \"+m,=m\", \"\"(%1, %2)\n"), "{text}");
8362        assert!(text.contains("%1 = alloca, size 4, align 4\n"), "{text}");
8363        assert!(text.contains("%2 = alloca, size 8, align 4\n"), "{text}");
8364    }
8365
8366    #[test]
8367    fn an_asm_goto_falls_through_to_its_first_target_and_writes_its_outputs_there() {
8368        // The output is only in scope where the instruction dominates, which is the fall through
8369        // block, so the edge to the label carries the value the object had before the assembly
8370        // ran. That is what document 11 asks for and it is what putting the fall through first
8371        // buys.
8372        let source = "\
8373int f(int x) {
8374  int r = 7;
8375  __asm__ goto(\"cbnz %0, %l1\" : \"=r\"(r) : \"r\"(x) :: away);
8376  return r;
8377away:
8378  return r;
8379}
8380";
8381        let expected = "\
8382block0(%0: i32):
8383    %1 = iconst.i32 7
8384    %2 = inline_asm.volatile \"cbnz %0, %l1\", \"=r,r\", \"\"(%0), labels [block1, block2]
8385
8386block1:
8387    return %2
8388
8389block2:
8390    return %1
8391";
8392        assert_eq!(body(source), expected);
8393    }
8394
8395    #[test]
8396    fn an_asm_statement_that_is_not_well_formed_is_reported_in_the_words_gcc_uses() {
8397        // The operands are checked here rather than by the assembler, because by the time the
8398        // assembler sees the template the operands have become registers and it has nothing left
8399        // to say about the C that named them.
8400        let mut opts = options();
8401        opts.emit = EmitKind::Ir;
8402        for (source, expected) in [
8403            (
8404                "void f(int x) { __asm__(\"\" : \"r\"(x)); }\n",
8405                "output operand constraint lacks '='",
8406            ),
8407            (
8408                "void f(int x) { __asm__(\"\" : \"=r\"(x + 1)); }\n",
8409                "lvalue required in 'asm' statement",
8410            ),
8411            (
8412                "const int g = 1;\nvoid f(void) { __asm__(\"\" : \"=r\"(g)); }\n",
8413                "read-only variable 'g' used as 'asm' output",
8414            ),
8415            (
8416                "void f(int x) { __asm__(\"\" : : \"=r\"(x)); }\n",
8417                "input operand constraint contains '='",
8418            ),
8419            (
8420                "void f(void) { __asm__(\"\" : : \"m\"(1)); }\n",
8421                "memory input 0 is not directly addressable",
8422            ),
8423            ("void f(void) { __asm__(L\"\"); }\n", "wide string literal in 'asm'"),
8424            (
8425                "void f(int x, int y) { __asm__(\"\" : [a] \"=r\"(x) : [a] \"r\"(y)); }\n",
8426                "duplicate asm operand name 'a'",
8427            ),
8428            ("void f(int x) { __asm__(\"%[in]\" : \"=r\"(x)); }\n", "undefined named operand 'in'"),
8429        ] {
8430            let result = run(&opts, source);
8431            assert!(result.failed(), "expected this to be reported:\n{source}");
8432            assert!(
8433                result.messages.iter().any(|m| m.contains(expected)),
8434                "{expected}\n{:?}",
8435                result.messages
8436            );
8437        }
8438    }
8439
8440    /// An `asm` at file scope whose template is directives is the whole of what the incbin
8441    /// header, an alias table and a hand written jump table each write, and what it says is a
8442    /// section holding named bytes. So it becomes the globals it names, in the order it names
8443    /// them, which is what `spec/11-asm-objects-debug.md` section 11.2 asks for.
8444    #[test]
8445    fn an_asm_at_file_scope_that_is_directives_becomes_the_objects_it_defines() {
8446        let text = ir(concat!(
8447            "__asm__(\n",
8448            "  \".section .rodata\\n\"\n",
8449            "  \".globl first\\n\"\n",
8450            "  \".balign 8\\n\"\n",
8451            "  \"first:\\n\"\n",
8452            "  \".long 1\\n\"\n",
8453            "  \".long 2\\n\"\n",
8454            "  \".globl last\\n\"\n",
8455            "  \"last:\\n\"\n",
8456            "  \".quad last - first\\n\");\n",
8457            "extern const int first[];\n",
8458            "extern const long last;\n",
8459        ));
8460        assert!(text.contains("global @first : bytes 8 = { i32 1, i32 2 }, align 8"), "{text}");
8461        assert!(text.contains("global @last : i64 = 8"), "{text}");
8462    }
8463
8464    /// The distance between two labels is what the incbin header hands a program as the size of
8465    /// the data, so a declaration of one of the names has to find the definition the template
8466    /// made rather than turn it back into something the linker is asked for.
8467    #[test]
8468    fn a_name_an_asm_at_file_scope_defined_is_not_undone_by_a_declaration_of_it() {
8469        let text = ir(concat!(
8470            "__asm__(\".data\\n.globl counter\\ncounter:\\n.long 7\\n\");\n",
8471            "extern int counter;\n",
8472            "int read(void) { return counter; }\n",
8473        ));
8474        assert!(text.contains("global @counter : i32 = 7"), "{text}");
8475    }
8476
8477    /// Bytes written before any label are a global with a name minted for them, in front of the
8478    /// label written under them, which is what makes the first byte of the name the one written
8479    /// under it. The block is the one tcc's test file writes, without the line of it that measures
8480    /// from one section to another.
8481    #[test]
8482    fn bytes_under_no_label_at_file_scope_are_a_global_in_front_of_the_label() {
8483        let text = ir(concat!(
8484            "__asm__(\".data\\n.byte 41\\nstuff:\\n661:\\n.byte 42\\n662:\\n",
8485            ".pushsection .data.ignore\\n.byte 7\\n.popsection\\n.byte 662b - 661b\\n\");\n",
8486            "extern unsigned char stuff[];\n",
8487            "int read(void) { return stuff[0]; }\n",
8488        ));
8489        let under = text.find("global @.Lasm.0 : i8 = 41").expect(&text);
8490        let named = text.find("global @stuff : i8 = 42").expect(&text);
8491        assert!(under < named, "the bytes under no label come first: {text}");
8492        assert!(text.contains("global @.Lasm.1 : i8 = 7, align 1, linkage(internal), section"));
8493        // The byte after the pop is a run of its own, because coming back to a section finishes
8494        // what was being written to it the way a label does. It is the next global of that
8495        // section all the same, so the byte lands where the template put it, which is the one
8496        // after the byte under `stuff`.
8497        let after = text.find("global @.Lasm.2 : i8 = 1").expect(&text);
8498        assert!(named < after, "{text}");
8499    }
8500
8501    /// How far a place is from the bytes holding the answer, which is what tcc's test file writes
8502    /// last and what the alternative instruction tables in a kernel header are made of. It is the
8503    /// linker's answer rather than the compiler's, because the two sections are placed by the
8504    /// linker, so the image holds a hole and a name for it.
8505    #[test]
8506    fn a_distance_from_here_at_file_scope_is_a_hole_naming_the_global_it_measures_to() {
8507        let text = ir(concat!(
8508            "__asm__(\".data\\n.byte 41\\nstuff:\\n661:\\n.byte 42\\n",
8509            ".pushsection .data.ignore\\n.long 661b - .\\n.popsection\\n\");\n",
8510            "extern unsigned char stuff[];\n",
8511            "int read(void) { return stuff[0]; }\n",
8512        ));
8513        // The label the template measured to is a local one and no symbol, so what the hole names
8514        // is the global it stands inside, which is the byte under `stuff`, and nothing further on
8515        // since it is the first byte of it.
8516        assert!(text.contains("global @.Lasm.1 : bytes 4 = { away.4 @stuff }"), "{text}");
8517    }
8518
8519    /// A `.set` says one name stands for another, which is a second symbol at the first one's
8520    /// address and is an alias and nothing else. What the directives around it said about the
8521    /// name is what the name gets, and a name the file defines itself keeps its own definition,
8522    /// which is what gcc's symbol table shows for the block tcc's test file writes.
8523    #[test]
8524    fn a_set_at_file_scope_is_a_second_name_for_what_it_names() {
8525        let text = ir(concat!(
8526            "void base(void) {}\n",
8527            "__asm__(\".weak one\\n.set one, base\");\n",
8528            "__asm__(\".globl two\\n.set two, base\");\n",
8529            "__asm__(\".set three, base\");\n",
8530            "void three(void) {}\n",
8531        ));
8532        assert!(text.contains("alias @one = @base, linkage(weak)"), "{text}");
8533        assert!(text.contains("alias @two = @base"), "{text}");
8534        assert!(!text.contains("alias @three"), "a definition of the name wins: {text}");
8535        assert!(text.contains("func @three"), "{text}");
8536    }
8537
8538    /// The target has to be something this file defines, because an alias is a symbol at an
8539    /// address in this object and a name only declared here has none to be at. The same rule and
8540    /// the same words as for `__attribute__((alias))`, since it is the same thing written another
8541    /// way.
8542    #[test]
8543    fn a_set_of_a_name_this_file_does_not_define_says_so() {
8544        let messages = errors("__asm__(\".set here, elsewhere\");\n");
8545        assert!(
8546            messages
8547                .iter()
8548                .any(|m| m.contains("'here' is aliased to undefined symbol 'elsewhere'")
8549                    && m.contains("E0697")),
8550            "{messages:?}"
8551        );
8552    }
8553
8554    /// `.incbin` is the one directive that reads something, and what it reads comes through the
8555    /// same file system the sources did.
8556    #[test]
8557    fn an_incbin_at_file_scope_is_the_bytes_of_the_file_it_names() {
8558        let mut opts = options();
8559        opts.emit = EmitKind::Ir;
8560        let mut fs = MemoryFileSystem::new();
8561        fs.insert(
8562            "/main.c",
8563            b"__asm__(\".data\\n.globl blob\\nblob:\\n.incbin \\\"seed\\\"\\n\");\n".to_vec(),
8564        );
8565        fs.insert("seed", b"hi".to_vec());
8566        let result = compile(&opts, "/main.c", &fs);
8567        assert_eq!(result.messages, Vec::<String>::new());
8568        let text = result.text();
8569        assert!(text.contains("global @blob : bytes 2 = { bytes \"hi\" }"), "{text}");
8570    }
8571
8572    /// A file that is not there is the mistake a build makes when it runs the compiler from the
8573    /// wrong directory, and it is worth saying which file rather than saying the template failed.
8574    #[test]
8575    fn an_incbin_naming_a_file_that_is_not_there_says_which_file() {
8576        let messages = errors("__asm__(\".data\\nb:\\n.incbin \\\"nowhere\\\"\\n\");\n");
8577        assert!(
8578            messages
8579                .iter()
8580                .any(|m| m.contains("cannot open 'nowhere' for reading") && m.contains("E0702")),
8581            "{messages:?}"
8582        );
8583    }
8584
8585    /// The line drawn is the same one the `asm` inside a function draws: directives are read and
8586    /// an instruction waits for an assembler. Refusing by name is what makes the wait visible.
8587    #[test]
8588    fn an_instruction_in_an_asm_at_file_scope_is_refused_rather_than_ignored() {
8589        for source in [
8590            "__asm__(\".text\\n.globl f\\nf:\\n  ret\\n\");\n",
8591            "__asm__(\".data\\n.set alias, 4\\n\");\n",
8592        ] {
8593            let messages = errors(source);
8594            assert!(
8595                messages
8596                    .iter()
8597                    .any(|m| m.contains("not supported yet")
8598                        && m.contains("in an `asm` at file scope")),
8599                "{source}\n{messages:?}"
8600            );
8601        }
8602    }
8603
8604    /// micropython's `nlr_push`, which is the program that asks for all of this. The body is the
8605    /// whole of the function: the return address is read out of `(%rsp)` where the call left it,
8606    /// the registers the convention preserves are saved by hand, and the frame that was just built
8607    /// is handed to a function written in C that never comes back.
8608    ///
8609    /// What is checked is what gcc writes for the same file. No prologue in front of the saves,
8610    /// since a push would move the return address the first of them reads. No epilogue and no
8611    /// `ret`, since the jump is where the function ends. And a `ud2` behind the jump, which is
8612    /// where control arrives if the jump is ever not taken and is exactly what gcc puts there.
8613    #[test]
8614    fn a_naked_function_is_its_own_prologue_and_its_own_ending() {
8615        let text = asm(concat!(
8616            "unsigned nlr_push_tail(void *nlr);\n",
8617            "__attribute__((naked)) unsigned nlr_push(void *nlr) {\n",
8618            "  __asm volatile(\n",
8619            "    \"movq (%rsp), %rax\\n\"\n",
8620            "    \"movq %rax, 16(%rdi)\\n\"\n",
8621            "    \"movq %rbx, 40(%rdi)\\n\"\n",
8622            "    \"jmp nlr_push_tail\\n\");\n",
8623            "}\n",
8624        ));
8625        assert!(text.contains("\tmovq\t(%rsp), %rax\n"), "{text}");
8626        assert!(text.contains("\tjmp\tnlr_push_tail\n"), "{text}");
8627        assert!(text.contains("\tud2\n"), "{text}");
8628        assert!(!text.contains("\tpushq\t"), "nothing is saved in front of it: {text}");
8629        assert!(!text.contains("\tret\n"), "the jump is where it ends: {text}");
8630    }
8631
8632    /// The three things a naked function may not ask for, each of which is a frame nothing sets up
8633    /// or a jump over an epilogue there is one of.
8634    #[test]
8635    fn what_a_function_without_a_prologue_cannot_be_given_is_refused() {
8636        let mut opts = options();
8637        opts.emit = EmitKind::Asm;
8638        for (source, why) in [
8639            (
8640                "__attribute__((naked)) void f(void) { volatile long a[8]; a[0] = 1; }\n",
8641                "bytes of frame",
8642            ),
8643            (
8644                "__attribute__((naked)) void f(int n) { char a[n]; __asm(\"nop\" ::\"r\"(a)); }\n",
8645                "has no prologue to point a frame pointer at it with",
8646            ),
8647            ("void elsewhere(void); void f(void) { __asm(\"jmp elsewhere\"); }\n", "jumps out of"),
8648        ] {
8649            let result = run(&opts, source);
8650            assert!(result.failed(), "expected this to be refused:\n{source}");
8651            assert!(
8652                result.messages.iter().any(|message| message.contains(why)),
8653                "{:?}",
8654                result.messages
8655            );
8656        }
8657    }
8658
8659    #[test]
8660    fn what_the_walk_cannot_build_yet_is_reported_rather_than_mislowered() {
8661        let mut opts = options();
8662        opts.emit = EmitKind::Ir;
8663        for source in [
8664            "int f(int n) { void *p = &&out; if (n) goto *p; { int a[n]; out: return 1; } }\n",
8665            "int f(int n) { int a[n]; __asm__ goto(\"\" ::::out); out: return a[0]; }\n",
8666        ] {
8667            let result = run(&opts, source);
8668            assert!(result.failed(), "expected this to be reported:\n{source}");
8669            assert!(
8670                result.messages.iter().any(|m| m.contains("not supported yet")),
8671                "{:?}",
8672                result.messages
8673            );
8674        }
8675    }
8676
8677    /// Compiles `source` to IR, reads that back as an input, and gives back both texts.
8678    fn round_trip(source: &str) -> (String, String) {
8679        let printed = ir(source);
8680        let mut opts = options();
8681        opts.emit = EmitKind::Ir;
8682        let mut fs = MemoryFileSystem::new();
8683        fs.insert("/main.ir", printed.clone().into_bytes());
8684        let result = compile_ir(&opts, "/main.ir", &fs);
8685        assert_eq!(result.messages, Vec::<String>::new(), "expected this to read back:\n{printed}");
8686        (printed, result.text().to_owned())
8687    }
8688
8689    #[test]
8690    fn ir_that_arrives_as_an_input_is_read_back_and_written_out_the_same() {
8691        // The other half of the round trip test below, through the driver rather than through
8692        // the library, which is what makes the property something to run over a real program
8693        // rather than over the modules a test builds.
8694        let (printed, again) = round_trip(
8695            "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",
8696        );
8697        assert_eq!(printed, again);
8698    }
8699
8700    #[test]
8701    fn ir_that_is_not_ir_says_which_line_stopped_it() {
8702        let mut opts = options();
8703        opts.emit = EmitKind::Ir;
8704        let mut fs = MemoryFileSystem::new();
8705        let text = "\
8706; ModuleID = 'a.c'
8707; format 0
8708target triple = \"x86_64-unknown-linux-gnu\"
8709target datalayout = \"e-p:64:64-i64:64-S128\"
8710
8711func @f(), linkage(external) {
8712block0:
8713    frobnicate
8714}
8715";
8716        fs.insert("/main.ir", text.as_bytes().to_vec());
8717        let result = compile_ir(&opts, "/main.ir", &fs);
8718        assert!(result.failed());
8719        assert!(result.messages[0].contains("/main.ir:8"), "{:?}", result.messages);
8720    }
8721
8722    #[test]
8723    fn ir_that_reads_but_does_not_hold_together_is_reported_by_the_verifier() {
8724        // A module that a person edited has not been through the verifier, and the return of
8725        // an `i32` from a function that returns nothing is the kind of thing editing produces.
8726        let mut opts = options();
8727        opts.emit = EmitKind::Ir;
8728        let mut fs = MemoryFileSystem::new();
8729        let text = "\
8730; ModuleID = 'a.c'
8731; format 0
8732target triple = \"x86_64-unknown-linux-gnu\"
8733target datalayout = \"e-p:64:64-i64:64-S128\"
8734
8735func @f(), linkage(external) {
8736block0:
8737    %0 = iconst.i32 1
8738    return %0
8739}
8740";
8741        fs.insert("/main.ir", text.as_bytes().to_vec());
8742        let result = compile_ir(&opts, "/main.ir", &fs);
8743        assert!(result.failed());
8744        assert!(result.messages[0].contains("invalid IR"), "{:?}", result.messages);
8745    }
8746
8747    #[test]
8748    fn a_typed_tree_is_not_something_an_input_of_ir_can_produce() {
8749        // The C that became this is not here any more, so there is nothing to print a tree of.
8750        let mut fs = MemoryFileSystem::new();
8751        fs.insert("/main.ir", Vec::new());
8752        let result = compile_ir(&options(), "/main.ir", &fs);
8753        assert!(result.failed());
8754        assert!(result.messages[0].contains("can only be emitted as IR"), "{:?}", result.messages);
8755    }
8756
8757    #[test]
8758    fn the_printed_ir_reads_back_as_the_same_module() {
8759        // The M2 exit criterion: the text is the module and nothing about it is lost by
8760        // writing it down. Anything the printer invents or the parser drops shows up here.
8761        let text = ir("\
8762struct point { int x, y; };
8763static const char greeting[] = \"hi\";
8764int table[4] = { 1, 2, 3 };
8765int puts(const char *);
8766double half(double x) { return x / 2.0; }
8767int f(int n) {
8768  int total = 0;
8769  for (int i = 0; i < n; i++) {
8770    if (i == 3) continue;
8771    total += table[i];
8772  }
8773  switch (n) {
8774    case 0: total = 1;
8775    case 1: total++; break;
8776    default: total = -total;
8777  }
8778  struct point p = { total, 1 };
8779  int *q = &p.y;
8780  puts(greeting);
8781  return p.x + *q;
8782}
8783int dispatch(int c) {
8784  void *p = c ? &&one : &&two;
8785  goto *p;
8786one:
8787  return 1;
8788two:
8789  return 2;
8790}
8791int assembly(int x, int *p) {
8792  int r;
8793  __asm__ volatile(\"xadd %0, %2\" : \"=r\"(r), \"+m\"(*p) : \"0\"(x) : \"cc\");
8794  __asm__ goto(\"cbnz %0, %l1\" : : \"r\"(r) : : away);
8795  return r;
8796away:
8797  return 0;
8798}
8799");
8800        let mut names = Interner::new();
8801        let module = rucc_ir::parse(&text, &mut names).expect("the printer writes what it reads");
8802        assert_eq!(rucc_ir::print(&module, &names), text);
8803    }
8804
8805    #[test]
8806    fn what_save_temps_keeps_is_the_text_that_was_compiled_and_the_assembly_that_was_assembled() {
8807        // The point of the flag is that these two are the compilation rather than a description
8808        // of one, so both come out of the run that produced the object rather than out of a
8809        // second run under different flags.
8810        let mut opts = options();
8811        opts.emit = EmitKind::Object;
8812        opts.save_temps = rucc_session::SaveTemps::Object;
8813        let result = run(&opts, "#define N 2\nint a[N];\n");
8814        assert_eq!(result.messages, Vec::<String>::new());
8815        let text = result.temps.preprocessed.expect("the preprocessed text");
8816        assert!(text.contains("int a[2];"), "{text}");
8817        assert!(text.starts_with("# 1 \"/main.c\""), "{text}");
8818        let asm = result.temps.assembly.expect("the assembly");
8819        assert!(asm.contains("a:"), "{asm}");
8820        assert!(matches!(result.artifact, Artifact::Object { .. }), "{:?}", result.artifact);
8821    }
8822
8823    #[test]
8824    fn nothing_is_kept_unless_the_flag_asked_for_it() {
8825        // A compilation that was not asked to keep anything must not pay for printing text
8826        // nobody will read, and the empty value is what says so.
8827        let mut opts = options();
8828        opts.emit = EmitKind::Object;
8829        assert_eq!(run(&opts, "int a;\n").temps, Temps::default());
8830    }
8831
8832    #[test]
8833    fn a_compilation_that_stops_before_the_back_end_keeps_the_text_and_no_assembly() {
8834        // `--emit=ir` never produces any, and the text is worth keeping all the same: it is
8835        // what a report about the file being read wrongly has to have in it.
8836        let mut opts = options();
8837        opts.emit = EmitKind::Ir;
8838        opts.save_temps = rucc_session::SaveTemps::Cwd;
8839        let result = run(&opts, "int a;\n");
8840        assert!(result.temps.preprocessed.is_some());
8841        assert_eq!(result.temps.assembly, None);
8842    }
8843
8844    /// A stretch of a local's life, written short because these tests are about nothing else.
8845    fn span(from: u64, len: u64, held: rucc_debug::Held) -> rucc_debug::Span {
8846        rucc_debug::Span { from, len, held }
8847    }
8848
8849    #[test]
8850    fn two_stretches_that_meet_and_agree_come_out_as_one() {
8851        let one = span(0, 4, rucc_debug::Held::Reg(3));
8852        let two = span(4, 4, rucc_debug::Held::Reg(3));
8853        assert_eq!(settle(vec![two, one]), vec![span(0, 8, rucc_debug::Held::Reg(3))]);
8854    }
8855
8856    #[test]
8857    fn two_stretches_that_disagree_leave_the_addresses_they_share_unanswered() {
8858        let one = span(0, 8, rucc_debug::Held::Reg(3));
8859        let two = span(4, 8, rucc_debug::Held::Reg(4));
8860        // The four bytes in the middle are the ones neither can speak for, and what is left is
8861        // each stretch over the part of itself the other does not reach.
8862        let settled = settle(vec![one, two]);
8863        assert_eq!(
8864            settled,
8865            vec![span(0, 4, rucc_debug::Held::Reg(3)), span(8, 4, rucc_debug::Held::Reg(4))]
8866        );
8867    }
8868
8869    #[test]
8870    fn a_stretch_two_others_disagree_over_the_whole_of_says_nothing_at_all() {
8871        let one = span(0, 8, rucc_debug::Held::Reg(3));
8872        let two = span(0, 8, rucc_debug::Held::Frame(-16));
8873        assert_eq!(settle(vec![one, two]), Vec::new());
8874    }
8875
8876    #[test]
8877    fn stretches_with_a_gap_between_them_keep_the_gap() {
8878        let one = span(0, 4, rucc_debug::Held::Reg(3));
8879        let two = span(16, 4, rucc_debug::Held::Reg(3));
8880        assert_eq!(settle(vec![one, two]), vec![one, two]);
8881    }
8882
8883    /// A function of `len` bytes, since that is the only thing about one these tests look at.
8884    fn extent(len: usize) -> rucc_object::Extent {
8885        rucc_object::Extent {
8886            name: "f".to_owned(),
8887            start: 0,
8888            len,
8889            align: 1,
8890            binding: rucc_object::Binding::Global,
8891            visibility: rucc_object::Visibility::Default,
8892            patch: None,
8893        }
8894    }
8895
8896    /// A line table row at `at` built for the source bytes `lo` to `hi`.
8897    fn row(at: usize, lo: u32, hi: u32) -> rucc_asm::Row {
8898        let span = Span::new(lo, hi);
8899        rucc_asm::Row { at, span, inst: None }
8900    }
8901
8902    #[test]
8903    fn a_row_ends_where_the_next_address_begins() {
8904        let rows = [row(0, 0, 1), row(4, 1, 2), row(10, 2, 3)];
8905        assert_eq!(ends(&extent(16), &rows), vec![4, 10, 16]);
8906    }
8907
8908    #[test]
8909    fn rows_sharing_an_address_all_end_where_the_next_address_begins() {
8910        // Two instructions that encoded to nothing sit on the address of the one after them, and
8911        // none of the three ends in front of that one.
8912        let rows = [row(0, 0, 1), row(4, 1, 2), row(4, 2, 3), row(4, 3, 4)];
8913        assert_eq!(ends(&extent(12), &rows), vec![4, 12, 12, 12]);
8914    }
8915
8916    #[test]
8917    fn the_rows_of_a_scope_that_are_next_to_each_other_come_out_as_one_stretch() {
8918        let rows = [row(0, 0, 4), row(4, 10, 14), row(8, 14, 18), row(12, 40, 44)];
8919        let ends = ends(&extent(16), &rows);
8920        let scope = Span::new(8, 20);
8921        assert_eq!(spread(scope, &ends, &rows), vec![rucc_debug::Reach { from: 4, len: 8 }]);
8922    }
8923
8924    #[test]
8925    fn a_scope_the_back_end_split_in_two_comes_out_as_two_stretches() {
8926        let rows = [row(0, 10, 14), row(4, 40, 44), row(8, 14, 18)];
8927        let ends = ends(&extent(12), &rows);
8928        let scope = Span::new(8, 20);
8929        let over = spread(scope, &ends, &rows);
8930        assert_eq!(
8931            over,
8932            vec![rucc_debug::Reach { from: 0, len: 4 }, rucc_debug::Reach { from: 8, len: 4 }]
8933        );
8934    }
8935
8936    #[test]
8937    fn a_row_with_no_source_of_its_own_belongs_to_no_scope() {
8938        // The prologue is the one of these every function has, and it is not inside any block.
8939        let rows = [rucc_asm::Row { at: 0, span: Span::DUMMY, inst: None }, row(4, 10, 14)];
8940        let ends = ends(&extent(8), &rows);
8941        let scope = Span::new(0, 20);
8942        assert_eq!(spread(scope, &ends, &rows), vec![rucc_debug::Reach { from: 4, len: 4 }]);
8943    }
8944
8945    /// A scope of the unit, written short because these tests are about nothing else.
8946    fn scope(parent: Option<usize>, lo: u32, hi: u32) -> crate::shapes::Scope {
8947        let span = Span::new(lo, hi);
8948        crate::shapes::Scope { parent, span }
8949    }
8950
8951    #[test]
8952    fn a_function_gets_the_scopes_its_own_locals_are_in_and_nothing_else() {
8953        // Two functions' worth of scopes in one table, and this one is in the second pair.
8954        let scopes = [scope(None, 0, 10), scope(None, 20, 30), scope(Some(1), 22, 26)];
8955        let rows = [row(0, 22, 24), row(4, 26, 28)];
8956        let (out, at) = nests(&[Some(2)], &scopes, &extent(8), &rows);
8957        // The one the local is in and the one that is inside, numbered from zero for this
8958        // function, with the parent named by the entry it became rather than by where it was.
8959        assert_eq!(at.get(&1), Some(&0));
8960        assert_eq!(at.get(&2), Some(&1));
8961        assert_eq!(at.get(&0), None);
8962        assert_eq!(out.len(), 2);
8963        assert_eq!(out[0].parent, None);
8964        assert_eq!(out[1].parent, Some(0));
8965        assert_eq!(out[0].over, vec![rucc_debug::Reach { from: 0, len: 8 }]);
8966        assert_eq!(out[1].over, vec![rucc_debug::Reach { from: 0, len: 4 }]);
8967    }
8968
8969    #[test]
8970    fn a_local_written_straight_into_the_body_pulls_no_scope_in() {
8971        let scopes = [scope(None, 20, 30)];
8972        let rows = [row(0, 22, 24)];
8973        let (out, at) = nests(&[None], &scopes, &extent(4), &rows);
8974        assert_eq!(out, Vec::new());
8975        assert!(at.is_empty());
8976    }
8977
8978    #[test]
8979    fn a_scope_whose_code_all_went_away_is_still_one_of_the_functions_scopes() {
8980        // Nothing was built for the bytes it covers, so there is nowhere to say its names were
8981        // live. The entry is written anyway, since dropping it would move a local up into the
8982        // function and make it answer to a name it was not declared under.
8983        let scopes = [scope(None, 20, 30)];
8984        let rows = [row(0, 40, 44)];
8985        let (out, at) = nests(&[Some(0)], &scopes, &extent(4), &rows);
8986        assert_eq!(at.get(&0), Some(&0));
8987        assert_eq!(out.len(), 1);
8988        assert_eq!(out[0].over, Vec::new());
8989    }
8990}