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::path::Path;
14
15use rucc_base::Interner;
16use rucc_codegen::coverage::Fired;
17use rucc_codegen::elsewhere::Elsewhere;
18use rucc_codegen::lowering::Lowerings;
19use rucc_codegen::pipeline::{self, Machine, Recording};
20use rucc_codegen::pressure::Pressure;
21use rucc_cost::Goal;
22use rucc_diag::{Diagnostic, Severity, SourceMap, Span};
23use rucc_ir::{FpContract, Pic as IrPic, Visibility as IrVisibility};
24use rucc_lex::{Convert, Keywords, PpToken, convert};
25use rucc_lower::Protector as LowerProtector;
26use rucc_sema::{Checker, Context as CheckContext};
27use rucc_session::{
28    Contract, EmitKind, FileSystem, Options, Padding, Pic, Protector, Session, Visibility,
29};
30use rucc_target::TargetInfo;
31use rucc_tuple::{Arch, ObjectFormat};
32
33use crate::preprocess::render;
34
35/// What a compilation produced, which is text for most of the kinds and bytes for one of them.
36///
37/// Two variants rather than a string, because an object file is not text and a `Vec<u8>` holding
38/// UTF-8 for six kinds and a file format for the seventh would leave every reader guessing which
39/// it had. [`Artifact::Nothing`] is what a compilation that stopped early gives back, and it is
40/// not the same as an empty file: nothing is written for it at all.
41#[derive(Debug, Clone, PartialEq, Eq, Default)]
42pub enum Artifact {
43    /// The compilation stopped before it produced anything, or the kind asked for produces
44    /// nothing yet.
45    #[default]
46    Nothing,
47    /// Text, which is every kind up to and including assembly.
48    Text(String),
49    /// An object file, which is `-c`, and the names a linker can find in it.
50    ///
51    /// The names travel with the bytes rather than beside them because what wants them is the
52    /// archive step, and an index entry that does not match the member is worse than no archive:
53    /// the linker searches the index, pulls the member out, and still reports the name undefined.
54    /// One value holding both is one value the two cannot disagree in.
55    Object {
56        /// The file.
57        bytes: Vec<u8>,
58        /// Every name another object can reach, as the object writer wrote them. Empty is a real
59        /// answer: a translation unit of nothing but `static` functions is a member an archive
60        /// carries and nothing ever pulls out.
61        defines: Vec<String>,
62    },
63}
64
65impl Artifact {
66    /// The bytes to write, which is nothing at all for [`Artifact::Nothing`].
67    #[must_use]
68    pub fn bytes(&self) -> &[u8] {
69        match self {
70            Artifact::Nothing => &[],
71            Artifact::Text(text) => text.as_bytes(),
72            Artifact::Object { bytes, .. } => bytes,
73        }
74    }
75}
76
77/// What compiling one file produced.
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct Compiled {
80    /// What to write, which is nothing when the compilation failed or produced nothing.
81    pub artifact: Artifact,
82    /// The diagnostics, already rendered, one per element, in the order they were reported.
83    pub messages: Vec<String>,
84    /// How many of them were errors.
85    pub errors: u32,
86    /// Which lowering rules this file fired, for `-Zrule-coverage`.
87    ///
88    /// Empty for a compilation that stopped before the back end, which every kind up to and
89    /// including `--emit=ir` does. That is not the same as a rule set nothing reaches and the
90    /// caller unions these rather than reading one, so a file that fired nothing adds nothing.
91    pub fired: Fired,
92    /// What the register allocator had to put on the stack, for `-Zregister-pressure`.
93    ///
94    /// Empty for the same compilations `fired` is empty for and for the same reason, since both
95    /// are written by the back end and neither is a fact a file that stopped before it has.
96    pub pressure: Pressure,
97    /// What the pre-selection lowering group did, for `-Zlowering`.
98    ///
99    /// Empty for the same compilations `fired` is empty for and for the same reason, since the
100    /// group runs in the back end and a file that stopped before it lowered nothing.
101    pub lowerings: Lowerings,
102    /// What `-fdump-ir=` asked to see, in the order the passes ran.
103    ///
104    /// The optimizer does not write files, because nothing below the driver in
105    /// `spec/18-package-layout.md` knows what a file is, so the text comes back here and the
106    /// caller decides where it goes.
107    pub dumps: Vec<rucc_opt::Dump>,
108    /// What `-fopt-info` asked to hear, already rendered, one remark per line.
109    ///
110    /// Empty when the flag was not given, and also empty when it was given and no pass had
111    /// anything of the kinds asked for to say. Those two are the same text and different facts,
112    /// which is why a misspelled keyword is an error rather than a quiet nothing.
113    pub remarks: String,
114    /// Every file an `#include` found, for the `-M` family.
115    ///
116    /// The same list `Preprocessed` carries and for the same reason. A `-MD` writes it beside
117    /// the object, so the compiling path needs it as much as the preprocessing one does.
118    pub deps: Vec<rucc_pp::Dependency>,
119    /// What `-save-temps` asked to be kept, which is nothing at all unless it was given.
120    ///
121    /// It comes back from here rather than being produced by a second run of the compiler under
122    /// different flags, because a second run is a second answer: the file a person reads has to
123    /// be the file that was compiled, and two runs of anything with a `__TIME__` in it are not
124    /// the same text.
125    pub temps: Temps,
126}
127
128/// The intermediate text a compilation went through, kept when `-save-temps` asked for it.
129///
130/// Both are `None` on a compilation that was not asked to keep anything, and the assembly is
131/// `None` on one that stopped before there was any. Holding the text rather than writing it is
132/// what keeps this function free of the file system, which is what lets it be tested against a
133/// map from path to bytes.
134#[derive(Debug, Clone, PartialEq, Eq, Default)]
135pub struct Temps {
136    /// Phase 4's output, the same text `-E` would have printed.
137    pub preprocessed: Option<String>,
138    /// The assembly the back end produced on the way to the object file.
139    pub assembly: Option<String>,
140}
141
142impl Compiled {
143    /// Whether anything went wrong badly enough that the output should not be used.
144    #[must_use]
145    pub fn failed(&self) -> bool {
146        self.errors > 0
147    }
148
149    /// The text that was produced, and the empty string for anything that is not text.
150    ///
151    /// A caller that asked for one of the text kinds knows which it asked for, so this saves it
152    /// matching on a variant it has already ruled out.
153    #[must_use]
154    pub fn text(&self) -> &str {
155        match &self.artifact {
156            Artifact::Text(text) => text,
157            _ => "",
158        }
159    }
160}
161
162/// Compiles one file as far as `opts.emit` asks for and renders the result.
163///
164/// `name` is the path as the user wrote it, which is the name every diagnostic about the file
165/// uses. Every kind but the executable produces something today, and that one runs the same front
166/// end and gives back nothing, so that a file with a mistake in it is reported the same way
167/// whichever kind was asked for, rather than compiling silently until the part that is written
168/// notices.
169///
170/// The checking is skipped when the parse reported an error. The two poisoning rules mean a
171/// diagnosed expression produces no further complaints, but a declaration the parser had to skip
172/// past leaves no declaration behind at all, and every later use of that name would be reported
173/// as undeclared. One mistake is worth one message.
174#[must_use]
175pub fn compile(opts: &Options, name: &str, fs: &dyn FileSystem) -> Compiled {
176    let mut sess = Session::new(opts.clone());
177    // Before anything else interns a name. The keyword symbols have to be one unbroken run for
178    // a lookup to be a subtraction, and the preprocessor interns every identifier it reads, so
179    // building this after the expansion would mean building it after `char` had been seen.
180    let keywords = Keywords::new(&mut sess.interner, opts.std, opts.gnu_extensions);
181    let mut diagnostics: Vec<Diagnostic> = Vec::new();
182    // Filled in by the back end when there is one, and empty for every kind that stops before it.
183    let mut fired = Fired::new();
184    // The same, and the other thing the back end is asked to record about itself.
185    let mut pressure = Pressure::new();
186    let mut lowerings = Lowerings::asked(opts.lowering_dump.is_some());
187    // Filled in by the optimizer, and only when `-fdump-ir=` asked for something.
188    let mut dumps = Vec::new();
189    let mut remarks = String::new();
190    // Filled in as the compilation goes past each of them, and only under `-save-temps`.
191    let mut temps = Temps::default();
192
193    let bytes = match fs.read(Path::new(name)) {
194        Ok(bytes) => bytes,
195        Err(e) => return failure(format!("{name}: {e}")),
196    };
197    let Ok(file) = sess.sources.add_shared(crate::phase::source_name(name), bytes, None) else {
198        return failure(format!("{name}: the source map has no room left for this file"));
199    };
200
201    // Phases 1 to 4. The expanded stream is turned into pp-tokens straight away, because the
202    // include context borrows the source map that rendering a diagnostic reads and the borrow
203    // has to end before anything is rendered.
204    let mut pp = rucc_pp::Preprocessor::with_prefix_map(opts.prefix_map.macros.clone());
205    let predef = rucc_pp::Predef::for_options(opts);
206    let expanded: Vec<PpToken> = {
207        let mut tokens = Vec::new();
208        // The inner block is the borrow. The printer under `-save-temps` reads the source map
209        // that the include context is holding, so the context has to be gone before it runs, and
210        // nothing happens in between, which is what makes the text it prints the text that is
211        // compiled below rather than a second answer to the same question.
212        {
213            let mut cx =
214                rucc_pp::Context::new(&mut sess.interner, &mut sess.sources, fs, &opts.search);
215            cx.lex = rucc_lex::Options::for_dialect(opts.std, opts.gnu_extensions);
216            cx.pedantic = opts.pedantic;
217            if pp.predefine(&sess.target, &predef, &mut cx).is_err() {
218                return failure(format!(
219                    "{name}: the source map has no room for the built in macros"
220                ));
221            }
222            if pp.preinclude(&opts.preincludes, &mut tokens, &mut cx).is_err() {
223                return failure(format!("{name}: the source map has no room for the command line"));
224            }
225            tokens.append(&mut pp.run(file, &mut cx));
226        }
227        if opts.save_temps.wanted() {
228            temps.preprocessed = Some(rucc_pp::print(
229                file,
230                &tokens,
231                pp.line_directives(),
232                &sess.sources,
233                &sess.interner,
234                rucc_pp::PrintOptions { line_markers: opts.line_markers },
235            ));
236        }
237        tokens.iter().map(|token| token.to_pp()).collect()
238    };
239    diagnostics.extend(pp.take_diagnostics());
240    // Taken here rather than at the end, because the preprocessor is done with and everything
241    // after this is about the tree it produced.
242    let deps = pp.dependencies().to_vec();
243
244    // Phase 7, which is where a spelling becomes a keyword and a preprocessing number becomes
245    // a constant of a type.
246    let cx = Convert {
247        keywords: &keywords,
248        interner: &sess.interner,
249        target: &sess.target,
250        std: opts.std,
251        gnu: opts.gnu_extensions,
252        pedantic: opts.pedantic,
253    };
254    let (tokens, complaints) = convert(&expanded, &cx);
255    diagnostics.extend(complaints);
256
257    let parsed = rucc_parse::parse(
258        &tokens,
259        rucc_parse::Context {
260            interner: &sess.interner,
261            std: opts.std,
262            gnu: opts.gnu_extensions,
263            pedantic: opts.pedantic,
264            error_limit: opts.error_limit as usize,
265        },
266    );
267    let parse_failed = parsed.diagnostics.iter().any(|d| d.severity.is_fatal());
268    diagnostics.extend(parsed.diagnostics);
269
270    let mut artifact = Artifact::Nothing;
271    // Zero when nothing instruments, which is the truthful summary of a file built without
272    // `-fsafety`: no checks went in, so none is standing, and every call it makes is unmodelled.
273    let mut instrumented = Instrumented::default();
274    if !parse_failed {
275        let mut checker = Checker::new(
276            &parsed.ast,
277            CheckContext {
278                names: &sess.interner,
279                target: &sess.target,
280                std: opts.std,
281                gnu: opts.gnu_extensions,
282                pedantic: opts.pedantic,
283                permissive: opts.permissive,
284                gnu89_inline: opts.gnu89_inline,
285                error_limit: opts.error_limit as usize,
286                // A freestanding program has no C library, so a name that is the library's
287                // everywhere else is the program's own here and means whatever it defined.
288                builtins: opts.builtins && opts.hosted,
289                no_builtin: &opts.no_builtin,
290                short_enums: opts.short_enums,
291                ms_extensions: sess.ms_extensions(),
292                trapping_math: opts.trapping_math,
293            },
294        );
295        checker.check_unit();
296        let checked = checker.finish();
297        if !checked.failed() {
298            match opts.emit {
299                EmitKind::Tast => {
300                    artifact = Artifact::Text(rucc_sema::print(
301                        &checked.tast,
302                        &checked.types,
303                        &sess.interner,
304                    ));
305                }
306                // Nothing past the checker, because a granule is a fact about a layout and a
307                // layout is settled the moment the closing brace is seen. Lowering the
308                // function bodies would take minutes on an amalgamation and answer nothing.
309                EmitKind::TypeGranules => {
310                    artifact = Artifact::Text(rucc_types::granule_report(
311                        &checked.types,
312                        &sess.interner,
313                        &sess.target,
314                    ));
315                }
316                EmitKind::Ir
317                | EmitKind::MirFinal
318                | EmitKind::Asm
319                | EmitKind::Object
320                | EmitKind::Archive
321                | EmitKind::Executable
322                | EmitKind::SafetySummary => {
323                    // What a `.incbin` in an `asm` at file scope names is read through the same
324                    // file system the sources came through, and from where the compiler was run
325                    // rather than from beside the source, because that is where an assembler
326                    // looks for it.
327                    let mut read = |named: &str| {
328                        fs.read(Path::new(named))
329                            .map(|bytes| bytes.as_slice().to_vec())
330                            .map_err(|why| why.to_string())
331                    };
332                    let mut lowered = rucc_lower::lower(
333                        crate::phase::source_name(name),
334                        rucc_lower::Context {
335                            tast: &checked.tast,
336                            types: &checked.types,
337                            target: &sess.target,
338                            names: &mut sess.interner,
339                            visibility: match opts.visibility {
340                                Visibility::Default => IrVisibility::Default,
341                                Visibility::Hidden => IrVisibility::Hidden,
342                                Visibility::Protected => IrVisibility::Protected,
343                            },
344                            protector: match opts.protector {
345                                Protector::None => LowerProtector::None,
346                                Protector::Buffers => LowerProtector::Buffers,
347                                Protector::Strong => LowerProtector::Strong,
348                                Protector::All => LowerProtector::All,
349                            },
350                            wrapping: rucc_lower::Wrapping {
351                                signed: opts.wrapping.signed,
352                                pointer: opts.wrapping.pointer,
353                                trap: opts.wrapping.trap,
354                            },
355                            aliasing: opts.strict_aliasing,
356                            padding: opts.padding == Padding::Ignored,
357                            contract: match opts.fp_contract {
358                                Contract::Off => FpContract::Off,
359                                Contract::On => FpContract::On,
360                                Contract::Fast => FpContract::Fast,
361                            },
362                            align: opts.align_functions,
363                            read: &mut read,
364                        },
365                    );
366                    // The walk reports what it cannot build, and what it did build is printed
367                    // anyway: a file with one construct missing from it is more use to read
368                    // than nothing at all, and the errors are what stop it being compiled.
369                    let failed = lowered.diagnostics.iter().any(|d| d.severity.is_fatal());
370                    if !failed {
371                        // The verifier runs on everything the walk builds, always. It is the
372                        // one check that a bug in the walk cannot talk its way past, and a
373                        // wrong instruction found here costs a message rather than an hour
374                        // in front of a debugger over the assembly it turned into.
375                        if let Err(errors) = rucc_ir::verify(&lowered.module, &sess.interner) {
376                            for error in errors {
377                                diagnostics.push(internal(&format!("invalid IR, {error}")));
378                            }
379                        } else if let Err(complaints) =
380                            instrument(&mut lowered.module, &mut sess.interner, opts)
381                                .map(|done| instrumented = done)
382                        {
383                            diagnostics.extend(complaints);
384                        } else if let Err(complaints) = optimize(
385                            &mut lowered.module,
386                            &sess.interner,
387                            &sess.target,
388                            opts,
389                            name,
390                            &mut dumps,
391                            &mut remarks,
392                        ) {
393                            diagnostics.extend(complaints);
394                        } else if opts.emit == EmitKind::SafetySummary {
395                            // After the optimizer, because the number that matters is how many
396                            // checks are still standing and there is no way to know that before it
397                            // has run. Before the back end, because the back end turns a check into
398                            // a call and a summary of calls is not a summary of checks.
399                            artifact = Artifact::Text(
400                                rucc_safety::summarize(
401                                    &lowered.module,
402                                    &sess.interner,
403                                    name,
404                                    opts.safety.as_str(),
405                                    instrumented.checks,
406                                    instrumented.interposed,
407                                    instrumented.crossings,
408                                )
409                                .render(),
410                            );
411                        } else if opts.emit == EmitKind::Ir {
412                            // After the optimizer rather than before it, so that `--emit=ir -O2`
413                            // is the IR the back end will be given rather than the IR it would
414                            // have been given at `-O0`. There is no other way to see what a pass
415                            // did without reading the assembly it turned into.
416                            artifact =
417                                Artifact::Text(rucc_ir::print(&lowered.module, &sess.interner));
418                        } else {
419                            // The back end, which is every pass after the IR and which is
420                            // where a construct nothing has a rule for is finally noticed.
421                            match generate(
422                                &mut lowered.module,
423                                &mut sess.interner,
424                                &sess.target,
425                                opts,
426                                &mut Recording {
427                                    fired: &mut fired,
428                                    pressure: &mut pressure,
429                                    lowerings: &mut lowerings,
430                                },
431                                &mut temps.assembly,
432                                Origin { map: &sess.sources, name },
433                            ) {
434                                Ok(made) => artifact = made,
435                                Err(complaints) => diagnostics.extend(complaints),
436                            }
437                        }
438                    }
439                    diagnostics.extend(lowered.diagnostics);
440                }
441                _ => {}
442            }
443        }
444        diagnostics.extend(checked.diagnostics);
445    }
446
447    let mut messages = Vec::with_capacity(diagnostics.len());
448    let mut errors = 0;
449    for diag in &diagnostics {
450        // `-w` drops the warning here rather than at the several hundred places one is raised,
451        // and it drops it before the count, so `-w -Werror` compiles. A warning that was never
452        // raised is not a warning there is anything to promote. A warning about something in a
453        // header that came with the machine goes the same way for the same reason, unless
454        // `-Wsystem-headers` asked for it.
455        if rucc_diag::dropped(diag, &sess.sources, opts.warnings, opts.system_header_warnings) {
456            continue;
457        }
458        if diag.severity.is_fatal()
459            || (diag.severity == Severity::Warning && opts.warnings_are_errors)
460        {
461            errors += 1;
462        }
463        messages.push(render(diag, &sess.sources, opts.warnings_are_errors));
464    }
465    if errors > 0 {
466        // A tree built from a file that did not compile is not a tree anything should read.
467        artifact = Artifact::Nothing;
468    }
469    // Kept even when the compilation failed, because a rule that fired did fire and a report about
470    // which rules a corpus reaches should not lose the ones a file with a mistake in it reached.
471    Compiled { artifact, messages, errors, fired, pressure, lowerings, dumps, remarks, deps, temps }
472}
473
474/// Reads one file of IR, checks it, and prints it back.
475///
476/// This is the compiler's own textual IR arriving as an input rather than leaving as an output,
477/// which is what makes the round trip in the M2 exit criterion something to run rather than
478/// something to believe: what the printer wrote is read back, verified, and written again, and
479/// the two files are either the same bytes or they are not.
480///
481/// The verifier runs here for the reason it runs after the walk. A module that was printed by
482/// this compiler has been through it once already, and one that a person edited has not.
483#[must_use]
484pub fn compile_ir(opts: &Options, name: &str, fs: &dyn FileSystem) -> Compiled {
485    let mut sess = Session::new(opts.clone());
486    if opts.emit != EmitKind::Ir {
487        return failure(format!(
488            "{name}: an input of IR can only be emitted as IR, and `--emit={}` asks for what \
489             the C in front of it became",
490            opts.emit.as_str()
491        ));
492    }
493    let bytes = match fs.read(Path::new(name)) {
494        Ok(bytes) => bytes,
495        Err(e) => return failure(format!("{name}: {e}")),
496    };
497    let Ok(text) = std::str::from_utf8(bytes.as_slice()) else {
498        return failure(format!("{name}: this is not text, so it is not IR"));
499    };
500
501    let module = match rucc_ir::parse(text, &mut sess.interner) {
502        Ok(module) => module,
503        Err(error) => {
504            return failure(format!("{name}:{}: {}", error.line, error.message));
505        }
506    };
507    let mut diagnostics: Vec<Diagnostic> = Vec::new();
508    if let Err(errors) = rucc_ir::verify(&module, &sess.interner) {
509        for error in errors {
510            diagnostics.push(invalid(&format!("invalid IR, {error}")));
511        }
512    }
513    let mut messages = Vec::with_capacity(diagnostics.len());
514    for diag in &diagnostics {
515        messages.push(render(diag, &sess.sources, opts.warnings_are_errors));
516    }
517    let errors = u32::try_from(messages.len()).unwrap_or(u32::MAX);
518    let artifact = if errors > 0 {
519        Artifact::Nothing
520    } else {
521        Artifact::Text(rucc_ir::print(&module, &sess.interner))
522    };
523    // Nothing here reaches the back end, so no rule fired and there is nothing to record.
524    Compiled {
525        artifact,
526        messages,
527        errors,
528        fired: Fired::new(),
529        pressure: Pressure::new(),
530        lowerings: Lowerings::new(),
531        dumps: Vec::new(),
532        remarks: String::new(),
533        deps: Vec::new(),
534        temps: Temps::default(),
535    }
536}
537
538/// Puts the memory safety checks in and redirects the calls that cross the boundary, when
539/// `-fsafety=` asked for them.
540///
541/// Between the walk and the optimizer, which is where section 15.3 of
542/// `spec/safe-memory/15-integration.md` puts it and which is the whole design in one line: the
543/// checks go in while the addresses the program computes still exist, and the optimizer then
544/// discharges the ones it can prove. Every sanitizer that came before instruments after the
545/// optimizer so that its checks cannot be deleted, and pays for all of them forever.
546///
547/// The calls to the C library are redirected here too, and in the same window and for a related
548/// reason. `spec/safe-memory/10-boundaries.md` section 10.3 wants a `memcpy` modelled by a wrapper
549/// that performs the judgements, and `rucc_safety::wrap` is why that has to happen before the
550/// optimizer sees the call rather than after.
551///
552/// The verifier runs again afterwards, for the reason it runs after the walk. This pass rewrites
553/// every function in the module, and a pass that produced IR nothing else accepts should say so
554/// here rather than in the assembly it turned into.
555///
556/// # Errors
557///
558/// When the inserted checks left the module in a state the verifier refuses, which is a bug in
559/// this compiler and not in the program being compiled.
560fn instrument(
561    module: &mut rucc_ir::Module,
562    names: &mut Interner,
563    opts: &Options,
564) -> Result<Instrumented, Vec<Diagnostic>> {
565    if !opts.safety.instruments() {
566        return Ok(Instrumented::default());
567    }
568    let mut checks = rucc_safety::run(module, opts.subobject, opts.promise, opts.races);
569    // The one check that is about a call rather than about an access, so it is a walk of its own
570    // and it is here rather than in the walk above. `rucc_safety::ending` is why, and the short
571    // version is that deciding it means resolving a name, which takes the interner.
572    //
573    // Before the redirection for the same reason the redirection is before the optimizer: what this
574    // reads is the name the program wrote, and a pass that had already pointed the call somewhere
575    // else would leave it with a name this one has no row for.
576    checks.freed = rucc_safety::ending::checks(module, names);
577    // Before the optimizer rather than beside the check lowering, which is what
578    // `rucc_safety::wrap` argues out: `memcpy` is a name an optimizer knows things about, and a
579    // pass that turns a short copy into a pair of loads and stores would leave behind accesses the
580    // check insertion has already finished walking past.
581    let interposed = rucc_safety::redirect(module, names);
582    // After the redirection, so that a call this build models with a wrapper is not also counted
583    // as a crossing it did not model.
584    let crossings = rucc_safety::witness(module, names);
585    match rucc_ir::verify(module, names) {
586        Ok(()) => Ok(Instrumented { checks, interposed, crossings }),
587        Err(errors) => Err(errors
588            .iter()
589            .map(|e| internal(&format!("invalid IR after check insertion, {e}")))
590            .collect()),
591    }
592}
593
594/// What the instrumentation did, which nothing but the summary reads.
595///
596/// Carried out of [`instrument`] rather than recovered from the module afterwards because neither
597/// number survives the optimizer: a check that was discharged leaves nothing behind saying it was
598/// ever there, and a call that was pointed at a wrapper looks like a call that always named one.
599#[derive(Clone, Copy, Debug, Default)]
600struct Instrumented {
601    /// How many checks of each class went in.
602    checks: rucc_safety::Counts,
603    /// How many calls were pointed at an interposition wrapper.
604    interposed: usize,
605    /// How many places a pointer crosses to or from code this build did not instrument.
606    crossings: rucc_safety::Sites,
607}
608
609/// Runs the optimizer over the module, and collects whatever the dumps asked for.
610///
611/// The level chooses a pipeline, the `-f` flags edit it, and at `-O0` there is nothing in it, so
612/// this is a walk over an empty list rather than a branch on the level. See section 9.1 of
613/// `spec/09-optimizer.md` for why the pipelines are written out rather than assembled.
614///
615/// # Errors
616///
617/// When a pass left the module in a state the verifier refuses, which is a bug in the pass and
618/// not in the program being compiled, so it is reported as an internal error the way a bad
619/// lowering is.
620fn optimize(
621    module: &mut rucc_ir::Module,
622    names: &Interner,
623    target: &TargetInfo,
624    opts: &Options,
625    file: &str,
626    dumps: &mut Vec<rucc_opt::Dump>,
627    remarks: &mut String,
628) -> Result<(), Vec<Diagnostic>> {
629    let mut settings = rucc_opt::Options::for_level(opts.opt_level);
630    // What the analyses that read a body may believe about it. The same question the back end asks
631    // about addresses, with one thing on top: `-fno-semantic-interposition` is the build promising
632    // that a name it exports is the one that will run, which is what every distribution builds a
633    // library with. It says nothing about how an address is reached, and gcc does not change that
634    // under the flag either, so the back end is not given this value.
635    settings.interposition = match opts.interposition {
636        true => replaceable(target, opts),
637        false => IrPic::Executable,
638    };
639    settings.toggles.clone_from(&opts.passes);
640    settings.fuel = opts.pass_fuel.iter().cloned().collect();
641    settings.global_fuel = opts.pass_fuel_global;
642    settings.verify |= opts.verify_each;
643    for (on, spec) in &opts.pass_gates {
644        // Same argument as the dumps below: every spelling in here was checked while the
645        // arguments were parsed, so a rejection now is this compiler disagreeing with itself.
646        if let Err(why) = settings.gates.add(*on, spec) {
647            return Err(vec![internal(&why)]);
648        }
649    }
650    for spec in &opts.dump_ir {
651        // Every spelling in here was checked while the arguments were parsed, so a rejection
652        // now is this compiler disagreeing with itself rather than the command line being wrong.
653        if let Err(why) = settings.dumps.add(spec) {
654            return Err(vec![internal(&why)]);
655        }
656    }
657    let mut wants = rucc_opt::Wants::none();
658    for spec in &opts.opt_info {
659        // Same argument as the dumps above: every spelling was checked while the arguments were
660        // parsed, so a rejection now is the compiler disagreeing with itself.
661        if let Err(why) = wants.add(spec) {
662            return Err(vec![internal(&why)]);
663        }
664    }
665    let report = rucc_opt::run(module, names, &settings);
666    remarks.push_str(&rucc_opt::optinfo::render(file, &report, names, wants));
667    dumps.extend(report.dumps);
668    match report.broke.is_empty() {
669        true => Ok(()),
670        false => Err(report.broke.iter().map(|why| internal(why)).collect()),
671    }
672}
673
674/// Runs the back end over every function in `module` and writes what came out.
675///
676/// One machine function per definition in the module, in the order the module holds them, every
677/// register physical and every frame offset a constant. A declaration has no body and is skipped,
678/// because there is nothing in it to compile.
679///
680/// What the last step is, is the only thing `--emit=mir-final`, `-S` and `-c` disagree about. The
681/// three read the same functions and differ in whether they are printed as machine IR, printed as
682/// assembly, or encoded and put in a file, which is the point of section 11.1 of
683/// `spec/11-asm-objects-debug.md`: a listing that disagrees with the object file beside it is
684/// worse than no listing, and the way to make that impossible is to have one description of an
685/// instruction and two ways of writing it down.
686///
687/// # Errors
688///
689/// One diagnostic per function the back end could not compile, or one about the target when no
690/// back end covers it at all. Every function is attempted rather than stopping at the first, so a
691/// file with three constructs missing from the rule set reports three rather than one at a time.
692///
693/// `assembly` is where `-save-temps` gets its listing from on the path that does not print one,
694/// which is the same functions written the other way rather than a second compilation of the same
695/// file. A listing that disagrees with the object beside it would be worse than none.
696/// Whether a name this file exports is one another object may define or replace.
697///
698/// The link that reads the object decides half of what is in it, and the command line is where that
699/// is said, which is why the flag reaches this far down. See #756.
700///
701/// ELF only, because it is a question about a format rather than about a machine and the other two
702/// answer it differently. Mach-O has a two level namespace, so a name a library defines is bound to
703/// that library and is not replaced by a definition loaded earlier, and it has no copy relocations,
704/// so a variable defined elsewhere needs the table whichever link is coming. COFF decides what
705/// leaves a DLL by an export table the linker is handed. Neither has an object writer here yet, so
706/// what this does is decline to say the ELF answer about them.
707fn replaceable(target: &TargetInfo, opts: &Options) -> IrPic {
708    match (target.tuple.os().object_format(), opts.pic) {
709        (Some(ObjectFormat::Elf), Pic::Library) => IrPic::Library,
710        _ => IrPic::Executable,
711    }
712}
713
714/// Where the file being generated came from, which is what the debug information is about.
715///
716/// The two together rather than separately because neither is any use on its own here: a span
717/// without the map it points into is a pair of numbers, and a name without the spans is a file
718/// nothing in the object refers to.
719#[derive(Clone, Copy)]
720struct Origin<'a> {
721    /// Where every span in the module points.
722    map: &'a SourceMap,
723    /// What the command line called the file, which is what `DW_AT_name` says.
724    name: &'a str,
725}
726
727fn generate(
728    module: &mut rucc_ir::Module,
729    names: &mut Interner,
730    target: &TargetInfo,
731    opts: &Options,
732    recording: &mut Recording<'_>,
733    assembly: &mut Option<String>,
734    origin: Origin<'_>,
735) -> Result<Artifact, Vec<Diagnostic>> {
736    let Some(machine) = Machine::for_target(target) else {
737        return Err(vec![unsupported(&format!(
738            "there is no back end for {} in this compiler yet, so there is nothing to generate",
739            target.tuple
740        ))]);
741    };
742    // Refused rather than dropped. A command line that asks for a stack protector on a target
743    // that has nowhere to keep the word one is compared against would otherwise get code with no
744    // protection in it and no indication that the flag did nothing, which is the one outcome worse
745    // than the error. Windows is the case: it has a protector and it is a different mechanism.
746    if opts.protector != Protector::None && machine.conv.guard.is_none() {
747        return Err(vec![unsupported(&format!(
748            "{} is not supported for {} yet, because the stack protector on that target is not \
749             the one this compiler writes",
750            opts.protector, target.tuple
751        ))]);
752    }
753    // The same answer for the same reason. What says a file was built to have its control flow
754    // checked is a note, the note is an ELF one, and a target whose objects are not ELF has nowhere
755    // to put it: the landing pads would go in and nothing would ever turn the check on. Windows has
756    // the same hardware and asks for it a different way, which is a bit in the image the linker is
757    // told to set rather than anything a compiler writes into an object.
758    if opts.control.any() && target.tuple.os().object_format() != Some(ObjectFormat::Elf) {
759        return Err(vec![unsupported(&format!(
760            "-fcf-protection={} is not supported for {} yet, because what says a file was built \
761             for it there is not the note this compiler writes",
762            opts.control, target.tuple
763        ))]);
764    }
765    // And once more. A profiled build is one whose functions call a routine the runtime provides,
766    // and a target whose runtime provides no such routine would get a call to a name nothing
767    // defines, which is a link error a long way from the flag that caused it. Windows profiles a
768    // build by calling something else, asked for a different way and taking its argument in a
769    // register, so it is not this hook spelled differently.
770    let profile = match machine.conv.trace {
771        Some(trace) => opts.profile.then(|| opts.hook.early(trace.fentry)),
772        None if opts.profile => {
773            return Err(vec![unsupported(&format!(
774                "-pg is not supported for {} yet, because the profiler's hook on that target is \
775                 not the one this compiler calls",
776                target.tuple
777            ))]);
778        }
779        None => None,
780    };
781    // And once more. The room a patcher was promised is only half the feature: the other half is a
782    // section listing where every function's room is, and both the section's shape and the way it
783    // points at the text it belongs to are ELF's. A format that has no such section would take the
784    // nops and quietly lose the list, which is a build that looks patchable and is not.
785    if opts.patchable.any() && target.tuple.os().object_format() != Some(ObjectFormat::Elf) {
786        return Err(vec![unsupported(&format!(
787            "-fpatchable-function-entry= is not supported for {} yet, because what records where \
788             the room is there is not the section this compiler writes",
789            target.tuple
790        ))]);
791    }
792    let flags = pipeline::Flags {
793        frame_pointer: opts.frame_pointer,
794        red_zone: opts.red_zone,
795        stack_clash: opts.stack_clash,
796        landing: opts.control.branch(),
797        profile: match profile {
798            None => pipeline::Profile::No,
799            Some(true) => pipeline::Profile::Early,
800            Some(false) => pipeline::Profile::Late,
801        },
802        patch: pipeline::Room { after: opts.patchable.after(), before: opts.patchable.before },
803        // On at every level above `-O0`, which is where gcc turns `-freorder-blocks` on
804        // (`gcc/opts.cc:604`) and what `spec/optimizer/38-scheduling-and-layout.md` section 38.3
805        // reads off that: it is one of the earliest optimizations there is, it is nearly free,
806        // and it helps every target. `-O0` keeps the order the shape of the graph gives, so that
807        // the blocks come out in the order they were written and a person stepping through the
808        // code walks down the screen.
809        reorder: opts.reorder_blocks.unwrap_or_else(|| opts.opt_level.runs_optimizer()),
810        // On at every level above `-O0`, for the reason the line above is off at it. Sharing one
811        // run of bytes between two locals is a smaller frame and a worse debugger: a variable that
812        // is out of scope reads as whatever took its place, which is what `-O0` exists not to do.
813        // Above it the frame is the win, and `-fstack-reuse=` says either answer at any level.
814        reuse: opts.stack_reuse.unwrap_or_else(|| opts.opt_level.runs_optimizer()),
815        // On from `-O2`, which is where gcc turns `-fschedule-insns2` on and what
816        // `spec/optimizer/38-scheduling-and-layout.md` section 38.6 asks for. Not at `-O1`,
817        // because a schedule is a whole dependence graph per block and `-O1` is the level whose
818        // budget is roughly `-O0`'s. Not at `-O0` for the reason nothing else is.
819        schedule: opts.schedule_insns.unwrap_or_else(|| opts.opt_level.schedules()),
820        // Whatever the command line said, and the model's own answer when it said nothing.
821        accurate: opts.cycle_accurate_model,
822        // The same flag that turns the IR verifier on in a release build, since what it says is
823        // that this run should check itself and the back end has checks of its own.
824        verify: opts.verify_each,
825        // What the level asked for. The back end had no way to know until now, which is
826        // tamnd/rucc#741: `-Os` picked a shorter list of middle end passes and then compiled the
827        // result exactly as `-O2` would have. The level is asked whether it optimizes for size
828        // rather than matched against, so a level added later answers this without editing it.
829        goal: Goal::for_size(opts.opt_level.is_size()),
830    };
831
832    // The checks become calls here rather than beside the insertion, because the id each one
833    // carries is an index into a table and a row for a check the optimizer deleted is a row nothing
834    // will ever name. Section 6.3.1 of `spec/safe-memory/06-instrumentation.md` is what this
835    // eventually becomes and `rucc_safety::lower` says why it is not that yet.
836    //
837    // It is inside the back end rather than beside the optimizer so that `--emit=ir` still shows
838    // the checks. The IR a person reads should say what the compiler decided, not how it spelled it
839    // for the machine.
840    if opts.safety.instruments() {
841        // Which calls hand back storage, which the lowering needs and `-O0` has not worked out.
842        // `rucc_opt::pipeline` runs this only when some pass in the run reads the summaries, since a
843        // flag nothing reads is noise in a dump, and at `-O0` nothing did. Something does now: the
844        // capability for a pointer an allocator just returned is the one capability that is exact
845        // and costs a load, and `rucc_safety::slot` finds those sites by the flag. The safety suite
846        // runs at `-O0`, so without this the cheap case would be the one case that never happens.
847        //
848        // Safe to run twice and safe to run late, because it only ever sets the flag and never
849        // clears one, so a build that had it already gets the same module back.
850        rucc_opt::heap::annotate(module, names);
851        // Which calls hand their capabilities to the callee and which say there are none. Here and
852        // not beside the insertion, because the rule is what each function still has left to check
853        // and the optimizer is what makes that small: running before it would give every callee a
854        // frame for checks that are about to be discharged. `rucc_safety::handover` is the rule and
855        // the pass both, and the census in `--emit=safety-summary` reads the same rule, so the
856        // buckets it prints describe the code that was actually built.
857        rucc_safety::handover::arrange(module);
858        rucc_safety::lower(module, names);
859        if let Err(errors) = rucc_ir::verify(module, names) {
860            return Err(errors
861                .iter()
862                .map(|e| internal(&format!("invalid IR after check lowering, {e}")))
863                .collect());
864        }
865    }
866
867    // Worked out before the loop and not inside it, because it reads the whole module and the loop
868    // is holding one function of it. It has to be after the check lowering above, since that adds
869    // calls to the runtime and so can add a name this file does not define.
870    //
871    // The link that reads the object decides half of what is in it, and the command line is where
872    // that is said, which is why the flag reaches this far down. See #756. The format decides the
873    // other half, since a table only exists on a format that has one to reach through.
874    //
875    let elsewhere = Elsewhere::of(module, replaceable(target, opts), target.object_format);
876
877    let mut funcs = Vec::new();
878    let mut complaints = Vec::new();
879    for id in module.funcs() {
880        if module[id].is_declaration() {
881            continue;
882        }
883        match pipeline::compile_recording(
884            &mut module[id],
885            names,
886            &machine,
887            &elsewhere,
888            flags,
889            recording,
890        ) {
891            Ok(func) => funcs.push(func),
892            Err(why) => {
893                let name = names.resolve(module[id].name).to_owned();
894                // The function knows where the instruction came from, so the message lands on
895                // the line somebody wrote rather than on the file as a whole.
896                let span = why.inst().map_or(Span::DUMMY, |inst| module[id].span(inst));
897                let said = format!("cannot generate code for '{name}': {why}");
898                complaints.push(unsupported_at(&said, span));
899            }
900        }
901    }
902    if !complaints.is_empty() {
903        return Err(complaints);
904    }
905    // The variables the file defines, which go through the back end the way the functions did not:
906    // there is nothing in a variable to select instructions for, so the module is what says what
907    // one is right up to the point where it is written down.
908    // The second names go the same way and for the same reason, and they are neither a function
909    // nor a variable: an alias is an entry in the symbol table and no bytes of anything.
910    let (globals, aliases) = match opts.emit {
911        EmitKind::Asm | EmitKind::Object | EmitKind::Archive | EmitKind::Executable => (
912            rucc_asm::globals(module, names, target.object_format).map_err(refused)?,
913            rucc_asm::aliases(module, names).map_err(refused)?,
914        ),
915        _ => (rucc_asm::Globals::default(), Vec::new()),
916    };
917    // A failure in either of the last two is a bug here rather than a program this compiler is
918    // behind on, because every instruction in a function that got this far came out of the same
919    // description both of them read and every register in it has been allocated.
920    let unwind = opts.unwinds();
921    match opts.emit {
922        EmitKind::Asm => {
923            rucc_asm::print(&funcs, &globals, &aliases, names, target, unwind, output(opts, target))
924                .map(Artifact::Text)
925                .map_err(refused)
926        }
927        // An executable is an object as far as this gets: one is what each file of a link
928        // contributes, and the linker is what turns them into the other. An archive is the same
929        // again, with the archive writer in place of the linker.
930        EmitKind::Object | EmitKind::Archive | EmitKind::Executable => {
931            if opts.save_temps.wanted() {
932                let listing = rucc_asm::print(
933                    &funcs,
934                    &globals,
935                    &aliases,
936                    names,
937                    target,
938                    unwind,
939                    output(opts, target),
940                );
941                *assembly = Some(listing.map_err(refused)?);
942            }
943            let assembled = rucc_asm::assemble(&funcs, names, target, unwind, opts.debug_info)
944                .map_err(refused)?;
945            let text = assembled.text;
946            let data = globals.image();
947            // The line table, from the spans the assembler kept beside the bytes. Empty when the
948            // build asked for no debug information, which is the case the rows above are not even
949            // recorded in.
950            let info = if opts.debug_info {
951                describe(&text, &assembled.lines, origin, opts, target)
952                    .map_err(|why| vec![internal(&why)])?
953            } else {
954                rucc_object::Info::default()
955            };
956            // A format with no writer is a target this compiler is behind on and anything else
957            // the writer refused is a bug here, and the two are not the same news to get.
958            let bytes =
959                rucc_object::write(&text, &data, &aliases, target, output(opts, target), &info)
960                    .map_err(wrote)?;
961            // Asked of the writer rather than worked out from the same three values here, so that
962            // what the archive's index says and what is in the member cannot come apart. It is
963            // wanted only by `--emit=archive` and is cheap enough that the other two kinds are not
964            // worth a second path.
965            let defines = rucc_object::defines(&text, &data, &aliases, target).map_err(wrote)?;
966            Ok(Artifact::Object { bytes, defines })
967        }
968        _ => Ok(Artifact::Text(rucc_mir::print(&funcs, names, target.regs))),
969    }
970}
971
972/// The debug sections for what was just assembled, as bytes and relocations.
973///
974/// This is where a span becomes a file and a line, and it is here rather than anywhere further down
975/// because the source map is the driver's and because the paths in it are still paths at this point.
976/// [`rucc_session::PrefixMap::apply`] is run over every one of them, which is the whole of what
977/// `-fdebug-prefix-map=` and `-ffile-prefix-map=` asked for: a build is only reproducible if all of
978/// the paths in it are rewritten rather than most, so the file names, the name of the unit and the
979/// directory it was compiled in all go through it.
980///
981/// A row whose span is [`Span::DUMMY`] is dropped rather than written at line zero. Those are the
982/// instructions a pass invented, a prologue and a spill among them, and a debugger asking what a
983/// program counter is in the middle of is better told the line before than told a line that is not
984/// in the file. The row that follows covers those bytes, which is the same answer gcc gives.
985///
986/// # Errors
987///
988/// Whatever the DWARF writer refused, which is a bug here rather than a program this compiler is
989/// behind on.
990fn describe(
991    text: &rucc_object::Text,
992    lines: &[Vec<rucc_asm::Row>],
993    origin: Origin<'_>,
994    opts: &Options,
995    target: &TargetInfo,
996) -> Result<rucc_object::Info, String> {
997    let rewrite = |path: &str| opts.prefix_map.debug.apply(path).into_owned();
998    // The file table, built as the rows are walked rather than up front, because what belongs in it
999    // is the files the code came from and not the files the preprocessor opened. A header that
1000    // contributed nothing but declarations is not one of them.
1001    let mut files: Vec<String> = Vec::new();
1002    let mut funcs = Vec::with_capacity(text.funcs.len());
1003    for (extent, rows) in text.funcs.iter().zip(lines) {
1004        let mut out: Vec<rucc_debug::Row> = Vec::with_capacity(rows.len());
1005        for row in rows {
1006            if row.span.is_dummy() {
1007                continue;
1008            }
1009            let Some(at) = origin.map.presumed(row.span.lo) else {
1010                continue;
1011            };
1012            let name = rewrite(at.name);
1013            let found = files.iter().position(|have| *have == name);
1014            let which = match found {
1015                Some(which) => which,
1016                None => {
1017                    files.push(name);
1018                    files.len() - 1
1019                }
1020            };
1021            let place = rucc_debug::Row {
1022                at: row.at as u64,
1023                file: which,
1024                line: at.line,
1025                column: at.column,
1026            };
1027            // Two rows at one address is one row, and the first of the two wins. The only place it
1028            // happens is the front of a function, where the row the assembler writes for the
1029            // declaration and the row for the first instruction land on the same byte, which is
1030            // what a function this compiler built no prologue for looks like: two instructions
1031            // cannot start at one address, so nowhere else has the question. The declaration is the
1032            // better answer there because it is the answer gcc gives, which it gives because gcc
1033            // always builds a frame at -O0 and so always has a byte of prologue for the brace to be
1034            // about. A breakpoint on a function wants the line of the function rather than the line
1035            // of whatever its first statement happened to be.
1036            match out.last() {
1037                Some(last) if last.at == place.at => {}
1038                _ => out.push(place),
1039            }
1040        }
1041        // And the front of the function, for a function whose declaration had no span to give. The
1042        // assembler writes a row there from `Func::declared` and that is the usual way this is
1043        // covered, but a function that came from something other than a C source has no such span,
1044        // and the front of one is the one part of it no row would otherwise cover. A program
1045        // counter in there would get no answer at all rather than a slightly early one, and no
1046        // answer is the worse of the two for anybody reading a backtrace.
1047        if let Some(first) = out.first_mut() {
1048            first.at = 0;
1049        }
1050        funcs.push(rucc_debug::Function {
1051            name: extent.name.clone(),
1052            len: extent.len as u64,
1053            rows: out,
1054        });
1055    }
1056    let unit = rucc_debug::Unit {
1057        name: rewrite(origin.name),
1058        // A single dot when the process could not say where it was, which is a directory name every
1059        // debugger understands and which leaves a relative file name meaning what it already meant.
1060        dir: rewrite(opts.working_dir.as_deref().unwrap_or(".")),
1061        producer: format!("rucc {}", crate::VERSION),
1062        files,
1063        funcs,
1064        pointer: u8::try_from(target.pointer_width / 8).unwrap_or(8),
1065    };
1066    rucc_debug::write(&unit).map_err(|why| why.to_string())
1067}
1068
1069/// What the command line decided about the file being written, in the words the assembler and the
1070/// object writer use.
1071///
1072/// Two spellings of the same facts, because the flags are the command line's and the answer the two
1073/// writers want is the object format's. The conversion is here rather than in either of them so
1074/// that the two output paths are handed the same thing and cannot come to disagree about what is
1075/// in a file.
1076///
1077/// The feature word is empty on a machine whose bits these are not. It is the x86 one, and a target
1078/// that wanted its control flow checked would want a property of its own with a key of its own, so
1079/// writing this one there would be recording something untrue rather than recording nothing.
1080fn output(opts: &Options, target: &TargetInfo) -> rucc_object::Output {
1081    let mut features = 0;
1082    if target.tuple.arch() == Arch::X86_64 {
1083        if opts.control.branch() {
1084            features |= rucc_object::Property::IBT;
1085        }
1086        if opts.control.ret() {
1087            features |= rucc_object::Property::SHSTK;
1088        }
1089    }
1090    rucc_object::Output {
1091        sections: rucc_object::Sections {
1092            functions: opts.function_sections,
1093            data: opts.data_sections,
1094        },
1095        property: rucc_object::Property { features },
1096    }
1097}
1098
1099/// What the object writer said, as the kind of news it is.
1100///
1101/// A format with no writer is a target this compiler is behind on, which is a program nobody can
1102/// compile today and not a mistake in the one being compiled. Anything else it refused is a bug
1103/// here, because every value it was handed came out of this compiler.
1104fn wrote(why: rucc_object::Error) -> Vec<Diagnostic> {
1105    match why {
1106        rucc_object::Error::Format { .. } => vec![unsupported(&why.to_string())],
1107        rucc_object::Error::Refused { .. } => vec![internal(&why.to_string())],
1108    }
1109}
1110
1111/// What the assembler said, as the kind of news it is.
1112///
1113/// Three of these are about a program and the rest are about this compiler. A thread-local
1114/// variable, an ifunc and a prologue the target's unwind table cannot describe are all valid C that
1115/// the back end does not build yet, and everything else the assembler refuses is something that
1116/// should never have reached it.
1117fn refused(why: rucc_asm::Error) -> Vec<Diagnostic> {
1118    match why {
1119        rucc_asm::Error::Thread { .. }
1120        | rucc_asm::Error::IFunc { .. }
1121        | rucc_asm::Error::Frame { .. } => {
1122            vec![unsupported(&why.to_string())]
1123        }
1124        _ => vec![internal(&why.to_string())],
1125    }
1126}
1127
1128/// A diagnostic about a program this compiler is not finished enough to compile.
1129///
1130/// Not an internal error, because nothing here is wrong: the program is valid C and the part of
1131/// the back end that would handle it has not been written. The note says so, so that a report
1132/// about one of these is filed against the milestone rather than as a miscompilation.
1133fn unsupported(message: &str) -> Diagnostic {
1134    unsupported_at(message, Span::DUMMY)
1135}
1136
1137/// The same, about somewhere in the file rather than about the file.
1138///
1139/// The note names the issue tracker rather than `spec/17-milestones.md`, which is a document
1140/// about the plan: a reader who follows it wants to know whether the construct in front of them
1141/// is already written down as work, and the milestone list does not answer that.
1142fn unsupported_at(message: &str, span: Span) -> Diagnostic {
1143    Diagnostic::error(message.to_owned(), span)
1144        .with_code("E0653")
1145        .note("this construct is not lowered yet, see https://github.com/tamnd/rucc/issues", span)
1146}
1147
1148/// A diagnostic about IR that was handed to us rather than built by us.
1149fn invalid(message: &str) -> Diagnostic {
1150    Diagnostic::error(message.to_owned(), Span::DUMMY).with_code("E0661")
1151}
1152
1153/// A diagnostic about this compiler rather than about the program it was given.
1154fn internal(message: &str) -> Diagnostic {
1155    Diagnostic::error(format!("internal error: {message}"), Span::DUMMY)
1156        .with_code("E0652")
1157        .note("this is a bug in rucc rather than in the program, please report it", Span::DUMMY)
1158}
1159
1160/// A result that is nothing but one message, for the failures that happen before there is
1161/// anything to compile.
1162fn failure(message: String) -> Compiled {
1163    Compiled {
1164        artifact: Artifact::Nothing,
1165        messages: vec![format!("rucc: error: {message}")],
1166        errors: 1,
1167        fired: Fired::new(),
1168        pressure: Pressure::new(),
1169        lowerings: Lowerings::new(),
1170        dumps: Vec::new(),
1171        remarks: String::new(),
1172        deps: Vec::new(),
1173        temps: Temps::default(),
1174    }
1175}
1176
1177#[cfg(test)]
1178mod tests {
1179    use rucc_session::{MemoryFileSystem, Std};
1180    use rucc_target::Triple;
1181
1182    use super::*;
1183
1184    fn options() -> Options {
1185        let mut opts = Options::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
1186        opts.emit = EmitKind::Tast;
1187        opts
1188    }
1189
1190    fn run(opts: &Options, source: &str) -> Compiled {
1191        let mut fs = MemoryFileSystem::new();
1192        fs.insert("/main.c", source.to_owned().into_bytes());
1193        compile(opts, "/main.c", &fs)
1194    }
1195
1196    /// Options with the compiler's own headers on the search path and nothing else, which is
1197    /// what a freestanding compilation is. There is no file system underneath these tests,
1198    /// so a header that reached for one would fail to resolve and say so.
1199    fn freestanding() -> Options {
1200        let mut opts = options();
1201        opts.hosted = false;
1202        opts.search.push_system(rucc_session::runtime::DIR);
1203        opts
1204    }
1205
1206    /// The typed tree of a freestanding `source`, insisting that it compiled cleanly.
1207    fn shipped(source: &str) -> String {
1208        let result = run(&freestanding(), source);
1209        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
1210        result.text().to_owned()
1211    }
1212
1213    /// The typed tree of `source`, insisting that it compiled cleanly.
1214    fn tast(source: &str) -> String {
1215        let result = run(&options(), source);
1216        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
1217        result.text().to_owned()
1218    }
1219
1220    #[test]
1221    fn the_shipped_stdarg_declares_a_list_and_the_four_operators() {
1222        let text = shipped(concat!(
1223            "#include <stdarg.h>\n",
1224            "int sum(int n, ...) {\n",
1225            "  va_list ap, copy;\n",
1226            "  va_start(ap, n);\n",
1227            "  va_copy(copy, ap);\n",
1228            "  int total = va_arg(ap, int) + va_arg(copy, int);\n",
1229            "  va_end(ap);\n",
1230            "  va_end(copy);\n",
1231            "  return total;\n",
1232            "}\n",
1233        ));
1234        assert!(text.contains("va-start"), "{text}");
1235        assert!(text.contains("va-copy"), "{text}");
1236        assert!(text.contains("va-arg"), "{text}");
1237        assert!(text.contains("va-end"), "{text}");
1238    }
1239
1240    /// glibc includes `<stdarg.h>` this way from every header that declares a `vprintf`, and
1241    /// what it wants is the type without the four macro names. Answering the whole header
1242    /// would put `va_start` in the way of a program that has its own.
1243    #[test]
1244    fn stdarg_hands_out_the_type_alone_when_that_is_all_that_was_asked_for() {
1245        let text = shipped(concat!(
1246            "#define __need___va_list\n",
1247            "#include <stdarg.h>\n",
1248            "int vprint(const char *f, __gnuc_va_list ap);\n",
1249            "#ifdef va_start\n",
1250            "#error va_start should not be defined\n",
1251            "#endif\n",
1252            "#ifdef _VA_LIST_DEFINED\n",
1253            "#error va_list should not have been made\n",
1254            "#endif\n",
1255        ));
1256        assert!(text.contains("vprint"), "{text}");
1257    }
1258
1259    /// The same protocol on `<stddef.h>`, which glibc uses far more heavily: `<stdio.h>` asks
1260    /// for `size_t` and `NULL` and would be wrong to receive `offsetof` as well.
1261    #[test]
1262    fn stddef_answers_one_piece_at_a_time_and_the_next_request_still_gets_through() {
1263        let text = shipped(concat!(
1264            "#define __need_size_t\n",
1265            "#include <stddef.h>\n",
1266            "#ifdef offsetof\n",
1267            "#error offsetof should not be defined yet\n",
1268            "#endif\n",
1269            "#define __need_ptrdiff_t\n",
1270            "#include <stddef.h>\n",
1271            "#include <stddef.h>\n",
1272            "size_t a;\n",
1273            "ptrdiff_t b;\n",
1274            "wchar_t c;\n",
1275            "max_align_t d;\n",
1276            "void *e = NULL;\n",
1277            "struct P { int x; long y; };\n",
1278            "size_t f = offsetof(struct P, y);\n",
1279        ));
1280        assert!(text.contains("decl #0 a : unsigned long"), "{text}");
1281        assert!(text.contains("decl #1 b : long"), "{text}");
1282    }
1283
1284    #[test]
1285    fn the_shipped_limits_and_float_are_the_targets_own_answers() {
1286        let text = shipped(concat!(
1287            "#include <limits.h>\n",
1288            "#include <float.h>\n",
1289            "int bits = CHAR_BIT;\n",
1290            "long big = LONG_MAX;\n",
1291            "int low = INT_MIN;\n",
1292            "int radix = FLT_RADIX;\n",
1293            "int digits = DBL_MANT_DIG;\n",
1294        ));
1295        assert!(text.contains("const 8 : int"), "{text}");
1296        assert!(text.contains("const 9223372036854775807 : long"), "{text}");
1297        assert!(text.contains("const 2 : int"), "{text}");
1298        assert!(text.contains("const 53 : int"), "{text}");
1299    }
1300
1301    /// Freestanding, so there is no library header to chain to and `<stdint.h>` writes the
1302    /// whole set out itself. The widths are the ones the target picked, which is the only
1303    /// reason this header is the compiler's.
1304    #[test]
1305    fn the_shipped_stdint_writes_the_whole_set_when_there_is_no_library_to_defer_to() {
1306        let text = shipped(concat!(
1307            "#include <stdint.h>\n",
1308            "int64_t a = INT64_C(1);\n",
1309            "uint_least16_t b;\n",
1310            "intptr_t c;\n",
1311            "uintmax_t d = UINTMAX_MAX;\n",
1312            "int wide = sizeof(int_fast64_t);\n",
1313        ));
1314        assert!(text.contains("decl #0 a : long"), "{text}");
1315        assert!(text.contains("decl #1 b : unsigned short"), "{text}");
1316        assert!(text.contains("decl #2 c : long"), "{text}");
1317    }
1318
1319    /// `<mmintrin.h>` is the base of the vector header chain and the first one whose contents
1320    /// are C rather than declarations, so what this checks is that the C in it compiles: a
1321    /// header that is nothing but definitions fails as a whole or not at all.
1322    ///
1323    /// What the intrinsics answer is not checked here and cannot be, because the answer is
1324    /// only interesting next to another compiler's. Every intrinsic in the header was built
1325    /// and run against GCC 16.2.0 on the same inputs, at `-O0`, `-O1`, `-O2` and `-Os`, and
1326    /// gave the same bytes in all four. Carrying that comparison rather than repeating it by
1327    /// hand needs a facet in `tamnd/rucc-corpus` that works out the expected bytes itself,
1328    /// which is a second implementation of MMX and is `tamnd/rucc#1150`.
1329    #[test]
1330    fn the_shipped_mmintrin_defines_the_mmx_type_and_the_operations_over_it() {
1331        let text = shipped(concat!(
1332            "#include <mmintrin.h>\n",
1333            "__m64 add(__m64 a, __m64 b) { return _mm_add_pi16(a, b); }\n",
1334            "__m64 pack(__m64 a, __m64 b) { return _m_packsswb(a, b); }\n",
1335            "__m64 shift(__m64 a) { return _mm_srai_pi32(a, 3); }\n",
1336            "int low(__m64 a) { return _mm_cvtsi64_si32(a); }\n",
1337            "void done(void) { _mm_empty(); }\n",
1338        ));
1339        assert!(text.contains("add"), "{text}");
1340        assert!(text.contains("pack"), "{text}");
1341        assert!(text.contains("shift"), "{text}");
1342    }
1343
1344    /// The allocator beside the vector headers, which is the one piece of the family that is
1345    /// not a vector operation. It reaches for `<stddef.h>` and for three names out of the
1346    /// library, and the point of the test is that the reach resolves with nothing on the
1347    /// search path but the compiler's own directory.
1348    #[test]
1349    fn the_shipped_mm_malloc_asks_for_aligned_memory_and_gives_it_back() {
1350        let text = shipped(concat!(
1351            "#include <mm_malloc.h>\n",
1352            "void *get(void) { return _mm_malloc(64, 16); }\n",
1353            "void put(void *p) { _mm_free(p); }\n",
1354        ));
1355        assert!(text.contains("get"), "{text}");
1356        assert!(text.contains("put"), "{text}");
1357    }
1358
1359    /// `<xmmintrin.h>` is the next rung of the chain and pulls the other two in behind it, so a
1360    /// program that includes this one alone has to get all three. What the intrinsics answer is
1361    /// checked the same way `<mmintrin.h>` next door is checked and for the same reason: a
1362    /// hundred and forty eight lines of answers over nans, infinities, both zeros and values
1363    /// that do not fit in the integer they convert to, identical to GCC 16.2.0 at `-O0`, `-O1`,
1364    /// `-O2` and `-Os`.
1365    ///
1366    /// `_mm_rcp_ps` is the one answer in that run that is not identical, and is not meant to be.
1367    /// The instruction approximates a reciprocal and this computes one exactly, so the bits
1368    /// differ while both sit inside the relative error Intel documents, which the same program
1369    /// checks directly rather than by comparing bits.
1370    #[test]
1371    fn the_shipped_xmmintrin_defines_the_sse_type_and_the_operations_over_it() {
1372        let text = shipped(concat!(
1373            "#include <xmmintrin.h>\n",
1374            "__m128 add(__m128 a, __m128 b) { return _mm_add_ps(a, b); }\n",
1375            "__m128 one(__m128 a, __m128 b) { return _mm_max_ss(a, b); }\n",
1376            "__m128 mask(__m128 a, __m128 b) { return _mm_cmpnle_ps(a, b); }\n",
1377            "__m128 pick(__m128 a, __m128 b) { return _mm_shuffle_ps(a, b, _MM_SHUFFLE(0,1,2,3)); }\n",
1378            "int bits(__m128 a) { return _mm_movemask_ps(a); }\n",
1379            "int near(__m128 a) { return _mm_cvtss_si32(a); }\n",
1380            "__m128 wide(__m64 a) { return _mm_cvtpi16_ps(a); }\n",
1381            "void *room(void) { return _mm_malloc(64, 16); }\n",
1382            "void hint(const float *p) { _mm_prefetch(p, _MM_HINT_T0); _mm_sfence(); }\n",
1383        ));
1384        assert!(text.contains("add"), "{text}");
1385        assert!(text.contains("mask"), "{text}");
1386        assert!(text.contains("pick"), "{text}");
1387        assert!(text.contains("wide"), "{text}");
1388    }
1389
1390    /// The six names of gcc's header this one leaves out, each of which is an instruction whose
1391    /// answer no plain C reproduces exactly. Leaving them out is what turns a program that wants
1392    /// one into a diagnostic naming the function it called, rather than into a wrong answer, and
1393    /// this is what notices if one is ever quietly defined to something close.
1394    ///
1395    /// `tamnd/rucc#1157` is the square root, which brings the first four back.
1396    #[test]
1397    fn the_shipped_xmmintrin_leaves_out_the_names_that_need_an_instruction() {
1398        let text = rucc_session::runtime::header("xmmintrin.h").expect("xmmintrin.h is shipped");
1399        for absent in [
1400            "_mm_sqrt_ps",
1401            "_mm_sqrt_ss",
1402            "_mm_rsqrt_ps",
1403            "_mm_rsqrt_ss",
1404            "_mm_getcsr",
1405            "_mm_setcsr",
1406        ] {
1407            let defined = text.contains(&format!("{absent}("));
1408            assert!(!defined, "{absent} is defined and the header says it is not");
1409            assert!(text.contains(absent), "{absent} is absent and unexplained");
1410        }
1411    }
1412
1413    #[test]
1414    fn the_shipped_emmintrin_defines_both_sse2_types_and_the_operations_over_them() {
1415        let text = shipped(concat!(
1416            "#include <emmintrin.h>\n",
1417            "__m128i add(__m128i a, __m128i b) { return _mm_add_epi64(a, b); }\n",
1418            "__m128i wide(__m128i a, __m128i b) { return _mm_mul_epu32(a, b); }\n",
1419            "__m128i pick(__m128i a) { return _mm_shuffle_epi32(a, _MM_SHUFFLE(0,1,2,3)); }\n",
1420            "__m128i up(__m128i a) { return _mm_slli_epi64(a, 13); }\n",
1421            "__m128i down(__m128i a) { return _mm_srli_si128(a, 3); }\n",
1422            "__m128i pack(__m128i a, __m128i b) { return _mm_packus_epi16(a, b); }\n",
1423            "int bits(__m128i a) { return _mm_movemask_epi8(a); }\n",
1424            "__m128d sum(__m128d a, __m128d b) { return _mm_add_sd(a, b); }\n",
1425            "__m128d mask(__m128d a, __m128d b) { return _mm_cmpunord_pd(a, b); }\n",
1426            "__m128i near(__m128d a) { return _mm_cvtpd_epi32(a); }\n",
1427            "__m128d over(__m128 a) { return _mm_cvtps_pd(a); }\n",
1428            "__m128i half(__m64 a) { return _mm_movpi64_epi64(a); }\n",
1429            "__m128i grab(void const *p) { return _mm_loadu_si128(p); }\n",
1430            "void wall(void) { _mm_lfence(); _mm_mfence(); }\n",
1431        ));
1432        assert!(text.contains("wide"), "{text}");
1433        assert!(text.contains("pack"), "{text}");
1434        assert!(text.contains("near"), "{text}");
1435        assert!(text.contains("half"), "{text}");
1436    }
1437
1438    /// The umbrella header reaches the three underneath it. This is brotli's use of it, from
1439    /// `c/enc/matching_tag_mask.h`, which is the whole of what `tamnd/rucc#1236` was about: four
1440    /// SSE2 names that were already shipped and no way to get at them by the name gcc uses.
1441    #[test]
1442    fn the_shipped_immintrin_reaches_the_names_the_headers_under_it_define() {
1443        let text = shipped(concat!(
1444            "#include <immintrin.h>\n",
1445            "unsigned long long matching(unsigned char tag, unsigned char const *bucket) {\n",
1446            "  __m128i const want = _mm_set1_epi8((char)tag);\n",
1447            "  __m128i const chunk = _mm_loadu_si128((__m128i const *)(void const *)bucket);\n",
1448            "  __m128i const same = _mm_cmpeq_epi8(chunk, want);\n",
1449            "  return (unsigned long long)_mm_movemask_epi8(same);\n",
1450            "}\n",
1451            "__m64 narrow(__m64 a, __m64 b) { return _mm_add_pi32(a, b); }\n",
1452            "__m128 single(__m128 a, __m128 b) { return _mm_add_ps(a, b); }\n",
1453        ));
1454        assert!(text.contains("matching"), "{text}");
1455        assert!(text.contains("narrow"), "the MMX header is not reached: {text}");
1456        assert!(text.contains("single"), "the SSE header is not reached: {text}");
1457    }
1458
1459    /// The wider umbrella reaches everything the narrower one does, and the fence family with it.
1460    /// This is what mingw-w64's `<winnt.h>` includes and what it then uses, so a Windows program
1461    /// that has never heard of an intrinsic gets here through `<windows.h>`.
1462    #[test]
1463    fn the_shipped_x86intrin_reaches_the_fences_windows_headers_ask_it_for() {
1464        let text = shipped(concat!(
1465            "#include <x86intrin.h>\n",
1466            "void barriers(void *p) {\n",
1467            "  _mm_lfence();\n",
1468            "  _mm_sfence();\n",
1469            "  _mm_mfence();\n",
1470            "  _mm_pause();\n",
1471            "  _mm_clflush(p);\n",
1472            "}\n",
1473            "__m128i wide(__m128i a, __m128i b) { return _mm_add_epi32(a, b); }\n",
1474        ));
1475        assert!(text.contains("barriers"), "{text}");
1476        assert!(text.contains("wide"), "the SSE2 header is not reached: {text}");
1477    }
1478
1479    /// Including it twice is the same as including it once, and so is including it beside the
1480    /// header it reaches. A program that includes both spellings is the usual case rather than an
1481    /// odd one, because one of its own headers includes the umbrella and another includes SSE2.
1482    #[test]
1483    fn the_umbrella_and_the_header_under_it_can_both_be_included() {
1484        let text = shipped(concat!(
1485            "#include <immintrin.h>\n",
1486            "#include <emmintrin.h>\n",
1487            "#include <immintrin.h>\n",
1488            "#include <x86intrin.h>\n",
1489            "__m128i twice(__m128i a, __m128i b) { return _mm_add_epi32(a, b); }\n",
1490        ));
1491        assert!(text.contains("twice"), "{text}");
1492    }
1493
1494    /// The float header omits four square roots and SSE2 omits the matching two, for the reason
1495    /// both headers write down. A later change that quietly defines one as an approximation
1496    /// would be a wrong answer nobody sees, so the absence is held in place here.
1497    #[test]
1498    fn the_shipped_emmintrin_leaves_out_the_two_square_roots() {
1499        let text = rucc_session::runtime::header("emmintrin.h").expect("emmintrin.h is shipped");
1500        for absent in ["_mm_sqrt_pd", "_mm_sqrt_sd"] {
1501            let defined = text.contains(&format!("{absent}("));
1502            assert!(!defined, "{absent} is defined and the header says it is not");
1503            assert!(text.contains(absent), "{absent} is absent and unexplained");
1504        }
1505    }
1506
1507    #[test]
1508    fn the_three_formality_headers_still_have_to_work() {
1509        let text = shipped(concat!(
1510            "#include <stdbool.h>\n",
1511            "#include <stdalign.h>\n",
1512            "#include <iso646.h>\n",
1513            "#include <stdnoreturn.h>\n",
1514            "int t = true and not false;\n",
1515            "_Alignas(16) char buf[16];\n",
1516            "int a = alignof(long);\n",
1517        ));
1518        assert!(text.contains("decl #0 t : int"), "{text}");
1519        assert!(text.contains("const 8 : unsigned long"), "{text}");
1520    }
1521
1522    /// Including everything twice has to change nothing, because that is what happens in any
1523    /// program large enough to matter and a guard that is wrong shows up nowhere else.
1524    ///
1525    /// Stated as the two trees being the same rather than as a fact about what is in either
1526    /// one. A header that carries definitions puts them in the tree and moves everything
1527    /// after them along, so an assertion about where the program's own declaration landed is
1528    /// an assertion about how much `<mmintrin.h>` defines, which is not what is being asked.
1529    #[test]
1530    fn every_shipped_header_can_be_included_twice() {
1531        let once: String = rucc_session::runtime::names()
1532            .iter()
1533            .map(|name| format!("#include <{name}>\n"))
1534            .collect();
1535        let twice = once.repeat(2);
1536        assert_eq!(shipped(&format!("{once}int x;\n")), shipped(&format!("{twice}int x;\n")));
1537    }
1538
1539    #[test]
1540    fn a_file_that_is_not_there_says_so_and_produces_nothing() {
1541        let fs = MemoryFileSystem::new();
1542        let result = compile(&options(), "/nope.c", &fs);
1543        assert!(result.failed());
1544        assert!(result.messages[0].contains("/nope.c"), "{:?}", result.messages);
1545        assert!(result.text().is_empty());
1546    }
1547
1548    #[test]
1549    fn an_object_comes_out_with_its_type_its_linkage_and_how_much_of_a_definition_it_is() {
1550        let text = tast("int x = 1;\n");
1551        let expected = "\
1552decl #0 x : int object external static defined
1553  init
1554    +0
1555      const 1 : int
1556";
1557        assert_eq!(text, expected);
1558    }
1559
1560    #[test]
1561    fn the_macros_are_expanded_before_anything_is_parsed() {
1562        // The whole pipeline in one line. The bound came out of a macro, so it was expanded,
1563        // converted from a preprocessing number to a constant of a type, parsed as an
1564        // expression, and folded to the number the array type carries.
1565        let text = tast("#define N 2\nint a[N];\n");
1566        assert!(text.starts_with("decl #0 a : int[2] object external static tentative"), "{text}");
1567    }
1568
1569    /// A pragma survives the preprocessor on purpose, since what one means is not its
1570    /// business, and nothing after it has a place for a `#` in the grammar. `pack` is the one
1571    /// the parser reads and every other line is walked past. Both spellings are here because
1572    /// they arrive by different routes and only one of them was ever on a line of its own in
1573    /// the source.
1574    #[test]
1575    fn a_pragma_is_not_a_declaration_and_the_parse_walks_past_the_ones_it_does_not_read() {
1576        let text = tast(concat!(
1577            "#pragma pack(4)\n",
1578            "struct s { int a; };\n",
1579            "#pragma pack()\n",
1580            "int b;\n",
1581            "_Pragma(\"GCC visibility push(default)\") int c;\n",
1582        ));
1583        assert!(text.contains("decl #0 b : int"), "{text}");
1584        assert!(text.contains("decl #1 c : int"), "{text}");
1585    }
1586
1587    /// Every number in these two tests was read off gcc 16 on x86-64 under `-std=gnu23`
1588    /// rather than reasoned about, which is why they are written as assertions the program
1589    /// makes about itself: a compilation with no messages is every one of them holding.
1590    ///
1591    /// This half is the attributes. `packed` takes the padding out, on the record or on one
1592    /// member, `aligned` raises and never lowers, and the two written together are the
1593    /// combination that packs and then aligns the whole thing.
1594    #[test]
1595    fn the_layout_attributes_move_the_members_and_the_record_the_way_gcc_lays_them_out() {
1596        tast(concat!(
1597            "struct A { char c; int i; } __attribute__((packed));\n",
1598            "_Static_assert(sizeof(struct A) == 5 && _Alignof(struct A) == 1, \"A\");\n",
1599            "_Static_assert(__builtin_offsetof(struct A, i) == 1, \"A.i\");\n",
1600            // `aligned` with nothing in the parentheses is the largest alignment the target
1601            // has, which gcc calls BIGGEST_ALIGNMENT and which is sixteen everywhere here.
1602            "struct B { char c; int i; } __attribute__((aligned));\n",
1603            "_Static_assert(sizeof(struct B) == 16 && _Alignof(struct B) == 16, \"B\");\n",
1604            "struct C { char c; int i __attribute__((packed)); };\n",
1605            "_Static_assert(sizeof(struct C) == 5 && _Alignof(struct C) == 1, \"C\");\n",
1606            "_Static_assert(__builtin_offsetof(struct C, i) == 1, \"C.i\");\n",
1607            "struct D { char c; int i; } __attribute__((packed, aligned(4)));\n",
1608            "_Static_assert(sizeof(struct D) == 8 && _Alignof(struct D) == 4, \"D\");\n",
1609            "_Static_assert(__builtin_offsetof(struct D, i) == 1, \"D.i\");\n",
1610            "struct E { char c; _Alignas(8) int i; };\n",
1611            "_Static_assert(sizeof(struct E) == 16 && _Alignof(struct E) == 8, \"E\");\n",
1612            "_Static_assert(__builtin_offsetof(struct E, i) == 8, \"E.i\");\n",
1613            "struct F { char c; int i __attribute__((aligned(8))); };\n",
1614            "_Static_assert(sizeof(struct F) == 16 && _Alignof(struct F) == 8, \"F\");\n",
1615            // Two the record already had, so the attribute asks for nothing new, and two
1616            // where four was already there, so the attribute is ignored rather than obeyed.
1617            "struct G { char c; short s; } __attribute__((aligned(2)));\n",
1618            "_Static_assert(sizeof(struct G) == 4 && _Alignof(struct G) == 2, \"G\");\n",
1619            "struct H { char c; int i; } __attribute__((aligned(2)));\n",
1620            "_Static_assert(sizeof(struct H) == 8 && _Alignof(struct H) == 4, \"H\");\n",
1621            // `packed` on a member takes the padding out in front of that member alone, so on
1622            // the first one it does nothing and on the second one it does all of it.
1623            "struct I { [[gnu::packed]] char c; int i; };\n",
1624            "_Static_assert(sizeof(struct I) == 8 && _Alignof(struct I) == 4, \"I\");\n",
1625            "struct J { char c; [[gnu::packed]] int i; };\n",
1626            "_Static_assert(sizeof(struct J) == 5 && _Alignof(struct J) == 1, \"J\");\n",
1627            "struct M { char c; int i : 5; int j : 20; } __attribute__((packed));\n",
1628            "_Static_assert(sizeof(struct M) == 5 && _Alignof(struct M) == 1, \"M\");\n",
1629            "struct N { char c; long long l; } __attribute__((aligned(32)));\n",
1630            "_Static_assert(sizeof(struct N) == 32 && _Alignof(struct N) == 32, \"N\");\n",
1631            "union L { char c; int i; } __attribute__((packed));\n",
1632            "_Static_assert(sizeof(union L) == 4 && _Alignof(union L) == 1, \"L\");\n",
1633            // The armoured spellings, which are the ones a system header writes, since a
1634            // program is entitled to a macro called `packed` and is not entitled to one called
1635            // `__packed__`. The two names are one attribute and the layout is the same one.
1636            "struct O { char c; int i; } __attribute__((__packed__));\n",
1637            "_Static_assert(sizeof(struct O) == 5 && _Alignof(struct O) == 1, \"O\");\n",
1638            "struct P { char c; int i; } __attribute__((__aligned__(8)));\n",
1639            "_Static_assert(sizeof(struct P) == 8 && _Alignof(struct P) == 8, \"P\");\n",
1640        ));
1641    }
1642
1643    /// The attribute that changes what a call means rather than what a record lays out.
1644    ///
1645    /// Both halves are here. A call hands a value to a parameter of the union type and the value
1646    /// goes into the member that takes it, which is a compound literal of the union and is the
1647    /// same object the GNU cast to a union builds. And a declaration written with a member's type
1648    /// declares the same function as one written with the union, which is what lets a pointer to
1649    /// either be assigned from the other, and is what gnulib's signature checks do.
1650    ///
1651    /// The `void *` member is last on purpose: the search takes a member whose type the value
1652    /// already has wherever it sits, and falls back to a pointer member that would take the value
1653    /// silently only when there is no such member, so `char *` reaches the catch-all past two
1654    /// members that are not it.
1655    #[test]
1656    fn a_transparent_union_takes_the_member_a_value_fits_and_is_declared_either_way() {
1657        let text = tast(concat!(
1658            "struct one { int x; };\n",
1659            "struct two { long y; };\n",
1660            "typedef union { struct one *a; struct two *b; void *any; }\n",
1661            "  __attribute__((__transparent_union__)) arg;\n",
1662            "int takes(arg v);\n",
1663            "int f(struct one *p, struct two *q, char *c) {\n",
1664            "  return takes(p) + takes(q) + takes(c) + takes(0);\n",
1665            "}\n",
1666            // The other half, which is about declarations and not about values.
1667            "int takes(struct one *p);\n",
1668            "int (*as_a_member)(struct one *) = takes;\n",
1669            "int (*as_the_union)(arg) = takes;\n",
1670        ));
1671        assert!(text.contains("compound-literal"), "{text}");
1672    }
1673
1674    /// The other place glibc writes it, which is the one that matters.
1675    ///
1676    /// `sys/socket.h` puts the attribute on the declarator of the typedef rather than after the
1677    /// closing brace, so a compiler that reads only the second position reads nothing at all of
1678    /// the eleven pointer union that `bind` and `connect` and five others take.
1679    #[test]
1680    fn the_attribute_on_the_declarator_of_a_typedef_is_the_one_glibc_writes() {
1681        let text = tast(concat!(
1682            "struct sockaddr { int family; };\n",
1683            "struct sockaddr_in { int family; int addr; };\n",
1684            "typedef union { struct sockaddr *plain; struct sockaddr_in *inet; }\n",
1685            "  addr_arg __attribute__((__transparent_union__));\n",
1686            "int bind_to(int fd, addr_arg where);\n",
1687            "int f(struct sockaddr_in *where) { return bind_to(0, where); }\n",
1688        ));
1689        assert!(text.contains("compound-literal"), "{text}");
1690    }
1691
1692    /// What the attribute promises has to be a promise this can keep, and is checked rather than
1693    /// believed.
1694    ///
1695    /// A union wider than its first member is not passed the way that member is, and a structure
1696    /// has no members that are alternatives to each other at all. gcc drops the attribute in both
1697    /// cases with a warning and compiles the program, because the type is still a perfectly good
1698    /// type and only the extra rule is gone.
1699    #[test]
1700    fn a_transparent_union_that_cannot_keep_the_promise_is_dropped_with_a_word_about_it() {
1701        let result = run(
1702            &options(),
1703            concat!(
1704                "union wider { int small; double large; } __attribute__((transparent_union));\n",
1705                "struct plain { int x; } __attribute__((transparent_union));\n",
1706            ),
1707        );
1708        assert_eq!(result.messages.len(), 2, "{:?}", result.messages);
1709        assert!(!result.failed(), "{:?}", result.messages);
1710        for message in &result.messages {
1711            assert!(message.contains("'transparent_union' attribute ignored"), "{message}");
1712        }
1713        assert!(result.messages[0].contains("first member"), "{:?}", result.messages);
1714        assert!(result.messages[1].contains("only a union"), "{:?}", result.messages);
1715    }
1716
1717    /// What an access to a packed member is allowed to assume about where it starts.
1718    ///
1719    /// C 6.2.8 gives an object of type `int` four byte alignment and `packed` takes it away: the
1720    /// member goes wherever the members in front of it ended, and an `int` one byte into a record
1721    /// is aligned to one. The number on the access has to say so, because it is what the back end
1722    /// picks instructions from and what judgement J1 of `spec/safe-memory/04-safety-model.md`
1723    /// tests at run time. Four on an address that is a multiple of one is the compiler refusing a
1724    /// program that is doing nothing wrong.
1725    #[test]
1726    fn an_access_to_a_packed_member_says_the_alignment_the_layout_left_it() {
1727        let packed = body(concat!(
1728            "struct P { char c; int v; } __attribute__((packed));\n",
1729            "int f(struct P *p) { return p->v; }\n",
1730        ));
1731        assert!(packed.contains("load.i32 %2, align 1,"), "{packed}");
1732        // The same record without the attribute, which is where the type's own answer is right.
1733        let plain = body(concat!(
1734            "struct P { char c; int v; };\n",
1735            "int f(struct P *p) { return p->v; }\n",
1736        ));
1737        assert!(plain.contains("load.i32 %2, align 4,"), "{plain}");
1738    }
1739
1740    /// The same, for the two ways of being further in than the member itself.
1741    ///
1742    /// An array member is stepped through rather than offset to, and a record member is offset to
1743    /// twice, and both have to carry the outer record's alignment with them. A step of a whole
1744    /// number of elements leaves what the element width and the address had in common, which for
1745    /// a one byte aligned base is one byte however wide the elements are.
1746    #[test]
1747    fn what_is_inside_a_packed_member_is_no_more_aligned_than_the_member_is() {
1748        let stepped = body(concat!(
1749            "struct P { char c; int v[4]; } __attribute__((packed));\n",
1750            "int f(struct P *p, int i) { return p->v[i]; }\n",
1751        ));
1752        assert!(stepped.contains(", align 1,"), "{stepped}");
1753        assert!(!stepped.contains(", align 4,"), "{stepped}");
1754        let nested = body(concat!(
1755            "struct Inner { int v; };\n",
1756            "struct P { char c; struct Inner in; } __attribute__((packed));\n",
1757            "int f(struct P *p) { return p->in.v; }\n",
1758        ));
1759        assert!(nested.contains(", align 1,"), "{nested}");
1760        assert!(!nested.contains(", align 4,"), "{nested}");
1761    }
1762
1763    /// The other way an access gets an alignment its type would not have given it, which is a
1764    /// typedef that lowered one.
1765    ///
1766    /// `aligned` raises on a declaration and replaces on a typedef, so `typedef aligned(1) U32
1767    /// unalign32` really is a four byte integer that may sit anywhere. Reading a word out of a
1768    /// buffer nothing aligned is what every compression library does and this is how they write
1769    /// it: zstd's `lib/common/mem.h` is four typedefs of exactly this shape and `MEM_read32` is
1770    /// `*(const unalign32 *)ptr`.
1771    ///
1772    /// What made this worth a test is where it went wrong. `__alignof__` was right the whole time,
1773    /// because that asks about the type and the type knew. The access was wrong, because the type
1774    /// of `*p` was worked out by resolving every typedef in `p`'s type rather than only the one on
1775    /// the pointer, so the thing being read came back as the `unsigned int` the typedef stands for
1776    /// and the alignment came off that. The number on the access is what judgement J1 tests, so
1777    /// the monitor refused fifty six of zstd's reads, all of them correct.
1778    #[test]
1779    fn an_access_through_a_typedef_that_lowered_its_alignment_says_the_one_the_typedef_asked_for() {
1780        let through = body(concat!(
1781            "typedef __attribute__((aligned(1))) unsigned int unalign32;\n",
1782            "unsigned int f(const void *p) { return *(const unalign32 *)p; }\n",
1783        ));
1784        assert!(through.contains("load.i32 %0, align 1,"), "{through}");
1785        // A subscript is `*(p + i)` and a member through an arrow is a dereference and then an
1786        // offset, so both read the pointee the same way and both have to come out the same.
1787        let stepped = body(concat!(
1788            "typedef __attribute__((aligned(1))) unsigned int unalign32;\n",
1789            "unsigned int f(unalign32 *p, int i) { return p[i]; }\n",
1790        ));
1791        assert!(stepped.contains(", align 1,"), "{stepped}");
1792        assert!(!stepped.contains(", align 4,"), "{stepped}");
1793        // And the same typedef without the attribute, which is where the type's own answer is the
1794        // right one and nothing above should have changed it.
1795        let plain = body(concat!(
1796            "typedef unsigned int word;\n",
1797            "unsigned int f(const void *p) { return *(const word *)p; }\n",
1798        ));
1799        assert!(plain.contains("load.i32 %0, align 4,"), "{plain}");
1800    }
1801
1802    /// The same thing where the object does not fit in a register, which is what `_mm_loadu_si128`
1803    /// is and is the reason the intrinsic header exists at all.
1804    ///
1805    /// `__m128i_u` is `__m128i` with `aligned(1)` on it and `_mm_loadu_si128` is one line,
1806    /// `return *(const __m128i_u *)__p;`. Two things had to be right for that to come out as the
1807    /// unaligned read it is. The dereference has to keep the typedef, which is what the test above
1808    /// covers, and then the return has to read the object as aligned as the object is rather than
1809    /// as aligned as the type it is being returned as: a vector comes back in registers on this
1810    /// ABI, so the sixteen bytes are read as two pieces of eight and the ABI's own alignment is
1811    /// what lays the two pieces out rather than what either read may claim.
1812    #[test]
1813    fn a_vector_read_through_a_typedef_that_lowered_its_alignment_comes_back_a_piece_at_a_time() {
1814        let prefix = concat!(
1815            "typedef long long v2di __attribute__((__vector_size__(16)));\n",
1816            "typedef long long v2di_u __attribute__((__vector_size__(16), __aligned__(1)));\n",
1817        );
1818        let loaded =
1819            body(&format!("{prefix}v2di f(const void *p) {{ return *(const v2di_u *)p; }}"));
1820        assert_eq!(loaded.matches("align 1\n").count(), 2, "{loaded}");
1821        assert!(!loaded.contains("align 16"), "{loaded}");
1822        // The store side, which travels as a copy into whatever the pointer names and so carries
1823        // one number for both ends of it.
1824        let stored = body(&format!("{prefix}void f(void *p, v2di b) {{ *(v2di_u *)p = b; }}"));
1825        assert!(stored.contains("memcpy %0, %3, size 16, align 1"), "{stored}");
1826        // And the aligned spelling of the same two, which is where sixteen is the right answer.
1827        let aligned =
1828            body(&format!("{prefix}v2di f(const void *p) {{ return *(const v2di *)p; }}"));
1829        assert!(aligned.contains("align 16"), "{aligned}");
1830    }
1831
1832    /// The same attribute on a declaration rather than on a type, which asks that this object or
1833    /// this function be at a multiple of that, and which is where a program that has to hand a
1834    /// buffer to hardware or keep two counters off one cache line writes it.
1835    ///
1836    /// A raise and never a lower, which is the one place it does not agree with `_Alignas`: below
1837    /// what the type already has, `_Alignas` is a constraint violation and this is ignored without
1838    /// a word. `__alignof__` of the object answers what the object got and not what its type has,
1839    /// because that is the question a program asking it is asking.
1840    #[test]
1841    fn the_aligned_attribute_on_a_declaration_raises_what_that_one_object_is_aligned_to() {
1842        tast(concat!(
1843            "int v __attribute__((aligned(64)));\n",
1844            "_Static_assert(__alignof__(v) == 64, \"v\");\n",
1845            // Written on the specifiers rather than after the declarator, which asks the same
1846            // thing and is the spelling a header is more likely to use.
1847            "__attribute__((aligned(32))) int w;\n",
1848            "_Static_assert(__alignof__(w) == 32, \"w\");\n",
1849            "[[gnu::aligned(16)]] int x;\n",
1850            "_Static_assert(__alignof__(x) == 16, \"x\");\n",
1851            // Two below the four an `int` already has, so nothing is asked for and nothing is
1852            // said, and the type still answers for the object.
1853            "int y __attribute__((aligned(2)));\n",
1854            "_Static_assert(__alignof__(y) == 4, \"y\");\n",
1855            // A local, which is the same question one scope down.
1856            "void f(void) { int a __attribute__((aligned(128)));\n",
1857            "_Static_assert(__alignof__(a) == 128, \"a\"); (void)a; }\n",
1858            // The type is untouched by any of it: `aligned` on a declaration says where that
1859            // declaration goes and says nothing about every other `int` in the program.
1860            "_Static_assert(__alignof__(int) == 4, \"int\");\n",
1861            // A function, which has no alignment of its own for this to be measured against and
1862            // takes whatever was asked for.
1863            "void g(void) __attribute__((aligned(256)));\n",
1864            "void g(void) {}\n",
1865            "_Static_assert(__alignof__(g) == 256, \"g\");\n",
1866        ));
1867    }
1868
1869    /// And what the object file says, which is the half that makes the answer above true. A
1870    /// function is at a fixed offset inside the text section, so it is at a multiple of two
1871    /// hundred and fifty six only if the section is at one too.
1872    #[test]
1873    fn what_a_declaration_asked_to_be_aligned_to_is_what_the_assembler_is_told() {
1874        let text = asm(concat!(
1875            "int v __attribute__((aligned(64)));\n",
1876            "void g(void) __attribute__((aligned(256)));\n",
1877            "void g(void) {}\n",
1878            "void plain(void) {}\n",
1879        ));
1880        assert!(text.contains("\t.p2align\t6\n\t.type\tv, @object\n"), "{text}");
1881        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "{text}");
1882        assert!(text.contains("\t.p2align\t4, 0x90\n\t.globl\tplain\n"), "{text}");
1883    }
1884
1885    /// The same question asked by the command line instead of by a declaration, which is
1886    /// `-falign-functions` and is what femtolisp's Makefile writes on every compile. The flag is a
1887    /// floor: a function that named a larger boundary itself keeps it, and one that named a
1888    /// smaller one is moved up, because the attribute is a requirement about one function and the
1889    /// flag is a preference about all of them.
1890    #[test]
1891    fn the_alignment_the_command_line_asked_of_every_function_is_a_floor_under_all_of_them() {
1892        let source = concat!(
1893            "void g(void) __attribute__((aligned(256)));\n",
1894            "void g(void) {}\n",
1895            "void small(void) __attribute__((aligned(4)));\n",
1896            "void small(void) {}\n",
1897            "void plain(void) {}\n",
1898        );
1899        let listing = |align: Option<u32>| {
1900            let mut opts = options();
1901            opts.emit = EmitKind::Asm;
1902            opts.align_functions = align;
1903            let result = run(&opts, source);
1904            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
1905            result.text().to_owned()
1906        };
1907
1908        let text = listing(Some(32));
1909        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "the larger one wins: {text}");
1910        assert!(text.contains("\t.p2align\t5, 0x90\n\t.globl\tsmall\n"), "{text}");
1911        assert!(text.contains("\t.p2align\t5, 0x90\n\t.globl\tplain\n"), "{text}");
1912
1913        // And the negative form, which asks for the smallest boundary the target has and is the
1914        // one spelling that takes a function below the sixteen bytes it would get anyway.
1915        let text = listing(Some(8));
1916        assert!(text.contains("\t.p2align\t3, 0x90\n\t.globl\tplain\n"), "{text}");
1917        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "{text}");
1918    }
1919
1920    /// And the one position where the attribute means something else. On a declaration it raises
1921    /// what that one object is aligned to, and on a typedef it says what the type is aligned to,
1922    /// which gcc lets it lower as well: `typedef int L __attribute__((aligned(2)))` really is an
1923    /// `int` at a multiple of two and a record with one in it really is smaller for it.
1924    ///
1925    /// The size is left alone, which is gcc's answer rather than an omission here. An aligned
1926    /// typedef whose alignment is larger than what it stands for keeps the size it stands for,
1927    /// and gcc refuses an array of one rather than padding the elements out to fit.
1928    #[test]
1929    fn an_aligned_typedef_says_what_an_object_of_it_is_aligned_to_and_may_lower_it() {
1930        tast(concat!(
1931            "typedef int L __attribute__((aligned(2)));\n",
1932            "_Static_assert(__alignof__(L) == 2, \"L\");\n",
1933            "_Static_assert(_Alignof(L) == 2, \"L alignof\");\n",
1934            // Below what an `int` has, which is the half a declaration cannot ask for.
1935            "_Static_assert(sizeof(L) == 4, \"L size\");\n",
1936            "struct T { char c; L x; };\n",
1937            "_Static_assert(sizeof(struct T) == 6, \"T\");\n",
1938            "_Static_assert(__builtin_offsetof(struct T, x) == 2, \"T.x\");\n",
1939            // And upwards, which is the ordinary direction and the one a header writes.
1940            "typedef int H __attribute__((aligned(16)));\n",
1941            "_Static_assert(__alignof__(H) == 16, \"H\");\n",
1942            "_Static_assert(sizeof(H) == 4, \"H size\");\n",
1943            "struct U { char c; H x; };\n",
1944            "_Static_assert(sizeof(struct U) == 32, \"U\");\n",
1945            "_Static_assert(__builtin_offsetof(struct U, x) == 16, \"U.x\");\n",
1946            // A typedef of a typedef, where the nearer one is the one the declaration was
1947            // written with and is the one that answers.
1948            "typedef L M __attribute__((aligned(8)));\n",
1949            "_Static_assert(__alignof__(M) == 8, \"M\");\n",
1950            // And one that asked for nothing, which still has whatever the one behind it asked
1951            // for because it is the same type spelled again.
1952            "typedef L N;\n",
1953            "_Static_assert(__alignof__(N) == 2, \"N\");\n",
1954            // The type it stands for is untouched by any of it.
1955            "_Static_assert(__alignof__(int) == 4, \"int\");\n",
1956        ));
1957        let text = asm(concat!(
1958            "typedef int L __attribute__((aligned(2)));\n",
1959            "typedef int H __attribute__((aligned(16)));\n",
1960            "L low;\n",
1961            "H high;\n",
1962        ));
1963        assert!(text.contains("\t.p2align\t1\n\t.type\tlow, @object\n"), "{text}");
1964        assert!(text.contains("\t.p2align\t4\n\t.type\thigh, @object\n"), "{text}");
1965    }
1966
1967    /// The attribute that builds a type rather than changing a layout. `vector_size(n)` says the
1968    /// declared type is `n` bytes of what was written, taken as lanes, and every operator over
1969    /// one is that operator over each lane.
1970    ///
1971    /// The size is in bytes and not in lanes, which is the part a reader gets backwards: sixteen
1972    /// of `int` is four lanes and sixteen of `char` is sixteen. A vector is aligned to its own
1973    /// size, which is what a machine that has the registers wants and what gcc gives one here.
1974    #[test]
1975    fn the_vector_size_attribute_builds_a_type_of_lanes_and_measures_it_in_bytes() {
1976        tast(concat!(
1977            "typedef int __attribute__((vector_size(16))) v4si;\n",
1978            "_Static_assert(sizeof(v4si) == 16 && _Alignof(v4si) == 16, \"v4si\");\n",
1979            "typedef char __attribute__((vector_size(16))) v16qi;\n",
1980            "_Static_assert(sizeof(v16qi) == 16, \"v16qi\");\n",
1981            // One lane, which is a power of two and is a vector rather than the type it was
1982            // written on: the operators it takes are the vector's and not the scalar's.
1983            "typedef int __attribute__((vector_size(4))) v1si;\n",
1984            "_Static_assert(sizeof(v1si) == 4, \"v1si\");\n",
1985            // The armoured spelling and the bracket one, which are the same attribute.
1986            "typedef float __attribute__((__vector_size__(8))) v2sf;\n",
1987            "_Static_assert(sizeof(v2sf) == 8, \"v2sf\");\n",
1988            "typedef short [[gnu::vector_size(8)]] v4hi;\n",
1989            "_Static_assert(sizeof(v4hi) == 8, \"v4hi\");\n",
1990            // A lane is what a subscript answers with, and a vector is not a pointer: there is
1991            // nothing to decay and the lane type is the one the arithmetic happens in.
1992            "v4si g;\n",
1993            "_Static_assert(sizeof(g[0]) == 4, \"lane\");\n",
1994            "_Static_assert(sizeof(g + g) == 16, \"whole\");\n",
1995            // A scalar beside a vector stands for itself in every lane, so the answer is still
1996            // the vector and not the wider of the two types.
1997            "_Static_assert(sizeof(g + 1) == 16, \"broadcast\");\n",
1998            // An array of them, which is the ordinary way a program holds several.
1999            "_Static_assert(sizeof(v4si[3]) == 48, \"array\");\n",
2000        ));
2001    }
2002
2003    /// A whole vector written into an array of them, and a vector named by a type name rather
2004    /// than by a typedef.
2005    ///
2006    /// Both are the same question asked twice. A vector is filled like an array of its lanes when
2007    /// a list is written into it, so a braced element that is itself a vector has to be taken
2008    /// whole rather than started as the first lane, and the type of what was written is the only
2009    /// thing that says which was meant. And a type name is where a compound literal and a cast
2010    /// spell the type out, which a macro taking a lane type and a lane count does, so the
2011    /// attribute has to be read there and not only on a declaration.
2012    #[test]
2013    fn a_vector_is_written_whole_into_an_array_of_them_and_named_by_a_type_name() {
2014        tast(concat!(
2015            "typedef int __attribute__((vector_size(8))) v2si;\n",
2016            "v2si table[] = { (v2si){ 1, 2 }, (v2si){ 3, 4 } };\n",
2017            "_Static_assert(sizeof(table) == 16, \"two of them and not eight lanes\");\n",
2018            // The size written out rather than named, which is the spelling a macro expands to.
2019            "v2si written = (int __attribute__((vector_size(8)))){ 5, 6 };\n",
2020            "_Static_assert(sizeof((int __attribute__((vector_size(16)))){ 0 }) == 16, \"named\");\n",
2021            // A lane is still a lane, so a list of them fills the vector the way it always did
2022            // and the rule above did not turn brace elision off.
2023            "v2si lanes[2] = { 1, 2, 3, 4 };\n",
2024            "_Static_assert(sizeof(lanes) == 16, \"still elided\");\n",
2025        ));
2026    }
2027
2028    /// A lane written rather than read, and a shift whose two vectors are not the same type.
2029    ///
2030    /// Both are places where a vector is not the aggregate it looks like. A subscript of one is
2031    /// an lvalue because the vector it came from is an object, so a lane can be assigned to and
2032    /// has an address, and a qualifier written on the vector reaches every lane the way it does
2033    /// on an array. And a shift is the one lanewise operator whose sides are not brought to a
2034    /// single type, since the right side counts rather than computes.
2035    #[test]
2036    fn a_lane_is_assignable_and_a_shift_takes_a_count_of_its_own_lane() {
2037        let result = run(
2038            &options(),
2039            concat!(
2040                "typedef int __attribute__((vector_size(16))) v4si;\n",
2041                "typedef unsigned __attribute__((vector_size(16))) v4ui;\n",
2042                "void write(v4si *out, v4ui a, v4si b, int n) {\n",
2043                "  v4si v = { 1, 2, 3, 4 };\n",
2044                "  v[0] = n;\n",
2045                "  v[1] += n;\n",
2046                "  v[2]++;\n",
2047                "  *&v[3] = n;\n",
2048                // The count is signed and the value is not, which no other operator allows.
2049                "  v4ui shifted = a >> b;\n",
2050                "  shifted <<= b;\n",
2051                // A scalar stands in every lane on either side of a shift, which is the half
2052                // that looks wrong: the shape of the answer comes off the count here.
2053                "  *out = v + (v4si)shifted + (1 << b);\n",
2054                "}\n",
2055                // A qualifier on the vector is a qualifier on the lane, so there is nothing here
2056                // to write to.
2057                "void refused(const v4si c) {\n",
2058                "  c[0] = 1;\n",
2059                "}\n",
2060            ),
2061        );
2062        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
2063        assert!(result.messages[0].contains("assignment of read-only"), "{:?}", result.messages);
2064    }
2065
2066    /// The third layout attribute, and the one that is refused rather than read. Reversing the
2067    /// byte order of every scalar in a record is not something a compiler can do half of, and a
2068    /// compilation that ignored it would lay the record out in the host's order and hand back
2069    /// every field with its bytes the wrong way round. Both spellings are here because a header
2070    /// writes the armoured one, and the member is here because the refusal has to arrive before
2071    /// the layout is used rather than after.
2072    #[test]
2073    fn a_record_that_asks_for_the_other_byte_order_is_refused_rather_than_laid_out_in_this_one() {
2074        let opts = options();
2075        let big = "struct s { int i; } __attribute__((scalar_storage_order(\"big-endian\")));\n";
2076        assert_eq!(
2077            run(&opts, big).messages,
2078            ["/main.c:1:36: error: 'scalar_storage_order' is not implemented yet [E0688]\n\
2079              /main.c:1:36: note: every scalar in this record would be read in the wrong byte \
2080              order"]
2081        );
2082
2083        let armoured =
2084            "struct s { int i; } __attribute__((__scalar_storage_order__(\"little-endian\")));\n";
2085        let messages = run(&opts, armoured).messages;
2086        assert!(messages[0].contains("[E0688]"), "{messages:?}");
2087
2088        // The attribute in front of the body reaches the same list as the one behind it, and
2089        // the C23 spelling in gcc's namespace is the same attribute written a third way.
2090        let front = "struct __attribute__((scalar_storage_order(\"big-endian\"))) s { int i; };\n";
2091        assert!(run(&opts, front).messages[0].contains("[E0688]"), "{front}");
2092        let standard = "struct s { int i; } [[gnu::scalar_storage_order(\"big-endian\")]];\n";
2093        assert!(run(&opts, standard).messages[0].contains("[E0688]"), "{standard}");
2094    }
2095
2096    /// Where a bit-field goes, which packing decides and which is the part of all this that
2097    /// is not what the names suggest. A bit-field goes at the next free bit unless that would
2098    /// make it span more storage than its own type occupies, and then it moves to the next
2099    /// boundary of its alignment. Any packing at all takes that rule out, and `#pragma pack`
2100    /// counts even where it lowers nothing, which is the fourth and seventh cases here.
2101    ///
2102    /// Nothing in the language can be asked where a bit-field is, since `offsetof` refuses one
2103    /// and every size below comes out the same either way, so what is asked is the byte a read
2104    /// of the field loads from.
2105    #[test]
2106    fn packing_is_what_decides_whether_a_bit_field_may_straddle_its_own_storage() {
2107        // A `char` field after twelve bits, which will not straddle unpacked and does packed.
2108        assert_eq!(bit_field_byte("struct s { int x : 12; char y : 6; };"), 2);
2109        assert_eq!(
2110            bit_field_byte("struct s { int x : 12; char y : 6; } __attribute__((packed));"),
2111            1
2112        );
2113        assert_eq!(
2114            bit_field_byte("struct s { int x : 12; __attribute__((packed)) char y : 6; };"),
2115            1
2116        );
2117        assert_eq!(bit_field_byte("#pragma pack(4)\nstruct s { int x : 12; char y : 6; };"), 1);
2118        // A thirty bit field after a byte, which is the case the rule was written for.
2119        assert_eq!(bit_field_byte("struct s { char x; int y : 30; };"), 4);
2120        assert_eq!(bit_field_byte("struct s { char x; int y : 30; } __attribute__((packed));"), 1);
2121        // Four is what an `int` asked for anyway, so this caps nothing and still counts.
2122        assert_eq!(bit_field_byte("#pragma pack(4)\nstruct s { char x; int y : 30; };"), 1);
2123        assert_eq!(bit_field_byte("#pragma pack(2)\nstruct s { char x; int y : 30; };"), 1);
2124    }
2125
2126    /// The byte a read of `s.y` loads from, which is where the bit-field was placed.
2127    fn bit_field_byte(record: &str) -> u64 {
2128        let source = format!("{record}\nint f(struct s *p) {{ return p->y; }}\n");
2129        let body = body(&source);
2130        let Some((before, _)) = body.split_once("ptr_add") else { return 0 };
2131        let (_, constant) = before.rsplit_once("iconst.i64 ").expect("an offset constant");
2132        constant.lines().next().expect("a line").trim().parse().expect("a byte offset")
2133    }
2134
2135    /// An attribute in the middle of a specifier list, which is where a member usually carries
2136    /// one and which was read and then thrown away. The `[[...]]` spelling and whatever was
2137    /// written in front of the declaration are collected as the list is walked and the
2138    /// `__attribute__` spelling is put straight on the specifiers, and the two were assigned
2139    /// over each other rather than joined.
2140    #[test]
2141    fn an_attribute_among_the_specifiers_is_kept_beside_the_ones_written_in_front() {
2142        tast(concat!(
2143            "struct a { char c; __attribute__((aligned(8))) int i; };\n",
2144            "_Static_assert(sizeof(struct a) == 16 && _Alignof(struct a) == 8, \"a\");\n",
2145            "_Static_assert(__builtin_offsetof(struct a, i) == 8, \"a.i\");\n",
2146            "struct b { char c; __attribute__((packed)) int i; };\n",
2147            "_Static_assert(sizeof(struct b) == 5 && _Alignof(struct b) == 1, \"b\");\n",
2148            "_Static_assert(__builtin_offsetof(struct b, i) == 1, \"b.i\");\n",
2149            "typedef struct { char c; int i; } __attribute__((packed)) c;\n",
2150            "_Static_assert(sizeof(c) == 5 && _Alignof(c) == 1, \"c\");\n",
2151        ));
2152    }
2153
2154    /// The other half, which is `#pragma pack`. It caps a member's alignment where `packed`
2155    /// drops it, so `pack(2)` leaves a `short` where it was and moves an `int`, and it caps a
2156    /// member the program asked to align as well, which is where the two differ. It is read
2157    /// at the closing brace of the body, so a line written in the middle of one settles the
2158    /// whole record rather than the members after it, and `push` and `pop` nest.
2159    #[test]
2160    fn pragma_pack_caps_every_member_and_is_read_where_the_body_closes() {
2161        tast(concat!(
2162            "#pragma pack(1)\n",
2163            "struct A { char c; int i; };\n",
2164            "_Static_assert(sizeof(struct A) == 5 && _Alignof(struct A) == 1, \"A\");\n",
2165            "_Static_assert(__builtin_offsetof(struct A, i) == 1, \"A.i\");\n",
2166            "#pragma pack()\n",
2167            "struct B { char c; int i; };\n",
2168            "_Static_assert(sizeof(struct B) == 8 && _Alignof(struct B) == 4, \"B\");\n",
2169            "#pragma pack(2)\n",
2170            "struct C { char c; int i; double d; };\n",
2171            "_Static_assert(sizeof(struct C) == 14 && _Alignof(struct C) == 2, \"C\");\n",
2172            "_Static_assert(__builtin_offsetof(struct C, d) == 6, \"C.d\");\n",
2173            // A member the program aligned, which `pack` caps and `packed` would not.
2174            "struct K { char c; int i __attribute__((aligned(8))); };\n",
2175            "_Static_assert(sizeof(struct K) == 6 && _Alignof(struct K) == 2, \"K\");\n",
2176            "_Static_assert(__builtin_offsetof(struct K, i) == 2, \"K.i\");\n",
2177            // The record's own `aligned` is not a member's, so it is not capped.
2178            "struct J { char c; int i; } __attribute__((aligned(8)));\n",
2179            "_Static_assert(sizeof(struct J) == 8 && _Alignof(struct J) == 8, \"J\");\n",
2180            "#pragma pack()\n",
2181            "#pragma pack(push, 1)\n",
2182            "struct D { char c; short s; };\n",
2183            "_Static_assert(sizeof(struct D) == 3 && _Alignof(struct D) == 1, \"D\");\n",
2184            "#pragma pack(pop)\n",
2185            "struct E { char c; short s; };\n",
2186            "_Static_assert(sizeof(struct E) == 4 && _Alignof(struct E) == 2, \"E\");\n",
2187            // Written in the middle of a body, and it still settles the whole record.
2188            "struct H { char c;\n",
2189            "#pragma pack(1)\n",
2190            "  int i; };\n",
2191            "_Static_assert(sizeof(struct H) == 5 && _Alignof(struct H) == 1, \"H\");\n",
2192            "#pragma pack(1)\n",
2193            "struct I { char c;\n",
2194            "#pragma pack()\n",
2195            "  int i; };\n",
2196            "_Static_assert(sizeof(struct I) == 8 && _Alignof(struct I) == 4, \"I\");\n",
2197            "#pragma pack()\n",
2198            // Nested pushes, each one giving back what the one under it had.
2199            "#pragma pack(push, 8)\n",
2200            "#pragma pack(push, 1)\n",
2201            "struct P { char c; int i; };\n",
2202            "_Static_assert(sizeof(struct P) == 5 && _Alignof(struct P) == 1, \"P\");\n",
2203            "#pragma pack(pop)\n",
2204            "struct Q { char c; int i; };\n",
2205            "_Static_assert(sizeof(struct Q) == 8 && _Alignof(struct Q) == 4, \"Q\");\n",
2206            "#pragma pack(pop)\n",
2207            // A cap above what every member already asks for changes nothing at all.
2208            "#pragma pack(16)\n",
2209            "struct R { char c; int i; };\n",
2210            "_Static_assert(sizeof(struct R) == 8 && _Alignof(struct R) == 4, \"R\");\n",
2211            "#pragma pack()\n",
2212            "#pragma pack(1)\n",
2213            "struct S { char c; int i : 5; int j : 20; };\n",
2214            "_Static_assert(sizeof(struct S) == 5 && _Alignof(struct S) == 1, \"S\");\n",
2215            "union T { char c; int i; };\n",
2216            "_Static_assert(sizeof(union T) == 4 && _Alignof(union T) == 1, \"T\");\n",
2217            "#pragma pack()\n",
2218        ));
2219    }
2220
2221    /// A line the reader cannot make sense of is a warning and the line is dropped, which is
2222    /// what GCC does with one, and these are its words for each of them. The last line is the
2223    /// one nothing else would reach, since it stands after every record in the file.
2224    #[test]
2225    fn a_pack_line_that_is_not_one_is_reported_in_the_words_gcc_uses() {
2226        let result = run(
2227            &options(),
2228            concat!(
2229                "#pragma pack 4\n",
2230                "#pragma pack(pop)\n",
2231                "#pragma pack(3)\n",
2232                "#pragma pack(1) junk\n",
2233                "#pragma pack(push, 1\n",
2234                "#pragma pack(x)\n",
2235                // These two are well formed and say nothing. Zero is how a line asks for the
2236                // target's own alignments back without writing empty parentheses.
2237                "#pragma pack(0)\n",
2238                "#pragma pack(push)\n",
2239                "struct s { char c; int i; };\n",
2240                "#pragma pack(pop)\n",
2241                "#pragma pack(pop, foo)\n",
2242            ),
2243        );
2244        let expected = [
2245            "missing `(` after `#pragma pack` - ignored",
2246            "`#pragma pack (pop)` encountered without matching `#pragma pack (push)`",
2247            "alignment must be a small power of two, not 3",
2248            "junk at end of `#pragma pack`",
2249            "malformed `#pragma pack(push[, id][, <n>])` - ignored",
2250            "unknown action `x` for `#pragma pack` - ignored",
2251            "`#pragma pack(pop, foo)` encountered without matching `#pragma pack(push, foo)`",
2252        ];
2253        assert_eq!(result.messages.len(), expected.len(), "{:?}", result.messages);
2254        for (message, want) in result.messages.iter().zip(expected) {
2255            assert!(message.contains(want), "expected {want:?} in {message:?}");
2256        }
2257    }
2258
2259    /// A pragma line ends where the next line starts, so a macro that comes to nothing and was
2260    /// written first on that next line has to hand the line on rather than take it away. This
2261    /// is SQLite through mingw-w64's headers: `<stdarg.h>` leaves a `#pragma pack(pop)` behind
2262    /// it and `sqlite3.h` writes every declaration with `SQLITE_API` in front, which is empty.
2263    /// Without it the pragma swallows the declaration, the program is left without it, and the
2264    /// only thing said about any of it is that there was junk on the pragma.
2265    #[test]
2266    fn a_declaration_behind_an_empty_macro_is_not_eaten_by_the_pragma_above_it() {
2267        let result = run(
2268            &options(),
2269            concat!(
2270                "#pragma pack(push, 1)\n",
2271                "#pragma pack(pop)\n",
2272                "#define API\n",
2273                "API const char version[] = \"3.53.4\";\n",
2274                "const char *get(void) { return version; }\n",
2275            ),
2276        );
2277        assert!(result.messages.is_empty(), "{:?}", result.messages);
2278    }
2279
2280    /// The two typedef spellings of the 128 bit types. gcc offers them as keywords rather
2281    /// than as typedefs in a header, which is the only way a program that includes nothing at
2282    /// all can still use them, and Apple's `<mach/arm/_structs.h>` is one such program.
2283    #[test]
2284    fn the_wide_integer_answers_to_all_three_of_its_names() {
2285        let text = tast("__uint128_t a; __int128_t b; unsigned __int128 c;\n");
2286        assert!(text.contains("decl #0 a : unsigned __int128"), "{text}");
2287        assert!(text.contains("decl #1 b : __int128"), "{text}");
2288        assert!(text.contains("decl #2 c : unsigned __int128"), "{text}");
2289    }
2290
2291    #[test]
2292    fn every_conversion_the_language_performs_is_a_node_in_the_output() {
2293        // The point of a typed tree. The source has one operator and the output has the
2294        // widening that operator asked for, spelled out, so that nothing downstream has to
2295        // work out the conversion rules a second time.
2296        let text = tast("long f(int a, long b) { return a + b; }\n");
2297        assert!(text.contains("convert arithmetic"), "{text}");
2298    }
2299
2300    #[test]
2301    fn a_mistake_in_each_phase_reaches_the_caller_and_writes_no_tree() {
2302        for source in [
2303            "#error stop\n",
2304            "int f(void) { return 1 + ; }\n",
2305            "int f(void) { return undeclared; }\n",
2306        ] {
2307            let result = run(&options(), source);
2308            assert!(result.failed(), "expected this to fail:\n{source}");
2309            assert!(
2310                result.text().is_empty(),
2311                "a file that did not compile wrote a tree:\n{source}"
2312            );
2313        }
2314    }
2315
2316    #[test]
2317    fn one_undeclared_name_is_one_message_and_not_one_per_use() {
2318        // The poisoning rule from `spec/06-lexer-and-parser.md` section 6.8, seen from the
2319        // outside. Three uses of a name that was never declared, and the operators over them
2320        // say nothing at all.
2321        let result = run(&options(), "int f(void) { return nope + nope * nope; }\n");
2322        assert_eq!(result.errors, 1, "{:?}", result.messages);
2323    }
2324
2325    #[test]
2326    fn a_declaration_the_parser_skipped_does_not_become_an_undeclared_name_as_well() {
2327        // The reason the checking is skipped after a failed parse. The parser gave up on the
2328        // first line and there is no `x` in the tree, so a checker run over it would report
2329        // every use of `x` below as undeclared, which is a second message about one mistake.
2330        let result = run(&options(), "int x = ;\nint f(void) { return x; }\n");
2331        assert_eq!(result.errors, 1, "{:?}", result.messages);
2332    }
2333
2334    #[test]
2335    fn werror_turns_a_warning_into_an_error_in_the_count_and_in_the_word() {
2336        let source = "int f(void) { char c = 300; return c; }\n";
2337        let plain = run(&options(), source);
2338        assert_eq!(plain.errors, 0, "{:?}", plain.messages);
2339        assert_eq!(plain.messages.len(), 1, "expected a warning about the narrowed constant");
2340        assert!(!plain.text().is_empty(), "a warning is not a reason to write nothing");
2341
2342        let mut opts = options();
2343        opts.warnings_are_errors = true;
2344        let strict = run(&opts, source);
2345        assert!(strict.failed());
2346        assert!(strict.text().is_empty(), "and under -Werror it is a reason to write nothing");
2347        for message in &strict.messages {
2348            assert!(!message.contains("warning:"), "{message}");
2349        }
2350    }
2351
2352    #[test]
2353    fn w_drops_the_warning_before_werror_can_promote_it() {
2354        let source = "int f(void) { char c = 300; return c; }\n";
2355        let mut opts = options();
2356        opts.warnings = false;
2357        let quiet = run(&opts, source);
2358        assert_eq!(quiet.messages, Vec::<String>::new());
2359        assert_eq!(quiet.errors, 0);
2360        assert!(!quiet.text().is_empty(), "and the file still compiles");
2361
2362        // A build that passes both means it wants neither, and the order it wrote them in is not
2363        // something to make it think about.
2364        opts.warnings_are_errors = true;
2365        let both = run(&opts, source);
2366        assert_eq!(both.messages, Vec::<String>::new());
2367        assert!(!both.failed(), "-w -Werror is not an error about a warning nobody saw");
2368    }
2369
2370    #[test]
2371    fn the_dialect_reaches_the_keywords_and_the_checking() {
2372        // `typeof` is C23's and GNU's, so the same source is a declaration under one dialect
2373        // and a mistake under the other, which is the keyword table being built per dialect.
2374        let source = "typeof(1) x;\n";
2375        let mut opts = options();
2376        opts.std = Std::C23;
2377        opts.gnu_extensions = false;
2378        assert!(!run(&opts, source).failed(), "{:?}", run(&opts, source).messages);
2379
2380        opts.std = Std::C17;
2381        assert!(run(&opts, source).failed());
2382    }
2383
2384    #[test]
2385    fn asking_for_a_kind_that_is_not_written_yet_runs_the_front_end_and_writes_nothing() {
2386        let mut opts = options();
2387        opts.emit = EmitKind::Object;
2388        let result = run(&opts, "int x = 1;\n");
2389        assert!(!result.failed(), "{:?}", result.messages);
2390        assert!(result.text().is_empty());
2391        // And it still finds what the checking finds, so a later kind on a broken file is not
2392        // a silent success.
2393        assert!(run(&opts, "int f(void) { return undeclared; }\n").failed());
2394    }
2395
2396    /// The machine code of `source`, insisting that it compiled cleanly.
2397    fn mir(source: &str) -> String {
2398        let mut opts = options();
2399        opts.emit = EmitKind::MirFinal;
2400        let result = run(&opts, source);
2401        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
2402        result.text().to_owned()
2403    }
2404
2405    /// The whole compiler in one assertion, which is what this emit kind is for.
2406    ///
2407    /// C in, machine instructions out, every register a real one and every frame offset a
2408    /// number. Everything between the two is checked somewhere else, one pass at a time. What is
2409    /// checked here is that the passes are joined up and that the driver runs them.
2410    #[test]
2411    fn a_function_goes_from_c_to_instructions_with_real_registers_in_them() {
2412        let text = mir("int add(int a, int b) { return a + b; }\n");
2413        assert!(text.starts_with("mfunc @add {"), "{text}");
2414        assert!(text.contains("x64.add_rr_32"), "{text}");
2415        assert!(text.contains("x64.ret"), "{text}");
2416        // A virtual register is what the allocator was there to remove, so one left in the
2417        // output is the difference between code and something that looks like code.
2418        assert!(!text.contains('%'), "{text}");
2419    }
2420
2421    /// A declaration has no body, so there is nothing to generate for one and nothing is.
2422    #[test]
2423    fn a_function_with_no_body_produces_no_machine_function() {
2424        let text = mir("int g(int);\nint f(int a) { return g(a); }\n");
2425        assert_eq!(text.matches("mfunc @").count(), 1, "{text}");
2426        assert!(text.contains("mfunc @f {"), "{text}");
2427        assert!(text.contains("x64.call"), "{text}");
2428    }
2429
2430    /// Two functions come out in the order the module holds them, which is source order.
2431    #[test]
2432    fn every_definition_in_the_file_is_generated_and_they_keep_their_order() {
2433        let text = mir("int a(int x) { return x; }\nint b(int x) { return x; }\n");
2434        let first = text.find("mfunc @a").expect("the first function");
2435        let second = text.find("mfunc @b").expect("the second function");
2436        assert!(first < second, "{text}");
2437    }
2438
2439    /// The target reaches the back end, so the same C is different instructions on Windows.
2440    #[test]
2441    fn the_target_decides_which_convention_the_generated_code_follows() {
2442        let mut opts = options();
2443        opts.emit = EmitKind::MirFinal;
2444        let linux = run(&opts, "int f(int a) { return a; }\n").text().to_owned();
2445        assert!(linux.contains("$rdi"), "{linux}");
2446
2447        opts.target = "x86_64-pc-windows-msvc".parse::<Triple>().unwrap();
2448        let windows = run(&opts, "int f(int a) { return a; }\n").text().to_owned();
2449        assert!(windows.contains("$rcx"), "{windows}");
2450        assert!(!windows.contains("$rdi"), "{windows}");
2451    }
2452
2453    /// And it reaches the front end, where it decides what an anonymous member is.
2454    ///
2455    /// This is the shape `<objidl.h>` writes and the Windows headers are full of: the union inside
2456    /// `STGMEDIUM` closes with `} DUMMYUNIONNAME;`, and the macro expands to nothing unless the
2457    /// program defined `NONAMELESSUNION`, so what is left is a union with a tag and no name. On a
2458    /// Windows target that is an anonymous member, and reading it as a declaration of nothing
2459    /// drops it, which loses the names and the eight bytes the member takes up both.
2460    #[test]
2461    fn a_tagged_member_with_no_name_is_a_member_on_windows_and_nothing_on_linux() {
2462        let source = concat!(
2463            "struct S { union U { int i; void *p; }; unsigned long tymed; };\n",
2464            "int size(void) { return sizeof(struct S); }\n",
2465            "int f(struct S *s) { s->i = 1; return s->i; }\n",
2466        );
2467
2468        let mut opts = options();
2469        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
2470        let windows = run(&opts, source);
2471        assert!(windows.messages.is_empty(), "{:?}", windows.messages);
2472
2473        let linux = run(&options(), source);
2474        assert_eq!(linux.messages.len(), 3, "{:?}", linux.messages);
2475        assert!(linux.messages[0].contains("does not declare anything"), "{:?}", linux.messages);
2476
2477        // And the flag answers for either of them, so a program built for Linux against a header
2478        // written for Windows can be read the way the header meant it.
2479        let mut opts = options();
2480        opts.ms_extensions = Some(true);
2481        let asked = run(&opts, source);
2482        assert!(asked.messages.is_empty(), "{:?}", asked.messages);
2483    }
2484
2485    /// A target with no back end says so rather than generating something for another machine.
2486    #[test]
2487    fn a_target_this_has_no_back_end_for_is_reported_rather_than_generated() {
2488        let mut opts = options();
2489        opts.emit = EmitKind::MirFinal;
2490        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2491        let result = run(&opts, "int f(int a) { return a; }\n");
2492        assert!(result.failed());
2493        assert!(result.messages[0].contains("no back end for aarch64"), "{:?}", result.messages);
2494        assert!(result.text().is_empty());
2495    }
2496
2497    /// A construct the rule set does not reach yet is named, along with the function it is in.
2498    ///
2499    /// The message is about this compiler being unfinished rather than about the program, which
2500    /// is valid C either way, so it carries the note that says where the work is tracked. Both
2501    /// functions are attempted, so a file that is ahead of the back end in three places says so
2502    /// three times rather than one recompilation at a time.
2503    ///
2504    /// The construct is a local of a fixed size wanting more alignment than a call leaves the
2505    /// stack pointer on, in a function whose frame also grows. The prologue would force the
2506    /// alignment and the array would move the stack pointer afterwards, and those are two frames
2507    /// that each want the one register the rest of the frame is counted from.
2508    #[test]
2509    fn a_construct_the_back_end_cannot_reach_yet_is_reported_against_its_function() {
2510        let mut opts = options();
2511        opts.emit = EmitKind::MirFinal;
2512        let source = "void a(int n) { int v[n]; struct __attribute__((aligned(32))) S { int x; } \
2513                      s; s.x = 1; v[0] = s.x; }\n\
2514                      void b(int n) { int v[n]; struct __attribute__((aligned(32))) S { int x; } \
2515                      s; s.x = 1; v[0] = s.x; }\n";
2516        let result = run(&opts, source);
2517        assert!(result.failed());
2518        assert_eq!(result.messages.len(), 2, "{:?}", result.messages);
2519        assert!(result.messages[0].contains("cannot generate code for 'a'"), "{:?}", result);
2520        assert!(result.messages[0].contains("wants more alignment"), "{:?}", result);
2521        assert!(result.messages[1].contains("cannot generate code for 'b'"), "{:?}", result);
2522        assert!(result.text().is_empty());
2523    }
2524
2525    /// A variable length array walks its pages under the flag that says every page is touched.
2526    ///
2527    /// The pages the prologue takes are touched by the prologue. The pages the array takes are
2528    /// however many the size worked out to, so touching them is a loop written around the
2529    /// declaration rather than anything a prologue can do. What says the loop is there is the
2530    /// ordered comparison it ends each step with, which nothing else in a function writes, and the
2531    /// touch behind it. Without the flag the declaration is still the one subtraction it always was.
2532    #[test]
2533    fn a_variable_length_array_walks_its_pages_where_every_page_of_the_frame_is_to_be_touched() {
2534        let mut opts = options();
2535        opts.emit = EmitKind::MirFinal;
2536        let source = "void a(int n) { int v[n]; v[0] = 1; }\n";
2537        let plain = run(&opts, source);
2538        assert!(!plain.failed(), "{:?}", plain.messages);
2539        assert!(!plain.text().contains("cmp_set_a_64"), "{}", plain.text());
2540
2541        opts.stack_clash = true;
2542        let result = run(&opts, source);
2543        assert!(!result.failed(), "{:?}", result.messages);
2544        assert!(result.text().contains("cmp_set_a_64"), "{}", result.text());
2545        assert!(result.text().contains("or_mi_8"), "{}", result.text());
2546    }
2547
2548    /// A function that keeps a frame pointer on Windows now has an unwind record and an object.
2549    ///
2550    /// The record that platform carries counts every slot in it from where the stack pointer ends
2551    /// the prologue, and it gets to that place by taking a constant off the frame pointer, so a
2552    /// register pushed after the pointer was established has no row the format can write. The order
2553    /// that does have one is the pushes, then the frame, and only then the pointer, which is what
2554    /// the back end writes there and only there. A variable length array and an `alloca` keep a
2555    /// pointer whatever the flags asked for, so before this they were the two shapes of C that
2556    /// could not be compiled for that target at all. See tamnd/rucc#1403.
2557    #[test]
2558    fn a_function_that_keeps_a_frame_pointer_on_windows_reaches_an_object_file() {
2559        let mut opts = options();
2560        opts.emit = EmitKind::Object;
2561        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
2562        let source = concat!(
2563            "void use(void *p);\n",
2564            "void array(int n) { int v[n]; v[0] = 1; use(v); }\n",
2565            "void taken(unsigned long n) { use(__builtin_alloca(n)); }\n",
2566        );
2567        let result = run(&opts, source);
2568        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
2569        let bytes = match result.artifact {
2570            Artifact::Object { bytes, .. } => bytes,
2571            other => panic!("expected an object, got {other:?}"),
2572        };
2573        assert_eq!(&bytes[..2], b"\x64\x86", "an object that says which machine it is for");
2574
2575        // And the same two functions for Linux, so that what the test is measuring is the target
2576        // rather than the program being one this compiler cannot reach yet.
2577        let mut opts = options();
2578        opts.emit = EmitKind::Object;
2579        assert_eq!(run(&opts, source).messages, Vec::<String>::new());
2580    }
2581
2582    /// The address of a name this file only declares, on the format with no table to read it out
2583    /// of.
2584    ///
2585    /// Every such name went into the table on every target, and COFF has no table, so the object
2586    /// writer was handed a relocation it has no way to write and refused the whole file. What the
2587    /// name stands for on this format is an address in the image whichever way the link supplies
2588    /// it, so the instruction pointer reaches it and gcc writes the same. Three shapes here, since
2589    /// the one that found it was a callback stored in a table of its own: a function passed as an
2590    /// argument, one put in a variable that lives past the call, and one called outright, which
2591    /// never needed the table and is here so the test says which of the three changed.
2592    #[test]
2593    fn the_address_of_a_function_this_file_only_declares_reaches_a_windows_object() {
2594        let source = concat!(
2595            "void other(void *p);\n",
2596            "void takes(void (*f)(void *));\n",
2597            "void (*held)(void *);\n",
2598            "void pass(void) { takes(other); }\n",
2599            "void keep(void) { held = other; }\n",
2600            "void call(void) { other(0); }\n",
2601        );
2602        let mut opts = options();
2603        opts.emit = EmitKind::Object;
2604        opts.target = "x86_64-pc-windows-gnu".parse::<Triple>().unwrap();
2605        let result = run(&opts, source);
2606        assert_eq!(result.messages, Vec::<String>::new(), "{result:?}");
2607        let bytes = match result.artifact {
2608            Artifact::Object { bytes, .. } => bytes,
2609            other => panic!("expected an object, got {other:?}"),
2610        };
2611        assert_eq!(&bytes[..2], b"\x64\x86", "an object that says which machine it is for");
2612
2613        // And the same source for Linux, which does have a table and still uses it, so what this
2614        // measures is the format rather than the program.
2615        let mut opts = options();
2616        opts.emit = EmitKind::Object;
2617        assert_eq!(run(&opts, source).messages, Vec::<String>::new());
2618    }
2619
2620    /// An opcode the rule language has no word for is named anyway, and pointed at.
2621    ///
2622    /// The rule language's spelling is the better name when there is one, but an opcode it has
2623    /// no word for is exactly the opcode no rule lowers, so falling back to the opcode and the
2624    /// type is what makes the message say anything at all in the cases that happen. The span is
2625    /// the instruction's own, so the message lands on the line rather than on the file.
2626    ///
2627    /// The width of the float is what keeps the program refused. Everything else here is split into
2628    /// halves by `rucc_codegen::wide`, including the divisions and the conversions to a `float` and
2629    /// a `double`, which became calls into the compiler runtime. A `long double` is the eighty bit
2630    /// float on this target, the runtime has no conversion at that width because the back end has no
2631    /// register that holds one, which is tamnd/rucc#326, so a function converting to it is left with
2632    /// its wide values and reaches the selector the way every function of this width used to.
2633    #[test]
2634    fn an_opcode_with_no_name_in_the_rule_language_is_named_by_its_own_spelling() {
2635        let mut opts = options();
2636        opts.emit = EmitKind::MirFinal;
2637        let source =
2638            "long double f(int a) {\n  __int128 wide = a;\n  return (long double) wide;\n}\n";
2639        let result = run(&opts, source);
2640        assert!(result.failed());
2641        assert!(
2642            result.messages[0].contains("no rule lowers a `sext` producing a `i128`"),
2643            "{result:?}"
2644        );
2645        assert!(result.messages[0].contains(":2:"), "the line the widening is on: {result:?}");
2646        assert!(!result.messages[0].contains("this instruction"), "{result:?}");
2647    }
2648
2649    /// The note names the issue tracker, which is where a reader finds out whether it is known.
2650    #[test]
2651    fn the_note_on_unfinished_work_points_at_the_issues_rather_than_at_the_plan() {
2652        let mut opts = options();
2653        opts.emit = EmitKind::MirFinal;
2654        let source = "long double f(int a) { __int128 wide = a; return (long double) wide; }\n";
2655        let result = run(&opts, source);
2656        assert!(result.failed());
2657        let note = result.messages.iter().find(|line| line.contains("note:")).expect("a note");
2658        assert!(note.contains("https://github.com/tamnd/rucc/issues"), "{note}");
2659        assert!(!note.contains("spec/17-milestones.md"), "{note}");
2660    }
2661
2662    /// The two frame flags reach the frame, which is the only thing either of them does.
2663    #[test]
2664    fn the_frame_flags_on_the_command_line_reach_the_generated_frame() {
2665        let source = "int f(int a) { return a; }\n";
2666        assert!(!mir(source).contains("$rbp"), "a leaf needs no frame pointer by default");
2667
2668        let mut opts = options();
2669        opts.emit = EmitKind::MirFinal;
2670        opts.frame_pointer = true;
2671        let kept = run(&opts, source).text().to_owned();
2672        assert!(kept.contains("x64.push_64 $rbp"), "{kept}");
2673    }
2674
2675    /// The assembly of `source`, insisting that it compiled cleanly.
2676    fn asm(source: &str) -> String {
2677        let mut opts = options();
2678        opts.emit = EmitKind::Asm;
2679        let result = run(&opts, source);
2680        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
2681        result.text().to_owned()
2682    }
2683
2684    /// `-S`, which is the same compiler as the kind above it with a different last step.
2685    ///
2686    /// What the assembly says is checked in `rucc-asm`, one instruction at a time and against the
2687    /// target's own description of what an instruction is. What is checked here is that a C file
2688    /// goes all the way to a listing an assembler would take, which means the directives around
2689    /// the function as well as the instructions in it.
2690    #[test]
2691    fn a_function_goes_from_c_to_assembly_an_assembler_would_take() {
2692        let text = asm("int add(int a, int b) { return a + b; }\n");
2693        assert!(text.contains("\t.globl\tadd\n"), "{text}");
2694        assert!(text.contains("\t.type\tadd, @function\n"), "{text}");
2695        assert!(text.contains("\nadd:\n"), "{text}");
2696        assert!(text.contains("\taddl\t"), "{text}");
2697        assert!(text.contains("\tret\n"), "{text}");
2698        assert!(text.contains("\t.size\tadd, .-add\n"), "{text}");
2699        // Without this the stack the program runs on is executable, which is not a default
2700        // anybody chose and is not a thing a reader would notice missing.
2701        assert!(text.contains(".note.GNU-stack"), "{text}");
2702    }
2703
2704    /// A call through a function pointer, which is a different instruction from a call to a name.
2705    ///
2706    /// Both are in the one function on purpose. What is being read is that the two calls are told
2707    /// apart all the way down: one carries a name the linker resolves and one carries a register,
2708    /// and neither turns into the other on the way.
2709    #[test]
2710    fn a_call_through_a_function_pointer_goes_through_the_register_it_is_in() {
2711        let text = asm("int g(int);\nint f(int (*p)(int), int a) { return p(a) + g(a); }\n");
2712        assert!(text.contains("\tcall\t*%"), "{text}");
2713        assert!(text.contains("\tcall\tg\n"), "{text}");
2714        // The address arrived in the first argument register and the argument the call passes has
2715        // to end up there, so the two cannot be the same register and the compiler has to have
2716        // moved one of them.
2717        assert!(text.contains("%rdi"), "{text}");
2718    }
2719
2720    /// A name at file scope, which is the one address a function cannot compute for itself. The
2721    /// `lea` that computes it is folded into the load that reads through it, so what is left to
2722    /// read is the addressing mode, which is where the instruction pointer shows up.
2723    #[test]
2724    fn the_address_of_a_global_is_read_from_the_instruction_pointer() {
2725        let text = asm("extern int counter;\nint f(void) { return counter; }\n");
2726        assert!(text.contains("\tmovl\tcounter(%rip), %eax\n"), "{text}");
2727    }
2728
2729    /// Every comparison a branch can be on, which the machine jumps on without keeping a byte.
2730    ///
2731    /// Ten conditions, and each of them comes out as its opposite because the block falls into the
2732    /// arm the comparison is true for and jumps to the other one. That is the half of this most
2733    /// worth pinning: a jump on the condition rather than on its opposite compiles, encodes and
2734    /// runs, and gets every one of these ten functions backwards. The unsigned four and the signed
2735    /// four are separate for the same reason, since `jl` where `jb` was meant is a program that
2736    /// works until an address is above two gigabytes.
2737    #[test]
2738    fn a_branch_on_a_comparison_jumps_on_the_opposite_of_what_it_compared() {
2739        let arms = "return 1; return 2;";
2740        let signed = [("==", "jne"), ("!=", "je"), ("<", "jge"), ("<=", "jg"), (">", "jle")];
2741        for (operator, jump) in signed.into_iter().chain([(">=", "jl")]) {
2742            let text = asm(&format!("int f(int a, int b) {{ if (a {operator} b) {arms} }}\n"));
2743            assert!(
2744                text.contains(&format!("\tcmpl\t%esi, %edi\n\t{jump}\t")),
2745                "{operator}: {text}"
2746            );
2747            assert!(!text.contains("\tset"), "{operator}: {text}");
2748            assert!(!text.contains("\ttest"), "{operator}: {text}");
2749        }
2750        let unsigned = [("<", "jae"), ("<=", "ja"), (">", "jbe"), (">=", "jb")];
2751        for (operator, jump) in unsigned {
2752            let source =
2753                format!("int f(unsigned a, unsigned b) {{ if (a {operator} b) {arms} }}\n");
2754            let text = asm(&source);
2755            assert!(
2756                text.contains(&format!("\tcmpl\t%esi, %edi\n\t{jump}\t")),
2757                "{operator}: {text}"
2758            );
2759        }
2760
2761        // And against a constant, which is four comparisons in five and is where the saving
2762        // mostly is, since the byte that goes was the only reason the constant was in a register.
2763        let text = asm("int f(int a) { if (a < 7) return 1; return 2; }\n");
2764        assert!(text.contains("\tcmpl\t$7, %edi\n\tjge\t"), "{text}");
2765    }
2766
2767    /// The comparison whose answer is a value rather than a branch, which keeps its byte.
2768    ///
2769    /// The one that goes is the byte nothing but the branch reads. A comparison the program asked
2770    /// for the answer of is not that, and there is no branch behind it to fold into in any case,
2771    /// so this is here to say that what was taken out was taken out of one place and not two.
2772    #[test]
2773    fn a_comparison_whose_answer_the_program_wanted_still_writes_a_byte() {
2774        let text = asm("int f(int a, int b) { return a < b; }\n");
2775        assert!(text.contains("\tsetl\t"), "{text}");
2776    }
2777
2778    /// The same source at `-O2`, which is where the optimizer's passes are in the list.
2779    fn optimized(source: &str) -> String {
2780        let mut opts = options();
2781        opts.emit = EmitKind::Asm;
2782        opts.opt_level = rucc_session::OptLevel::O2;
2783        let result = run(&opts, source);
2784        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
2785        result.text().to_owned()
2786    }
2787
2788    /// A dense `switch` whose arms are a function of the label, which is arithmetic.
2789    ///
2790    /// Sixteen labels, and the arm for label `k` gives `k + 1`. What came out of this was a
2791    /// comparison and a jump for every one of them, which is tamnd/rucc#728. What comes out now is
2792    /// one comparison and one addition, and the count is the whole of the claim: it does not grow
2793    /// with the number of labels, so sixteen and a hundred and sixty compile to the same thing.
2794    ///
2795    /// The comparison is unsigned because the range check is the label minus the lowest one, which
2796    /// is a count and not a number the program wrote.
2797    #[test]
2798    fn a_switch_whose_arms_are_a_function_of_the_label_is_a_range_check_and_arithmetic() {
2799        let arms: String =
2800            (0..16).map(|k| format!("case {k}: return {};", k + 1)).collect::<Vec<_>>().join(" ");
2801        let text = optimized(&format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
2802        assert!(text.contains("\tcmpl\t$15, %edi\n\tja\t"), "{text}");
2803        assert!(text.contains("\taddl\t$1, %edi"), "{text}");
2804        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
2805    }
2806
2807    /// The same `switch` with one arm off the line, which keeps every comparison it had.
2808    ///
2809    /// The answers being a line is what licenses the range check, since a range check answers for
2810    /// every label in the range at once. One label whose arm disagrees is a label the check would
2811    /// answer wrongly, so this is here to say that the pass is reading the arms and not counting
2812    /// the labels.
2813    #[test]
2814    fn a_dense_switch_whose_arms_are_not_a_line_keeps_its_comparisons() {
2815        let arms: String = (0..16)
2816            .map(|k| format!("case {k}: return {};", if k == 9 { 100 } else { k + 1 }))
2817            .collect::<Vec<_>>()
2818            .join(" ");
2819        let text = optimized(&format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
2820        assert!(text.matches("\tcmp").count() > 1, "{text}");
2821    }
2822
2823    /// A conversion whose operand the optimizer turned into a constant, which is the whole of what
2824    /// `rucc_opt::fold` does with floating point.
2825    ///
2826    /// The cast is not a constant expression, so the front end leaves it alone and the pipeline is
2827    /// what has to see it. Load forwarding turns the local back into the constant that was stored
2828    /// into it, and the conversion then has an `fconst` in front of it. What came out before was
2829    /// the sixty four bit pattern moved into a register, moved into an `xmm`, and a `cvttsd2si`.
2830    #[test]
2831    fn a_conversion_from_a_constant_double_is_the_number_it_converts_to() {
2832        let text = optimized("int f(void) { double d = 2.75; return (int) d; }\n");
2833        assert!(text.contains("movl\t$2, %eax"), "{text}");
2834        assert!(!text.contains("cvttsd2si"), "{text}");
2835    }
2836
2837    /// A slot of a `const` table read at an index the optimizer works out, which is what
2838    /// `rucc_opt::image` is for.
2839    ///
2840    /// The subscript is not a constant expression and the front end does not fold it. What it
2841    /// writes is the index sign extended, multiplied by four and added to the address of the
2842    /// table, so the offset only exists once `fold` has run and the load only folds after that.
2843    /// What came out before was a `movl t+8(%rip), %eax`.
2844    #[test]
2845    fn a_slot_of_a_read_only_table_is_the_value_the_table_holds() {
2846        let text =
2847            optimized("static const int t[4] = {10, 20, 30, 40};\nint f(void) { return t[2]; }\n");
2848        assert!(text.contains("movl\t$30, %eax"), "{text}");
2849        assert!(!text.contains("t(%rip)"), "{text}");
2850    }
2851
2852    /// A byte of a string literal, which is the same fold reading literal bytes rather than the
2853    /// scalars an `int` array is written as.
2854    #[test]
2855    fn a_byte_of_a_read_only_string_is_the_byte_the_string_spells() {
2856        let text = optimized("static const char s[] = \"abc\";\nint f(void) { return s[1]; }\n");
2857        assert!(text.contains("movl\t$98, %eax"), "{text}");
2858    }
2859
2860    /// A global something can write to, which is the condition the fold turns on and therefore
2861    /// the one worth a test of its own. Nothing here is `const`, so the store in `g` could be the
2862    /// store that ran last and the load has to happen.
2863    #[test]
2864    fn a_table_that_is_not_read_only_keeps_its_load() {
2865        let text = optimized(
2866            "static int t[4] = {10, 20, 30, 40};\nvoid g(int x) { t[2] = x; }\nint f(void) { return t[2]; }\n",
2867        );
2868        assert!(!text.contains("movl\t$30, %eax"), "{text}");
2869    }
2870
2871    /// `gcc.c-torture/execute/20030216-1.c`, which is the program the whole of this is for.
2872    ///
2873    /// It calls a function nothing defines, guarded by a condition the optimizer is meant to prove
2874    /// false, so the program links exactly when the call has been folded away. Getting there is
2875    /// three folds standing on each other: the load of the `const double`, the conversion of it to
2876    /// an `int`, and the comparison against one.
2877    #[test]
2878    fn a_call_guarded_by_a_condition_a_read_only_object_settles_is_not_emitted() {
2879        let text = optimized(
2880            "void link_error(void);\nconst double one = 1.0;\nint main(void) { if ((int) one != 1) link_error(); return 0; }\n",
2881        );
2882        assert!(!text.contains("call\tlink_error"), "{text}");
2883    }
2884
2885    /// A cast between a pointer and an integer as wide as one, which is every one C writes here.
2886    #[test]
2887    fn a_cast_between_a_pointer_and_an_integer_leaves_the_value_where_it_is() {
2888        let text = asm("long f(void *p) { return (long)p; }\n");
2889        // Every instruction in the body is a full width move or the return. The copies are the
2890        // allocator taking no hints, and what matters here is what is not among them: nothing
2891        // narrows the value and nothing widens it again, which is what a cast that did something
2892        // would look like.
2893        for line in text.lines().filter(|line| line.starts_with('\t') && !line.contains('.')) {
2894            let mnemonic = line.split_whitespace().next().unwrap_or("");
2895            assert!(matches!(mnemonic, "movq" | "ret"), "{line} in\n{text}");
2896        }
2897    }
2898
2899    /// The arguments past the sixth arrive in the caller's memory rather than in a register, and
2900    /// where that memory is depends on what the prologue did, so this is checked at the end of the
2901    /// pipeline rather than in the middle of it.
2902    #[test]
2903    fn an_argument_past_the_last_register_is_read_out_of_the_caller_s_stack() {
2904        let six = "long a, long b, long c, long d, long e, long f";
2905        let text = asm(&format!("long f({six}, long g, long h) {{ return g + h; }}\n"));
2906
2907        // Nothing is pushed and no frame is taken, so the only thing between the stack pointer and
2908        // the caller's arguments is the return address the call pushed. Which is where gcc 16.2.0
2909        // reads them from too, at `-O0`, though it reads them in three instructions where this
2910        // reads them in two: the second read is the addition's own memory operand, which is
2911        // `rucc_codegen::combine`, and the offset in it is the one the frame layout wrote into the
2912        // load before the two were put together.
2913        assert!(text.contains("\tmovq\t8(%rsp), "), "{text}");
2914        assert!(text.contains("\taddq\t16(%rsp), "), "{text}");
2915
2916        // A narrower one is read at its own width, because the bits above it are bits the
2917        // convention says nothing about, and one in the other register file with the other file's
2918        // instruction.
2919        let narrow = asm(&format!("int f({six}, int g) {{ return g; }}\n"));
2920        assert!(narrow.contains("\tmovl\t8(%rsp), "), "{narrow}");
2921        let eight =
2922            "double a, double b, double c, double d, double e, double f, double g, double h";
2923        let float = asm(&format!("double f({eight}, double i) {{ return i; }}\n"));
2924        assert!(float.contains("\tmovsd\t8(%rsp), "), "{float}");
2925    }
2926
2927    /// The other end of the same thing. What the caller writes is at the stack pointer, because
2928    /// that is the bottom of its frame and the bottom of its frame is where the callee looks.
2929    #[test]
2930    fn a_call_writes_the_arguments_with_no_register_left_at_the_stack_pointer() {
2931        let six = "1, 2, 3, 4, 5, 6";
2932        let decl = "long g(long, long, long, long, long, long, long, long);\n";
2933        let text = asm(&format!("{decl}long f(void) {{ return g({six}, 7, 8); }}\n"));
2934
2935        assert!(text.contains("\tmovq\t%"), "{text}");
2936        assert!(text.contains(", (%rsp)\n"), "{text}");
2937        assert!(text.contains(", 8(%rsp)\n"), "{text}");
2938        // And it reserved the bytes it wrote into, so nothing else in the frame is on top of them.
2939        assert!(text.contains("\tsubq\t$"), "{text}");
2940
2941        // A narrower one is written at its own width, matching what the callee reads it back with.
2942        let narrow = "int g(int, int, int, int, int, int, int);\n";
2943        let text = asm(&format!("{narrow}int f(void) {{ return g({six}, 7); }}\n"));
2944        assert!(text.contains("\tmovl\t%"), "{text}");
2945        assert!(text.contains(", (%rsp)\n"), "{text}");
2946    }
2947
2948    /// The count a variadic callee on this convention reads is a count of vector registers, so a
2949    /// float that ran out of them and went to memory is not in it.
2950    #[test]
2951    fn a_variadic_call_counts_registers_and_not_arguments() {
2952        let nine = "1., 2., 3., 4., 5., 6., 7., 8., 9.";
2953        let decl = "int g(int, ...);\n";
2954        let text = asm(&format!("{decl}int f(void) {{ return g(0, {nine}); }}\n"));
2955
2956        assert!(text.contains("\tmovl\t$8, "), "eight registers, not nine: {text}");
2957        assert!(text.contains("\tmovsd\t%"), "{text}");
2958        assert!(text.contains(", (%rsp)\n"), "{text}");
2959    }
2960
2961    /// The callee's half of the same convention. Every argument register it was handed is written
2962    /// into its frame on the way in, because which of them hold anything is a thing only the caller
2963    /// knew, and the ones the signature does name are left out because `va_start` sets the offsets
2964    /// past them and nothing ever reads their slots.
2965    #[test]
2966    fn a_variadic_function_writes_the_argument_registers_it_was_handed_into_its_frame() {
2967        let body =
2968            "__builtin_va_list ap; __builtin_va_start(ap, n); __builtin_va_end(ap); return n;";
2969        let text = asm(&format!("int f(int n, ...) {{ {body} }}\n"));
2970
2971        // Five general purpose registers and eight vector ones, since the one parameter the
2972        // signature names took the first of the six.
2973        let stores = |mnemonic: &str| text.matches(&format!("\t{mnemonic}\t%")).count();
2974        assert!(text.contains(", 8(%r"), "the second slot, not the first: {text}");
2975        assert!(!text.contains(", 0(%r"), "{text}");
2976        // All sixteen bytes of each vector register, which is what gcc writes and what a `va_arg`
2977        // of a `_Float128` reads back, so the mnemonic is the one that moves a whole register.
2978        assert_eq!(stores("movaps"), 8, "every vector register: {text}");
2979        assert_eq!(stores("movsd"), 0, "and the whole of each one: {text}");
2980
2981        // And the area is one of the function's own stack objects, so the frame holds it.
2982        assert!(text.contains("\tsubq\t$"), "{text}");
2983    }
2984
2985    /// What `va_start` writes is the four fields of the list, and the two numbers among them are
2986    /// where the arguments the signature names left the walk over each file's registers.
2987    #[test]
2988    fn va_start_writes_the_four_fields_the_psabi_describes() {
2989        let start = "__builtin_va_list ap; __builtin_va_start(ap, d);";
2990        let params = "int a, int b, int c, double d";
2991        let text = asm(&format!("int f({params}, ...) {{ {start} return a; }}\n"));
2992
2993        // Three integers took three of the six general purpose registers, and one double took one
2994        // of the eight vector ones, so the walk starts at twenty four bytes into the first half and
2995        // sixteen bytes into the second, which begins at forty eight.
2996        assert!(text.contains("	movl	$24, "), "{text}");
2997        assert!(text.contains("	movl	$64, "), "{text}");
2998        // The other two fields are addresses rather than numbers, so each is stored as a word and
2999        // each is a `lea` away. One of them reaches above the frame, which is where the caller's
3000        // arguments are and is the only thing in this function that is not below the stack pointer.
3001        assert!(text.contains(", 8(%r"), "{text}");
3002        assert!(text.contains(", 16(%r"), "{text}");
3003        let frame: u32 = text
3004            .lines()
3005            .find_map(|line| line.trim().strip_prefix("subq	$")?.split(',').next()?.parse().ok())
3006            .expect("a variadic function takes a frame for the save area");
3007        let above = |line: &str| {
3008            let at: u32 = line.trim().strip_prefix("leaq	")?.split('(').next()?.parse().ok()?;
3009            Some(at > frame)
3010        };
3011        assert!(text.lines().filter_map(above).any(|it| it), "{frame}: {text}");
3012    }
3013
3014    /// A `va_arg` is a branch on whether the argument it wants is still in the save area, and which
3015    /// of the two halves it walks is the type's answer.
3016    #[test]
3017    fn va_arg_branches_on_whether_the_argument_is_still_in_the_save_area() {
3018        let read = "__builtin_va_list ap; __builtin_va_start(ap, n);";
3019        let ints = format!("int f(int n, ...) {{ {read} return __builtin_va_arg(ap, int); }}\n");
3020        let text = asm(&ints);
3021
3022        // The last general purpose slot begins at forty, so an offset above it is an argument the
3023        // caller left in its own memory instead.
3024        assert!(text.contains("$40, "), "{text}");
3025        assert!(text.contains("	cmpl	"), "{text}");
3026        // The jump is the unsigned one, since an offset is a count of bytes. It is the opposite
3027        // of the comparison the front end wrote, because the block falls into the half taken when
3028        // the argument is still in the save area and jumps to the other one.
3029        assert!(text.contains("	ja	"), "{text}");
3030
3031        let arg = "__builtin_va_arg(ap, double)";
3032        let text = asm(&format!("double f(int n, ...) {{ {read} return {arg}; }}\n"));
3033        assert!(text.contains("$160, "), "the last vector slot: {text}");
3034    }
3035
3036    /// A structure assigned is a copy of a known size, and a copy of a known size is a run of
3037    /// moves rather than a call to a library this compiler has no way to reach yet.
3038    #[test]
3039    fn a_structure_assignment_is_a_move_for_each_word_of_it() {
3040        let decl = "struct pair { long a, b; };\n";
3041        let body = "struct pair p = *q; return p.a + p.b;";
3042        let text = asm(&format!("{decl}long f(struct pair *q) {{ {body} }}\n"));
3043
3044        assert!(!text.contains("memcpy"), "nothing calls the library: {text}");
3045        assert!(!text.contains("\tcall"), "{text}");
3046        // Sixteen bytes aligned to eight is two words, and each is a load and a store.
3047        assert!(text.matches("\tmovq\t").count() >= 4, "two words each way: {text}");
3048    }
3049
3050    /// A word is as wide as the object is aligned to and no wider, so a character array is copied
3051    /// a byte at a time and a structure of longs eight bytes at a time.
3052    #[test]
3053    fn how_wide_a_word_of_a_copy_is_follows_the_alignment() {
3054        let decl = "struct bytes { char a[8]; };\n";
3055        let body = "struct bytes p = *q; return p.a[0];";
3056        let text = asm(&format!("{decl}int f(struct bytes *q) {{ {body} }}\n"));
3057
3058        // Eight bytes aligned to one is eight words, and each is a load and a store.
3059        assert!(text.matches("\tmovb\t").count() >= 16, "a byte at a time: {text}");
3060    }
3061
3062    /// What an initialiser does not name is zero, which the front end writes as a fill and this
3063    /// writes as the byte spread across each word.
3064    #[test]
3065    fn the_part_of_an_initialiser_that_names_nothing_is_stored_as_zero() {
3066        let decl = "struct wide { long a, b, c; };\n";
3067        let text = asm(&format!("{decl}long f(void) {{ struct wide w = {{ 7 }}; return w.c; }}\n"));
3068
3069        assert!(!text.contains("memset"), "nothing calls the library: {text}");
3070        // Either spelling of a zero in a register, the move of one or the exclusive or of the
3071        // register with itself that `rucc_codegen::shorten` writes instead where it is free. The
3072        // exclusive or is the thirty-two bit one whatever the width of the word, since the half of
3073        // the register it does not write is cleared rather than left alone.
3074        assert!(text.contains("\tmovq\t$0, ") || text.contains("\txorl\t"), "the zero: {text}");
3075    }
3076
3077    /// A copy too large to be worth unrolling is a call to the runtime, which is the C library on
3078    /// a hosted target and `rucc-builtins` on a freestanding one.
3079    #[test]
3080    fn a_copy_too_large_to_unroll_calls_the_runtime() {
3081        let decl = "struct huge { char a[4096]; };\n";
3082        let mut opts = options();
3083        opts.emit = EmitKind::Asm;
3084        let source = format!("{decl}void f(struct huge *p, struct huge *q) {{ *p = *q; }}\n");
3085        let result = run(&opts, &source);
3086        assert!(!result.failed(), "{:?}", result.messages);
3087        let text = result.text();
3088        assert!(text.contains("call") && text.contains("memcpy"), "{text}");
3089        // The size in the register the convention passes the third argument in, which is what
3090        // says the call was built from the convention and not from the shape of the IR.
3091        assert!(text.contains("4096"), "the size travels: {text}");
3092    }
3093
3094    /// And an object passed by value with more words in it than that is the same call again,
3095    /// written in front of the call the object is an argument of.
3096    ///
3097    /// The copy is one the caller owes the callee, since the callee is free to write to what it
3098    /// was handed, so it is not an optimization that the size decides but the only way the call
3099    /// can be made at all.
3100    #[test]
3101    fn a_structure_too_large_to_unroll_is_copied_into_the_argument_area_by_the_runtime() {
3102        let decl = "struct huge { char a[4096]; };\nint take(struct huge);\n";
3103        let text = asm(&format!("{decl}int f(struct huge *p) {{ return take(*p); }}\n"));
3104
3105        let copy = text.find("call\tmemcpy").expect("the copy");
3106        let call = text.find("call\ttake").expect("the call");
3107        assert!(copy < call, "the copy comes first: {text}");
3108        // Into the bottom of the outgoing area, which is where the stack pointer already is, and
3109        // with the size in the register the convention passes the third argument in. The address
3110        // of the bottom of the frame is the stack pointer itself, so what carries it is the move
3111        // rather than the address computation the selector wrote. See `rucc_codegen::shorten`.
3112        assert!(text.contains("movq\t%rsp, %rdi"), "the destination: {text}");
3113        assert!(text.contains("$4096, %edx"), "the size: {text}");
3114    }
3115
3116    /// A frame that had to force its own alignment cannot say how far away the caller's stack
3117    /// pointer was, so it reaches back through the frame pointer instead.
3118    #[test]
3119    fn a_realigned_frame_reads_them_through_the_frame_pointer() {
3120        let six = "long a, long b, long c, long d, long e, long f";
3121        let body = "_Alignas(32) long wide[4]; wide[0] = g; return wide[0];";
3122        let text = asm(&format!("long f({six}, long g) {{ {body} }}\n"));
3123
3124        // The frame pointer is saved and pointed at where it was saved before the alignment is
3125        // forced, so the caller's arguments stay a constant distance from it: one word for the
3126        // saved frame pointer and one for the return address.
3127        assert!(text.contains("\tandq\t$-32, %rsp"), "{text}");
3128        assert!(text.contains("\tmovq\t16(%rbp), "), "{text}");
3129        assert!(!text.contains("\tmovq\t16(%rsp), "), "{text}");
3130    }
3131
3132    /// The object format decides the directives, and the target decides the object format.
3133    #[test]
3134    fn the_target_decides_how_the_assembly_is_spelled() {
3135        let mut opts = options();
3136        opts.emit = EmitKind::Asm;
3137        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
3138        let text = run(&opts, "int f(void) { return 0; }\n").text().to_owned();
3139        assert!(text.contains("__TEXT,__text"), "{text}");
3140        assert!(text.contains("\n_f:\n"), "{text}");
3141        assert!(!text.contains(".note.GNU-stack"), "{text}");
3142    }
3143
3144    /// The object file of `source`, insisting that it compiled cleanly.
3145    fn obj(source: &str) -> Vec<u8> {
3146        let mut opts = options();
3147        opts.emit = EmitKind::Object;
3148        let result = run(&opts, source);
3149        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3150        match result.artifact {
3151            Artifact::Object { bytes, .. } => bytes,
3152            other => panic!("expected an object, got {other:?}"),
3153        }
3154    }
3155
3156    /// `-c`, which is the last step of the three the back end can end with.
3157    ///
3158    /// What is in the file is checked in `rucc-object`, a field at a time. What is checked here is
3159    /// that a C file goes all the way to one, which is the whole compiler in one line and the
3160    /// thing that stops working when a layer between them changes its mind about something.
3161    #[test]
3162    fn a_function_goes_from_c_to_an_object_a_linker_would_take() {
3163        let bytes = obj("int add(int a, int b) { return a + b; }\n");
3164        assert_eq!(&bytes[..4], b"\x7fELF", "an object file starts by saying it is one");
3165        let text = asm("int add(int a, int b) { return a + b; }\n");
3166        assert!(
3167            text.contains("\taddl\t"),
3168            "and the listing of it is the same instructions:\n{text}"
3169        );
3170    }
3171
3172    /// A variable this file defines, which is what a reference to one has to resolve against.
3173    #[test]
3174    fn a_variable_goes_from_c_to_the_section_it_belongs_in() {
3175        let text = asm("int counter = 42;\nstatic int hidden;\nconst int fixed = 7;\n");
3176        assert!(text.contains("\t.data\n\t.globl\tcounter\n"), "{text}");
3177        assert!(text.contains("\ncounter:\n\t.long\t42\n"), "{text}");
3178        assert!(text.contains("\t.size\tcounter, .-counter\n"), "{text}");
3179        // A zeroed variable carries its size and none of its bytes, and a `static` one is not
3180        // announced to the linker at all, which is the whole of what `static` means here.
3181        assert!(text.contains("\t.bss\n\t.p2align\t2\n"), "{text}");
3182        assert!(text.contains("\nhidden:\n\t.space\t4\n"), "{text}");
3183        assert!(!text.contains(".globl\thidden"), "{text}");
3184        // Nothing writes through it, so it goes in a page the loader can map read only and every
3185        // process running the program can share.
3186        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
3187    }
3188
3189    /// A bit-field with a value in it, which is written as the bytes the value lands in.
3190    ///
3191    /// The interesting one is the field whose lowest byte is zero. The bytes a bit-field
3192    /// initializer makes are put together first and then taken back out as the run they make,
3193    /// and taking them out starts at the byte the field starts at, so a zero byte at the front
3194    /// used to end the object up in `.bss` with the rest of its value thrown away.
3195    #[test]
3196    fn a_bit_field_initializer_writes_every_byte_of_the_value_and_not_only_the_ones_that_are_set() {
3197        let text = asm("struct s { unsigned f : 20; } x = { 0x12300 };\n");
3198        assert!(text.contains("\t.data\n"), "there is something to write: {text}");
3199        assert!(text.contains("\nx:\n\t.ascii\t\"\\000#\\001\"\n"), "and it is the value: {text}");
3200
3201        // Two fields, the first of them zero, which is the same thing said with the zero byte
3202        // inside the run rather than at the front of it.
3203        let text = asm("struct s { unsigned a : 8; unsigned b : 8; } x = { 0, 3 };\n");
3204        assert!(text.contains("\nx:\n\t.ascii\t\"\\000\\003\"\n"), "{text}");
3205
3206        // Wider than an `int`, which is the same code and is worth saying because the value no
3207        // longer fits in the thirty two bits a bit-field used to be read at.
3208        let text = asm("struct s { unsigned long long f : 40; } x = { 0x100000 };\n");
3209        assert!(text.contains("\nx:\n\t.ascii\t\"\\000\\000\\020\"\n\t.space\t5\n"), "{text}");
3210
3211        // Nothing in it, which still costs no bytes in the file.
3212        let text = asm("struct s { unsigned f : 20; } x = { 0 };\n");
3213        assert!(text.contains("\t.bss\n"), "an object of zeroes is zeroes: {text}");
3214        assert!(text.contains("\nx:\n\t.space\t4\n"), "{text}");
3215    }
3216
3217    /// A string literal, which is a variable the program never named.
3218    #[test]
3219    fn a_string_literal_is_a_variable_with_a_name_no_program_could_write() {
3220        let text = asm("const char *f(void) { return \"hi\"; }\n");
3221        assert!(text.contains("\t.ascii\t\"hi\\000\"\n"), "{text}");
3222        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
3223        let label = text
3224            .lines()
3225            .find(|line| line.starts_with(".Lstr"))
3226            .unwrap_or_else(|| panic!("a label for the literal in\n{text}"));
3227        assert!(!text.contains(&format!(".globl\t{}", label.trim_end_matches(':'))), "{text}");
3228    }
3229
3230    /// A variable holding the address of another one, which is the only hole an image has in it.
3231    #[test]
3232    fn an_address_in_an_initializer_is_left_to_the_linker() {
3233        let source = "int counter;\nint *p = &counter;\n";
3234        let text = asm(source);
3235        assert!(text.contains("\np:\n\t.quad\tcounter\n"), "{text}");
3236        // And in the object it is eight zero bytes and a relocation, which is what the two paths
3237        // being one description is for.
3238        let bytes = obj(source);
3239        assert!(bytes.windows(8).any(|w| w == b"counter\0"), "the object has to name it");
3240    }
3241
3242    /// A const table of function pointers, which is the shape that made SQLite link with a warning.
3243    ///
3244    /// The table is const so nothing in the program writes it, but the addresses in it are not
3245    /// numbers a link knows, so the loader writes it once at startup. Putting it in `.rodata`
3246    /// leaves a relocation in a section that is never writable, and what the linker does about
3247    /// that is set `DT_TEXTREL` on the whole image and say so. `.data.rel.ro` is writable for
3248    /// exactly as long as the loader is writing it and read only afterwards, which is what the
3249    /// program asked for in the first place.
3250    #[test]
3251    fn a_constant_holding_an_address_goes_in_the_section_the_loader_may_write_once() {
3252        // Both names are `static` and both are defined here, so nothing else can be the one that
3253        // defines them and the linker may lay the table out in the first pages of the segment.
3254        let text = asm("static void a(void) {}\nstatic void b(void) {}\n\
3255             struct m { void (*x)(void); void (*y)(void); };\n\
3256             const struct m t = { a, b };\n");
3257        assert!(text.contains("\t.section\t.data.rel.ro.local,\"aw\",@progbits\n"), "{text}");
3258        assert!(text.contains("\nt:\n\t.quad\ta\n\t.quad\tb\n"), "{text}");
3259
3260        // One name this file only declares is enough to lose the `.local` half, because a name the
3261        // link resolves from somewhere else is one another object may turn out to define.
3262        let text =
3263            asm("void a(void);\nstruct m { void (*x)(void); };\nconst struct m t = { a };\n");
3264        assert!(text.contains("\t.section\t.data.rel.ro,\"aw\",@progbits\n"), "{text}");
3265
3266        // And a constant with no address in it stays exactly where it was.
3267        let text = asm("const int fixed = 7;\n");
3268        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
3269    }
3270
3271    /// A thread-local variable, which is the whole of one: the storage and the way to reach it.
3272    ///
3273    /// The two halves are in one test on purpose. Either one alone is worse than neither: a
3274    /// definition with no way to reach it is a variable nothing can read, and a reference with no
3275    /// definition behind it is the bug this pair was written to prevent, where a thread-local is
3276    /// read as though it were an ordinary global and every thread quietly shares one copy.
3277    #[test]
3278    fn a_thread_local_variable_is_storage_a_thread_gets_a_copy_of_and_an_offset_into_it() {
3279        let text = asm("_Thread_local int x = 1;\nint read(void) { return x; }\n");
3280        // The storage: the section the loader makes a copy of for every thread, and the symbol
3281        // type that makes a linker refuse an ordinary relocation aimed at it.
3282        assert!(text.contains("\t.section\t.tdata,\"awT\",@progbits\n"), "{text}");
3283        assert!(text.contains("\t.type\tx, @tls_object\n"), "{text}");
3284        // The way to reach it: how far into a thread's block it sits, out of the table, plus where
3285        // this thread's block is, out of the segment register.
3286        assert!(text.contains("x@GOTTPOFF(%rip)"), "{text}");
3287        assert!(text.contains("%fs:0"), "{text}");
3288    }
3289
3290    /// The second half of that on its own, which is what a program asks for when the number it
3291    /// wants is the thread rather than anything in it.
3292    ///
3293    /// rpmalloc writes this to find its per thread cache, and it is the whole of what stood
3294    /// between that library and a build. gcc 16 writes the same one instruction.
3295    #[test]
3296    fn the_address_of_this_thread_s_own_storage_is_read_out_of_the_segment_register() {
3297        let text = asm("void *here(void) { return __builtin_thread_pointer(); }\n");
3298        assert!(text.contains("movq\t%fs:0, "), "{text}");
3299        // No table slot and no addition, because there is no variable to find inside the block.
3300        assert!(!text.contains("GOTTPOFF"), "{text}");
3301    }
3302
3303    /// The four hints and the one thing that decides between them, which is the locality.
3304    ///
3305    /// A prefetch promises nothing, so what is checked here is the instruction rather than any
3306    /// effect: the program runs the same whichever of the four it gets, and the whole point of
3307    /// writing one is which. The four spellings are what gcc 16.2.0 writes for the same four
3308    /// programs, measured on x86-64 rather than read off a manual.
3309    ///
3310    /// The write hint is not one of them. `prefetchw` is not in the base instruction set and gcc
3311    /// writes it only when the command line says the part has it, so a prefetch for a write is the
3312    /// same instruction as a prefetch for a read, which is the fourth line here.
3313    #[test]
3314    fn a_prefetch_is_one_of_four_instructions_and_the_locality_is_what_picks() {
3315        for (locality, wanted) in
3316            [(0, "prefetchnta"), (1, "prefetcht2"), (2, "prefetcht1"), (3, "prefetcht0")]
3317        {
3318            let source =
3319                format!("void warm(void *p) {{ __builtin_prefetch(p, 0, {locality}); }}\n");
3320            let text = asm(&source);
3321            assert!(text.contains(&format!("\t{wanted}\t")), "locality {locality}: {text}");
3322        }
3323        // The one argument form, which means a read that wants all of the data afterwards.
3324        let text = asm("void warm(void *p) { __builtin_prefetch(p); }\n");
3325        assert!(text.contains("\tprefetcht0\t"), "{text}");
3326        // A prefetch for a write, which on a part nobody said has `prefetchw` is the same
3327        // instruction as the read above.
3328        let text = asm("void warm(void *p) { __builtin_prefetch(p, 1); }\n");
3329        assert!(text.contains("\tprefetcht0\t"), "{text}");
3330        assert!(!text.contains("prefetchw"), "{text}");
3331    }
3332
3333    /// The stop, which is the one instruction the machine is promised never to have a meaning for.
3334    ///
3335    /// What is checked is the instruction and not any effect, because the effect is a fault and a
3336    /// unit test has nowhere to take one. gcc 16.2.0 writes the same instruction for the same
3337    /// program, and it is not a call, which is the half that matters in a kernel and in a
3338    /// freestanding program: neither has an `abort` for a call to reach.
3339    ///
3340    /// The second half is the block going on after it. A statement written under a stop is
3341    /// compiled the way it would have been without one, so the addition is still there, and that
3342    /// is the front end declining to treat a stop as the end of a path.
3343    #[test]
3344    fn a_trap_is_the_instruction_the_machine_has_no_meaning_for() {
3345        let text = asm("void stop(void) { __builtin_trap(); }\n");
3346        assert!(text.contains("\tud2\n"), "{text}");
3347        assert!(!text.contains("\tcall"), "a stop is not a call to anything: {text}");
3348
3349        let text = asm("int stop(int a) { __builtin_trap(); return a + 1; }\n");
3350        assert!(text.contains("\tud2\n"), "{text}");
3351        assert!(text.contains("\taddl\t"), "the block goes on after a stop: {text}");
3352    }
3353
3354    /// The promise about the low bits of an address, whose value is the address.
3355    ///
3356    /// Nothing here reads an alignment fact about a value yet, so what the call leaves behind is
3357    /// its first argument and no instruction at all. The claim worth checking end to end is that
3358    /// the name is gone: a builtin nothing lowers reaches the assembler as a call to a name no
3359    /// object file defines, which is how this one used to fail to link out of glibc's string
3360    /// headers.
3361    ///
3362    /// The arguments behind the address are still evaluated, because gcc 16.2.0 evaluates them at
3363    /// every optimization level even though it has folded the call away. A constant has nothing to
3364    /// run and is dropped, and a call does, so the second half asks for the callee by name.
3365    #[test]
3366    fn assume_aligned_is_its_first_argument_and_keeps_the_rest() {
3367        let text = asm("void *aligned(char *p) { return __builtin_assume_aligned(p, 16); }\n");
3368        assert!(!text.contains("assume_aligned"), "{text}");
3369        assert!(!text.contains("\tcall"), "nothing is called for an alignment fact: {text}");
3370
3371        let source = "unsigned long width(void);\n\
3372                      void *aligned(char *p) { return __builtin_assume_aligned(p, width()); }\n";
3373        let text = asm(source);
3374        assert!(!text.contains("assume_aligned"), "{text}");
3375        assert!(text.contains("width"), "the argument that is not the answer still runs: {text}");
3376    }
3377
3378    /// Where a frame is, which on this machine is what the frame pointer holds.
3379    ///
3380    /// The first half is a function that would have kept no frame pointer at all, since it is a
3381    /// leaf with no locals, and keeps one because it asked where its frame is. The answer being
3382    /// `%rbp` rather than an offset off `%rsp` is the whole of the builtin at a depth of zero.
3383    ///
3384    /// The second half is the walk. Each link above zero is one load through the register the last
3385    /// one wrote, so a depth of two is two loads and a depth of three is three, which is what gcc
3386    /// 16.2.0 writes for the same programs at `-O2`.
3387    #[test]
3388    fn the_frame_address_is_the_frame_pointer_after_walking_that_many_links() {
3389        let text = asm("void *here(void) { return __builtin_frame_address(0); }\n");
3390        assert!(text.contains("pushq\t%rbp"), "a function that asks keeps a frame pointer: {text}");
3391        assert!(text.contains("movq\t%rbp, %rax"), "{text}");
3392        assert!(!text.contains("\tcall"), "a frame address is not a call to anything: {text}");
3393
3394        let walk = |depth: u32| {
3395            let source = format!("void *up(void) {{ return __builtin_frame_address({depth}); }}\n");
3396            asm(&source).matches("movq\t(%r").count()
3397        };
3398        assert_eq!(walk(1), 1, "one link is one load");
3399        assert_eq!(walk(3), 3, "three links are three loads");
3400    }
3401
3402    /// The address a frame returns to, which is one word above the frame the walk ended at.
3403    ///
3404    /// A word is eight bytes here and the `8(...)` is the whole claim: the call instruction pushed
3405    /// the return address and the prologue pushed the caller's frame pointer under it, so what the
3406    /// frame pointer points at is the link and what is above it is where control goes back to.
3407    /// gcc 16.2.0 writes `movq 8(%rbp), %rax` for the first of these, measured at `-O2`.
3408    ///
3409    /// The second half is the same walk the frame address does, with the load at the end of it
3410    /// reading one word further along rather than the register itself being the answer.
3411    #[test]
3412    fn the_return_address_is_one_word_above_the_frame_the_walk_ended_at() {
3413        let text = asm("void *back(void) { return __builtin_return_address(0); }\n");
3414        assert!(text.contains("pushq\t%rbp"), "a function that asks keeps a frame pointer: {text}");
3415        assert!(text.contains("movq\t8(%rbp), %rax"), "{text}");
3416        assert!(!text.contains("\tcall"), "a return address is not a call to anything: {text}");
3417
3418        let text = asm("void *back(void) { return __builtin_return_address(2); }\n");
3419        assert_eq!(text.matches("movq\t(%r").count(), 2, "two links are two loads: {text}");
3420        assert!(text.contains("movq\t8(%r"), "and the answer is above the last of them: {text}");
3421    }
3422
3423    /// A depth that is not a constant is refused, and so is one past the limit.
3424    ///
3425    /// The first is gcc's rule and not a convenience: what the call becomes is a walk that many
3426    /// links long, written out, so a number that is not known until the program runs has nothing
3427    /// to walk. gcc 16.2.0 says `invalid argument to '__builtin_return_address'` for the same
3428    /// program.
3429    ///
3430    /// The second is where this and gcc part company. gcc writes the walk however long it is, and
3431    /// this refuses a depth no program has a use for rather than filling an object file with loads
3432    /// that fault part way up.
3433    #[test]
3434    fn a_depth_that_is_not_a_small_constant_is_refused() {
3435        let mut opts = options();
3436        opts.emit = EmitKind::Ir;
3437        for source in [
3438            "void *up(int n) { return __builtin_return_address(n); }\n",
3439            "void *up(void) { return __builtin_frame_address(1000); }\n",
3440        ] {
3441            let messages = run(&opts, source).messages;
3442            let named = messages.iter().any(|m| m.contains("E0705"));
3443            assert!(named, "expected a refusal in {messages:?}");
3444        }
3445    }
3446
3447    /// Bytes off the frame, which is the stack pointer moving down and the answer being where it
3448    /// moved to.
3449    ///
3450    /// The rounding is the alignment: the size is taken up to the next sixteen before it is
3451    /// subtracted, so the pointer suits anything the program puts behind it. gcc 16.2.0 rounds the
3452    /// same way at `-O0` and spends a division doing it, which is the one place the two differ and
3453    /// is about how the rounding is written rather than about what it answers.
3454    ///
3455    /// There is no call anywhere in either program. An alloca that had reached the linker would
3456    /// have found the C library's, which is a real function with a real frame and is not what a
3457    /// program writing the builtin asked for.
3458    #[test]
3459    fn an_alloca_takes_the_bytes_off_the_stack_pointer_and_answers_where_they_are() {
3460        let text =
3461            asm("void use(void *p); void f(unsigned long n) { use(__builtin_alloca(n)); }\n");
3462        assert!(text.contains("andq\t$-16"), "the size is rounded up to sixteen: {text}");
3463        assert!(text.contains("subq\t%rdi, %rsp"), "and taken off the stack pointer: {text}");
3464        assert_eq!(text.matches("\tcall").count(), 1, "the only call is the one written: {text}");
3465
3466        // The plain name, which a program that declares it the way the C library does means the
3467        // same thing by. `gcc.c-torture/execute/20010122-1.c` is exactly this program.
3468        let plain = concat!(
3469            "extern void *alloca(__SIZE_TYPE__);\n",
3470            "void use(void *p);\n",
3471            "void f(unsigned long n) { use(alloca(n)); }\n",
3472        );
3473        let text = asm(plain);
3474        assert!(text.contains("subq\t%rdi, %rsp"), "the plain name is the same bytes: {text}");
3475        assert_eq!(text.matches("\tcall").count(), 1, "and is not a call either: {text}");
3476
3477        // And a program that means something of its own by the name keeps it, which is what the
3478        // declaration is looked at for.
3479        let own = concat!(
3480            "static void *alloca(unsigned long n) { return 0; }\n",
3481            "void *f(unsigned long n) { return alloca(n); }\n",
3482        );
3483        assert!(asm(own).contains("\tcall"), "a name the program took back is a call");
3484    }
3485
3486    /// The bytes an alloca took live until the function returns and not until the end of the block
3487    /// the call was written in.
3488    ///
3489    /// That is what makes it different from a variable length array, and the way it is kept is that
3490    /// every scope open where the call was written stops giving the stack back. The second program
3491    /// is the mixed case: an array in the outer block and an alloca in the inner one, where the
3492    /// inner block gives nothing back either even though an array is in scope that ordinarily
3493    /// would. gcc 16.2.0 at `-O0` writes no restore at the end of either block, measured rather
3494    /// than read off the manual.
3495    #[test]
3496    fn the_bytes_an_alloca_took_are_still_there_at_the_end_of_the_block_that_took_them() {
3497        let inner = "{ use(__builtin_alloca(n)); }";
3498        for body in [inner.to_owned(), format!("int a[n]; {inner} use(a);")] {
3499            let source = format!("void use(void *p);\nvoid f(unsigned long n) {{ {body} }}\n");
3500            let text = asm(&source);
3501            // Every instruction that writes the stack pointer, which in a function that gives
3502            // nothing back is the alloca taking bytes and the epilogue putting the frame pointer
3503            // there. A restore would be a third kind, a move out of a register the save wrote.
3504            for line in text.lines().filter(|line| line.trim_end().ends_with(", %rsp")) {
3505                let taking = line.contains("subq");
3506                let leaving = line.contains("%rbp");
3507                assert!(taking || leaving, "nothing puts the stack back: {line} in {text}");
3508            }
3509        }
3510    }
3511
3512    /// Not a rewording of the check above: what the two paths agree about is the point.
3513    #[test]
3514    fn the_object_and_the_listing_are_two_spellings_of_one_compilation() {
3515        // A call, because it is the one thing whose spelling in the two differs completely: the
3516        // listing writes a name and the object writes four zero bytes and a relocation asking the
3517        // linker for the same name. If either path had lost the callee, one of these would fail.
3518        let source = "int callee(void); int g(void) { return callee(); }\n";
3519        let bytes = obj(source);
3520        assert!(
3521            bytes.windows(7).any(|w| w == b"callee\0"),
3522            "the object has to name the callee for the linker to find it"
3523        );
3524        let text = asm(source);
3525        assert!(text.contains("\tcall\tcallee\n"), "{text}");
3526    }
3527
3528    /// What a file of a link contributes is an object, and the default emit is a link.
3529    ///
3530    /// This is here because getting it wrong is silent in the worst way: an empty file is a valid
3531    /// empty linker script, so a link fed one gets as far as reporting every symbol of the file as
3532    /// undefined and says nothing about the compilation that produced nothing.
3533    #[test]
3534    fn compiling_for_an_executable_produces_an_object_and_not_a_dump() {
3535        let mut opts = options();
3536        // What a command line with no `-c` and no `-S` on it asks for.
3537        opts.emit = EmitKind::Executable;
3538        let result = run(&opts, "int main(void) { return 0; }\n");
3539        assert_eq!(result.messages, Vec::<String>::new());
3540        match result.artifact {
3541            Artifact::Object { bytes, .. } => assert_eq!(&bytes[..4], b"\x7fELF"),
3542            other => panic!("expected an object, got {other:?}"),
3543        }
3544    }
3545
3546    /// A target with a back end but no object writer says so rather than writing the wrong file.
3547    #[test]
3548    fn a_platform_with_no_object_writer_is_said_so_rather_than_written_as_elf() {
3549        let mut opts = options();
3550        opts.emit = EmitKind::Object;
3551        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
3552        let result = run(&opts, "int f(void) { return 0; }\n");
3553        assert!(result.failed(), "an object nobody can read is worse than a message");
3554        assert!(
3555            result.messages.iter().any(|m| m.contains("no object writer")),
3556            "{:?}",
3557            result.messages
3558        );
3559    }
3560
3561    /// The IR of `source`, insisting that it compiled cleanly.
3562    fn ir(source: &str) -> String {
3563        let mut opts = options();
3564        opts.emit = EmitKind::Ir;
3565        let result = run(&opts, source);
3566        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3567        result.text().to_owned()
3568    }
3569
3570    /// What was said about `source`, insisting that something was.
3571    fn errors(source: &str) -> Vec<String> {
3572        let mut opts = options();
3573        opts.emit = EmitKind::Ir;
3574        let result = run(&opts, source);
3575        assert!(result.failed(), "expected this to be refused:\n{source}");
3576        result.messages
3577    }
3578
3579    /// The body of the one function in `source`, which is what most of these are about.
3580    fn body(source: &str) -> String {
3581        let text = ir(source);
3582        let (_, rest) = text.split_once("{\n").expect("a function definition");
3583        let (body, _) = rest.rsplit_once("}\n").expect("a function definition");
3584        body.to_owned()
3585    }
3586
3587    /// What `-fgnu89-inline` is for, seen at the only place it shows: whether a body reached the
3588    /// module or only a declaration did.
3589    ///
3590    /// The C99 reading is the one an inline definition is written for and is not being changed
3591    /// here. What the flag is for is a program written before C99 swapped the two, which relies on
3592    /// `inline` alone leaving something behind for another unit to call, and there are twelve of
3593    /// those in the GCC torture suite alone.
3594    #[test]
3595    fn gnu89_inline_is_what_decides_whether_a_bare_inline_definition_reaches_the_module() {
3596        let source = "inline int f(int x) { return x + 1; }\n";
3597        let with = |flag: bool| {
3598            let mut opts = options();
3599            opts.emit = EmitKind::Ir;
3600            opts.gnu89_inline = flag;
3601            let result = run(&opts, source);
3602            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
3603            result.text().to_owned()
3604        };
3605
3606        // Under C's reading the module holds the declaration and the calls in this unit go to
3607        // whatever definition another unit has, which is C 6.7.4p7 and is what gcc does too.
3608        assert!(!with(false).contains("block0"), "no body: {}", with(false));
3609
3610        // Under GNU's it is an ordinary external definition, so the body is there and the symbol
3611        // is one the linker can resolve against.
3612        assert!(with(true).contains("block0"), "a body: {}", with(true));
3613    }
3614
3615    /// Every shape that reads or writes through a C type names that type.
3616    ///
3617    /// The tree itself is `rucc_lower::aliasing`'s and is tested there. What this is about is that
3618    /// the walk reaches it from every shape a program actually writes, since a node on the scalar
3619    /// load and nothing on the member load would be a layer that answers for a third of the
3620    /// accesses in a program and is not worth having.
3621    #[test]
3622    fn an_access_through_a_type_names_the_type_it_went_through() {
3623        let source = "\
3624struct s { int a; float b; };\n\
3625union u { int i; float f; };\n\
3626int scalar(int *p) { return *p; }\n\
3627float member(struct s *p) { p->a = 1; return p->b; }\n\
3628int element(int *a, long i) { return a[i]; }\n\
3629float through_a_union(union u *p) { p->i = 1; return p->f; }\n";
3630        let text = ir(source);
3631        assert!(text.contains(r#"!0 = tbaa "char""#), "the root: {text}");
3632        assert!(text.contains(r#"tbaa "int", parent !0"#), "int under it: {text}");
3633        assert!(text.contains(r#"tbaa "float", parent !0"#), "float under it: {text}");
3634        // One per access, and a function whose accesses all go through one type says so once per
3635        // access rather than once per function.
3636        let named = text.lines().filter(|line| line.contains(", tbaa !")).count();
3637        assert_eq!(named, 6, "six accesses: {text}");
3638    }
3639
3640    /// `-fno-strict-aliasing` is the front end leaving the name off.
3641    ///
3642    /// Nothing asks the alias analysis anything yet, so no program compiles differently for having
3643    /// passed this today. What this test is for is the day one does: the flag has to be the
3644    /// absence of the names rather than a condition somewhere downstream, since that is the only
3645    /// version of it that a pass added later cannot forget about.
3646    #[test]
3647    fn turning_strict_aliasing_off_leaves_the_type_off_every_access() {
3648        let source = "int punned(float *f, int *i) { *i = 1; *f = 2.0f; return *i; }\n";
3649        let mut opts = options();
3650        opts.emit = EmitKind::Ir;
3651        opts.strict_aliasing = false;
3652        let result = run(&opts, source);
3653        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
3654        let text = result.text().to_owned();
3655        assert!(!text.contains("tbaa"), "not even the root: {text}");
3656    }
3657
3658    /// `return;` from a function that promised a value, which only C89 lets through and which
3659    /// therefore only reaches the IR builder under that dialect.
3660    ///
3661    /// Zero goes back. The alternatives are worse: an empty return list builds a `ret` the
3662    /// verifier refuses, which is what a torture case found, and `unreachable` would be a claim
3663    /// that the branch reaching this never runs, which is a claim about the program rather than
3664    /// about the value and lets the optimizer delete the path that led here.
3665    #[test]
3666    fn a_bare_return_from_a_function_that_promised_a_value_gives_back_a_zero() {
3667        let mut opts = options();
3668        opts.emit = EmitKind::Ir;
3669        opts.std = Std::C89;
3670        let compiled = |source: &str| {
3671            let result = run(&opts, source);
3672            assert_eq!(result.messages, Vec::<String>::new(), "C89 has nothing to say about this");
3673            result.text().to_owned()
3674        };
3675
3676        let text = compiled("int f(int x) { if (x) return; return 3; }\n");
3677        assert!(text.contains("iconst.i32 0\n    return"), "zero goes back: {text}");
3678        assert!(!text.contains("unreachable"), "the branch that reached it is kept: {text}");
3679
3680        // A floating point return needs the constant of its own kind rather than an integer one.
3681        let text = compiled("double f(int x) { if (x) return; return 1.0; }\n");
3682        assert!(text.contains("fconst.f64 0x0\n    return"), "a float zero goes back: {text}");
3683    }
3684
3685    /// What C89 6.3.2.2 declares for a call to a name nothing declared, seen in the IR rather than
3686    /// in what was said about it.
3687    ///
3688    /// `extern int f();`, so the call gives back an `int` and its arguments are promoted rather
3689    /// than converted to parameters there are none of. The declaration lasts for the file, which
3690    /// is what makes a second call to the same name ordinary and is why gcc says this once per
3691    /// file rather than once per call.
3692    #[test]
3693    fn a_call_to_a_name_nothing_declared_declares_it_as_c89_said_to() {
3694        let mut opts = options();
3695        opts.emit = EmitKind::Ir;
3696        opts.std = Std::C89;
3697        let compiled = |source: &str| {
3698            let result = run(&opts, source);
3699            assert_eq!(result.messages, Vec::<String>::new(), "C89 has nothing to say about this");
3700            result.text().to_owned()
3701        };
3702
3703        // An `int` back, which is the whole of what the implicit declaration says.
3704        let text = compiled("int f(void) { return g(); }\n");
3705        assert!(text.contains("call @g"), "the call is to the name that was written: {text}");
3706        assert!(text.contains("i32"), "and it gives back an int: {text}");
3707
3708        // No prototype, so a `char` argument arrives promoted to `int` the way an argument to a
3709        // function whose parameters are unspecified does.
3710        let text = compiled("int f(char c) { return g(c); }\n");
3711        assert!(text.contains("sext.i32"), "the argument is promoted: {text}");
3712
3713        // A name written as a value rather than called is still undeclared, since the rule is
3714        // about a call and nothing else.
3715        let mut opts = options();
3716        opts.std = Std::C89;
3717        let said = run(&opts, "int f(void) { return h; }\n").messages.join("\n");
3718        assert!(said.contains("'h' undeclared"), "not a call, so not declared: {said}");
3719    }
3720
3721    /// A file that calls a name above the definition of it, which is the shape the implicit
3722    /// declaration has to survive rather than swallow.
3723    ///
3724    /// The definition merges into the declaration the call already made rather than making a
3725    /// second one, so a declaration the tree does not carry at the top level takes the definition
3726    /// down with it: the body is attached to a node nothing walks and no function comes out.
3727    /// Nothing about the call itself looks wrong when that happens, and the program gets to the
3728    /// linker before anyone finds out, which is where `execute/cmpsi-1.c` in the torture suite
3729    /// found it, as an undefined reference to a name defined eleven lines further down.
3730    #[test]
3731    fn a_name_called_before_it_is_defined_still_gets_its_definition() {
3732        let mut opts = options();
3733        opts.emit = EmitKind::Ir;
3734        opts.std = Std::C89;
3735        let text = run(&opts, "int f(void) { return dummy(); }\ndummy () { return 7; }\n")
3736            .text()
3737            .to_owned();
3738        assert!(text.contains("func @f()"), "the caller is there: {text}");
3739        assert!(text.contains("func @dummy"), "and so is what it calls: {text}");
3740        assert!(text.contains("iconst.i32 7"), "with the body it was given: {text}");
3741    }
3742
3743    /// An old style definition whose parameter is narrower than what a call passes it.
3744    ///
3745    /// There is no prototype for a call to convert its argument to, so the argument is promoted
3746    /// and an `int` arrives for a parameter the body reads as an `unsigned char`. The entry block
3747    /// is where the two meet, and gcc writes the same pair of instructions there: store the low
3748    /// byte, read it back widened. `execute/950605-1.c` in the torture suite calls `f(-1)` and
3749    /// checks the parameter against `0xFF`, which is the difference between converting and not.
3750    #[test]
3751    fn an_old_style_parameter_is_converted_from_what_the_call_promoted_it_to() {
3752        let mut opts = options();
3753        opts.emit = EmitKind::Ir;
3754        opts.std = Std::C89;
3755        let compiled = |source: &str| run(&opts, source).text().to_owned();
3756
3757        let text = compiled("f (c) unsigned char c; { return c; }\n");
3758        assert!(text.contains("func @f(i32"), "an int arrives: {text}");
3759        assert!(text.contains("trunc.i8"), "and is cut down to what was declared: {text}");
3760        assert!(text.contains("zext.i32"), "then read back unsigned: {text}");
3761
3762        // A `short` is the same shape and signed, so it comes back the other way.
3763        let text = compiled("f (s) short s; { return s; }\n");
3764        assert!(text.contains("trunc.i16"), "cut down: {text}");
3765        assert!(text.contains("sext.i32"), "and read back signed: {text}");
3766
3767        // A `float` parameter is promoted to `double`, and without the conversion the multiply
3768        // below has one f64 operand and one f32, which the verifier refuses as invalid IR.
3769        let text = compiled("f (x) float x; { return x * 2; }\n");
3770        assert!(text.contains("func @f(f64"), "a double arrives: {text}");
3771        assert!(text.contains("fptrunc.f32"), "and is narrowed to the float: {text}");
3772
3773        // A parameter a prototype named arrives as itself and nothing is converted, which is the
3774        // case this must not have changed.
3775        let text = compiled("int f(unsigned char c) { return c; }\n");
3776        assert!(text.contains("func @f(i8)"), "the declared type arrives: {text}");
3777        assert!(!text.contains("trunc"), "so there is nothing to cut down: {text}");
3778    }
3779
3780    /// The six rules gcc 14 turned from a warning into an error, and the three answers each one
3781    /// gets depending on the dialect and on `-fpermissive`.
3782    ///
3783    /// The table is a measurement rather than a reading of the release notes. Six files, one per
3784    /// rule, put through gcc 16.2.0 on x86-64 Linux under each of the four command lines below
3785    /// with no `-W` flags on any of them, and what came back is what is written here. The three
3786    /// rules that say nothing under C89 are the three C89 did not have, and the three that warn
3787    /// there were constraint violations then as well.
3788    #[test]
3789    fn the_rules_gcc_promoted_are_decided_by_the_dialect_and_by_fpermissive() {
3790        // `-std=gnu89`, `-std=gnu17`, `-std=gnu17 -fpermissive`, and `-std=gnu23`.
3791        let modes = [(Std::C89, false), (Std::C17, false), (Std::C17, true), (Std::C23, false)];
3792        let cases = [
3793            ("static counted;\n", ["", "error", "warning", "error"]),
3794            ("int f(void) { return g(); }\n", ["", "error", "warning", "error"]),
3795            ("int f(x) { return x; }\n", ["", "error", "warning", "error"]),
3796            ("int *p;\nvoid h(void) { p = 1; }\n", ["warning", "error", "warning", "error"]),
3797            (
3798                "char *q;\nint *r;\nvoid k(void) { r = q; }\n",
3799                ["warning", "error", "warning", "error"],
3800            ),
3801            ("int f(void) { return; }\n", ["", "error", "warning", "error"]),
3802            ("void g(void) { return 1; }\n", ["warning", "error", "warning", "error"]),
3803        ];
3804
3805        for (source, wanted) in cases {
3806            for (&(std, permissive), wanted) in modes.iter().zip(wanted) {
3807                let mut opts = options();
3808                opts.std = std;
3809                opts.permissive = permissive;
3810                let said = run(&opts, source).messages.join("\n");
3811                let severity = if said.contains(": error: ") {
3812                    "error"
3813                } else if said.contains(": warning: ") {
3814                    "warning"
3815                } else {
3816                    ""
3817                };
3818                let how = if permissive { " -fpermissive" } else { "" };
3819                assert_eq!(
3820                    severity,
3821                    wanted,
3822                    "under -std={}{how}, {source} was answered with `{said}`",
3823                    std.as_str()
3824                );
3825                if wanted.is_empty() {
3826                    assert!(said.is_empty(), "nothing to say, but said `{said}`");
3827                }
3828            }
3829        }
3830    }
3831
3832    /// A first argument that is not a list, which the four variadic operators answer in two ways.
3833    ///
3834    /// gcc has `va_arg` as an operator, since it takes a type name and no function can, and the
3835    /// other three as builtin functions taking the address of a list. The difference is not a
3836    /// naming one: the operator's complaint is its own and is an error under every dialect, and
3837    /// the three functions go through the ordinary rule about an argument of the wrong type,
3838    /// which is one of the rules the table above is about. The same four command lines through
3839    /// gcc 16.2.0 on x86-64 Linux is where these came from.
3840    #[test]
3841    fn the_three_variadic_builtins_answer_a_bad_list_the_way_a_call_answers_a_bad_argument() {
3842        let modes = [(Std::C89, false), (Std::C17, false), (Std::C17, true), (Std::C23, false)];
3843        let cases = [
3844            (
3845                "int f(int n, ...) { char *p; return __builtin_va_arg(p, int); }\n",
3846                "first argument to 'va_arg' not of type 'va_list'",
3847                ["error", "error", "error", "error"],
3848            ),
3849            (
3850                "void f(int n, ...) { char *p; __builtin_va_start(p, n); }\n",
3851                "passing argument 1 of '__builtin_va_start' from incompatible pointer type",
3852                ["warning", "error", "warning", "error"],
3853            ),
3854            (
3855                "void f(int n, ...) { int x; __builtin_va_end(x); }\n",
3856                "passing argument 1 of '__builtin_va_end' makes pointer from integer without a \
3857                 cast",
3858                ["warning", "error", "warning", "error"],
3859            ),
3860            (
3861                "void f(int n, ...) { __builtin_va_list a; char *p; __builtin_va_copy(a, p); }\n",
3862                "passing argument 2 of '__builtin_va_copy' from incompatible pointer type",
3863                ["warning", "error", "warning", "error"],
3864            ),
3865        ];
3866
3867        for (source, message, wanted) in cases {
3868            for (&(std, permissive), wanted) in modes.iter().zip(wanted) {
3869                let mut opts = options();
3870                opts.std = std;
3871                opts.permissive = permissive;
3872                let said = run(&opts, source).messages.join("\n");
3873                let how = if permissive { " -fpermissive" } else { "" };
3874                assert!(
3875                    said.contains(&format!(": {wanted}: {message}")),
3876                    "under -std={}{how}, {source} was answered with `{said}`",
3877                    std.as_str()
3878                );
3879            }
3880        }
3881    }
3882
3883    /// The IR of `source` at one safety tier, insisting that it compiled cleanly.
3884    fn safe_ir(tier: rucc_session::Safety, source: &str) -> String {
3885        let mut opts = options();
3886        opts.emit = EmitKind::Ir;
3887        opts.safety = tier;
3888        let result = run(&opts, source);
3889        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3890        result.text().to_owned()
3891    }
3892
3893    const READS_THROUGH_A_POINTER: &str = "int read(int *p) { return p[1]; }\n";
3894
3895    /// The IR for a source built with a tier and a padding mode.
3896    fn padded_ir(padding: Padding, source: &str) -> String {
3897        let mut opts = options();
3898        opts.emit = EmitKind::Ir;
3899        opts.safety = rucc_session::Safety::Detect;
3900        opts.padding = padding;
3901        let result = run(&opts, source);
3902        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3903        result.text().to_owned()
3904    }
3905
3906    const FILLS_A_RECORD_A_MEMBER_AT_A_TIME: &str = "struct padded { char tag; int value; };\n\
3907         void fill(struct padded *p) { p->tag = 1; p->value = 2; }\n";
3908
3909    #[test]
3910    fn a_record_filled_a_member_at_a_time_comes_out_whole_when_padding_does_not_participate() {
3911        // Section 9.3 of document 09, and the reason the default is the one it gives library code.
3912        // Four bytes from the `char` and four from the `int` is the whole of an eight byte record,
3913        // so the `memcmp` or the hash or the `write` that reads it back is not refused.
3914        let text = padded_ir(Padding::Ignored, FILLS_A_RECORD_A_MEMBER_AT_A_TIME);
3915        assert_eq!(text.matches("owns 4").count(), 2, "{text}");
3916    }
3917
3918    #[test]
3919    fn a_store_says_only_what_it_wrote_when_padding_does_participate() {
3920        // The kernel profile's default, which is section 9.3's actual rule: the padding stays
3921        // unwritten and the read of the record that would leak it is the one that reports.
3922        let text = padded_ir(Padding::Tracked, FILLS_A_RECORD_A_MEMBER_AT_A_TIME);
3923        assert!(!text.contains("owns"), "{text}");
3924    }
3925
3926    #[test]
3927    fn a_member_of_a_union_owns_nothing_after_it() {
3928        // The bytes after a short member of a union belong to a longer member rather than to
3929        // padding, and saying a store through the short one wrote them would be saying the longer
3930        // one holds a value nobody put there.
3931        let text = padded_ir(
3932            Padding::Ignored,
3933            "union u { char tag; long wide; };\nvoid fill(union u *p) { p->tag = 1; }\n",
3934        );
3935        assert!(!text.contains("owns"), "{text}");
3936    }
3937
3938    #[test]
3939    fn an_inner_records_trailing_padding_reaches_the_outer_records() {
3940        // The composition. `in` owns four bytes of `outer` because `x` starts there, and `c` is
3941        // the last member of `in`, so what it owns is what `in` owns rather than its own one byte.
3942        // Without that the three bytes between them would stay unwritten and a read of the whole
3943        // thing would report.
3944        let text = padded_ir(
3945            Padding::Ignored,
3946            "struct inner { char c; };\n\
3947             struct outer { struct inner in; int x; };\n\
3948             void fill(struct outer *p) { p->in.c = 1; p->x = 2; }\n",
3949        );
3950        assert_eq!(text.matches("owns 4").count(), 2, "{text}");
3951    }
3952
3953    #[test]
3954    fn a_build_that_did_not_ask_for_the_monitor_is_compiled_the_way_it_always_was() {
3955        // This is the load bearing test of the whole flag. The monitor is being built in the open
3956        // and every build in the world is compiled by this compiler with the flag absent, so a
3957        // check that leaked into that path would be a regression for everybody.
3958        let text = ir(READS_THROUGH_A_POINTER);
3959        assert!(!text.contains("check_"), "{text}");
3960        assert!(!text.contains("cap_of"), "{text}");
3961    }
3962
3963    #[test]
3964    fn asking_for_a_tier_puts_the_checks_in_before_the_optimizer_sees_them() {
3965        let text = safe_ir(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
3966        assert!(text.contains("cap_of"), "{text}");
3967        assert!(text.contains("check_bounds"), "{text}");
3968        assert!(text.contains("check_live"), "{text}");
3969        // The subscript is address arithmetic, so J2 applies to it as well as J1.
3970        assert!(text.contains("check_deriv"), "{text}");
3971        // And the read names a type, so it asks the type plane about the bytes as well.
3972        assert!(text.contains("check_type"), "{text}");
3973    }
3974
3975    #[test]
3976    fn the_three_tiers_that_are_not_off_all_check_the_same_accesses_so_far() {
3977        // What separates them is the reporter and the boundary, which are milestones S2 and S3.
3978        // Pinning it here means the day they stop agreeing, this test says so rather than the
3979        // difference going unnoticed.
3980        let detect = safe_ir(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
3981        for tier in [rucc_session::Safety::Enforce, rucc_session::Safety::Kernel] {
3982            assert_eq!(safe_ir(tier, READS_THROUGH_A_POINTER), detect, "{tier}");
3983        }
3984    }
3985
3986    /// The safety summary of `source` at one tier, insisting that it compiled cleanly.
3987    fn summary(tier: rucc_session::Safety, source: &str) -> String {
3988        let mut opts = options();
3989        opts.emit = EmitKind::SafetySummary;
3990        opts.safety = tier;
3991        let result = run(&opts, source);
3992        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3993        result.text().to_owned()
3994    }
3995
3996    #[test]
3997    fn the_summary_counts_the_checks_that_went_in_and_the_ones_still_standing() {
3998        let text = summary(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
3999        assert!(text.contains("\"tier\": \"detect\""), "{text}");
4000        // One load, so one of each of the two access checks, and the subscript is a derivation.
4001        assert!(
4002            text.contains("\"bounds\": { \"emitted\": 1, \"remaining\": 1, \"discharged\": 0 }"),
4003            "{text}"
4004        );
4005        assert!(
4006            text.contains(
4007                "\"derivation\": { \"emitted\": 1, \"remaining\": 1, \"discharged\": 0 }"
4008            ),
4009            "{text}"
4010        );
4011    }
4012
4013    #[test]
4014    fn a_build_without_the_monitor_summarises_as_a_build_with_no_checks_in_it() {
4015        // Which is the honest summary rather than an error. A build system that emits a summary
4016        // for every unit should get one for the units nobody asked to instrument too, and the
4017        // zeroes are what say that the guarantee over that file is nothing at all.
4018        let text = summary(rucc_session::Safety::Off, READS_THROUGH_A_POINTER);
4019        assert!(text.contains("\"tier\": \"off\""), "{text}");
4020        assert!(
4021            text.contains("\"bounds\": { \"emitted\": 0, \"remaining\": 0, \"discharged\": 0 }"),
4022            "{text}"
4023        );
4024    }
4025
4026    #[test]
4027    fn a_call_the_boundary_models_is_counted_apart_from_one_it_does_not() {
4028        let text = summary(
4029            rucc_session::Safety::Detect,
4030            "void *memcpy(void *, const void *, unsigned long);\n\
4031             int puts(const char *);\n\
4032             void f(char *d, char *s) { memcpy(d, s, 4); puts(d); }\n",
4033        );
4034        assert!(text.contains("\"interposed\": 1"), "{text}");
4035        assert!(text.contains("\"puts\""), "{text}");
4036        // The wrapper it was pointed at is ours, so it is not on the list of things this build
4037        // failed to model. Counting it there would make instrumenting a file look worse than
4038        // leaving it alone.
4039        assert!(!text.contains("__rucc_wrap_memcpy\""), "{text}");
4040    }
4041
4042    #[test]
4043    fn an_address_taken_of_a_library_function_is_counted_the_way_a_call_to_one_is() {
4044        // The shape SQLite's syscall table has, cut down to two rows. `memcpy` has a wrapper so the
4045        // table holds the wrapper's address and the build modelled it; `puts` has none, so what the
4046        // table holds is the real function and the build did not, and section 10.1 says the one it
4047        // did not is named rather than passed over.
4048        let text = summary(
4049            rucc_session::Safety::Detect,
4050            "void *memcpy(void *, const void *, unsigned long);\n\
4051             int puts(const char *);\n\
4052             void *table[2] = { (void *)memcpy, (void *)puts };\n\
4053             void *f(int i) { return table[i]; }\n",
4054        );
4055        assert!(text.contains("\"interposed\": 1"), "{text}");
4056        assert!(text.contains("\"puts\""), "{text}");
4057        assert!(!text.contains("\"memcpy\""), "{text}");
4058    }
4059
4060    #[test]
4061    fn the_two_directions_a_pointer_crosses_the_boundary_are_counted_apart() {
4062        // `f` is a name the linker can bind to and takes a pointer, so a pointer arrives there.
4063        // `notes_open` is a library this build did not instrument, so a pointer comes back from
4064        // it. Both are crossings and neither is the other, which is why there are two numbers.
4065        let text = summary(
4066            rucc_session::Safety::Detect,
4067            "void *notes_open(void);\n\
4068             char *f(char *p) { char *q = notes_open(); return q ? q : p; }\n",
4069        );
4070        assert!(text.contains("\"crossings\": { \"entered\": 1, \"returned\": 1 }"), "{text}");
4071        assert!(text.contains("\"notes_open\""), "{text}");
4072    }
4073
4074    #[test]
4075    fn a_static_function_nobody_takes_the_address_of_is_not_a_crossing() {
4076        // Nothing outside the file can reach it, so a witness on its parameters would be counting
4077        // a crossing that does not happen.
4078        let text = summary(
4079            rucc_session::Safety::Detect,
4080            "static int len(const char *p) { return p ? 1 : 0; }\n\
4081             int f(void) { return len(\"x\"); }\n",
4082        );
4083        assert!(text.contains("\"crossings\": { \"entered\": 0, \"returned\": 0 }"), "{text}");
4084    }
4085
4086    /// The granule report for `source`, insisting that it compiled cleanly.
4087    fn granules(source: &str) -> String {
4088        let mut opts = options();
4089        opts.emit = EmitKind::TypeGranules;
4090        let result = run(&opts, source);
4091        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4092        result.text().to_owned()
4093    }
4094
4095    #[test]
4096    fn the_granule_report_names_every_record_and_both_keyings() {
4097        let text = granules(
4098            "struct hot { char *p; int a; int b; };\n\
4099             int f(struct hot *h) { return h->a; }\n",
4100        );
4101        assert!(text.contains("struct hot"), "{text}");
4102        // Both keyings are reported because which types count as one is a decision the design
4103        // has not made yet, and a report that picked one would be hiding the cost of the other.
4104        assert!(text.contains("every type distinct"), "{text}");
4105        assert!(text.contains("every pointer one type"), "{text}");
4106        assert!(text.contains("budget"), "{text}");
4107    }
4108
4109    #[test]
4110    fn a_record_nothing_uses_is_still_measured() {
4111        // The measurement is about what a program declares, not about what it runs, so a type
4112        // that is only ever declared still costs the plane whatever its layout costs.
4113        let text = granules("struct unused { long a; double b; };\nint f(void) { return 0; }\n");
4114        assert!(text.contains("struct unused"), "{text}");
4115    }
4116
4117    #[test]
4118    fn the_granule_report_stops_before_anything_is_lowered() {
4119        // A layout is settled at the closing brace, so lowering the function bodies would take
4120        // minutes on an amalgamation and answer nothing. The evidence that it stops is that a
4121        // body the back end has no way to compile still produces a report.
4122        let text = granules(
4123            "struct wide { long double d; };\n\
4124             long double f(long double x) { return x * x; }\n",
4125        );
4126        assert!(text.contains("struct wide"), "{text}");
4127    }
4128
4129    #[test]
4130    fn a_witness_reaches_the_assembler_as_a_call_to_the_runtime() {
4131        // The count only means anything if the call is really there, and a summary saying one is
4132        // there is not evidence that the back end emitted it.
4133        let text = safe_asm(rucc_session::Safety::Detect, "char *f(char *p) { return p; }\n");
4134        assert!(text.contains("\tcall\t__rucc_cap_witness\n"), "{text}");
4135    }
4136
4137    #[test]
4138    fn a_pointer_turned_into_an_integer_is_on_the_trust_set() {
4139        let text = summary(
4140            rucc_session::Safety::Detect,
4141            "unsigned long f(int *p) { return (unsigned long) p; }\n",
4142        );
4143        assert!(text.contains("\"exposed\": 1"), "{text}");
4144    }
4145
4146    /// The assembly of `source` at one safety tier, insisting that it compiled cleanly.
4147    fn safe_asm(tier: rucc_session::Safety, source: &str) -> String {
4148        let mut opts = options();
4149        opts.emit = EmitKind::Asm;
4150        opts.safety = tier;
4151        let result = run(&opts, source);
4152        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
4153        result.text().to_owned()
4154    }
4155
4156    #[test]
4157    fn a_check_reaches_the_assembler_as_a_call_to_the_runtime() {
4158        let text = safe_asm(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
4159        assert!(text.contains("\tcall\t__rucc_check_bounds\n"), "{text}");
4160        assert!(text.contains("\tcall\t__rucc_check_live\n"), "{text}");
4161        assert!(text.contains("\tcall\t__rucc_check_deriv\n"), "{text}");
4162        assert!(text.contains("\tcall\t__rucc_check_type\n"), "{text}");
4163        assert!(text.contains("\tcall\t__rucc_check_init\n"), "{text}");
4164    }
4165
4166    #[test]
4167    fn every_check_that_reached_the_assembler_has_a_row_describing_it() {
4168        // Five checks and five descriptors, each in the section the runtime's reporter reads.
4169        // The width is `rucc_safety::lower::WIDTH` and the row is `rucc_safe_rt::fail::Descriptor`,
4170        // and the two agreeing is what makes the address a check is handed mean anything.
4171        let text = safe_asm(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
4172        let section = format!("\t.section\t{},", rucc_safety::SECTION);
4173        assert_eq!(text.matches(&section).count(), 5, "{text}");
4174        for index in 0..5 {
4175            let name = format!("__rucc_safety_desc_{index}");
4176            // Defined once and referenced once, because a descriptor nothing points at describes
4177            // nothing and a reference with no definition does not link.
4178            assert!(text.contains(&format!("{name}:\n")), "{text}");
4179            assert!(text.contains(&format!("{name}(%rip)")), "{text}");
4180        }
4181        assert!(!text.contains("__rucc_safety_desc_5"), "{text}");
4182    }
4183
4184    /// `__builtin_constant_p` is answered in the front end and never reaches the IR.
4185    ///
4186    /// gcc folds it after optimization, so its answer for an argument that is not written as a
4187    /// constant can differ between `-O0` and `-O2`. What is checked here is the front end's
4188    /// answer, which is the same at every level, and the four cases where gcc gives the same
4189    /// answer at both levels are the ones measured on gcc 16: a literal is one, a variable is
4190    /// zero, a string literal is one and the address of an object is zero.
4191    #[test]
4192    fn builtin_constant_p_is_folded_where_it_is_written_rather_than_called() {
4193        let text = ir(concat!(
4194            "int g;\n",
4195            "int a = __builtin_constant_p(1);\n",
4196            "int b = __builtin_constant_p(g);\n",
4197            "int c = __builtin_constant_p(\"abc\");\n",
4198            "int d = __builtin_constant_p(&g);\n",
4199            "int e = __builtin_constant_p(1.5);\n",
4200            "int h = __builtin_choose_expr(__builtin_constant_p(3), 11, 22);\n",
4201        ));
4202        assert!(text.contains("global @a : i32 = 1,"), "{text}");
4203        assert!(text.contains("global @b : i32 = 0,"), "{text}");
4204        assert!(text.contains("global @c : i32 = 1,"), "{text}");
4205        assert!(text.contains("global @d : i32 = 0,"), "{text}");
4206        assert!(text.contains("global @e : i32 = 1,"), "{text}");
4207        assert!(text.contains("global @h : i32 = 11,"), "{text}");
4208        assert!(!text.contains("__builtin_constant_p"), "it is not a call to anything:\n{text}");
4209
4210        // The argument is not evaluated, which is what gcc does with it as well, so `i` is
4211        // still zero. The second constant is the answer, which nothing reads and which the
4212        // first pass that looks for dead code will take out.
4213        let text = body("int f(void) { int i = 0; __builtin_constant_p(i++); return i; }\n");
4214        assert_eq!(text, "block0:\n    %0 = iconst.i32 0\n    %1 = iconst.i32 0\n    return %0\n");
4215    }
4216
4217    /// A library builtin is the library function of the same name, and the call says so.
4218    ///
4219    /// A program writes `__builtin_strlen` rather than `strlen` to reach the function the C
4220    /// library promises where its own name has been taken by a macro, and to say that the usual
4221    /// meaning is the one intended. So the name in the program and the name in the object file
4222    /// are two different names and the call carries the second one. gcc folds several of these
4223    /// when the arguments allow it, which is an optimization on top of a call that is already
4224    /// right rather than instead of it, so nothing here depends on any folding happening.
4225    #[test]
4226    fn a_call_to_a_library_builtin_reaches_the_library_function() {
4227        let text = body("void f(void) { __builtin_abort(); }\n");
4228        assert_eq!(text, "block0:\n    call @abort() : ()\n    return\n");
4229
4230        // Nothing declared either of these and nothing had to: the prefix is what says the name
4231        // belongs to the implementation, and the type comes out of `features.toml`.
4232        let text = ir("int f(const char *s) { return __builtin_puts(s) + __builtin_strlen(s); }\n");
4233        assert!(text.contains("call @puts(%0) : (ptr) -> i32"), "{text}");
4234        assert!(text.contains("call @strlen(%0) : (ptr) -> i64"), "{text}");
4235        assert!(!text.contains("__builtin_"), "the prefix is not part of any name here:\n{text}");
4236    }
4237
4238    /// A `_chk` builtin reaches the checking function in the library with the object size still
4239    /// on the end of it.
4240    ///
4241    /// This is what a fortified `string.h` turns every copy into, so it is what a program built
4242    /// the way a distribution builds one is full of, and the whole of what makes the call right
4243    /// is that the size goes with it. The checking function takes `(size_t) -1` to mean nothing
4244    /// is known and does no check, which is what the header passes when the destination's object
4245    /// is not in sight, so the unconditional call means the same thing in both cases and costs a
4246    /// call gcc would have folded away in the second.
4247    ///
4248    /// The name is the one place this family reads like an exception and is not one:
4249    /// `__builtin___memcpy_chk` with `__builtin_` taken off is `__memcpy_chk`.
4250    #[test]
4251    fn a_chk_builtin_reaches_the_checking_function_and_keeps_the_size() {
4252        let text = ir(concat!(
4253            "char d[8];\n",
4254            "void f(const char *s, unsigned long n) {\n",
4255            "  __builtin___memcpy_chk(d, s, n, __builtin_object_size(d, 0));\n",
4256            "  __builtin___strcpy_chk(d, s, __builtin_object_size(d, 1));\n",
4257            "  __builtin___memset_chk(d, 0, n, 8);\n",
4258            "}\n",
4259        ));
4260        assert!(text.contains("call @__memcpy_chk("), "{text}");
4261        assert!(text.contains("call @__strcpy_chk("), "{text}");
4262        assert!(text.contains("call @__memset_chk("), "{text}");
4263        assert!(text.contains("iconst.i64 8"), "the object size reaches the call: {text}");
4264        assert!(!text.contains("__builtin_"), "the prefix is not part of any name here:\n{text}");
4265    }
4266
4267    /// A checking call whose object size says nothing is known is the plain library call.
4268    ///
4269    /// That is the whole of the folding half of the family. The checking function reads the all
4270    /// ones value as do not check, so the call it was going to make is the function it guards with
4271    /// an argument nobody reads on the end of it, and gcc drops the argument and calls the plain
4272    /// function at every level including `-O0`. Where the size is a real number the checking call
4273    /// stands, because the check is the point.
4274    #[test]
4275    fn a_checking_call_whose_size_says_nothing_is_known_is_the_plain_library_call() {
4276        let text = ir(concat!(
4277            "extern char *p;\n",
4278            "char d[8];\n",
4279            "void f(const char *s, unsigned long n) {\n",
4280            "  __builtin___memcpy_chk(d, s, n, __builtin_object_size(d, 0));\n",
4281            "  __builtin___memcpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
4282            "  __builtin___strcpy_chk(p, s, __builtin_object_size(p, 0));\n",
4283            "  __builtin___stpncpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
4284            "  __builtin___sprintf_chk(p, 1, __builtin_object_size(p, 0), s);\n",
4285            "}\n",
4286        ));
4287
4288        // The destination whose object is in sight keeps its check, size and all.
4289        assert!(
4290            text.contains("call @__memcpy_chk(%2, %0, %1, %3) : (ptr, ptr, i64, i64)"),
4291            "{text}"
4292        );
4293
4294        // The three whose object is not lose the argument and the name along with it. The type of
4295        // the call goes with them, which is what says the argument is gone rather than ignored.
4296        assert!(text.contains("call @memcpy(%6, %0, %1) : (ptr, ptr, i64) -> ptr"), "{text}");
4297        assert!(text.contains("call @strcpy(%10, %0) : (ptr, ptr) -> ptr"), "{text}");
4298        assert!(text.contains("call @stpncpy(%14, %0, %1) : (ptr, ptr, i64) -> ptr"), "{text}");
4299
4300        // The formatted one never folds, whatever the size says, because refusing a `%n` in a
4301        // writable format is the other half of what it was asked to do.
4302        assert!(text.contains("call @__sprintf_chk("), "{text}");
4303
4304        // Nothing is left behind in the instructions either. The size the folded calls no longer
4305        // take is a constant nobody reads, and no instruction is written for one.
4306        let asm = asm(concat!(
4307            "void f(char *p, const char *s, unsigned long n) {\n",
4308            "  __builtin___memcpy_chk(p, s, n, __builtin_object_size(p, 0));\n",
4309            "}\n",
4310        ));
4311        assert!(asm.contains("call\tmemcpy"), "{asm}");
4312        assert!(!asm.contains("$-1"), "the size that went away leaves no instruction:\n{asm}");
4313    }
4314
4315    /// The `v` spellings take a `__builtin_va_list`, which is the first type in the table the
4316    /// target chooses the shape of rather than the width of.
4317    ///
4318    /// On x86-64 it is an array of one, so what the prototype has to say is the pointer that
4319    /// array decays to, which is the same adjustment C makes to any parameter written as an array
4320    /// and is what a `va_list` parameter already holds. A prototype that kept the array would be
4321    /// one no argument could ever match.
4322    #[test]
4323    fn the_v_spellings_of_the_chk_family_take_the_list_a_va_list_parameter_holds() {
4324        let text = ir(concat!(
4325            "char d[64];\n",
4326            "int f(const char *fmt, ...) {\n",
4327            "  __builtin_va_list ap;\n",
4328            "  __builtin_va_start(ap, fmt);\n",
4329            "  int n = __builtin___vsprintf_chk(d, 1, __builtin_object_size(d, 0), fmt, ap);\n",
4330            "  __builtin_va_end(ap);\n",
4331            "  return n;\n",
4332            "}\n",
4333        ));
4334        assert!(text.contains("call @__vsprintf_chk("), "{text}");
4335        assert!(text.contains("iconst.i64 64"), "the object size reaches the call: {text}");
4336    }
4337
4338    /// The absolute value family is four instructions and not a call, whoever declared the name.
4339    ///
4340    /// `abs`, `labs` and `llabs` are reserved to the implementation, so a program that writes one
4341    /// means the one the C library promises and the compiler is allowed to know what it does. The
4342    /// program in `gcc.c-torture/execute/20021127-1.c` is the one that insists: it defines `llabs`
4343    /// to abort and expects the call not to reach it. Measured against gcc 16.2.0, which writes a
4344    /// `neg` and a `cmovns` and never calls the definition either.
4345    ///
4346    /// The most negative value comes back as itself, which is what the arithmetic gives and what
4347    /// gcc's pair of instructions gives, and C says the answer is undefined there.
4348    #[test]
4349    fn the_absolute_value_family_is_the_magnitude_and_not_a_call() {
4350        let text = body(concat!(
4351            "long long llabs(long long);\n",
4352            "long long f(long long x) { return llabs(x); }\n",
4353        ));
4354        assert!(text.contains("%1 = iconst.i64 63"), "{text}");
4355        assert!(text.contains("%2 = ashr %0, %1"), "{text}");
4356        assert!(text.contains("%3 = xor %0, %2"), "{text}");
4357        assert!(text.contains("%4 = sub %3, %2"), "{text}");
4358        assert!(!text.contains("call"), "the call does not happen:\n{text}");
4359
4360        // The narrower two, whose width comes from the type the library gives the name and not
4361        // from anything at the call.
4362        let text = body("int abs(int);\nint f(int x) { return abs(x); }\n");
4363        assert!(text.contains("iconst.i32 31"), "{text}");
4364        let text = body("long labs(long);\nlong f(long x) { return labs(x); }\n");
4365        assert!(text.contains("iconst.i64 63"), "{text}");
4366
4367        // The prefixed spelling is the same node, and it is what a program writes to reach the
4368        // library's meaning where the plain name has been taken.
4369        let text = body("long long f(long long x) { return __builtin_llabs(x); }\n");
4370        assert!(!text.contains("call"), "{text}");
4371
4372        // A definition of the name in the same file changes nothing, which is the whole point.
4373        let text = ir(concat!(
4374            "long long llabs(long long b);\n",
4375            "long long g(long long x) { return llabs(x); }\n",
4376            "long long llabs(long long b) { return 7; }\n",
4377        ));
4378        assert!(!text.contains("call @llabs"), "{text}");
4379    }
4380
4381    /// A byte swap is one instruction and not a call, and nothing had to declare it.
4382    ///
4383    /// SQLite writes these for its page headers and glibc's `<endian.h>` defines `htobe32` and its
4384    /// neighbours as exactly these, so a program that reads a file format reaches one without ever
4385    /// naming it. There is no object file anywhere that defines `__builtin_bswap32`, so a call left
4386    /// standing here would not link.
4387    #[test]
4388    fn a_byte_swap_is_arithmetic_and_not_a_call() {
4389        let text = body("unsigned f(unsigned x) { return __builtin_bswap32(x); }\n");
4390        assert_eq!(text, "block0(%0: i32):\n    %1 = bswap %0\n    return %1\n");
4391
4392        // The argument is converted by the prototype the way any other call's would be, so the
4393        // swap happens at the width the name says and not at the width the program wrote.
4394        let text = body("unsigned f(unsigned char c) { return __builtin_bswap32(c); }\n");
4395        assert!(text.contains("zext.i32 %0"), "widened first: {text}");
4396        assert!(text.contains("bswap %1"), "and swapped at four bytes: {text}");
4397    }
4398
4399    /// Each of the three reverses in the width its name says, which is the type of the node.
4400    ///
4401    /// The width matters more here than it looks. `__builtin_bswap16` is the two bytes of a
4402    /// `uint16_t` exchanged, and if the node came out at the machine's width instead then the bits
4403    /// above the value would be dragged into the answer and the result would be zero.
4404    #[test]
4405    fn the_byte_swaps_reverse_at_the_width_their_name_says() {
4406        for (name, ty, width) in [
4407            ("__builtin_bswap16", "unsigned short", "i16"),
4408            ("__builtin_bswap32", "unsigned", "i32"),
4409            ("__builtin_bswap64", "unsigned long long", "i64"),
4410        ] {
4411            let source = format!("{ty} f({ty} x) {{ return {name}(x); }}\n");
4412            let text = body(&source);
4413            assert_eq!(
4414                text,
4415                format!("block0(%0: {width}):\n    %1 = bswap %0\n    return %1\n"),
4416                "{name}"
4417            );
4418        }
4419    }
4420
4421    /// The three bit counts the IR has an instruction for are that instruction and not a call.
4422    ///
4423    /// Eighteen rows of `features.toml` come out of six questions, and three of the six are one
4424    /// instruction each. The kernel's bitmap search is built on them, ffmpeg counts leading zeroes
4425    /// in its bitstream reader and SQLite uses one to size a page, so a call left standing here
4426    /// would not link against anything and would be slow if it did.
4427    #[test]
4428    fn the_bit_counts_are_instructions_and_not_calls() {
4429        let text = body("int f(unsigned x) { return __builtin_clz(x); }\n");
4430        assert_eq!(text, "block0(%0: i32):\n    %1 = ctlz %0\n    return %1\n");
4431
4432        let text = body("int f(unsigned x) { return __builtin_ctz(x); }\n");
4433        assert_eq!(text, "block0(%0: i32):\n    %1 = cttz %0\n    return %1\n");
4434
4435        let text = body("int f(unsigned x) { return __builtin_popcount(x); }\n");
4436        assert_eq!(text, "block0(%0: i32):\n    %1 = ctpop %0\n    return %1\n");
4437    }
4438
4439    /// The width counted is the operand's and the width answered is `int`, which are two different
4440    /// things at every spelling but the narrowest.
4441    ///
4442    /// This is the mistake the family invites. `__builtin_clz` of a value counts the leading zeroes
4443    /// of it narrowed to `unsigned int` and `__builtin_clzll` counts them at sixty four bits, and
4444    /// those are different numbers for the same value. What decides it is the prototype the row
4445    /// carries, so the count happens after the conversion and the narrowing back to `int` happens
4446    /// after the count.
4447    #[test]
4448    fn the_bit_counts_ask_about_the_width_their_name_says() {
4449        let text = body("int f(unsigned long long x) { return __builtin_clzll(x); }\n");
4450        assert!(text.starts_with("block0(%0: i64):"), "counted at eight bytes: {text}");
4451        assert!(text.contains("%1 = ctlz %0"), "{text}");
4452        assert!(text.contains("trunc.i32 %1"), "and answered in an int: {text}");
4453
4454        // The same value asked about at the narrower width, which converts first and so counts
4455        // something else.
4456        let text = body("int f(unsigned long long x) { return __builtin_clz(x); }\n");
4457        assert!(text.contains("trunc.i32 %0"), "narrowed to what was asked about: {text}");
4458        assert!(text.contains("ctlz %1"), "and counted there: {text}");
4459
4460        let text = body("int f(unsigned long x) { return __builtin_popcountl(x); }\n");
4461        assert!(text.contains("%1 = ctpop %0"), "{text}");
4462        assert!(!text.contains("call"), "{text}");
4463    }
4464
4465    /// A parity is whether the count of set bits is odd, which is that count and its low bit.
4466    ///
4467    /// Not the machine's parity flag, which on x86-64 is over the low byte of a result and so is a
4468    /// different question, and not the count itself, since C says the answer is zero or one.
4469    #[test]
4470    fn a_parity_is_the_low_bit_of_the_set_bit_count() {
4471        let text = body("int f(unsigned x) { return __builtin_parity(x); }\n");
4472        assert!(text.contains("%1 = ctpop %0"), "{text}");
4473        assert!(text.contains("iconst.i32 1"), "{text}");
4474        assert!(text.contains("and %1, %2"), "the low bit of it: {text}");
4475    }
4476
4477    /// `__builtin_ffs` is the trailing zero count and one, kept only when there was a bit to find.
4478    ///
4479    /// The one in the family defined at zero, where it answers zero. Written as a mask rather than
4480    /// as a branch: the count and the comparison do not depend on each other and both are cheap, so
4481    /// a branch would buy nothing and cost two blocks and a join.
4482    #[test]
4483    fn the_first_set_bit_is_one_based_and_zero_for_a_zero() {
4484        let text = body("int f(int x) { return __builtin_ffs(x); }\n");
4485        assert!(text.contains("%1 = cttz %0"), "{text}");
4486        assert!(text.contains("%4 = add %1, %2"), "one more than the count: {text}");
4487        assert!(text.contains("%5 = icmp ne %0, %3"), "whether there was a bit at all: {text}");
4488        assert!(text.contains("%7 = sub %3, %6"), "spread to a mask: {text}");
4489        assert!(text.contains("%8 = and %4, %7"), "and kept only then: {text}");
4490        assert!(!text.contains("br_if"), "no branch: {text}");
4491    }
4492
4493    /// `__builtin_clrsb` is how many bits below the sign bit repeat it, which is a leading zero
4494    /// count of the value folded onto its own sign.
4495    ///
4496    /// Exclusive or with the sign spread over every bit turns a negative value into its complement
4497    /// and leaves one that is not negative alone, so in both cases the top bit is clear and there
4498    /// is one zero above the highest bit that does not repeat the sign. The answer is one less
4499    /// than that count, and the shift left is what takes the one off, with the low bit set on the
4500    /// way so that zero and minus one have something to count: both of them fold to a word with no
4501    /// bits in it, which is the one input a leading zero count says nothing about.
4502    #[test]
4503    fn the_redundant_sign_bit_count_is_instructions_and_not_a_call() {
4504        let text = body("int f(int x) { return __builtin_clrsb(x); }\n");
4505        assert!(text.contains("%1 = iconst.i32 31"), "{text}");
4506        assert!(text.contains("%2 = ashr %0, %1"), "the sign over every bit: {text}");
4507        assert!(text.contains("%3 = xor %0, %2"), "folded onto it: {text}");
4508        assert!(text.contains("%5 = shl %3, %4"), "one less than the count: {text}");
4509        assert!(text.contains("%6 = or %5, %4"), "with something to count at zero: {text}");
4510        assert!(text.contains("%7 = ctlz %6"), "{text}");
4511        assert!(!text.contains("call"), "{text}");
4512        assert!(!text.contains("br_if"), "no branch: {text}");
4513    }
4514
4515    /// The unsigned four are the same four instructions answering in the unsigned type.
4516    ///
4517    /// Which on a two's complement machine is the same bits, so what this checks is that the type
4518    /// of the answer is the unsigned one. The reason the family exists is the most negative value,
4519    /// whose magnitude is not representable in the signed type and is representable in this one.
4520    #[test]
4521    fn the_unsigned_absolute_value_family_answers_in_the_unsigned_type() {
4522        let text = body("unsigned f(int x) { return __builtin_uabs(x); }\n");
4523        assert!(text.contains("%1 = iconst.i32 31"), "{text}");
4524        assert!(text.contains("%4 = sub %3, %2"), "{text}");
4525        assert!(!text.contains("call"), "nothing declares uabs, so a call would not link: {text}");
4526
4527        let text = body("unsigned long long f(long long x) { return __builtin_ullabs(x); }\n");
4528        assert!(text.contains("iconst.i64 63"), "at the width the name says: {text}");
4529
4530        // The answer is the unsigned type and not the signed one, which is what a comparison
4531        // against it is decided by.
4532        let text = body("int f(int x) { return __builtin_uabs(x) > 2147483647u; }\n");
4533        assert!(text.contains("icmp ugt"), "compared unsigned: {text}");
4534    }
4535
4536    /// `intmax_t` is not a fixed type, so the two widest spellings ask the target what it is.
4537    ///
4538    /// `long` where that is sixty four bits wide and `long long` where it is not, which is the rule
4539    /// `rucc_pp::predef` writes `__INTMAX_TYPE__` out of. The three targets here are all LP64, so
4540    /// the answer is `long` and the shift is sixty three, and the point of the test is that the
4541    /// signature was understood at all rather than refused for naming a type the table could not
4542    /// spell.
4543    #[test]
4544    fn the_widest_absolute_value_is_whichever_type_the_target_makes_intmax_t() {
4545        let text = body("long f(long x) { return __builtin_imaxabs(x); }\n");
4546        assert!(text.contains("iconst.i64 63"), "{text}");
4547        assert!(text.contains("%4 = sub %3, %2"), "{text}");
4548        assert!(!text.contains("call"), "{text}");
4549
4550        let text = body("unsigned long f(long x) { return __builtin_umaxabs(x); }\n");
4551        assert!(text.contains("iconst.i64 63"), "{text}");
4552        assert!(!text.contains("call"), "{text}");
4553    }
4554
4555    /// The `_p` spellings ask the same question, write nothing, and do not evaluate the third
4556    /// argument.
4557    ///
4558    /// gcc says the third argument is there for its type alone, so a call is two operands and a
4559    /// type by the time it reaches the IR. What the type decides is the same thing it decides for
4560    /// the three that write: whether the exact answer would have fit there, which is why the
4561    /// second call below is done at a wider width than the first.
4562    #[test]
4563    fn an_overflow_predicate_writes_nothing_and_answers_the_bit_the_check_would() {
4564        let text =
4565            body("int f(int a, int b) { return __builtin_add_overflow_p(a, b, (int) 0); }\n");
4566        assert!(text.contains("%2, %3 = sadd_overflow.(i32, i1) %0, %1"), "{text}");
4567        assert!(!text.contains("store"), "nothing is written: {text}");
4568        assert!(!text.contains("call"), "{text}");
4569
4570        // A wider destination is a wider arithmetic, and the narrowing test that goes with it is
4571        // what says whether the answer got there, exactly as for the spelling that stores.
4572        let text =
4573            body("int f(int a, int b) { return __builtin_mul_overflow_p(a, b, (long long) 0); }\n");
4574        assert!(text.contains("smul_overflow.(i64, i1)"), "{text}");
4575        assert!(!text.contains("store"), "{text}");
4576
4577        // The third argument is a value and not a pointer, and a side effect written in it does
4578        // not happen, because what the argument is there for is its type.
4579        let text = body(concat!(
4580            "int g(void);\n",
4581            "int f(int a, int b) { return __builtin_sub_overflow_p(a, b, g()); }\n",
4582        ));
4583        assert!(!text.contains("call @g"), "the third argument is not evaluated: {text}");
4584    }
4585
4586    /// The three overflow checks are arithmetic and a flag, and not a call to anything.
4587    ///
4588    /// gcc has emitted these since 5.0 and there is no object file that defines one, so a call left
4589    /// standing here would not link. SQLite reaches all three within twenty lines of each other, in
4590    /// `sqlite3AddInt64` and its two neighbours, which is the reason they were done now.
4591    ///
4592    /// The IR instruction answers two things at once, the wrapped value and whether it wrapped,
4593    /// which is a shape nothing else in the IR has. The store is the builtin writing the answer
4594    /// through the pointer it was handed.
4595    #[test]
4596    fn an_overflow_check_is_arithmetic_and_not_a_call() {
4597        let text =
4598            body("int f(int a, int b, int *r) { return __builtin_add_overflow(a, b, r); }\n");
4599        assert!(text.contains("%3, %4 = sadd_overflow.(i32, i1) %0, %1"), "{text}");
4600        assert!(text.contains("store %3 -> %2"), "{text}");
4601        assert!(!text.contains("call"), "{text}");
4602
4603        let text =
4604            body("int f(int a, int b, int *r) { return __builtin_sub_overflow(a, b, r); }\n");
4605        assert!(text.contains("ssub_overflow.(i32, i1) %0, %1"), "{text}");
4606
4607        let text =
4608            body("int f(int a, int b, int *r) { return __builtin_mul_overflow(a, b, r); }\n");
4609        assert!(text.contains("smul_overflow.(i32, i1) %0, %1"), "{text}");
4610
4611        // Unsigned operands get the unsigned form, which is a different question about the same
4612        // arithmetic: an unsigned sum wraps where a signed one of the same bits does not.
4613        let text = body(
4614            "int f(unsigned a, unsigned b, unsigned *r) { return __builtin_add_overflow(a, b, r); }\n",
4615        );
4616        assert!(text.contains("uadd_overflow.(i32, i1) %0, %1"), "{text}");
4617    }
4618
4619    /// The arithmetic happens at a type that holds every value all three written types can hold.
4620    ///
4621    /// That is what makes the check exact. `unsigned int` and `int` in one call need thirty three
4622    /// bits between them, so the add is done at sixty four with each operand extended the way its
4623    /// own signedness says: the unsigned one zero extended, the signed one sign extended. Sign
4624    /// extending the unsigned one would turn three billion into a negative number before the
4625    /// addition ever saw it.
4626    #[test]
4627    fn an_overflow_check_is_done_at_a_type_that_holds_every_operand() {
4628        let text = body(
4629            "int f(unsigned a, int b, long long *r) { return __builtin_add_overflow(a, b, r); }\n",
4630        );
4631        assert!(text.contains("%3 = zext.i64 %0"), "the unsigned operand keeps its value: {text}");
4632        assert!(text.contains("%4 = sext.i64 %1"), "and so does the signed one: {text}");
4633        assert!(text.contains("sadd_overflow.(i64, i1) %3, %4"), "{text}");
4634
4635        // Three types that agree need no extension at all, which is what nearly every real call
4636        // is written as.
4637        let text = body(
4638            "int f(long long a, long long b, long long *r) { return __builtin_mul_overflow(a, b, r); }\n",
4639        );
4640        assert!(text.contains("smul_overflow.(i64, i1) %0, %1"), "{text}");
4641        assert!(!text.contains("sext."), "{text}");
4642        // The one widening left is the answer, which is a bit becoming the `int` C says it is.
4643        assert!(!text.contains("zext.i64"), "{text}");
4644    }
4645
4646    /// The wrapped answer is written through the pointer whether or not it fit.
4647    ///
4648    /// That is gcc's rule and it is what makes the builtin usable as a wrapping add with a flag on
4649    /// the side. A destination narrower than the arithmetic is narrowed and widened back, and the
4650    /// answer being different is the second half of the test: the instruction says whether the
4651    /// arithmetic itself needed more room, and the round trip says whether what came out survived
4652    /// the trip down to where it was going.
4653    #[test]
4654    fn an_overflow_check_writes_the_wrapped_answer_whether_or_not_it_fit() {
4655        let text =
4656            body("int f(int a, int b, char *r) { return __builtin_sub_overflow(a, b, r); }\n");
4657        assert!(text.contains("%3, %4 = ssub_overflow.(i32, i1) %0, %1"), "{text}");
4658        assert!(text.contains("%5 = trunc.i8 %3"), "narrowed to where it goes: {text}");
4659        assert!(text.contains("%6 = sext.i32 %5"), "and back: {text}");
4660        assert!(text.contains("%7 = icmp ne %6, %3"), "which is whether it fit: {text}");
4661        assert!(text.contains("store %5 -> %2"), "the narrowed value is stored either way: {text}");
4662        assert!(text.contains("%8 = or %4, %7"), "and either bit is an overflow: {text}");
4663    }
4664
4665    /// A call needing more than the widest type there is compiles, by not asking for such a type.
4666    ///
4667    /// One way to reach it: an unsigned `__int128` mixed with a signed type, which needs a hundred
4668    /// and twenty nine bits to represent both and so has nowhere left to go. That used to be refused
4669    /// by name. It is done now by carrying the sign of each operand alongside its value rather than
4670    /// inside it, which is what gcc does, so all three of the family compile for that mix.
4671    #[test]
4672    fn a_call_needing_more_than_the_widest_type_still_compiles() {
4673        for name in ["add", "sub", "mul"] {
4674            let source = format!(
4675                "int f(unsigned __int128 a, long long b, __int128 *r) {{\n    \
4676                 return __builtin_{name}_overflow(a, b, r);\n}}\n"
4677            );
4678            let mut opts = options();
4679            opts.emit = EmitKind::MirFinal;
4680            assert!(!run(&opts, &source).failed(), "{name} was refused or stopped the back end");
4681        }
4682    }
4683
4684    /// An operand that is not an integer at all is the older message, from the type checking every
4685    /// type generic builtin shares.
4686    #[test]
4687    fn an_overflow_check_over_something_that_is_not_an_integer_says_so() {
4688        let messages =
4689            errors("int f(double a, int b, int *r) { return __builtin_add_overflow(a, b, r); }\n");
4690        assert!(messages.iter().any(|line| line.contains("E0671")), "{messages:?}");
4691
4692        let messages =
4693            errors("int f(int a, int b, double *r) { return __builtin_add_overflow(a, b, r); }\n");
4694        assert!(messages.iter().any(|line| line.contains("E0671")), "{messages:?}");
4695    }
4696
4697    /// An ordered access is an ordered access in the IR, with the ordering the program wrote.
4698    ///
4699    /// Which is the point of the node existing at all. An ordering is not an argument anything is
4700    /// passed, it is a thing the IR says about an access, so the number in the source is read once
4701    /// in the front end and after that the ordering travels on the instruction where every pass
4702    /// that moves code can see it.
4703    ///
4704    /// SQLite is why these are done: `AtomicLoad` and `AtomicStore` in `sqlite3.c` are
4705    /// `__atomic_load_n` and `__atomic_store_n` at the relaxed ordering, and there are thirty five
4706    /// calls to the pair.
4707    #[test]
4708    fn an_ordered_access_is_ordered_in_the_ir() {
4709        let text = body("int f(int *p) { return __atomic_load_n(p, 0); }\n");
4710        assert!(text.contains("atomic_load.i32 %0, align 4, relaxed"), "{text}");
4711
4712        let text = body("long f(long *p) { return __atomic_load_n(p, 2); }\n");
4713        assert!(text.contains("atomic_load.i64 %0, align 8, acquire"), "{text}");
4714
4715        let text = body("void f(int *p, int v) { __atomic_store_n(p, v, 3); }\n");
4716        assert!(text.contains("atomic_store %1 -> %0, align 4, release"), "{text}");
4717
4718        let text = body("void f(int *p, int v) { __atomic_store_n(p, v, 5); }\n");
4719        assert!(text.contains("atomic_store %1 -> %0, align 4, seq_cst"), "{text}");
4720
4721        // The value is converted to what the pointer points at before it is stored, which is what
4722        // the call would have done if it had a prototype to convert against.
4723        let text = body("void f(char *p, int v) { __atomic_store_n(p, v, 0); }\n");
4724        assert!(text.contains("trunc.i8 %1"), "{text}");
4725        assert!(text.contains("atomic_store %2 -> %0, align 1, relaxed"), "{text}");
4726    }
4727
4728    /// On this machine the ordered access is the plain instruction, except at the strongest
4729    /// ordering of a store.
4730    ///
4731    /// x86-64 is total store order: every load is already an acquire and every store is already a
4732    /// release, and an aligned access no wider than a word is indivisible whether or not anybody
4733    /// asked. So the whole family is `mov` and the one thing the machine does not give away is a
4734    /// store staying in front of a later load, which is `mfence` behind the store. Every line below
4735    /// is what gcc 16.2.0 writes for the same function.
4736    #[test]
4737    fn an_ordered_access_is_the_plain_instruction_on_this_machine() {
4738        let text = asm("int f(int *p) { return __atomic_load_n(p, 5); }\n");
4739        assert!(text.contains("movl\t(%rdi), %eax"), "{text}");
4740        assert!(!text.contains("mfence"), "a load needs no barrier here: {text}");
4741
4742        let text = asm("void f(int *p, int v) { __atomic_store_n(p, v, 3); }\n");
4743        assert!(text.contains("movl\t%esi, (%rdi)"), "{text}");
4744        assert!(!text.contains("mfence"), "a release store needs no barrier here: {text}");
4745
4746        let text = asm("void f(int *p, int v) { __atomic_store_n(p, v, 5); }\n");
4747        let (before, after) = text.split_once("mfence").expect("a barrier: {text}");
4748        assert!(before.contains("movl\t%esi, (%rdi)"), "the store comes first: {text}");
4749        assert!(!after.contains("movl"), "and nothing else is between them: {text}");
4750    }
4751
4752    /// A barrier is one instruction at the strongest ordering and no instruction below it.
4753    ///
4754    /// The same reasoning the other way round. An acquire, a release and an acquire release fence
4755    /// are already true of every program running on this machine, and what a program wanted from
4756    /// one is that the compiler not move accesses across it, which is already so by the time any
4757    /// instruction is picked. Sequential consistency is the one that costs something.
4758    ///
4759    /// `__sync_synchronize` is the older family's spelling of the strongest one and compiles to
4760    /// exactly the same instruction, which is what SQLite calls twice in `sqlite3.c`.
4761    #[test]
4762    fn a_barrier_is_one_instruction_at_the_strongest_ordering_and_none_below_it() {
4763        assert!(asm("void f(void) { __atomic_thread_fence(5); }\n").contains("mfence"));
4764        assert!(asm("void f(void) { __sync_synchronize(); }\n").contains("mfence"));
4765
4766        for weaker in ["1", "2", "3", "4"] {
4767            let source = format!("void f(void) {{ __atomic_thread_fence({weaker}); }}\n");
4768            assert!(!asm(&source).contains("mfence"), "{weaker} costs nothing here");
4769        }
4770    }
4771
4772    /// The three x86 fences under gcc's names are that same barrier at that same ordering.
4773    ///
4774    /// Exact for `mfence` and stronger than asked for the other two, which is a safe answer: a
4775    /// program that wanted its stores ordered gets that and more. Narrowing the two is worth doing
4776    /// once an instruction can be named from there, which is the note the shipped `xmmintrin.h`
4777    /// already carries at `_mm_sfence`.
4778    ///
4779    /// Each carries a signature, so an argument written on one is reported like an argument
4780    /// written on any other call, which is the whole reason they have one.
4781    #[test]
4782    fn the_three_x86_fences_are_the_barrier_the_strongest_ordering_gives() {
4783        for name in ["__builtin_ia32_sfence", "__builtin_ia32_lfence", "__builtin_ia32_mfence"] {
4784            let source = format!("void f(void) {{ {name}(); }}\n");
4785            assert!(asm(&source).contains("mfence"), "{name} is a barrier");
4786            let text = body(&source);
4787            assert!(text.contains("fence seq_cst"), "{name}: {text}");
4788        }
4789
4790        let result = run(&options(), "void f(void) { __builtin_ia32_sfence(1); }\n");
4791        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
4792        assert!(result.messages[0].contains("too many arguments"), "{:?}", result.messages);
4793    }
4794
4795    /// The four compare and exchange names are one IR instruction producing two values.
4796    ///
4797    /// Which of the two the expression answers is the difference between three of the four names,
4798    /// and the fourth difference is the C11 pair writing what they found back through the pointer
4799    /// they were handed, which is the branch after the instruction.
4800    #[test]
4801    fn a_compare_and_exchange_is_one_instruction_answering_two_things() {
4802        // The older family, whose two names are the same instruction read two ways. Neither has a
4803        // memory order argument and both are a full barrier, which is what `seq_cst` says.
4804        let text =
4805            body("int f(int *p, int e, int d) { return __sync_val_compare_and_swap(p, e, d); }\n");
4806        assert!(text.contains("%3, %4 = cmpxchg.(i32, i1) %0, %1, %2, align 4, seq_cst"), "{text}");
4807        assert!(text.contains("return %3"), "the value it found: {text}");
4808
4809        let text =
4810            body("int f(int *p, int e, int d) { return __sync_bool_compare_and_swap(p, e, d); }\n");
4811        assert!(text.contains("%3, %4 = cmpxchg.(i32, i1) %0, %1, %2, align 4, seq_cst"), "{text}");
4812        assert!(text.contains("zext.i32 %4"), "whether it happened: {text}");
4813
4814        // The C11 form, whose value expected arrives by pointer and is read before the exchange,
4815        // and whose answer is whether it happened. The write back is on the path where it did not.
4816        let text = body(
4817            "int f(int *p, int *e, int d) { return __atomic_compare_exchange_n(p, e, d, 0, 4, 2); }\n",
4818        );
4819        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
4820        assert!(text.contains("%4, %5 = cmpxchg.(i32, i1) %0, %3, %2, align 4, acq_rel"), "{text}");
4821        assert!(text.contains("br_if %5, block2, block1"), "{text}");
4822        assert!(text.contains("store %4 -> %1, align 4"), "{text}");
4823
4824        // And the form that takes the value to put there by pointer as well, which is one more
4825        // read and is otherwise the same node.
4826        let text = body(
4827            "int f(int *p, int *e, int *d) { return __atomic_compare_exchange(p, e, d, 0, 5, 5); }\n",
4828        );
4829        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
4830        assert!(text.contains("%4 = load.i32 %2, align 4"), "{text}");
4831        assert!(text.contains("%5, %6 = cmpxchg.(i32, i1) %0, %3, %4, align 4, seq_cst"), "{text}");
4832    }
4833
4834    /// On this machine it is `lock cmpxchg`, at the width of the object and at every ordering.
4835    ///
4836    /// The `lock` is what makes the whole of it one step as far as every other processor is
4837    /// concerned, and it is also what makes the instruction a full barrier, which is why the
4838    /// ordering the program wrote changes nothing in what is written here. Every line below is what
4839    /// gcc 16.2.0 writes for the same function.
4840    #[test]
4841    fn a_compare_and_exchange_is_a_locked_instruction_at_the_width_of_the_object() {
4842        let widths = [("char", "b", "%dl"), ("short", "w", "%dx"), ("int", "l", "%edx")];
4843        for (ty, suffix, reg) in widths {
4844            let source = format!(
4845                "int f({ty} *p, {ty} e, {ty} d) {{ return __sync_bool_compare_and_swap(p, e, d); }}\n"
4846            );
4847            let text = asm(&source);
4848            assert!(text.contains("\tlock\n"), "{ty}: {text}");
4849            assert!(text.contains(&format!("cmpxchg{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
4850            assert!(text.contains("sete\t"), "{ty}: {text}");
4851        }
4852        let source =
4853            "int f(long *p, long e, long d) { return __sync_bool_compare_and_swap(p, e, d); }\n";
4854        assert!(asm(source).contains("cmpxchgq\t%rdx, (%rdi)"), "{}", asm(source));
4855
4856        // The ordering the program asked for changes nothing, because a locked instruction on this
4857        // machine orders everything whatever it was asked for, so there is never a barrier beside
4858        // it either.
4859        for order in ["0", "2", "3", "4", "5"] {
4860            let call = format!("__atomic_compare_exchange_n(p, e, d, 0, {order}, 0)");
4861            let source = format!("int f(int *p, int *e, int d) {{ return {call}; }}\n");
4862            let text = asm(&source);
4863            assert!(text.contains("cmpxchgl\t"), "{order}: {text}");
4864            assert!(!text.contains("mfence"), "{order} needs no barrier here: {text}");
4865        }
4866    }
4867
4868    /// A read modify write is one IR instruction, and a name that asks for the value afterwards is
4869    /// that instruction and one more operation.
4870    ///
4871    /// The instruction answers what was there before, which is the convention every machine and
4872    /// every language in this area uses. Half the names in the family ask for the value afterwards
4873    /// instead, and that is the answer and the operand put together again, which is arithmetic on
4874    /// two values already in registers rather than a second flavour of the instruction.
4875    ///
4876    /// The two lock names are here too. They are not read modify writes in the same sense: one is
4877    /// an exchange and the other is a store of a zero, and what makes them a pair is the ordering,
4878    /// which is the one place in the older family that is not sequential consistency.
4879    #[test]
4880    fn a_read_modify_write_is_one_instruction_and_the_arithmetic_a_name_asks_for() {
4881        let text = body("int f(int *p, int v) { return __atomic_fetch_add(p, v, 5); }\n");
4882        assert!(text.contains("%2 = atomic_rmw.i32 add %0, %1, align 4, seq_cst"), "{text}");
4883        assert!(text.contains("return %2"), "the value that was there: {text}");
4884
4885        let text = body("int f(int *p, int v) { return __atomic_add_fetch(p, v, 5); }\n");
4886        assert!(text.contains("%2 = atomic_rmw.i32 add %0, %1, align 4, seq_cst"), "{text}");
4887        assert!(text.contains("%3 = add %2, %1"), "and the value afterwards: {text}");
4888
4889        let text = body("int f(int *p, int v) { return __atomic_sub_fetch(p, v, 5); }\n");
4890        assert!(text.contains("%2 = atomic_rmw.i32 sub %0, %1, align 4, seq_cst"), "{text}");
4891        assert!(text.contains("%3 = sub %2, %1"), "{text}");
4892
4893        // The older family, which passes no ordering and is a full barrier.
4894        let text = body("int f(int *p, int v) { return __sync_fetch_and_sub(p, v); }\n");
4895        assert!(text.contains("%2 = atomic_rmw.i32 sub %0, %1, align 4, seq_cst"), "{text}");
4896
4897        // The exchange, and the older family's spelling of it, which is taking a lock and so is an
4898        // acquire rather than the full barrier the rest of that family is.
4899        let text = body("int f(int *p, int v) { return __atomic_exchange_n(p, v, 5); }\n");
4900        assert!(text.contains("%2 = atomic_rmw.i32 xchg %0, %1, align 4, seq_cst"), "{text}");
4901
4902        let text = body("int f(int *p, int v) { return __sync_lock_test_and_set(p, v); }\n");
4903        assert!(text.contains("%2 = atomic_rmw.i32 xchg %0, %1, align 4, acquire"), "{text}");
4904
4905        // Giving the lock back, which is one of the two names in the family that is handed no value
4906        // to put there, because what it puts there is a zero.
4907        let text = body("void f(int *p) { __sync_lock_release(p); }\n");
4908        assert!(text.contains("release"), "{text}");
4909        assert!(text.contains("%1 = iconst.i32 0"), "{text}");
4910
4911        // And with something after the pointer, which is the list of variables the call promises to
4912        // protect rather than a value to write. Reading it as a value would store whatever the
4913        // caller happened to name there, which is the one thing giving a lock back must not do.
4914        let text = body("void f(int *p, int guard) { __sync_lock_release(p, guard); }\n");
4915        assert!(text.contains("%2 = iconst.i32 0"), "{text}");
4916        assert!(text.contains("atomic_store %2 -> %0, align 4, release"), "{text}");
4917
4918        // The bitwise four, which look no different here from the arithmetic ones: what the machine
4919        // has an instruction for is a question further down and this level does not ask it.
4920        let text = body("int f(int *p, int v) { return __atomic_fetch_and(p, v, 5); }\n");
4921        assert!(text.contains("%2 = atomic_rmw.i32 and %0, %1, align 4, seq_cst"), "{text}");
4922
4923        let text = body("int f(int *p, int v) { return __sync_or_and_fetch(p, v); }\n");
4924        assert!(text.contains("%2 = atomic_rmw.i32 or %0, %1, align 4, seq_cst"), "{text}");
4925        assert!(text.contains("%3 = or %2, %1"), "and the value afterwards: {text}");
4926
4927        // The nand, which is the one of the six that is two operations. The flip is an exclusive or
4928        // against every bit set because the IR has no not and that is what one is.
4929        let text = body("int f(int *p, int v) { return __atomic_nand_fetch(p, v, 5); }\n");
4930        assert!(text.contains("%2 = atomic_rmw.i32 nand %0, %1, align 4, seq_cst"), "{text}");
4931        assert!(text.contains("%3 = and %2, %1"), "{text}");
4932        assert!(text.contains("%4 = iconst.i32 -1"), "{text}");
4933        assert!(text.contains("%5 = xor %3, %4"), "{text}");
4934    }
4935
4936    /// The four operations with no instruction on this machine are a loop around `lock cmpxchg`.
4937    ///
4938    /// The shape is the one every architecture manual writes out by hand: read the word, work out
4939    /// what should be there instead, put it back if nothing else got in first, and go round again
4940    /// when something did. What is checked is that the loop is there at every width, that the
4941    /// operation is inside it, and that no `xchg` or `xadd` got used for something neither of them
4942    /// does.
4943    ///
4944    /// gcc 16.2.0 writes the same loop for the same functions, down to which register holds the
4945    /// value that was read.
4946    #[test]
4947    fn a_bitwise_read_modify_write_is_a_loop_around_the_compare_and_exchange() {
4948        let widths = [("char", "b", "%dl"), ("short", "w", "%dx"), ("int", "l", "%edx")];
4949        for (ty, suffix, reg) in widths {
4950            for (name, call, insn) in [
4951                ("and", "__atomic_fetch_and(p, v, 5)", "and"),
4952                ("or", "__sync_fetch_and_or(p, v)", "or"),
4953                ("xor", "__atomic_xor_fetch(p, v, 5)", "xor"),
4954            ] {
4955                let source = format!("{ty} f({ty} *p, {ty} v) {{ return {call}; }}\n");
4956                let text = asm(&source);
4957                assert!(text.contains("\tlock\n"), "{ty} {name}: {text}");
4958                assert!(
4959                    text.contains(&format!("cmpxchg{suffix}\t{reg}, (%rdi)")),
4960                    "{ty} {name}: {text}"
4961                );
4962                assert!(text.contains(&format!("{insn}{suffix}\t")), "{ty} {name}: {text}");
4963                // The tab matters on the second of these, since `cmpxchg` ends in the other name.
4964                assert!(!text.contains("\txadd"), "{ty} {name} is not an add: {text}");
4965                assert!(!text.contains("\txchg"), "{ty} {name} is not an exchange: {text}");
4966            }
4967        }
4968        let source = "long f(long *p, long v) { return __atomic_fetch_or(p, v, 5); }\n";
4969        assert!(asm(source).contains("cmpxchgq\t%rdx, (%rdi)"), "{}", asm(source));
4970
4971        // The nand, which puts two instructions inside the loop rather than one. The flip is an
4972        // exclusive or against every bit set in the IR and the folder turns that into the `not` the
4973        // machine has, which is what gcc writes here too.
4974        let text = asm("int f(int *p, int v) { return __sync_fetch_and_nand(p, v); }\n");
4975        assert!(text.contains("cmpxchgl\t"), "{text}");
4976        assert!(text.contains("andl\t"), "{text}");
4977        assert!(text.contains("notl\t"), "{text}");
4978    }
4979
4980    /// The three names that pass a value through a pointer are the same access and one plain one.
4981    ///
4982    /// They exist for an object too big to come back in a register, and the front end takes them at
4983    /// their word rather than folding them into the `_n` spellings, because the extra access is real:
4984    /// the caller handed over somewhere to read from or write into and that is where the value has
4985    /// to come from or go. Both of those accesses are plain. The object at the end of the caller's
4986    /// pointer is the caller's own and no other thread has its address, which is what the whole
4987    /// shape is for.
4988    #[test]
4989    fn an_access_through_a_second_pointer_is_the_same_access_and_one_more() {
4990        let text = body("void f(int *p, int *r) { __atomic_load(p, r, 5); }\n");
4991        assert!(text.contains("%2 = atomic_load.i32 %0, align 4, seq_cst"), "{text}");
4992        assert!(text.contains("store %2 -> %1, align 4"), "and out through the place: {text}");
4993
4994        let text = body("void f(int *p, int *v) { __atomic_store(p, v, 3); }\n");
4995        assert!(text.contains("%2 = load.i32 %1, align 4"), "in through the place: {text}");
4996        assert!(text.contains("atomic_store %2 -> %0, align 4, release"), "{text}");
4997
4998        // The exchange, which reads through one pointer and writes through another and is the same
4999        // instruction in between as the spelling that takes and answers values.
5000        let text = body("void f(int *p, int *v, int *r) { __atomic_exchange(p, v, r, 5); }\n");
5001        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
5002        assert!(text.contains("%4 = atomic_rmw.i32 xchg %0, %3, align 4, seq_cst"), "{text}");
5003        assert!(text.contains("store %4 -> %2, align 4"), "{text}");
5004    }
5005
5006    /// The flag pair is an exchange of one byte and a store of a zero over the same byte.
5007    ///
5008    /// One byte whatever the pointer was written as, which is the standard's reading rather than a
5009    /// liberty: the object is an `atomic_flag`, there is no other way to read or write one, so the
5010    /// type the pointer carries says nothing about the access and the width is the implementation's
5011    /// to fix. gcc 16.2.0 writes `xchgb` here through an `int *` too.
5012    ///
5013    /// The answer is a comparison against zero rather than the byte itself, because the type of the
5014    /// call is `_Bool` and a byte that is neither zero nor one is not one. gcc answers the raw byte,
5015    /// and the two agree wherever the flag is only ever touched through this pair.
5016    #[test]
5017    fn a_flag_is_an_exchange_of_one_byte_and_a_store_of_a_zero_over_the_same_byte() {
5018        for pointer in ["char", "int", "void"] {
5019            let source = format!("int f({pointer} *p) {{ return __atomic_test_and_set(p, 5); }}\n");
5020            let text = body(&source);
5021            assert!(text.contains("%1 = iconst.i8 1"), "{pointer}: {text}");
5022            assert!(
5023                text.contains("%2 = atomic_rmw.i8 xchg %0, %1, align 1, seq_cst"),
5024                "{pointer}: {text}"
5025            );
5026            assert!(text.contains("%4 = icmp ne %2, %3"), "{pointer}: {text}");
5027
5028            let source = format!("void f({pointer} *p) {{ __atomic_clear(p, 3); }}\n");
5029            let text = body(&source);
5030            assert!(text.contains("atomic_store %2 -> %0, align 1, release"), "{pointer}: {text}");
5031        }
5032
5033        // And on this machine, where the exchange carries no `lock` because one with memory locks
5034        // the bus whether it was asked to or not. Both lines are what gcc 16.2.0 writes.
5035        let text = asm("int f(int *p) { return __atomic_test_and_set(p, 5); }\n");
5036        assert!(text.contains("xchgb\t%al, (%rdi)"), "{text}");
5037        assert!(text.contains("setne\t"), "{text}");
5038    }
5039
5040    /// On this machine it is `xchg` where the machine has an exchange and `lock xadd` where it has
5041    /// an add, at the width of the object.
5042    ///
5043    /// The exchange carries no prefix and the add carries one, which is the machine rather than an
5044    /// oversight: an exchange with memory locks the bus whether it is asked to or not. Both are
5045    /// therefore full barriers whatever ordering the program wrote, so no ordering costs an
5046    /// `mfence` beside them. Every line below is what gcc 16.2.0 writes for the same function.
5047    #[test]
5048    fn a_read_modify_write_is_an_exchange_or_a_locked_add_at_the_width_of_the_object() {
5049        let widths = [("char", "b", "%sil"), ("short", "w", "%si"), ("int", "l", "%esi")];
5050        for (ty, suffix, reg) in widths {
5051            let source =
5052                format!("{ty} f({ty} *p, {ty} v) {{ return __atomic_fetch_add(p, v, 5); }}\n");
5053            let text = asm(&source);
5054            assert!(text.contains("\tlock\n"), "{ty}: {text}");
5055            assert!(text.contains(&format!("xadd{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
5056
5057            let source =
5058                format!("{ty} f({ty} *p, {ty} v) {{ return __atomic_exchange_n(p, v, 5); }}\n");
5059            let text = asm(&source);
5060            assert!(text.contains(&format!("xchg{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
5061            assert!(!text.contains("\tlock\n"), "an exchange is locked already: {ty}: {text}");
5062        }
5063        let source = "long f(long *p, long v) { return __atomic_fetch_add(p, v, 5); }\n";
5064        assert!(asm(source).contains("xaddq\t%rsi, (%rdi)"), "{}", asm(source));
5065
5066        // A subtraction is the same instruction over the negated operand, which is right at every
5067        // width because the machine's arithmetic wraps.
5068        let source = "int f(int *p, int v) { return __atomic_fetch_sub(p, v, 5); }\n";
5069        let text = asm(source);
5070        assert!(text.contains("negl\t"), "{text}");
5071        assert!(text.contains("xaddl\t"), "{text}");
5072
5073        // The ordering changes nothing, for the reason it changes nothing for a compare and
5074        // exchange: a locked instruction on this machine orders everything whatever it was asked.
5075        for order in ["0", "2", "3", "4", "5"] {
5076            let source =
5077                format!("int f(int *p, int v) {{ return __atomic_fetch_add(p, v, {order}); }}\n");
5078            let text = asm(&source);
5079            assert!(text.contains("xaddl\t"), "{order}: {text}");
5080            assert!(!text.contains("mfence"), "{order} needs no barrier here: {text}");
5081        }
5082
5083        // And the lock pair, which is the exchange and a store of a zero. Neither is a barrier
5084        // instruction: the exchange is one already and the store is a release, which this machine
5085        // gives away.
5086        let text = asm("int f(int *p, int v) { return __sync_lock_test_and_set(p, v); }\n");
5087        assert!(text.contains("xchgl\t%esi, (%rdi)"), "{text}");
5088        // The zero goes through a register on the way, which is where every constant this
5089        // compiler stores goes: gcc writes the one instruction because it has a store that takes an
5090        // immediate and no rule here does. That is a rule this rule set is missing rather than
5091        // anything about the builtin, and it is the same two instructions a plain `*p = 0` makes.
5092        // The register gets its zero from an exclusive or with itself rather than from a move of a
5093        // zero, which is `rucc_codegen::shorten` writing the shorter of the two spellings.
5094        let text = asm("void f(int *p) { __sync_lock_release(p); }\n");
5095        assert!(text.contains("xorl\t%eax, %eax"), "{text}");
5096        assert!(text.contains("movl\t%eax, (%rdi)"), "{text}");
5097        assert!(!text.contains("mfence"), "a release store needs no barrier here: {text}");
5098    }
5099
5100    /// The two lock free questions are numbers in the program rather than calls to anything.
5101    ///
5102    /// Both answer from the size, which has to be a power of two no wider than the widest access
5103    /// this compiler writes, and from what the pointer says about the alignment. Sixteen bytes is
5104    /// no here and is no in gcc without `-mcx16`, because `cmpxchg16b` is not in the baseline and
5105    /// nothing here writes it. Three bytes is no because there is no three byte access at all.
5106    ///
5107    /// The whole point of both names is that the answer is available before the program runs, so
5108    /// what is checked is that a `mov` of a constant is the whole function and that no call was
5109    /// left behind. A call would be to `__atomic_is_lock_free` in libatomic, which is not a library
5110    /// this links against.
5111    #[test]
5112    fn the_lock_free_questions_are_answered_as_constants() {
5113        for size in ["1", "2", "4", "8"] {
5114            let source =
5115                format!("int f(void) {{ return __atomic_always_lock_free({size}, 0); }}\n");
5116            let text = asm(&source);
5117            assert!(text.contains("movb\t$1, %al"), "{size} bytes is lock free: {text}");
5118            assert!(!text.contains("call"), "and is not a call: {text}");
5119        }
5120        for size in ["3", "16", "sizeof(long double)"] {
5121            let source = format!("int f(void) {{ return __atomic_is_lock_free({size}, 0); }}\n");
5122            let text = asm(&source);
5123            assert!(text.contains("movb\t$0, %al"), "{size} bytes is not: {text}");
5124            assert!(!text.contains("call"), "and is not a call either: {text}");
5125        }
5126
5127        // A size the compiler cannot work out, which is no rather than a refusal, and an object
5128        // whose type is aligned under the size asked about, which is the whole of what the second
5129        // argument is for.
5130        let text = asm("int f(int n) { return __atomic_is_lock_free(n, 0); }\n");
5131        assert!(text.contains("movb\t$0, %al"), "a size nobody knows is not lock free: {text}");
5132        let text = asm("int f(int *p) { return __atomic_always_lock_free(8, p); }\n");
5133        assert!(text.contains("movb\t$0, %al"), "eight bytes at four is not: {text}");
5134        let text = asm("int f(long *p) { return __atomic_always_lock_free(8, p); }\n");
5135        assert!(text.contains("movb\t$1, %al"), "and at eight it is: {text}");
5136    }
5137
5138    /// A memory order an operation cannot carry is read as the strongest one, and said so about.
5139    ///
5140    /// There are three ways the number is not one the operation can take: it is not a constant at
5141    /// all, it is not one of the six the headers define, or it is one of them and means nothing for
5142    /// this operation, which is a release load or an acquire store. All three become sequential
5143    /// consistency, which is stronger than anything the program could have meant, so a program that
5144    /// wrote nonsense gets a correct answer rather than a fast one. gcc does the same.
5145    ///
5146    /// The last two also warn, because the number was written down and is wrong. The first does
5147    /// not: gcc takes a computed order, and so does the C11 spelling, so a warning there would fire
5148    /// on correct programs.
5149    #[test]
5150    fn a_memory_order_an_operation_cannot_carry_is_read_as_the_strongest() {
5151        let mut opts = options();
5152        opts.emit = EmitKind::Ir;
5153
5154        let acquire_store = run(&opts, "void f(int *p, int v) { __atomic_store_n(p, v, 2); }\n");
5155        assert!(acquire_store.text().contains("seq_cst"), "{:?}", acquire_store.text());
5156        assert!(acquire_store.messages[0].contains("[W0333]"), "{:?}", acquire_store.messages);
5157
5158        let nonsense = run(&opts, "int f(int *p) { return __atomic_load_n(p, 99); }\n");
5159        assert!(nonsense.text().contains("seq_cst"), "{:?}", nonsense.text());
5160        assert!(nonsense.messages[0].contains("[W0333]"), "{:?}", nonsense.messages);
5161
5162        let computed = run(&opts, "int f(int *p, int n) { return __atomic_load_n(p, n); }\n");
5163        assert!(computed.text().contains("seq_cst"), "{:?}", computed.text());
5164        assert_eq!(computed.messages, Vec::<String>::new(), "a computed order is not a mistake");
5165    }
5166
5167    /// A conversion between a float and the widest unsigned integer, which the machine has not got.
5168    ///
5169    /// Every other conversion between a float and an integer is the signed one at some width with a
5170    /// widening in front or a narrowing behind. These two are not, because there is no signed width
5171    /// that holds every value of an unsigned sixty four bit integer, so each is the signed
5172    /// conversion with arithmetic around it that brings the value into range and puts it back.
5173    ///
5174    /// What is checked here is that the conversion happens at all and that it happens without a
5175    /// branch. gcc writes a branch for both; this writes the choice as a mask, because every rewrite
5176    /// in that pass stays inside the block it started in. The arithmetic itself is checked in
5177    /// `rucc-codegen`, where it can be run against the answer rather than read in the assembly.
5178    #[test]
5179    fn a_conversion_between_a_float_and_the_widest_unsigned_integer_is_written_without_a_branch() {
5180        let text = asm("double f(unsigned long long x) { return (double)x; }\n");
5181        assert!(text.contains("cvtsi2sdq"), "the signed conversion is what runs: {text}");
5182        assert!(text.contains("shrq"), "with the value halved first: {text}");
5183        assert!(text.contains("addsd"), "and doubled after: {text}");
5184        assert!(!text.contains("\tj"), "and no branch anywhere: {text}");
5185
5186        let text = asm("unsigned long long f(double d) { return (unsigned long long)d; }\n");
5187        assert!(text.contains("cvttsd2siq"), "the signed conversion is what runs: {text}");
5188        assert!(text.contains("subsd"), "with half the range taken off first: {text}");
5189        assert!(text.contains("shlq\t$63"), "and the top bit put back: {text}");
5190        assert!(!text.contains("\tj"), "and no branch anywhere: {text}");
5191    }
5192
5193    /// The plain names are the library's only where nothing else has taken them.
5194    ///
5195    /// Four ways a program says it means something else. A `static` definition is its own
5196    /// function and the name outside the file is somebody else's. A declaration of another type
5197    /// is another function. `-fno-builtin` and `-fno-builtin-<name>` say so outright, and
5198    /// `-ffreestanding` says there is no C library for the name to be the name of. Every one of
5199    /// these was measured against gcc 16.2.0, which calls the program's function in all of them.
5200    ///
5201    /// The `__builtin_` spelling goes on meaning the library's function through all of it, which
5202    /// is what the prefix is for and what lets a freestanding build reach one deliberately.
5203    #[test]
5204    fn a_plain_name_the_program_took_is_the_programs_own_function() {
5205        let taken = concat!(
5206            "static long long llabs(long long b) { return 7; }\n",
5207            "long long f(long long x) { return llabs(x); }\n",
5208        );
5209        assert!(ir(taken).contains("call @llabs"), "a static definition is the program's own");
5210
5211        let retyped = concat!("int llabs(int b);\n", "int f(int x) { return llabs(x); }\n",);
5212        assert!(ir(retyped).contains("call @llabs"), "another type is another function");
5213
5214        let plain = concat!(
5215            "long long llabs(long long b);\n",
5216            "long long f(long long x) { return llabs(x); }\n",
5217        );
5218        let mut opts = options();
5219        opts.emit = EmitKind::Ir;
5220        assert!(!run(&opts, plain).text().contains("call @llabs"), "the library's by default");
5221
5222        opts.builtins = false;
5223        assert!(run(&opts, plain).text().contains("call @llabs"), "-fno-builtin");
5224
5225        opts.builtins = true;
5226        opts.no_builtin = vec!["llabs".to_owned()];
5227        assert!(run(&opts, plain).text().contains("call @llabs"), "-fno-builtin-llabs");
5228        let one = "long labs(long b);\nlong f(long x) { return labs(x); }\n";
5229        assert!(!run(&opts, one).text().contains("call @labs"), "one name and not the family");
5230
5231        // `-ffreestanding` reaches the front end as the same answer, which is what the driver
5232        // does with it in `compile`, and the prefixed spelling is untouched by any of it.
5233        opts.no_builtin = Vec::new();
5234        opts.builtins = false;
5235        let prefixed = "long long f(long long x) { return __builtin_llabs(x); }\n";
5236        assert!(!run(&opts, prefixed).text().contains("call @llabs"), "the prefix is a promise");
5237    }
5238
5239    /// The hint builtins are their first argument, and nothing is left of the hint.
5240    ///
5241    /// Which way a branch is expected to go is the whole of what they say, and there is nothing
5242    /// here that reads a branch weight yet, so what reaches the IR is the value and the hint is
5243    /// gone. The one thing the prototype has to keep doing is converting: gcc gives both of them
5244    /// a `long` result, so `sizeof(__builtin_expect((char)1, 1))` is eight and a narrower argument
5245    /// widens before it is answered with.
5246    ///
5247    /// Whether a side effect in the hint happens depends on the first argument, which is gcc's
5248    /// answer rather than a rule anybody designed. A constant first argument folds the whole call
5249    /// where it is written and the hint goes with it, and a first argument that is not a constant
5250    /// leaves the hint standing. Both halves are below and both were measured on gcc 16.2.0.
5251    #[test]
5252    fn the_hint_builtins_are_their_first_argument_and_the_hint_leaves_no_trace() {
5253        let text = ir(concat!(
5254            "long a = __builtin_expect(7, 1);\n",
5255            "long b = __builtin_expect_with_probability(9, 1, 0.9);\n",
5256            "unsigned long c = sizeof(__builtin_expect((char)1, 1));\n",
5257        ));
5258        assert!(text.contains("global @a : i64 = 7,"), "{text}");
5259        assert!(text.contains("global @b : i64 = 9,"), "{text}");
5260        assert!(text.contains("global @c : i64 = 8,"), "{text}");
5261        assert!(!text.contains("__builtin_expect"), "it is not a call to anything:\n{text}");
5262
5263        // A narrower argument is widened by the prototype before it is handed back, and it is
5264        // widened with its sign, since the parameter is a signed `long`.
5265        let text = body("long f(char c) { return __builtin_expect(c, 1); }\n");
5266        assert!(text.contains("sext"), "{text}");
5267
5268        // The first argument is a constant, so the second is not evaluated and `i` is still zero,
5269        // and neither is the third. What is left of each statement is the first argument widened,
5270        // which nothing reads and which the first pass that looks for dead code will take out.
5271        let one = "block0:\n    %0 = iconst.i32 0\n    %1 = iconst.i32 1\n    %2 = sext.i64 %1\n    return %0\n";
5272        assert_eq!(body("int f(void) { int i = 0; __builtin_expect(1, i++); return i; }\n"), one);
5273        let source = "int g(void) { int i = 0; __builtin_expect_with_probability(1, i++, 0.5); return i; }\n";
5274        assert_eq!(body(source), one);
5275
5276        // The first argument is not a constant, so the hint runs and `i` comes back one. There is
5277        // an increment in the body and the value it returns is the load after it, which is what
5278        // gcc gives for the same program, and the whole of tamnd/rucc#584 is that this used to
5279        // come out the same as the pair above.
5280        let kept = body("int f(int n) { int i = 0; __builtin_expect(n, i++); return i; }\n");
5281        assert!(kept.contains("add.nsw"), "the hint still runs: {kept}");
5282        assert!(kept.ends_with("return %3\n"), "and the answer is what it left behind: {kept}");
5283        let both = "int g(int n) { int i = 0; __builtin_expect_with_probability(n, i++, 0.5); return i; }\n";
5284        assert!(body(both).contains("add.nsw"), "and so does the one with three arguments");
5285    }
5286
5287    /// A point control does not arrive at, in both of the ways the compiler has one.
5288    ///
5289    /// `__builtin_unreachable()` is the promise written down, and a function whose body can run
5290    /// off the bottom is the walk arriving at the same place on its own. Neither writes an
5291    /// instruction, which is what gcc 16.2.0 does at `-O0`: it emits the epilogue and the `ret`
5292    /// for both of the functions below and nothing else, and the two of them come out byte for
5293    /// byte the same there.
5294    ///
5295    /// The `ret` is the part worth holding on to. It is not there because anything runs it, it is
5296    /// there because a function whose last instruction is not a return is one that falls into
5297    /// whatever the assembler puts after it.
5298    #[test]
5299    fn a_promise_that_control_does_not_arrive_writes_no_instruction() {
5300        let promised = "int f(int x) { if (x) return 1; __builtin_unreachable(); }\n";
5301        let text = ir(promised);
5302        assert!(text.contains("    unreachable_hint\n"), "{text}");
5303        assert!(!text.contains("call"), "it is not a call to anything:\n{text}");
5304
5305        // The statement after it is still lowered. Continuing to translate a path the program
5306        // promised is dead is one of the things a compiler may do with undefined behaviour, and
5307        // it is the one that keeps a program built at `-O0` behaving the way it was watched to.
5308        let after = body("int g(int x) { __builtin_unreachable(); return x; }\n");
5309        assert!(after.contains("return"), "{after}");
5310
5311        // Both functions are the same instructions, because the hint writes none of them and the
5312        // terminator underneath it writes none either.
5313        let text = asm(promised);
5314        let mine = text.split_once("\nf:\n").expect("a definition").1;
5315        let mine = mine.split_once("\t.size").expect("a definition").0;
5316        let plain = asm("int f(int x) { if (x) return 1; }\n");
5317        let plain = plain.split_once("\nf:\n").expect("a definition").1;
5318        let plain = plain.split_once("\t.size").expect("a definition").0;
5319        assert_eq!(mine, plain);
5320        // The last instruction, rather than the last line, because the unwind record is closed
5321        // after it and a directive is not something the machine runs.
5322        let last = mine.lines().rfind(|line| !line.trim_start().starts_with('.'));
5323        assert_eq!(last.map(str::trim), Some("ret"), "{mine}");
5324        assert!(!mine.contains("ud2"), "{mine}");
5325    }
5326
5327    /// The two names stay apart, which is what having both of them is for.
5328    ///
5329    /// The one the program wrote is what the call is checked against and what a diagnostic about
5330    /// it says, and the one the library defines is what the call ends up carrying. A compiler
5331    /// that kept only the second would report this against `abort`, which is a function the
5332    /// program never mentions.
5333    #[test]
5334    fn a_library_builtin_is_diagnosed_under_the_name_the_program_wrote() {
5335        let mut opts = options();
5336        opts.emit = EmitKind::Ir;
5337        let messages = run(&opts, "void f(void) { __builtin_abort(1); }\n").messages;
5338        assert!(
5339            messages.iter().any(|m| m.contains("__builtin_abort")),
5340            "expected the written name in {messages:?}"
5341        );
5342    }
5343
5344    /// A builtin nothing lowers is refused where it is written, rather than at the link.
5345    ///
5346    /// One name is left, which is the last of the atomic family that is refused and is also the
5347    /// one whose prefix is not `__builtin_`; its older half has nothing left in it at all, and so
5348    /// does the half of the family that carries a prototype. What the message has to carry is the
5349    /// name, because the whole complaint about the link error this replaces is that the name in it
5350    /// was one the compiler chose.
5351    #[test]
5352    fn a_builtin_nothing_lowers_is_refused_by_name() {
5353        let mut opts = options();
5354        opts.emit = EmitKind::Ir;
5355        let builtin = "__atomic_signal_fence";
5356        let source = format!("int counter;\nint f(void) {{ return ({builtin}(5), 0); }}\n");
5357        let messages = run(&opts, &source).messages;
5358        let named = messages.iter().any(|m| m.contains(builtin) && m.contains("E0686"));
5359        assert!(named, "expected {builtin} to be refused by name in {messages:?}");
5360    }
5361
5362    /// The refusal is about a call and not about the name, so a program that defines the name
5363    /// itself gets the function it wrote.
5364    ///
5365    /// That is not the reason the refusal exists, but a definition in front of us is a definition
5366    /// and the call to it links. It works here because the name is one with no prototype and no
5367    /// meaning the front end knows, which is what is left once the rest of the family is
5368    /// implemented: a `__builtin_` name the front end does answer is answered whatever the program
5369    /// declares, the way gcc answers one.
5370    #[test]
5371    fn what_is_refused_is_the_call_and_not_the_name() {
5372        let text = ir(concat!(
5373            "void __atomic_signal_fence(int order) { (void)order; }\n",
5374            "void f(void) { __atomic_signal_fence(5); }\n",
5375        ));
5376        assert!(text.contains("call @__atomic_signal_fence"), "{text}");
5377    }
5378
5379    /// How many bytes are behind an address is read off the layout, for every shape the walk
5380    /// covers.
5381    ///
5382    /// This is what `_FORTIFY_SOURCE` runs on, so the numbers matter one at a time rather than in
5383    /// aggregate: a size too small turns a correct copy into an abort, and a size too large turns
5384    /// a checked copy back into an unchecked one. Every answer here was measured against gcc
5385    /// 16.2.0 first. They are written as initializers so that each one is a constant in the
5386    /// output and the test reads as the table it is.
5387    #[test]
5388    fn the_object_size_of_an_address_is_what_the_layout_leaves_in_front_of_it() {
5389        let text = ir(concat!(
5390            "struct S { char a[8]; int n; char b[12]; };\n",
5391            "char g[32];\n",
5392            "struct S gs;\n",
5393            "unsigned long whole = __builtin_object_size(g, 0);\n",
5394            "unsigned long moved = __builtin_object_size(g + 4, 0);\n",
5395            "unsigned long back = __builtin_object_size(g + 30 - 2, 0);\n",
5396            "unsigned long outer = __builtin_object_size(gs.a, 0);\n",
5397            "unsigned long inner = __builtin_object_size(gs.a, 1);\n",
5398            "unsigned long scalar = __builtin_object_size(&gs.n, 1);\n",
5399            "unsigned long after = __builtin_object_size(&gs.n, 0);\n",
5400            "unsigned long into = __builtin_object_size(&gs.b[2], 1);\n",
5401            "unsigned long text = __builtin_object_size(\"hello\", 0);\n",
5402            "unsigned long dyn = __builtin_dynamic_object_size(gs.b, 1);\n",
5403        ));
5404        for (name, size) in [
5405            ("whole", 32),
5406            ("moved", 28),
5407            ("back", 4),
5408            ("outer", 24),
5409            ("inner", 8),
5410            ("scalar", 4),
5411            ("after", 16),
5412            ("into", 10),
5413            ("text", 6),
5414            ("dyn", 12),
5415        ] {
5416            let said = format!("global @{name} : i64 = {size},");
5417            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
5418        }
5419    }
5420
5421    /// A local is as knowable as a global, which is the whole point of asking on the way into a
5422    /// copy.
5423    ///
5424    /// A fortified header expands around the destination the caller wrote, and the destination a
5425    /// program most wants checked is the buffer on its own stack. Nothing in the answer depends on
5426    /// storage duration, unlike in a constant expression, where the address of a local is exactly
5427    /// what is not allowed.
5428    #[test]
5429    fn the_object_behind_an_address_can_be_one_with_automatic_storage() {
5430        let text = body(concat!(
5431            "struct S { char a[8]; int n; char b[12]; };\n",
5432            "unsigned long f(void) {\n",
5433            "  char loc[20];\n",
5434            "  struct S ls;\n",
5435            "  return __builtin_object_size(loc + 3, 0) + __builtin_object_size(ls.b + 2, 1);\n",
5436            "}\n",
5437        ));
5438        assert!(text.contains("iconst.i64 17"), "twenty bytes with three used: {text}");
5439        assert!(text.contains("iconst.i64 10"), "twelve bytes with two used: {text}");
5440    }
5441
5442    /// An address whose object the walk cannot see answers at whichever end of the range the kind
5443    /// asks for.
5444    ///
5445    /// The two bits are a question and the answer has to fit it. A kind wanting the largest object
5446    /// the address could be in has to name a size nothing is bigger than, and a kind wanting the
5447    /// smallest has to name a size nothing is smaller than, so the unknown answers are all ones
5448    /// and zero. That pair is what a fortified header compares against to decide whether to check
5449    /// at all, and getting either of them the wrong way round turns every unknown copy into an
5450    /// abort.
5451    #[test]
5452    fn an_address_with_no_object_in_sight_answers_at_the_end_of_the_range_its_kind_asks_for() {
5453        let text = ir(concat!(
5454            "struct T { int n; char f[]; };\n",
5455            "extern char *p;\n",
5456            "extern struct T *t;\n",
5457            "unsigned long largest = __builtin_object_size(p, 0);\n",
5458            "unsigned long nearest = __builtin_object_size(p, 1);\n",
5459            "unsigned long least = __builtin_object_size(p, 2);\n",
5460            "unsigned long tight = __builtin_object_size(p, 3);\n",
5461            "unsigned long flex = __builtin_object_size(t->f, 1);\n",
5462            "int says = __builtin_object_size(p, 0) == (unsigned long)-1;\n",
5463        ));
5464        for name in ["largest", "nearest", "flex"] {
5465            // All ones, printed as the signed rendering of the sixty four bits it is held in.
5466            // `says` is what pins the pattern itself, since it is the comparison a fortified
5467            // header writes and it folds only if every bit is set.
5468            let said = format!("global @{name} : i64 = -1,");
5469            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
5470        }
5471        for name in ["least", "tight"] {
5472            let said = format!("global @{name} : i64 = 0,");
5473            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
5474        }
5475        assert!(text.contains("global @says : i32 = 1,"), "{text}");
5476    }
5477
5478    /// The address is not evaluated, which is the rule `sizeof` follows and for the same reason.
5479    ///
5480    /// What the builtin reads is the shape of the expression rather than the value it would
5481    /// produce, so there is nothing to run. It matters because a fortified header writes the
5482    /// destination twice, once into the copy and once into the size, and a program whose
5483    /// destination is `*next()` would advance twice if this evaluated.
5484    #[test]
5485    fn the_address_an_object_size_is_asked_about_is_not_evaluated() {
5486        let text = body(concat!(
5487            "extern char *side(void);\n",
5488            "unsigned long f(void) { return __builtin_object_size(side(), 0); }\n",
5489        ));
5490        assert!(!text.contains("call"), "nothing is called: {text}");
5491    }
5492
5493    /// The kind has to be a constant in range, because it says which of four questions was asked.
5494    ///
5495    /// A number that is not known until the program runs decides nothing, and one outside the two
5496    /// bits names no question at all. gcc refuses both in one sentence and so does this.
5497    #[test]
5498    fn a_kind_that_is_not_one_of_the_four_is_refused() {
5499        for source in [
5500            "extern char *p;\nextern int k;\nunsigned long f(void) ".to_owned()
5501                + "{ return __builtin_object_size(p, k); }\n",
5502            "extern char *p;\nunsigned long f(void) { return __builtin_object_size(p, 4); }\n"
5503                .to_owned(),
5504            "extern char *p;\nunsigned long f(void) ".to_owned()
5505                + "{ return __builtin_dynamic_object_size(p, -1); }\n",
5506        ] {
5507            let messages = errors(&source);
5508            let named = messages.iter().any(|m| m.contains("E0709") && m.contains("0 to 3"));
5509            assert!(named, "expected a complaint about the kind in {messages:?}");
5510        }
5511    }
5512
5513    /// The pair that saves a place in a function and comes back to it, which is not a call.
5514    ///
5515    /// What the IR has to show is one instruction each and no call to anything: there is no
5516    /// function of either name for a call to reach, and a program that got one would fail to link.
5517    /// The save answers an `int`, which is the value that says how control got there.
5518    #[test]
5519    fn the_pair_that_saves_a_place_lowers_to_the_two_markers() {
5520        let text = ir(concat!(
5521            "void *buf[5];\n",
5522            "int f(void) {\n",
5523            "  if (__builtin_setjmp(buf)) return 2;\n",
5524            "  return 1;\n",
5525            "}\n",
5526            "void g(void) { __builtin_longjmp(buf, 1); }\n",
5527        ));
5528        assert!(text.contains("= setjmp_marker.i32 %0\n"), "the save answers a value: {text}");
5529        assert!(text.contains("    longjmp_marker %0\n"), "the restore answers nothing: {text}");
5530        assert!(!text.contains("call @"), "neither of them is a call: {text}");
5531    }
5532
5533    /// Every local of a function that saves a place lives in the frame, and not in a value.
5534    ///
5535    /// The edge a restore travels is not an edge of the graph, so a local the SSA construction
5536    /// renamed would answer the write that reached the read along the edges there are rather than
5537    /// the write that last ran. The second function here is the same code without the save, where
5538    /// the local is a value and there is no slot at all, which is what makes the first one a rule
5539    /// about the save and not about the shape of the code.
5540    #[test]
5541    fn a_local_of_a_function_that_saves_a_place_gets_a_slot() {
5542        let text = ir(concat!(
5543            "void *buf[5];\n",
5544            "int f(int x) { int a = 0; if (__builtin_setjmp(buf)) return a; a = 1; return x; }\n",
5545            "int g(int x) { int a = 0; if (x) return a; a = 1; return x; }\n",
5546        ));
5547        let (saves, plain) = text.split_once("func @g").expect("both functions");
5548        assert_eq!(saves.matches("= alloca").count(), 2, "the parameter and the local: {text}");
5549        assert!(saves.contains("store %9 -> %2"), "the local is written through: {text}");
5550        assert!(!plain.contains("alloca"), "nothing in the plain one needs a slot: {text}");
5551    }
5552
5553    /// What the save writes and where it leaves control, which is a new block.
5554    ///
5555    /// Four words: the frame pointer, the address to come back to, the stack pointer, and the
5556    /// address of the word the answer arrives in, which is this compiler's own and is why the
5557    /// block after the save opens with a load. The frame pointer is kept although the function
5558    /// asked for nothing and calls nothing, since the epilogue has to find the caller's frame
5559    /// after control has come back, and the frame is grown although there is one word in it,
5560    /// since a function control comes back into cannot use the red zone.
5561    #[test]
5562    fn the_save_writes_four_words_and_carries_on_in_a_new_block() {
5563        let text =
5564            asm(concat!("void *buf[5];\n", "int f(void) { return __builtin_setjmp(buf); }\n",));
5565        let body = text.split_once("\nf:\n").expect("the function").1;
5566        assert!(body.contains("\tmovq\t%rsp, %rbp\n"), "a frame pointer whatever: {text}");
5567        assert!(body.contains("\tsubq\t$8, %rsp\n"), "no red zone: {text}");
5568        assert!(body.contains("\tmovq\t%rbp, (%rax)\n"), "the frame pointer: {text}");
5569        assert!(body.contains("\tmovq\t%rsp, 16(%rax)\n"), "the stack pointer: {text}");
5570        assert!(body.contains("\tleaq\t.Lf_1(%rip), %rcx\n"), "where to come back to: {text}");
5571        assert!(body.contains("\tmovq\t%rcx, 8(%rax)\n"), "and that goes in the buffer: {text}");
5572        let back = body.split_once(".Lf_1:\n").expect("the block control comes back to").1;
5573        assert!(back.starts_with("\tmovq\t(%rsp), %rax\n"), "the answer is read back: {text}");
5574    }
5575
5576    /// Nothing stays in a register across the save, which is said with a write of every one of
5577    /// them and shows up as the callee-saved registers the function saves and restores.
5578    ///
5579    /// The restore puts back two registers and no others, so a function coming back through one
5580    /// finds every other register holding whatever the code between the two put there. The pushes
5581    /// are what makes the epilogue right on that path: the values popped are the caller's, off the
5582    /// stack the restore put back, rather than whatever is in the registers when control arrives.
5583    #[test]
5584    fn a_save_destroys_every_register_the_allocator_hands_out() {
5585        let text =
5586            asm(concat!("void *buf[5];\n", "int f(void) { return __builtin_setjmp(buf); }\n",));
5587        for reg in ["%rbx", "%r12", "%r13", "%r14", "%r15"] {
5588            assert!(text.contains(&format!("\tpushq\t{reg}\n")), "{reg} is saved: {text}");
5589            assert!(text.contains(&format!("\tpopq\t{reg}\n")), "{reg} is restored: {text}");
5590        }
5591    }
5592
5593    /// The restore puts both registers back before it goes, at every level.
5594    ///
5595    /// The jump reads the two of them as well as the address it goes through, which is what keeps
5596    /// it behind them. Without that the two instructions write registers nothing reads, and the
5597    /// scheduler at `-O2` puts the jump in front of both and the program comes back to a frame
5598    /// that is not there.
5599    #[test]
5600    fn the_restore_puts_the_frame_back_before_it_jumps() {
5601        for level in [rucc_session::OptLevel::O0, rucc_session::OptLevel::O2] {
5602            let mut opts = options();
5603            opts.emit = EmitKind::Asm;
5604            opts.opt_level = level;
5605            let source = "void *buf[5];\nvoid g(void) { __builtin_longjmp(buf, 1); }\n";
5606            let result = run(&opts, source);
5607            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
5608            let text = result.text().to_owned();
5609            let jump = text.find("\tjmp\t*%").unwrap_or_else(|| panic!("an indirect jump: {text}"));
5610            let stack = text.find(", %rsp\n").unwrap_or_else(|| panic!("the stack back: {text}"));
5611            let frame = text.find(", %rbp\n").unwrap_or_else(|| panic!("the frame back: {text}"));
5612            assert!(stack < jump, "the stack goes back first at {level:?}: {text}");
5613            assert!(frame < jump, "and so does the frame at {level:?}: {text}");
5614        }
5615    }
5616
5617    /// The second argument of the restore has one allowed value, which gcc 16.2.0 also insists on.
5618    ///
5619    /// This pair does not carry a value back the way the library's `longjmp` does, because what
5620    /// the matching save answers is decided by which way control reached it. So the argument is a
5621    /// place-holder, and a program that wrote anything else meant the library's function.
5622    #[test]
5623    fn a_longjmp_whose_second_argument_is_not_one_is_turned_down() {
5624        for source in [
5625            "void *buf[5];\nvoid f(void) { __builtin_longjmp(buf, 0); }\n",
5626            "void *buf[5];\nextern int v;\nvoid f(void) { __builtin_longjmp(buf, v); }\n",
5627        ] {
5628            let messages = errors(source);
5629            let named = messages.iter().any(|m| m.contains("E0710"));
5630            assert!(named, "expected a complaint about the value in {messages:?}");
5631        }
5632    }
5633
5634    /// A `static` function nothing refers to is not emitted, and one that is refered to is.
5635    ///
5636    /// The pair is written as one program so that the two answers come out of one walk. What
5637    /// makes the difference is the call in `main` and nothing else about either definition.
5638    #[test]
5639    fn a_static_function_nothing_refers_to_is_not_emitted() {
5640        let text = ir("static int dropped(void) { return 1; }\n\
5641                       static int kept(void) { return 2; }\n\
5642                       int main(void) { return kept(); }\n");
5643        assert!(text.contains("func @kept"), "{text}");
5644        assert!(!text.contains("dropped"), "{text}");
5645    }
5646
5647    /// The set is transitive, so two of them that only call each other are both dropped.
5648    ///
5649    /// Counting the references to a name would keep this pair, since each is named once, and
5650    /// that is the mistake this is here to catch: what decides it is whether a root reaches the
5651    /// definition, and a root is something the file has a reason to emit on its own.
5652    #[test]
5653    fn two_static_functions_that_only_call_each_other_are_both_dropped() {
5654        let text = ir("static int ping(void);\n\
5655                       static int pong(void) { return ping(); }\n\
5656                       static int ping(void) { return pong(); }\n\
5657                       int main(void) { return 0; }\n");
5658        assert!(!text.contains("ping"), "{text}");
5659        assert!(!text.contains("pong"), "{text}");
5660    }
5661
5662    /// Everything that names a function keeps it, whether or not the name is being called.
5663    ///
5664    /// An address taken in a body, an image that holds one, and a body that is only reached
5665    /// through another `static` function are three different ways for a definition to be needed
5666    /// and none of them is a call at the top level of a reachable function.
5667    #[test]
5668    fn naming_a_static_function_anywhere_keeps_it() {
5669        let text = ir("static int by_address(void) { return 1; }\n\
5670                       static int in_an_image(void) { return 2; }\n\
5671                       static int deeper(void) { return 3; }\n\
5672                       static int reaches_deeper(void) { return deeper(); }\n\
5673                       static int (*table[1])(void) = {in_an_image};\n\
5674                       int main(void) {\n\
5675                         int (*p)(void) = by_address;\n\
5676                         return p() + table[0]() + reaches_deeper();\n\
5677                       }\n");
5678        for kept in ["by_address", "in_an_image", "deeper", "reaches_deeper"] {
5679            assert!(text.contains(&format!("func @{kept}")), "expected {kept} in:\n{text}");
5680        }
5681    }
5682
5683    /// An attribute that says something outside the file reaches it keeps the definition.
5684    ///
5685    /// None of the five is implemented as anything else yet, and this is the part of each of
5686    /// them that a program notices first: a symbol a linker script names or a function the
5687    /// run-up to `main` calls is not written about anywhere a C file can see.
5688    #[test]
5689    fn an_attribute_keeps_a_static_function_nothing_refers_to() {
5690        for attribute in ["used", "retain", "constructor", "destructor", "__used__"] {
5691            let source = format!(
5692                "__attribute__(({attribute})) static int kept(void) {{ return 1; }}\n\
5693                 int main(void) {{ return 0; }}\n"
5694            );
5695            let text = ir(&source);
5696            assert!(text.contains("func @kept"), "for {attribute}:\n{text}");
5697        }
5698    }
5699
5700    /// A function with external linkage is emitted whatever this file does with it, because
5701    /// another one may call it, and that is what external linkage is.
5702    #[test]
5703    fn a_function_anything_could_call_is_emitted_without_being_called() {
5704        let text =
5705            ir("int nobody_here_calls_it(void) { return 1; }\nint main(void) { return 0; }\n");
5706        assert!(text.contains("func @nobody_here_calls_it"), "{text}");
5707    }
5708
5709    /// Four of the classification builtins are operators C already has, and become those.
5710    ///
5711    /// What the standard's macro promises over the operator is that it does not raise the
5712    /// invalid operation exception on a quiet NaN. This compiler does not model floating point
5713    /// exceptions, so there is nothing left for a node of its own to carry and a second way of
5714    /// spelling a comparison would be a second thing every pass has to know about.
5715    #[test]
5716    fn a_classification_c_has_an_operator_for_is_that_operator() {
5717        for (builtin, operator) in [
5718            ("__builtin_isgreater", "binary >"),
5719            ("__builtin_isgreaterequal", "binary >="),
5720            ("__builtin_isless", "binary <"),
5721            ("__builtin_islessequal", "binary <="),
5722        ] {
5723            let source = format!("int f(double x, double y) {{ return {builtin}(x, y); }}\n");
5724            let text = tast(&source);
5725            assert!(text.contains(&format!("{operator} : int")), "for {builtin}:\n{text}");
5726        }
5727    }
5728
5729    /// The rest of the family are comparisons in the IR and never a call to anything.
5730    ///
5731    /// `math.h` defines the macro of each of these names as the builtin of the same name, so
5732    /// there is no function under any of them for a call to reach. `isunordered` and
5733    /// `islessgreater` are predicates the IR's comparison already has, `isnan` is the value that
5734    /// is unordered with itself, and the two that ask about a magnitude are written against the
5735    /// infinities. `signbit` is the one that is not a question about the value, since a negative
5736    /// zero compares equal to a positive one, so its answer comes from the bits.
5737    #[test]
5738    fn the_classification_builtins_are_comparisons_and_not_calls() {
5739        let text = body("int f(double x, double y) { return __builtin_isunordered(x, y); }\n");
5740        assert_eq!(
5741            text,
5742            "block0(%0: f64, %1: f64):\n    %2 = fcmp uno %0, %1\n    %3 = zext.i32 \
5743                          %2\n    return %3\n"
5744        );
5745
5746        // Not `x != y`, which is true when the two are unordered and so is true of a NaN.
5747        let text = body("int f(double x, double y) { return __builtin_islessgreater(x, y); }\n");
5748        assert!(text.contains("fcmp one %0, %1"), "{text}");
5749
5750        let text = body("int f(double x) { return __builtin_isnan(x); }\n");
5751        assert!(text.contains("fcmp uno %0, %0"), "{text}");
5752
5753        let text = body("int f(double x) { return __builtin_isinf(x); }\n");
5754        assert!(text.contains("fconst.f64 0x7ff0000000000000"), "{text}");
5755        assert!(text.contains("fconst.f64 0xfff0000000000000"), "{text}");
5756        assert!(text.contains("%3 = fcmp oeq %0, %1"), "{text}");
5757        assert!(text.contains("%4 = fcmp oeq %0, %2"), "{text}");
5758        assert!(text.contains("%5 = or %3, %4"), "{text}");
5759
5760        // Strictly between the two infinities, which a NaN is not, because an ordered comparison
5761        // against either of them is false. That is what makes this one test rather than two.
5762        let text = body("int f(double x) { return __builtin_isfinite(x); }\n");
5763        assert!(text.contains("%3 = fcmp olt %2, %0"), "{text}");
5764        assert!(text.contains("%4 = fcmp olt %0, %1"), "{text}");
5765        assert!(text.contains("%5 = and %3, %4"), "{text}");
5766
5767        let text = body("int f(double x) { return __builtin_signbit(x); }\n");
5768        assert!(text.contains("%1 = bitcast.i64 %0"), "{text}");
5769        assert!(text.contains("icmp slt %1, %2"), "{text}");
5770
5771        // The same question of a value in the target's widest format, where the bits are eighty
5772        // and the object they sit in is sixteen bytes.
5773        let text = body("int f(long double x) { return __builtin_signbitl(x); }\n");
5774        assert!(text.contains("%1 = bitcast.i80 %0"), "{text}");
5775
5776        // The operand is evaluated once however many times it is compared, which is the whole
5777        // reason these are nodes rather than a rewriting into the operators.
5778        let text = body("double g(void);\nint f(void) { return __builtin_isnan(g()); }\n");
5779        assert_eq!(text.matches("call @g()").count(), 1, "{text}");
5780    }
5781
5782    /// A spelling that names a width converts its argument before it asks.
5783    ///
5784    /// gcc gives `__builtin_isinff` a `float` parameter and `__builtin_isinf` no parameter type
5785    /// at all, and the difference is visible rather than academic: `1e300` does not fit in a
5786    /// `float`, so converting it first is an infinity and not converting it is not. Both numbers
5787    /// here are what gcc 16 gives.
5788    #[test]
5789    fn a_classification_spelling_that_names_a_width_converts_before_it_asks() {
5790        let text = ir(concat!(
5791            "int a = __builtin_isinff(1e300);\n",
5792            "int b = __builtin_isinf(1e300);\n",
5793            // Folded here rather than compared at run time, because a question about a value has
5794            // an answer as soon as the value is a constant, and an initializer for an object
5795            // with static storage duration has to have one.
5796            "int c = __builtin_isnan(0.0);\n",
5797            "int d = __builtin_signbit(-0.0);\n",
5798            "int e = __builtin_islessgreater(1.0, 2.0);\n",
5799        ));
5800        assert!(text.contains("global @a : i32 = 1,"), "{text}");
5801        assert!(text.contains("global @b : i32 = 0,"), "{text}");
5802        assert!(text.contains("global @c : i32 = 0,"), "{text}");
5803        assert!(text.contains("global @d : i32 = 1,"), "{text}");
5804        assert!(text.contains("global @e : i32 = 1,"), "{text}");
5805    }
5806
5807    /// An argument that is not floating point is refused, in gcc's words.
5808    #[test]
5809    fn a_classification_builtin_refuses_an_argument_that_is_not_floating_point() {
5810        let mut opts = options();
5811        opts.emit = EmitKind::Ir;
5812        let source = concat!(
5813            "int a(int x) { return __builtin_isnan(x); }\n",
5814            "int b(int x, int y) { return __builtin_isunordered(x, y); }\n",
5815            "int c(double x) { return __builtin_isnan(x, x); }\n",
5816        );
5817        let messages = run(&opts, source).messages;
5818        assert_eq!(
5819            messages,
5820            [
5821                "/main.c:1:23: error: non-floating-point argument in call to function \
5822                 '__builtin_isnan' [E0685]",
5823                "/main.c:2:30: error: non-floating-point arguments in call to function \
5824                 '__builtin_isunordered' [E0685]",
5825                "/main.c:3:26: error: too many arguments to function '__builtin_isnan' [E0511]",
5826            ]
5827        );
5828    }
5829
5830    /// The three of the family that need a constant of the format other than an infinity.
5831    ///
5832    /// `isnormal` is the one that needs the smallest normal, and it is asked of the magnitude, so
5833    /// the sign comes off first and what is left is the same shape as `isfinite`. `isinf_sign` is
5834    /// the one whose answer is a number: the two comparisons `isinf` builds, subtracted rather
5835    /// than combined. `fpclassify` is four questions of one value and five answers to pick from,
5836    /// and the picking is a mask because all five are constants and neither of them can have an
5837    /// effect.
5838    #[test]
5839    fn the_last_three_classification_builtins_are_comparisons_and_not_calls() {
5840        let text = body("int f(double x) { return __builtin_isnormal(x); }\n");
5841        // The sign off, which is the magnitude, and then the range, asked of the bits rather than
5842        // of the number, since the encoding of a value whose sign bit is clear rises with the
5843        // value in every format this compiles for.
5844        assert!(text.contains("%1 = bitcast.i64 %0"), "{text}");
5845        assert!(text.contains("%2 = iconst.i64 9223372036854775807"), "{text}");
5846        assert!(text.contains("%3 = and %1, %2"), "{text}");
5847        assert!(text.contains("%4 = iconst.i64 4503599627370496"), "{text}");
5848        assert!(text.contains("%5 = iconst.i64 9218868437227405312"), "{text}");
5849        assert!(text.contains("%6 = icmp uge %3, %4"), "{text}");
5850        assert!(text.contains("%7 = icmp ult %3, %5"), "{text}");
5851        assert!(text.contains("%8 = and %6, %7"), "{text}");
5852
5853        // The same question in the target's widest format, where the smallest normal has the
5854        // leading significand bit stored rather than implied, so its encoding is two bits and not
5855        // one.
5856        let text = body("int f(long double x) { return __builtin_isnormal(x); }\n");
5857        assert!(text.contains("%4 = iconst.i80 27670116110564327424"), "{text}");
5858        assert!(text.contains("%5 = iconst.i80 604453686435277732577280"), "{text}");
5859
5860        let text = body("int f(double x) { return __builtin_isinf_sign(x); }\n");
5861        assert!(text.contains("%3 = fcmp oeq %0, %1"), "{text}");
5862        assert!(text.contains("%4 = fcmp oeq %0, %2"), "{text}");
5863        assert!(text.contains("%7 = sub %5, %6"), "{text}");
5864
5865        let text = body("int f(double x) { return __builtin_fpclassify(0, 1, 2, 3, 4, x); }\n");
5866        assert!(text.contains("fcmp uno %0, %0"), "{text}");
5867        assert!(text.contains("fcmp oeq %0, %6"), "{text}");
5868        // Four questions, each of them a bit widened into the type of the answer and then spread
5869        // into a mask that picks between the answer and whatever the questions after it settled
5870        // on. Nothing sign extends, because no rule lowers a sign extension out of one bit.
5871        assert_eq!(text.matches(" = zext.i32 ").count(), 4, "{text}");
5872        assert_eq!(text.matches(" = xor ").count(), 4, "{text}");
5873        assert!(!text.contains("call"), "{text}");
5874
5875        // The value is evaluated once however many questions are asked of it, which is the whole
5876        // reason `fpclassify` is a node rather than the chain of tests it turns into.
5877        let text = body(concat!(
5878            "double g(void);\n",
5879            "int f(void) { return __builtin_fpclassify(0, 1, 2, 3, 4, g()); }\n",
5880        ));
5881        assert_eq!(text.matches("call @g()").count(), 1, "{text}");
5882    }
5883
5884    /// Each of the three answers a constant where its operand is one.
5885    ///
5886    /// glibc's `fpclassify` macro is exactly this builtin, so a program that writes
5887    /// `fpclassify(0.0)` in a static initializer is writing this, and it has to have a value at
5888    /// translation time or the program is refused rather than merely compiled slowly. Every
5889    /// number here is what gcc 16 gives.
5890    #[test]
5891    fn the_last_three_classification_builtins_fold_where_their_operand_is_a_constant() {
5892        let text = ir(concat!(
5893            "int a = __builtin_isnormal(1.0);\n",
5894            "int b = __builtin_isnormal(0.0);\n",
5895            "int c = __builtin_isnormal(1.0 / 0.0);\n",
5896            "int d = __builtin_isinf_sign(-1.0 / 0.0);\n",
5897            "int e = __builtin_isinf_sign(1.0);\n",
5898            "int g = __builtin_fpclassify(0, 1, 2, 3, 4, 0.0);\n",
5899            "int h = __builtin_fpclassify(0, 1, 2, 3, 4, 1.0);\n",
5900            "int i = __builtin_fpclassify(0, 1, 2, 3, 4, 1.0 / 0.0);\n",
5901        ));
5902        assert!(text.contains("global @a : i32 = 1,"), "{text}");
5903        assert!(text.contains("global @b : i32 = 0,"), "{text}");
5904        assert!(text.contains("global @c : i32 = 0,"), "{text}");
5905        assert!(text.contains("global @d : i32 = -1,"), "{text}");
5906        assert!(text.contains("global @e : i32 = 0,"), "{text}");
5907        assert!(text.contains("global @g : i32 = 4,"), "{text}");
5908        assert!(text.contains("global @h : i32 = 2,"), "{text}");
5909        assert!(text.contains("global @i : i32 = 1,"), "{text}");
5910    }
5911
5912    /// `fpclassify` refuses what gcc refuses, in gcc's words.
5913    ///
5914    /// The five answers have to be integer constant expressions, because what the builtin does is
5915    /// pick one of them and a pick between values that are not known here would be a chain of
5916    /// conditionals over expressions the call has already evaluated.
5917    #[test]
5918    fn fpclassify_refuses_an_answer_that_is_not_an_integer_constant() {
5919        let mut opts = options();
5920        opts.emit = EmitKind::Ir;
5921        let source = concat!(
5922            "int a(double x, int n) { return __builtin_fpclassify(0, 1, n, 3, 4, x); }\n",
5923            "int b(double x) { return __builtin_fpclassify(0, 1, 2, 3, x); }\n",
5924            "int c(int x) { return __builtin_fpclassify(0, 1, 2, 3, 4, x); }\n",
5925        );
5926        let messages = run(&opts, source).messages;
5927        assert_eq!(
5928            messages,
5929            [
5930                "/main.c:1:60: error: non-const integer argument 3 in call to function \
5931                 '__builtin_fpclassify' [E0687]",
5932                "/main.c:2:26: error: too few arguments to function '__builtin_fpclassify' \
5933                 [E0511]",
5934                "/main.c:3:23: error: non-floating-point argument in call to function \
5935                 '__builtin_fpclassify' [E0685]",
5936            ]
5937        );
5938    }
5939
5940    /// A builtin whose answer is a constant is one, and is not a call to the library.
5941    ///
5942    /// This is the reason the family is answered in the front end at all. `double x =
5943    /// __builtin_inf();` at file scope initializes an object with static storage duration, so
5944    /// there is no point in the program at which a call could be made, and a compiler that
5945    /// lowered it to one would reject a program gcc accepts. Every number here is the encoding
5946    /// gcc 16 gives on x86-64.
5947    #[test]
5948    fn a_builtin_whose_answer_is_a_constant_is_one_and_not_a_call() {
5949        let text = ir(concat!(
5950            "double a = __builtin_inf();\n",
5951            "float b = __builtin_huge_valf();\n",
5952            "long double c = __builtin_infl();\n",
5953            "double d = __builtin_huge_val();\n",
5954        ));
5955        assert!(text.contains("global @a : f64 = 0x7ff0000000000000,"), "{text}");
5956        assert!(text.contains("global @b : f32 = 0x7f800000,"), "{text}");
5957        assert!(text.contains("f80 0x7fff8000000000000000"), "{text}");
5958        assert!(text.contains("global @d : f64 = 0x7ff0000000000000,"), "{text}");
5959        assert!(!text.contains("call"), "{text}");
5960    }
5961
5962    /// A nan is written with the payload the program asked for.
5963    ///
5964    /// The string is read the way `strtoull` reads a number, which is what the library function
5965    /// of the same name does with it, and a string that is not one at all leaves the call for the
5966    /// library to answer at run time. A quiet nan has the high fraction bit set and a signalling
5967    /// one does not, except that a signalling nan with nothing in it would be an infinity, so it
5968    /// gets the next bit down instead. Every encoding here was measured against gcc 16, the two
5969    /// `long double` ones on a machine with the x87 format.
5970    #[test]
5971    fn a_nan_is_written_with_the_payload_the_program_asked_for() {
5972        let text = ir(concat!(
5973            "double a = __builtin_nan(\"\");\n",
5974            "double b = __builtin_nan(\"0x1\");\n",
5975            // Octal, since there is a leading zero, so this is eight and not ten.
5976            "double c = __builtin_nan(\"010\");\n",
5977            "double d = __builtin_nans(\"\");\n",
5978            "double e = __builtin_nans(\"0x1\");\n",
5979            "float f = __builtin_nanf(\"0x1\");\n",
5980            "float g = __builtin_nansf(\"\");\n",
5981            "long double h = __builtin_nansl(\"\");\n",
5982        ));
5983        assert!(text.contains("global @a : f64 = 0x7ff8000000000000,"), "{text}");
5984        assert!(text.contains("global @b : f64 = 0x7ff8000000000001,"), "{text}");
5985        assert!(text.contains("global @c : f64 = 0x7ff8000000000008,"), "{text}");
5986        assert!(text.contains("global @d : f64 = 0x7ff4000000000000,"), "{text}");
5987        assert!(text.contains("global @e : f64 = 0x7ff0000000000001,"), "{text}");
5988        assert!(text.contains("global @f : f32 = 0x7fc00001,"), "{text}");
5989        assert!(text.contains("global @g : f32 = 0x7fa00000,"), "{text}");
5990        assert!(text.contains("f80 0x7fffa000000000000000"), "{text}");
5991
5992        // A payload that is not a number, and one that is not known until run time, are both
5993        // left to the library, which is the same thing gcc emits for either of them.
5994        let text = ir(concat!(
5995            "double f(const char *p) { return __builtin_nan(p); }\n",
5996            "double g(void) { return __builtin_nans(\"1x\"); }\n",
5997        ));
5998        assert_eq!(text.matches("call @nan(").count(), 1, "{text}");
5999        assert_eq!(text.matches("call @nans(").count(), 1, "{text}");
6000    }
6001
6002    /// The length and the order of a string literal are known here.
6003    ///
6004    /// A program that asks for either of them is asking about something the translation already
6005    /// has in front of it, and folding is not only an optimization: `execute/921007-1.c` in the
6006    /// torture suite calls `__builtin_strcmp` in a file that defines its own `strcmp` with a
6007    /// different signature, so leaving the call behind is a name collision that gcc does not
6008    /// have. The comparison is over `unsigned char`, which is why the second one is negative.
6009    #[test]
6010    fn the_length_and_the_order_of_a_string_literal_are_known_here() {
6011        let text = ir(concat!(
6012            "unsigned long a = __builtin_strlen(\"hello\");\n",
6013            "unsigned long b = __builtin_strlen(\"a\\0bc\");\n",
6014            "int c = __builtin_strcmp(\"X\", \"X\\376\") < 0;\n",
6015            "int d = __builtin_strcmp(\"abc\", \"abc\");\n",
6016            "int e = __builtin_strcmp(\"abc\", \"ab\") > 0;\n",
6017        ));
6018        assert!(text.contains("global @a : i64 = 5,"), "{text}");
6019        assert!(text.contains("global @b : i64 = 1,"), "{text}");
6020        assert!(text.contains("global @c : i32 = 1,"), "{text}");
6021        assert!(text.contains("global @d : i32 = 0,"), "{text}");
6022        assert!(text.contains("global @e : i32 = 1,"), "{text}");
6023        assert!(!text.contains("call"), "{text}");
6024
6025        // An argument that is not a literal is the library's to answer, as it has to be.
6026        let text = ir("unsigned long f(const char *p) { return __builtin_strlen(p); }\n");
6027        assert!(text.contains("call @strlen("), "{text}");
6028    }
6029
6030    /// A sign builtin is a mask over the bits, and is not a call.
6031    ///
6032    /// `fabs` and `copysign` are in the math library rather than the C one, so a program that
6033    /// only ever wrote the prefixed spelling never asked for `-lm` and a call left behind here
6034    /// would not link. Neither needs anything the library has: one clears the sign bit and the
6035    /// other takes it from the second operand, and every other bit goes through untouched.
6036    #[test]
6037    fn a_sign_builtin_is_a_mask_over_the_bits_and_not_a_call() {
6038        let text = body("double f(double x) { return __builtin_fabs(x); }\n");
6039        assert!(text.contains("bitcast.i64 %0"), "{text}");
6040        assert!(text.contains("iconst.i64 9223372036854775807"), "{text}");
6041        assert!(text.contains("and %1, %2"), "{text}");
6042        assert!(text.contains("bitcast.f64 %3"), "{text}");
6043        assert!(!text.contains("call"), "{text}");
6044
6045        let text = body("double f(double x, double y) { return __builtin_copysign(x, y); }\n");
6046        assert!(text.contains("iconst.i64 -9223372036854775808"), "{text}");
6047        assert!(text.contains("%8 = or %4, %7"), "{text}");
6048        assert!(!text.contains("call"), "{text}");
6049
6050        // The x87 format, whose value is eighty bits sitting in an object of sixteen. The mask is
6051        // as wide as the value and not as wide as the object, so the padding is not part of it.
6052        let text = body("long double f(long double x) { return __builtin_fabsl(x); }\n");
6053        assert!(text.contains("bitcast.i80 %0"), "{text}");
6054        assert!(text.contains("bitcast.f80"), "{text}");
6055
6056        // The width a name does not spell out is `double`, so a `float` argument widens first and
6057        // the answer is a `double`, which is what gcc's declaration of it says.
6058        let text = body("double f(float x) { return __builtin_fabs(x); }\n");
6059        assert!(text.contains("fpext.f64 %0"), "{text}");
6060        assert!(text.contains("bitcast.i64 %1"), "{text}");
6061    }
6062
6063    /// The plain math library names are the same mask, which is what makes a program link.
6064    ///
6065    /// `math.h` declares `fabs` and never spells `__builtin_fabs`, so the plain name is the one
6066    /// every program that includes the header reaches. Recognising only the prefixed spelling
6067    /// leaves a call to the math library behind, and the math library is not on the link line
6068    /// unless the program asked for `-lm`. parson is the project that shows it: its makefile has
6069    /// no `-lm`, it does not need one under gcc, and `undefined reference to 'fabs'` is where the
6070    /// build stopped. That is issue 630.
6071    #[test]
6072    fn the_plain_math_names_are_the_same_mask_and_not_a_call() {
6073        let text =
6074            body(concat!("double fabs(double x);\n", "double f(double x) { return fabs(x); }\n",));
6075        assert!(text.contains("iconst.i64 9223372036854775807"), "{text}");
6076        assert!(!text.contains("call"), "{text}");
6077
6078        let text =
6079            body(concat!("float fabsf(float x);\n", "float f(float x) { return fabsf(x); }\n",));
6080        assert!(text.contains("bitcast.i32 %0"), "{text}");
6081        assert!(!text.contains("call"), "{text}");
6082
6083        let text = body(concat!(
6084            "double copysign(double x, double y);\n",
6085            "double f(double x, double y) { return copysign(x, y); }\n",
6086        ));
6087        assert!(text.contains("iconst.i64 -9223372036854775808"), "{text}");
6088        assert!(!text.contains("call"), "{text}");
6089
6090        let text = body(concat!(
6091            "float copysignf(float x, float y);\n",
6092            "float f(float x, float y) { return copysignf(x, y); }\n",
6093        ));
6094        assert!(!text.contains("call"), "{text}");
6095
6096        // The `long double` pair is left alone on purpose. The prefixed spelling of both stops in
6097        // the back end with `no rule lowers a bitcast producing an i80`, so expanding the plain
6098        // name would trade a link error for a worse one. They go in with issue 540.
6099        let text = ir(concat!(
6100            "long double fabsl(long double x);\n",
6101            "long double f(long double x) { return fabsl(x); }\n",
6102        ));
6103        assert!(text.contains("call @fabsl"), "{text}");
6104    }
6105
6106    /// A plain math name the program took is the program's own function.
6107    ///
6108    /// The same four ways as the absolute value family next door, asked again here because these
6109    /// two go through a different path: the plain names of this family are taken after the call
6110    /// has been checked against the declaration, and the declaration is the whole reason the
6111    /// question can be answered at all. Measured against gcc 16.2.0, which calls the program's
6112    /// function in every one of them.
6113    #[test]
6114    fn a_plain_math_name_the_program_took_is_the_programs_own_function() {
6115        let taken = concat!(
6116            "static double fabs(double b) { return 7; }\n",
6117            "double f(double x) { return fabs(x); }\n",
6118        );
6119        assert!(ir(taken).contains("call @fabs"), "a static definition is the program's own");
6120
6121        let retyped = concat!("int fabs(int b);\n", "int f(int x) { return fabs(x); }\n");
6122        assert!(ir(retyped).contains("call @fabs"), "another type is another function");
6123
6124        let plain = concat!("double fabs(double b);\n", "double f(double x) { return fabs(x); }\n");
6125        let mut opts = options();
6126        opts.emit = EmitKind::Ir;
6127        assert!(!run(&opts, plain).text().contains("call @fabs"), "the library's by default");
6128
6129        opts.builtins = false;
6130        assert!(run(&opts, plain).text().contains("call @fabs"), "-fno-builtin");
6131
6132        opts.builtins = true;
6133        opts.no_builtin = vec!["fabs".to_owned()];
6134        assert!(run(&opts, plain).text().contains("call @fabs"), "-fno-builtin-fabs");
6135        let one = concat!(
6136            "double copysign(double a, double b);\n",
6137            "double f(double x) { return copysign(x, 1.0); }\n",
6138        );
6139        assert!(!run(&opts, one).text().contains("call @copysign"), "one name and not the family");
6140
6141        // The prefixed spelling is untouched by any of it, which is what the prefix is for.
6142        opts.no_builtin = Vec::new();
6143        opts.builtins = false;
6144        let prefixed = "double f(double x) { return __builtin_fabs(x); }\n";
6145        assert!(!run(&opts, prefixed).text().contains("call @fabs"), "the prefix is not a library");
6146    }
6147
6148    /// The sign builtins answer a zero and a nan the way the bits say.
6149    ///
6150    /// This is why they are described over the bits rather than written with comparisons and
6151    /// negation. A negative zero compares equal to a positive one and has a sign bit to clear,
6152    /// and a nan compares equal to nothing at all and keeps its payload through both operations.
6153    /// `execute/ieee/copysign1.c` in the torture suite is the test that notices, because it
6154    /// compares its answers with `memcmp`. Every number here is what gcc 16 gives, the two in the
6155    /// x87 format measured on a machine that has it.
6156    #[test]
6157    fn the_sign_builtins_answer_a_zero_and_a_nan_the_way_the_bits_say() {
6158        let text = ir(concat!(
6159            "double a = __builtin_fabs(-3.5);\n",
6160            "double b = __builtin_copysign(1.0, -0.0);\n",
6161            "double c = __builtin_copysign(0.0, -2.0);\n",
6162            // The payload survives both, and only the sign bit moves.
6163            "double d = __builtin_copysign(-__builtin_nan(\"\"), 1.0);\n",
6164            "double e = __builtin_fabs(-__builtin_nan(\"0x1\"));\n",
6165            "float g = __builtin_copysignf(-0.0f, 2.0f);\n",
6166            "long double h = __builtin_copysignl(1.0L, -1.0L);\n",
6167            "long double i = __builtin_fabsl(-__builtin_infl());\n",
6168        ));
6169        assert!(text.contains("global @a : f64 = 0x400c000000000000,"), "{text}");
6170        assert!(text.contains("global @b : f64 = 0xbff0000000000000,"), "{text}");
6171        assert!(text.contains("global @c : f64 = 0x8000000000000000,"), "{text}");
6172        assert!(text.contains("global @d : f64 = 0x7ff8000000000000,"), "{text}");
6173        assert!(text.contains("global @e : f64 = 0x7ff8000000000001,"), "{text}");
6174        assert!(text.contains("global @g : f32 = 0x0,"), "{text}");
6175        assert!(text.contains("f80 0xbfff8000000000000000"), "{text}");
6176        assert!(text.contains("f80 0x7fff8000000000000000"), "{text}");
6177    }
6178
6179    /// The complex builtins are the halves of the value, and are not a call.
6180    ///
6181    /// `conj`, `creal` and `cimag` are `~`, `__real__` and `__imag__` under the names `complex.h`
6182    /// gives them, so there is nothing for the math library to do that the translation cannot do
6183    /// with the object in front of it. Leaving the call behind would not link either, since all
6184    /// three are in the math library and a program that wrote one never had a reason to ask for
6185    /// `-lm`. Measured against gcc 16.2.0, which emits no call for any of them even at `-O0`.
6186    #[test]
6187    fn the_complex_builtins_are_the_halves_of_the_value_and_not_a_call() {
6188        let text = body("double f(_Complex double z) { return __builtin_creal(z); }\n");
6189        assert!(!text.contains("call"), "{text}");
6190        let text = body("double f(_Complex double z) { return __builtin_cimag(z); }\n");
6191        assert!(!text.contains("call"), "{text}");
6192
6193        // The conjugate is the imaginary half negated and the real half as it stands, so there is
6194        // one negation in it. A complex negation is the one with two.
6195        let text = body("_Complex double f(_Complex double z) { return __builtin_conj(z); }\n");
6196        assert_eq!(text.matches("fneg").count(), 1, "{text}");
6197        assert!(!text.contains("call"), "{text}");
6198        let negated = body("_Complex double f(_Complex double z) { return -z; }\n");
6199        assert_eq!(negated.matches("fneg").count(), 2, "{negated}");
6200
6201        // `~` on a complex operand is the same operator, which is the spelling the language has
6202        // had all along and the one a program that never included the header writes.
6203        let written = body("_Complex double f(_Complex double z) { return ~z; }\n");
6204        assert_eq!(written, text, "the name and the operator are the same thing");
6205
6206        // The plain names, which are the ones the header declares and so the ones programs write.
6207        let text = body(concat!(
6208            "double creal(_Complex double z);\n",
6209            "double f(_Complex double z) { return creal(z); }\n",
6210        ));
6211        assert!(!text.contains("call"), "{text}");
6212        let text = body(concat!(
6213            "_Complex float conjf(_Complex float z);\n",
6214            "_Complex float f(_Complex float z) { return conjf(z); }\n",
6215        ));
6216        assert_eq!(text.matches("fneg").count(), 1, "{text}");
6217        assert!(!text.contains("call"), "{text}");
6218
6219        // A program that took the name means its own function, the same four ways the absolute
6220        // value family next door asks it.
6221        let taken = concat!(
6222            "static double creal(_Complex double z) { return 7; }\n",
6223            "double f(_Complex double z) { return creal(z); }\n",
6224        );
6225        assert!(ir(taken).contains("call @creal"), "a static definition is the program's own");
6226        let retyped = concat!("int cimag(int z);\n", "int f(int z) { return cimag(z); }\n");
6227        assert!(ir(retyped).contains("call @cimag"), "another type is another function");
6228        let plain = concat!(
6229            "double cimag(_Complex double z);\n",
6230            "double f(_Complex double z) { return cimag(z); }\n",
6231        );
6232        let mut opts = options();
6233        opts.emit = EmitKind::Ir;
6234        opts.builtins = false;
6235        assert!(run(&opts, plain).text().contains("call @cimag"), "-fno-builtin");
6236        opts.builtins = true;
6237        opts.no_builtin = vec!["cimag".to_owned()];
6238        assert!(run(&opts, plain).text().contains("call @cimag"), "-fno-builtin-cimag");
6239
6240        // A constant folds, which is what a static initializer written with one needs.
6241        let text = ir(concat!(
6242            "double a = __builtin_creal(1.5 + 2.5i);\n",
6243            "double b = __builtin_cimag(1.5 + 2.5i);\n",
6244            "_Complex double c = __builtin_conj(1.5 + 2.5i);\n",
6245        ));
6246        assert!(text.contains("global @a : f64 = 0x3ff8000000000000,"), "{text}");
6247        assert!(text.contains("global @b : f64 = 0x4004000000000000,"), "{text}");
6248        assert!(
6249            text.contains("{ f64 0x3ff8000000000000, f64 0xc004000000000000 }"),
6250            "the conjugate of a constant is the constant with the second half negated: {text}"
6251        );
6252        assert!(!text.contains("call"), "{text}");
6253    }
6254
6255    /// A math library builtin handed a constant is the answer, and is not a call.
6256    ///
6257    /// This is the reason the family is answered in the front end at all. `double x =
6258    /// __builtin_ceil(1.5);` at file scope initializes an object with static storage duration, so
6259    /// there is no point in the program at which a call could be made, and a compiler that lowered
6260    /// it to one would refuse a program gcc accepts. Every number here is the encoding gcc 16.2.0
6261    /// gives on x86-64, read out of the object file one initializer at a time.
6262    #[test]
6263    fn a_math_library_builtin_of_a_constant_is_the_answer_and_not_a_call() {
6264        let text = ir(concat!(
6265            "double a = __builtin_ceil(1.5);\n",
6266            "double b = __builtin_floor(1.5);\n",
6267            "double c = __builtin_trunc(-1.5);\n",
6268            // A half goes away from zero and not to even, which is where C and the default
6269            // rounding of IEEE 754 part company.
6270            "double d = __builtin_round(2.5);\n",
6271            // The sign survives a number that rounds away to nothing, so this is a negative zero.
6272            "double e = __builtin_ceil(-0.5);\n",
6273            "double f = __builtin_fmax(1.0, 2.0);\n",
6274            "double g = __builtin_fmin(1.0, 2.0);\n",
6275            "float h = __builtin_ceilf(1.25f);\n",
6276            // The plain name is the same answer, which is what a program that included `math.h`
6277            // and never wrote a prefix reaches.
6278            "double ceil(double x);\n",
6279            "double i = ceil(2.25);\n",
6280        ));
6281        assert!(text.contains("global @a : f64 = 0x4000000000000000,"), "{text}");
6282        assert!(text.contains("global @b : f64 = 0x3ff0000000000000,"), "{text}");
6283        assert!(text.contains("global @c : f64 = 0xbff0000000000000,"), "{text}");
6284        assert!(text.contains("global @d : f64 = 0x4008000000000000,"), "{text}");
6285        assert!(text.contains("global @e : f64 = 0x8000000000000000,"), "{text}");
6286        assert!(text.contains("global @f : f64 = 0x4000000000000000,"), "{text}");
6287        assert!(text.contains("global @g : f64 = 0x3ff0000000000000,"), "{text}");
6288        assert!(text.contains("global @h : f32 = 0x40000000,"), "{text}");
6289        assert!(text.contains("global @i : f64 = 0x4008000000000000,"), "{text}");
6290        assert!(!text.contains("call"), "{text}");
6291    }
6292
6293    /// A math library builtin handed anything else is a call to the library function it is.
6294    ///
6295    /// gcc emits `jmp ceil` for `__builtin_ceil` on x86-64 at the default architecture, measured
6296    /// on gcc 16.2.0, and reaches the `roundsd` instruction only under `-msse4.1`. So the call is
6297    /// what a program gets from gcc too, and the name on it is the plain one, which is the whole
6298    /// point of the prefixed spelling: a program writing it reaches the library's function even
6299    /// where a macro or a definition of its own has taken the short name.
6300    #[test]
6301    fn a_math_library_builtin_of_anything_else_is_a_call_to_the_library() {
6302        let text = ir(concat!(
6303            "double f(double x) { return __builtin_ceil(x); }\n",
6304            "float g(float x) { return __builtin_floorf(x); }\n",
6305            "double h(double x, double y) { return __builtin_fmax(x, y); }\n",
6306        ));
6307        assert!(text.contains("call @ceil("), "{text}");
6308        assert!(text.contains("call @floorf("), "{text}");
6309        assert!(text.contains("call @fmax("), "{text}");
6310
6311        // The two the rounding mode decides are calls even when the argument is a constant, since
6312        // what they answer is not known until the program runs. gcc refuses a static initializer
6313        // written with one for that reason, so there is nothing to fold here either.
6314        let text = ir(concat!(
6315            "double f(void) { return __builtin_rint(2.5); }\n",
6316            "double g(void) { return __builtin_nearbyint(2.5); }\n",
6317        ));
6318        assert!(text.contains("call @rint("), "{text}");
6319        assert!(text.contains("call @nearbyint("), "{text}");
6320
6321        // A nan operand is the library's rule rather than the machine's, 7.12.12.2 saying the
6322        // answer is the other operand, and gcc will not fold that one either.
6323        let text = ir("double f(void) { return __builtin_fmin(__builtin_nan(\"\"), 1.0); }\n");
6324        assert!(text.contains("call @fmin("), "{text}");
6325
6326        // `-fno-builtin-ceil` is a program saying it means its own `ceil`, and it leaves the
6327        // prefixed spelling alone, which is what writing the prefix is for.
6328        let plain = concat!("double ceil(double x);\n", "double f(void) { return ceil(2.25); }\n");
6329        let mut opts = options();
6330        opts.emit = EmitKind::Ir;
6331        opts.no_builtin = vec!["ceil".to_owned()];
6332        assert!(run(&opts, plain).text().contains("call @ceil("), "-fno-builtin-ceil");
6333    }
6334
6335    /// A `constexpr` object is a named constant, which is the whole reason the keyword exists.
6336    ///
6337    /// C23 6.6p8 puts two of them on the list an integer constant expression is built from: one
6338    /// of an arithmetic type, and a member of one of a structure or union type. A subscript of
6339    /// one is not on the list and is a variably modified type in gcc 16 as well, and every
6340    /// number here is what gcc 16 gives on x86-64.
6341    #[test]
6342    fn a_constexpr_object_is_a_constant_wherever_one_is_required() {
6343        let text = ir(concat!(
6344            "constexpr int side = 4;\n",
6345            "constexpr int wider = side + 1;\n",
6346            "constexpr double half = 1.5;\n",
6347            "struct point { int x; int y; };\n",
6348            "constexpr struct point origin = { 5, 6 };\n",
6349            "int square[side * side];\n",
6350            "int rectangle[wider];\n",
6351            "int rounded[(int)half * 2];\n",
6352            "int across[origin.y];\n",
6353            "enum named { four = side };\n",
6354            "int e = four;\n",
6355        ));
6356        assert!(text.contains("global @square : bytes 64 ="), "{text}");
6357        assert!(text.contains("global @rectangle : bytes 20 ="), "{text}");
6358        assert!(text.contains("global @rounded : bytes 8 ="), "{text}");
6359        assert!(text.contains("global @across : bytes 24 ="), "{text}");
6360        assert!(text.contains("global @e : i32 = 4,"), "{text}");
6361
6362        // A `const` object is not one of them, which is what makes `int a[n];` a variable
6363        // length array in C and is the distinction the keyword was added to draw.
6364        let mut opts = options();
6365        opts.emit = EmitKind::Ir;
6366        let konst = "const int n = 1;\nint a[n];\n";
6367        let message = "/main.c:2:5: error: variably modified 'a' at file scope [E0538]";
6368        assert_eq!(run(&opts, konst).messages, [message]);
6369
6370        // Nor is a subscript of one, which gcc 16 refuses in the same words.
6371        let subscript = "constexpr int t[3] = { 1, 2, 3 };\nint a[t[1]];\n";
6372        assert_eq!(run(&opts, subscript).messages, [message]);
6373
6374        // And `constexpr` implies `const`, so the address of one is an address of a `const`.
6375        let address = "constexpr int c = 3;\nint *p = &c;\n";
6376        let warning = "/main.c:2:6: warning: initialization discards 'const' qualifier from \
6377             pointer target type [E0514]";
6378        assert_eq!(run(&opts, address).messages, [warning]);
6379    }
6380
6381    /// A member whose size was refused is not a flexible array member, whatever it looks like.
6382    ///
6383    /// The refusal leaves the member with no size, which is also how `int a[]` is written, so
6384    /// without the count that tells the two apart the rules about where a flexible array member
6385    /// may sit read the wreckage of the first error as a second mistake. gcc 16.2.0 says one
6386    /// thing about each of these and so does this, which is what the program can act on: adding
6387    /// a named member to `struct D` makes the message about `k` no clearer, and moving `a` to
6388    /// the end of `struct E` does not either.
6389    #[test]
6390    fn a_member_whose_size_was_refused_is_not_a_flexible_array_member() {
6391        let mut opts = options();
6392        opts.emit = EmitKind::Ir;
6393
6394        let alone = "int k;\nextern struct D { int a[k]; } ed;\n";
6395        let message = "/main.c:2:23: error: variably modified 'a' at file scope [E0538]";
6396        assert_eq!(run(&opts, alone).messages, [message]);
6397
6398        // And not one in the wrong place either, which is the other half of the same rule.
6399        let first = "int k;\nextern struct E { int a[k]; int b; } ee;\n";
6400        assert_eq!(run(&opts, first).messages, [message]);
6401
6402        // A size that is refused for a reason of its own, to show the count is about the
6403        // refusal rather than about the one message that happens to have been found first.
6404        let negative = "struct F { int a[-1]; };\n";
6405        let refused = "/main.c:1:18: error: size of array 'a' is negative [E0536]";
6406        assert_eq!(run(&opts, negative).messages, [refused]);
6407
6408        // The member that was written with no size at all is still a flexible array member, and
6409        // a structure with nothing else in it still has no named member to hang one off.
6410        let flexible = "struct G { int a[]; };\n";
6411        let named = "/main.c:1:16: error: flexible array member in a struct with no named \
6412             members [E0554]";
6413        assert_eq!(run(&opts, flexible).messages, [named]);
6414    }
6415
6416    /// A pointer to an array, where the qualifiers are on the element and the comparison is not.
6417    ///
6418    /// 6.7.3p10 says the qualifiers in an array declaration belong to the element, so `const int
6419    /// [4]` is an unqualified array of `const int` and not a qualified array of `int`. Compatibility
6420    /// then reads the element types, finds one `const` and one not, and calls the two arrays
6421    /// incompatible, which makes `const int (*)[4] = p` an incompatible pointer rather than a
6422    /// pointer that gained a qualifier. That is what the wording said before C23 and it is not what
6423    /// any compiler does: gcc and clang take it, C23 wrote the rule the way they read it, and the
6424    /// two directions are told apart the way they are everywhere else, which is that adding a
6425    /// qualifier is silent and dropping one is worth a word.
6426    ///
6427    /// Found in libwebp, where `src/enc/vp8l_enc.c` takes the address of a `HistogramBuckets` out of
6428    /// a structure into a `const HistogramBuckets *const`, and a whole file of a real library did
6429    /// not compile for it.
6430    #[test]
6431    fn a_pointer_to_an_array_gains_a_qualifier_the_same_way_a_pointer_to_anything_else_does() {
6432        let mut opts = options();
6433        opts.emit = EmitKind::Ir;
6434        let prefix = "typedef unsigned int B[4];\nstruct H { B category[2]; };\n";
6435
6436        // Adding it, which is the direction the library writes and the one nothing is owed for.
6437        let adding = format!("{prefix}const B *f(struct H *h) {{ return &h->category[0]; }}\n");
6438        assert_eq!(run(&opts, &adding).messages, [] as [String; 0]);
6439
6440        // And the same thing written out rather than through the typedef, since the typedef is a
6441        // spelling and the rule is about the array.
6442        let plain = concat!(
6443            "const unsigned int (*f(unsigned int (*p)[4]))[4] { return p; }\n",
6444            "const unsigned int (*g(unsigned int (*p)[2][3]))[2][3] { return p; }\n",
6445        );
6446        assert_eq!(run(&opts, plain).messages, [] as [String; 0]);
6447
6448        // Dropping it, which is the direction that is worth a word, and the word is the one every
6449        // other pointer target gets rather than a complaint about the types not matching.
6450        let dropping = format!("{prefix}B *f(const B *p) {{ return p; }}\n");
6451        let warning = "/main.c:3:27: warning: return discards 'const' qualifier from pointer target type \
6452             [E0514]";
6453        assert_eq!(run(&opts, &dropping).messages, [warning]);
6454
6455        // A pointer to an array of something else is still an incompatible pointer, because
6456        // nothing here is about the element being a different type.
6457        let wrong = "const unsigned int (*f(unsigned short (*p)[4]))[4] { return p; }\n";
6458        let error = "/main.c:1:61: error: returning 'unsigned short (*)[4]' from a function with \
6459             incompatible return type 'const unsigned int (*)[4]' [E0512]";
6460        assert_eq!(run(&opts, wrong).messages, [error]);
6461    }
6462
6463    /// A definition that names its parameters and then declares them under the list.
6464    ///
6465    /// The declarations say what the types are, 6.9.1p6, and what the function takes is those
6466    /// types with the default argument promotions over them, which is what a caller of an
6467    /// unprototyped function hands over. A prototype already in scope overrules the promoted
6468    /// types, since a header saying `int narrow(char);` over a definition written this way is
6469    /// the pairing all the code written this way relies on and 6.7.6.3p15 is read that way by
6470    /// every compiler.
6471    #[test]
6472    fn an_old_style_definition_takes_its_types_from_the_declarations_under_its_list() {
6473        // C17, since the default dialect is the one that warns about the form and this is
6474        // about what it means rather than about the warning.
6475        let mut opts = options();
6476        opts.std = Std::C17;
6477        let source = concat!(
6478            "int add(a, b)\n",
6479            "int a;\n",
6480            "int b;\n",
6481            "{ return a + b; }\n",
6482            "int promoted(c)\n",
6483            "char c;\n",
6484            "{ return c; }\n",
6485            "int narrow(char);\n",
6486            "int narrow(c)\n",
6487            "char c;\n",
6488            "{ return c; }\n",
6489            "int first(a)\n",
6490            "int a[4];\n",
6491            "{ return a[0]; }\n",
6492        );
6493        let result = run(&opts, source);
6494        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
6495        let text = result.text();
6496        assert!(text.contains("add : int(int, int) function external defined"), "{text}");
6497        assert!(text.contains("promoted : int(int) function external defined"), "{text}");
6498        // The body still sees the `char` it was declared as, whatever the caller hands over.
6499        assert!(text.contains("c : char object automatic defined"), "{text}");
6500        assert!(text.contains("narrow : int(char) function external defined"), "{text}");
6501        // An array parameter is a pointer here as much as it is in a prototype.
6502        assert!(text.contains("first : int(int *) function external defined"), "{text}");
6503    }
6504
6505    /// What the two halves of an old-style parameter list can disagree about.
6506    ///
6507    /// Each of these is a sentence gcc 16 has, and every message below is the one it prints,
6508    /// read off it on x86-64 rather than reasoned about. The last two are the dialect: a name
6509    /// with no declaration is an `int` in C89 and a diagnostic from C99 on, and the whole form
6510    /// left the language in C23, where gcc still takes it and warns.
6511    #[test]
6512    fn the_two_halves_of_an_old_style_parameter_list_have_to_agree() {
6513        let mut opts = options();
6514        opts.std = Std::C17;
6515        for (source, message) in [
6516            ("int f(a, a)\nint a;\n{ return a; }\n", "1:10: error: multiple parameters named 'a'"),
6517            (
6518                "int f(a)\nint a;\nint b;\n{ return a; }\n",
6519                "3:5: error: declaration for parameter 'b' but no such parameter",
6520            ),
6521            ("int f(a)\nint a;\nint a;\n{ return a; }\n", "3:5: error: redefinition of parameter"),
6522            ("int f(a)\nint a = 1;\n{ return a; }\n", "2:5: error: parameter 'a' is initialized"),
6523            (
6524                "int f(a)\nstatic int a;\n{ return a; }\n",
6525                "2:12: error: storage class specified for parameter 'a'",
6526            ),
6527            (
6528                "int f(char);\nint f(a)\nshort a;\n{ return a; }\n",
6529                "2:7: error: argument 'a' doesn't match prototype",
6530            ),
6531        ] {
6532            let result = run(&opts, source);
6533            assert!(result.failed(), "expected this to fail:\n{source}");
6534            assert!(result.messages[0].contains(message), "{:?}", result.messages);
6535        }
6536
6537        // A name the declarations never mention. C89 gave it an `int` and gcc still takes it
6538        // in that dialect, and every dialect after it made the same line a diagnostic.
6539        let implicit = "int f(a, b)\nint a;\n{ return a + b; }\n";
6540        let mut older = options();
6541        older.std = Std::C89;
6542        assert!(!run(&older, implicit).failed(), "{:?}", run(&older, implicit).messages);
6543        let result = run(&opts, implicit);
6544        assert!(
6545            result.messages[0].contains("1:10: error: type of 'b' defaults to 'int'"),
6546            "{:?}",
6547            result.messages
6548        );
6549
6550        // C23 took the form out of the language and gcc kept accepting it with a warning, and
6551        // a warning is what this is, because the code written this way is not going to be
6552        // rewritten and refusing it would put the compiler out of reach of it.
6553        let mut newer = options();
6554        newer.std = Std::C23;
6555        let plain = "int f(a)\nint a;\n{ return a; }\n";
6556        let result = run(&newer, plain);
6557        assert!(!result.failed(), "{:?}", result.messages);
6558        assert_eq!(
6559            result.messages,
6560            ["/main.c:1:5: warning: old-style function definition [E0412]"]
6561        );
6562        assert!(run(&opts, plain).messages.is_empty(), "and nothing to say in the dialects before");
6563    }
6564
6565    /// The two obsolete designators, which are silent until `-pedantic` asks about them.
6566    ///
6567    /// `[3] 7` is what GCC had for an array before C99 settled on `[3] = 7`, and `x: 7` is the
6568    /// same era's spelling for a member. Both are still in code written against a compiler of
6569    /// that era, and gcc 16 takes both without a word unless it is asked to be pedantic, which
6570    /// is where the columns below come from as well.
6571    #[test]
6572    fn the_obsolete_designators_are_taken_and_are_pedantic_warnings() {
6573        let array = "int a[8] = { [3] 7 };\n";
6574        let member = "struct s { int x; } v = { x: 7 };\n";
6575        for source in [array, member] {
6576            let result = run(&options(), source);
6577            assert!(!result.failed(), "{:?}", result.messages);
6578            assert!(result.messages.is_empty(), "nothing to say: {:?}", result.messages);
6579        }
6580
6581        let mut asked = options();
6582        asked.pedantic = true;
6583        assert_eq!(
6584            run(&asked, array).messages,
6585            ["/main.c:1:18: warning: obsolete designator, write `[i] =` instead [E0415]"]
6586        );
6587        assert_eq!(
6588            run(&asked, member).messages,
6589            ["/main.c:1:27: warning: obsolete designator, write `.field =` instead [E0413]"]
6590        );
6591    }
6592
6593    /// A type nothing is ever an object of is a type `sizeof` still has to answer about, which
6594    /// is what `991014-1.c` in the gcc.c-torture execution suite asks.
6595    ///
6596    /// The limit is `PTRDIFF_MAX` and it is the same one for an array and for a record, so a
6597    /// record of every byte an object may have is laid out and one byte more is refused. All
6598    /// four numbers are what gcc 16 gives on x86-64.
6599    #[test]
6600    fn a_type_is_refused_when_it_passes_the_largest_object_and_not_before() {
6601        let text = ir(concat!(
6602            "struct huge_struct { short buf[(1L << 62) - 256]; int a, b, c, d; };\n",
6603            "struct brim { char buf[9223372036854775807L]; };\n",
6604            "struct bitty { char buf[9223372036854775800L]; int x : 1; };\n",
6605            "unsigned long h = sizeof(struct huge_struct);\n",
6606            "unsigned long b = sizeof(struct brim);\n",
6607            "unsigned long y = sizeof(struct bitty);\n",
6608        ));
6609        assert!(text.contains("global @h : i64 = 9223372036854775312,"), "{text}");
6610        assert!(text.contains("global @b : i64 = 9223372036854775807,"), "{text}");
6611        assert!(text.contains("global @y : i64 = 9223372036854775804,"), "{text}");
6612
6613        let mut opts = options();
6614        opts.emit = EmitKind::Ir;
6615        let over = "struct over { char buf[9223372036854775800L]; char x[8]; };\n";
6616        let message = "/main.c:1:1: error: type 'struct over' is too large [E0560]";
6617        assert_eq!(run(&opts, over).messages, [message]);
6618        let array = "struct wide { short buf[1L << 62]; };\n";
6619        let message = "/main.c:1:25: error: size of array 'buf' exceeds \
6620             maximum object size '9223372036854775807' [E0537]";
6621        assert_eq!(run(&opts, array).messages[0], message);
6622    }
6623
6624    /// A byte in the source that is not part of a character, which only a literal may hold.
6625    ///
6626    /// The source cannot be a `&str` here, which is the whole point: a file is bytes and only
6627    /// mostly text.
6628    fn compile_bytes(source: &[u8]) -> Compiled {
6629        let mut opts = options();
6630        opts.emit = EmitKind::Ir;
6631        let mut fs = MemoryFileSystem::new();
6632        fs.insert("/main.c", source.to_vec());
6633        compile(&opts, "/main.c", &fs)
6634    }
6635
6636    /// A raw byte inside a string literal is that byte, which gcc has always taken and which is
6637    /// the only place in a source file where a byte does not have to be part of a character.
6638    /// Replacing it would give the object three bytes rather than one, since the replacement
6639    /// character is three bytes of UTF-8, so the object would not be the one that was written
6640    /// even where the diagnostic is ignored. Anywhere else the byte is still a mistake, which
6641    /// is where gcc draws the same line.
6642    #[test]
6643    fn a_byte_that_is_not_a_character_is_kept_in_a_literal_and_refused_outside_one() {
6644        let mut source = b"char s[] = \"a".to_vec();
6645        source.push(0xff);
6646        source.extend_from_slice(b"b\";\nchar c = '");
6647        source.push(0xff);
6648        source.extend_from_slice(b"';\n");
6649        let result = compile_bytes(&source);
6650        assert_eq!(result.messages, Vec::<String>::new(), "a raw byte in a literal is that byte");
6651        assert!(result.text().contains(r#"bytes "a\ffb\00""#), "{}", result.text());
6652        // Plain `char` is signed on this target, so the constant is minus one rather than 255.
6653        assert!(result.text().contains("global @c : i8 = -1,"), "{}", result.text());
6654
6655        let mut stray = b"int a".to_vec();
6656        stray.push(0xff);
6657        stray.extend_from_slice(b" = 1;\n");
6658        let result = compile_bytes(&stray);
6659        assert!(
6660            result.messages.iter().any(|m| m.contains("source is not valid UTF-8 here")),
6661            "{:?}",
6662            result.messages
6663        );
6664    }
6665
6666    #[test]
6667    fn an_object_becomes_a_global_with_an_image_and_a_function_becomes_a_func() {
6668        let text = ir("int x = 7;\nint add(int a, int b) { return a + b; }\n");
6669        assert!(text.contains("global @x : i32 = 7, align 4, linkage(external)\n"), "{text}");
6670        let expected = "\
6671func @add(i32, i32) -> i32, linkage(external) {
6672block0(%0: i32, %1: i32):
6673    %2 = add.nsw %0, %1
6674    return %2
6675}
6676";
6677        assert!(text.contains(expected), "{text}");
6678    }
6679
6680    #[test]
6681    fn a_local_nothing_takes_the_address_of_is_a_value_and_never_a_stack_slot() {
6682        let text = body("int f(int n) { int a = n + 1; int b = a * 2; return a + b; }\n");
6683        assert!(!text.contains("alloca"), "{text}");
6684        assert!(!text.contains("load"), "{text}");
6685        assert!(!text.contains("store"), "{text}");
6686    }
6687
6688    #[test]
6689    fn a_local_whose_address_is_taken_gets_a_slot_in_the_entry_block() {
6690        let text = body("int g(int *);\nint f(void) { int a = 1; return g(&a); }\n");
6691        let expected = "\
6692block0:
6693    %0 = alloca, size 4, align 4
6694    %1 = iconst.i32 1
6695    store %1 -> %0, align 4, tbaa !1
6696    %2 = call @g(%0) : (ptr) -> i32
6697    return %2
6698";
6699        assert_eq!(text, expected);
6700    }
6701
6702    #[test]
6703    fn a_loop_carries_what_it_changes_as_block_parameters() {
6704        // The whole point of building SSA during the walk rather than after it: `i` and
6705        // `total` are values that arrive on an edge, and neither has ever been in memory.
6706        let text = body(
6707            "int f(int n) {\n  int total = 0;\n  for (int i = 0; i < n; i++) total += i;\n  \
6708             return total;\n}\n",
6709        );
6710        assert!(!text.contains("alloca"), "{text}");
6711        assert!(text.contains("block1(%3: i32, %4: i32):"), "{text}");
6712        assert!(text.contains("jump block1("), "{text}");
6713    }
6714
6715    #[test]
6716    fn a_comparison_used_as_a_condition_is_not_widened_and_narrowed_again() {
6717        let text = body("int f(int a, int b) { if (a < b) return 1; return 0; }\n");
6718        assert!(text.contains("icmp slt %0, %1"), "{text}");
6719        assert!(!text.contains("zext"), "{text}");
6720    }
6721
6722    #[test]
6723    fn the_right_side_of_a_short_circuit_is_in_a_block_of_its_own() {
6724        let text = body("int f(int a, int b) { return a && b; }\n");
6725        let expected = "\
6726block0(%0: i32, %1: i32):
6727    %2 = iconst.i32 0
6728    %3 = icmp ne %0, %2
6729    %4 = iconst.i1 0
6730    br_if %3, block1, block2(%4)
6731
6732block1:
6733    %5 = iconst.i32 0
6734    %6 = icmp ne %1, %5
6735    jump block2(%6)
6736
6737block2(%7: i1):
6738    %8 = zext.i32 %7
6739    return %8
6740";
6741        assert_eq!(text, expected);
6742    }
6743
6744    #[test]
6745    fn code_after_a_return_is_not_built_and_does_not_leave_an_empty_block_behind() {
6746        let text = body("int f(int a) { if (a) return 1; else return 2; return 3; }\n");
6747        // Three blocks, the test and the two arms. The join the `return 3` would need is
6748        // never created, because a block nothing branches to is not a block.
6749        assert!(!text.contains("block3"), "{text}");
6750        assert!(!text.contains("iconst.i32 3"), "{text}");
6751    }
6752
6753    #[test]
6754    fn falling_off_the_end_returns_zero_from_main_and_nothing_from_a_void_function() {
6755        assert!(body("int main(void) { }\n").contains("iconst.i32 0\n    return"));
6756        assert_eq!(body("void f(void) { }\n"), "block0:\n    return\n");
6757        assert!(body("int f(void) { }\n").contains("unreachable"));
6758    }
6759
6760    #[test]
6761    fn a_structure_is_copied_rather_than_held_in_a_value() {
6762        let text = body(
6763            "struct point { int x, y; };\n\
6764             int f(void) { struct point p = { 1, 2 }; struct point q = p; return q.x; }\n",
6765        );
6766        assert!(text.contains("memcpy"), "{text}");
6767    }
6768
6769    #[test]
6770    fn an_initializer_that_leaves_part_of_an_object_unwritten_zeroes_it_first() {
6771        let text = body("int f(void) { int a[4] = { 1 }; return a[3]; }\n");
6772        assert!(text.contains("memset"), "{text}");
6773    }
6774
6775    #[test]
6776    fn a_switch_is_one_branch_and_a_case_that_falls_through_carries_what_it_wrote() {
6777        let text = body(
6778            "int f(int x) { int r = 0; switch (x) { case 1: r = 1; case 2: r += 2; break; \
6779             default: r = 4; } return r; }\n",
6780        );
6781        let expected = "\
6782block0(%0: i32):
6783    %1 = iconst.i32 0
6784    switch %0, block1, [1 => block2, 2 => block3(%1)]
6785
6786block1:
6787    %2 = iconst.i32 4
6788    jump block4(%2)
6789
6790block2:
6791    %3 = iconst.i32 1
6792    jump block3(%3)
6793
6794block3(%4: i32):
6795    %5 = iconst.i32 2
6796    %6 = add.nsw %4, %5
6797    jump block4(%6)
6798
6799block4(%7: i32):
6800    return %7
6801";
6802        assert_eq!(text, expected);
6803    }
6804
6805    #[test]
6806    fn a_case_range_is_tested_for_rather_than_put_in_the_table() {
6807        // GNU's `case 1 ... 9`. Nine table entries would be nine here and four billion for the
6808        // range a program is allowed to write, so it is a subtraction and one unsigned compare.
6809        let text = body("int f(int x) { switch (x) { case 1 ... 9: return 1; } return 0; }\n");
6810        assert!(text.contains("%2 = sub %0, %1"), "{text}");
6811        assert!(text.contains("icmp ule"), "{text}");
6812        assert!(!text.contains("switch"), "{text}");
6813    }
6814
6815    #[test]
6816    fn break_leaves_the_switch_and_continue_leaves_the_loop_around_it() {
6817        let text = body(
6818            "int f(int n) { int t = 0; for (int i = 0; i < n; i++) { switch (i) { \
6819             case 0: continue; case 1: break; default: t += i; } t++; } return t; }\n",
6820        );
6821        // The `continue` goes to the step and the `break` goes to the `t++` after the switch,
6822        // which is also where the default falls out to.
6823        assert!(text.contains("switch %3, block4, [0 => block5, 1 => block6]"), "{text}");
6824        assert!(text.contains("block5:\n    jump block7("), "{text}");
6825        assert!(text.contains("block6:\n    jump block8("), "{text}");
6826    }
6827
6828    #[test]
6829    fn a_switch_with_nothing_to_branch_on_still_runs_what_comes_after_it() {
6830        assert_eq!(body("void f(int x) { switch (x) { } }\n"), "block0(%0: i32):\n    return\n");
6831    }
6832
6833    #[test]
6834    fn a_label_a_loop_is_only_entered_through_builds_the_loop_around_it() {
6835        // A branch into the middle of a loop that nothing else reaches, the Duff's device shape.
6836        // The `while` is not reached in order, so the walk starts a block nothing branches to and
6837        // builds it from there. What comes out is the loop with an edge straight into its body,
6838        // and the header that nothing arrives at is pruned.
6839        let text = body(
6840            "int f(int x, int n) { switch (x) { case 1: break; while (n) { case 2: n--; } } \
6841             return n; }\n",
6842        );
6843        // `case 2` lands on the body, `case 1` and the default land on the return, and the test
6844        // at the bottom of the loop comes back round to the body.
6845        assert!(text.contains("switch %0, block1(%1), [1 => block2, 2 => block3(%1)]"), "{text}");
6846        assert!(text.contains("block3(%3: i32):\n    %4 = iconst.i32 1"), "{text}");
6847        assert!(text.contains("block4:\n    jump block3("), "{text}");
6848    }
6849
6850    #[test]
6851    fn a_goto_into_a_loop_body_enters_it_without_the_test() {
6852        // The same thing through a `goto`. The first pass through the body runs whatever the
6853        // label is on, and only then does the loop reach its own test.
6854        let text = body("int f(int x, int n) { goto in; while (n) { in: n--; } return n; }\n");
6855        assert!(text.starts_with("block0(%0: i32, %1: i32):\n    jump block1(%1)"), "{text}");
6856        assert!(text.contains("block1(%2: i32):\n    %3 = iconst.i32 1"), "{text}");
6857        assert!(text.contains("br_if %6, block2, block3"), "{text}");
6858    }
6859
6860    #[test]
6861    fn a_goto_is_a_jump_to_the_block_the_label_starts() {
6862        let text = body("int f(int x) { int r = 0; if (x) goto out; r = 1; out: return r; }\n");
6863        // Both edges into `out` carry what `r` holds on the way, and neither is a stack slot. The
6864        // block the `goto` jumps out of is empty and hands its edge on, which is what moves `out`
6865        // up the block list to second place.
6866        assert!(!text.contains("alloca"), "{text}");
6867        assert!(text.contains("block2(%4: i32):\n    return %4"), "{text}");
6868        assert_eq!(text.matches("jump block2(").count(), 2, "{text}");
6869    }
6870
6871    #[test]
6872    fn a_backward_goto_is_a_loop_and_carries_what_it_changes() {
6873        let text =
6874            body("int f(int n) { int i = 0; again: if (i < n) { i++; goto again; } return i; }\n");
6875        assert!(!text.contains("alloca"), "{text}");
6876        assert!(text.contains("block1(%2: i32):"), "{text}");
6877        assert!(text.contains("jump block1(%5)"), "{text}");
6878    }
6879
6880    #[test]
6881    fn a_label_nothing_reaches_is_taken_out_rather_than_left_for_the_verifier() {
6882        // A block nothing branches to is not a legal function, and which labels are dead is not
6883        // known until the last statement has been walked, since the `goto` is allowed to be it.
6884        assert_eq!(
6885            body("int f(int x) { return x; spare: return 0; }\n"),
6886            "block0(%0: i32):\n    return %0\n"
6887        );
6888    }
6889
6890    #[test]
6891    fn a_bit_field_is_read_by_loading_the_bytes_it_lies_in_and_shifting() {
6892        let text = body(
6893            "struct s { unsigned a : 3; signed b : 5; };\nint f(struct s *p) { return p->b; }\n",
6894        );
6895        // One byte holds both fields, and the signed one needs no mask: shifting it down
6896        // arithmetically is what says its top bit is a sign.
6897        assert_eq!(
6898            text,
6899            "\
6900block0(%0: ptr):
6901    %1 = load.i8 %0, align 1
6902    %2 = iconst.i8 3
6903    %3 = ashr %1, %2
6904    %4 = sext.i32 %3
6905    return %4
6906"
6907        );
6908    }
6909
6910    #[test]
6911    fn a_store_to_a_bit_field_does_not_write_a_byte_it_has_no_bit_in() {
6912        // C11 says an ordinary member beside a bit-field is a memory location of its own, so
6913        // the four byte store this would take is a data race in a program that has none. The
6914        // three bytes of `a` go in as two and one, and `c` is not touched.
6915        let text =
6916            body("struct s { int a : 24; char c; };\nvoid f(struct s *p, int v) { p->a = v; }\n");
6917        assert_eq!(
6918            text,
6919            "\
6920block0(%0: ptr, %1: i32):
6921    %2 = iconst.i32 16777215
6922    %3 = and %1, %2
6923    %4 = trunc.i16 %3
6924    store %4 -> %0, align 2
6925    %5 = iconst.i32 16
6926    %6 = lshr %3, %5
6927    %7 = trunc.i8 %6
6928    %8 = iconst.i64 2
6929    %9 = ptr_add %0, %8
6930    store %7 -> %9, align 1
6931    return
6932"
6933        );
6934    }
6935
6936    #[test]
6937    fn what_an_assignment_to_a_bit_field_is_worth_is_what_fits_in_it() {
6938        let text =
6939            body("struct s { unsigned b : 5; };\nunsigned f(struct s *p) { return p->b = 33; }\n");
6940        // 33 does not fit in five bits, and 1 is both what goes in the field and what the
6941        // assignment is worth.
6942        assert!(text.contains("%3 = iconst.i8 31\n    %4 = and %2, %3"), "{text}");
6943        assert!(text.ends_with("%9 = zext.i32 %4\n    return %9\n"), "{text}");
6944    }
6945
6946    #[test]
6947    fn an_assignment_a_statement_throws_away_builds_none_of_what_it_is_worth() {
6948        // The value of an assignment to a bit-field takes a shift to build, and a statement
6949        // has no use for it. Nothing here reads back what was stored.
6950        let text = body("struct s { signed b : 5; };\nvoid f(struct s *p) { p->b = 3; }\n");
6951        assert_eq!(text.matches("ashr").count(), 0, "{text}");
6952        assert!(text.ends_with("store %8 -> %0, align 1\n    return\n"), "{text}");
6953    }
6954
6955    #[test]
6956    fn a_bit_field_in_an_initializer_goes_in_over_bytes_that_were_zeroed_first() {
6957        // A bit-field writes part of a byte and leaves the rest of it alone, so the object has
6958        // to be zero before it goes in or what the initializer did not name is whatever the
6959        // stack held.
6960        let text = body(
6961            "struct s { int a : 3; int b; };\nint f(void) { struct s v = { 1 }; return v.b; }\n",
6962        );
6963        assert!(text.contains("memset %0, %1, size 8, align 4"), "{text}");
6964    }
6965
6966    #[test]
6967    fn the_image_of_a_static_bit_field_is_the_bytes_the_fields_share() {
6968        // Two fields in one byte are not two entries in the image, because an image is written
6969        // in bytes: they are the byte they are both in.
6970        let text = ir("struct s { unsigned a : 3; unsigned b : 5; } g = { 1, 2 };\n");
6971        assert!(
6972            text.contains("global @g : bytes 4 = { bytes \"\\11\", zero 3 }, align 4"),
6973            "{text}"
6974        );
6975    }
6976
6977    #[test]
6978    fn an_initialized_flexible_array_member_makes_the_object_larger_than_its_type() {
6979        // `sizeof` answers without the array and the definition has to hold what was written, so
6980        // the object is the size of its image. gcc 16 gives these four, three and two bytes and
6981        // so does this. The image used to be written at the size the type had, which left the
6982        // verifier looking at twenty bytes going into four.
6983        let text = ir(concat!(
6984            "struct a { int i; int j[]; } x = { 1, { 2, 0, 2, 3 } };\n",
6985            "struct b { char c; char p[]; } y = { 'o', \"wx\" };\n",
6986            "struct c { char c; char p[]; } z = { '9', { 'e', 'b' } };\n",
6987            "char s[2] = \"hi\";\n",
6988        ));
6989        assert!(
6990            text.contains("global @x : bytes 20 = { i32 1, i32 2, i32 0, i32 2, i32 3 }"),
6991            "{text}"
6992        );
6993        assert!(text.contains("global @y : bytes 4 = { i8 111, bytes \"wx\\00\" }"), "{text}");
6994        assert!(text.contains("global @z : bytes 3 = { i8 57, i8 101, i8 98 }"), "{text}");
6995        // The array with a length of its own still cuts the literal down to it, which is the
6996        // one case in C where a string initializer drops its terminator.
6997        assert!(text.contains("global @s : bytes 2 = { bytes \"hi\" }"), "{text}");
6998    }
6999
7000    #[test]
7001    fn a_definition_takes_a_parameter_it_left_unnamed() {
7002        // The entry block's parameters are the definition's, and one the front end dropped for
7003        // having no name left the two lists different lengths, which the walk read as an
7004        // old-style definition and refused. gcc has taken these for far longer than C23 has.
7005        let text = ir("int f(int a, int) { return a; }\n");
7006        assert!(text.contains("func @f(i32, i32) -> i32"), "{text}");
7007        assert!(text.contains("block0(%0: i32, %1: i32):"), "{text}");
7008
7009        // The unnamed one first, so that the named one is the second parameter of the entry
7010        // block and not the first: the list says the order and not only how many there are.
7011        let text = ir("int g(int, int n) { return n; }\n");
7012        assert!(text.contains("block0(%0: i32, %1: i32):\n    return %1\n"), "{text}");
7013    }
7014
7015    #[test]
7016    fn an_assignment_of_a_structure_is_the_object_it_wrote() {
7017        // `d = e = c` used to be refused, because the middle assignment is a value of structure
7018        // type and the walk had nowhere to read one from. What an assignment is worth is the
7019        // value it stored, so the object it stored into is the answer and the chain is three
7020        // copies out of the one source with no temporary in it.
7021        let text = body(concat!(
7022            "struct s { int f; int g; };\n",
7023            "void h(struct s *a, struct s *c, struct s *d, struct s *e)\n",
7024            "{ *d = *e = a[0] = *c; }\n",
7025        ));
7026        assert_eq!(text.matches("memcpy").count(), 3, "{text}");
7027        assert!(text.contains("memcpy %8, %1, size 8, align 4\n"), "{text}");
7028        assert!(text.contains("memcpy %3, %8, size 8, align 4\n"), "{text}");
7029        assert!(text.contains("memcpy %2, %3, size 8, align 4\n"), "{text}");
7030    }
7031
7032    #[test]
7033    fn a_string_literal_stops_at_the_end_of_the_array_it_is_filling() {
7034        // The excess used to be laid into the object anyway, so the row after was written over
7035        // and the image refused the entry that came to it. C 6.7.10p14 says the terminator goes
7036        // in only if there is room for it, and gcc discards the rest of a literal that is longer
7037        // still, which is what the first of these is and why it warns.
7038        let mut opts = options();
7039        opts.emit = EmitKind::Ir;
7040        let result = run(
7041            &opts,
7042            concat!(
7043                "const char a[2][3] = { \"1234\", \"xyz\" };\n",
7044                "static const char b[3][5] = { \"12345\", \"678\", \"9\" };\n",
7045                "union u { struct { char x[4]; char y[4]; }; struct { char z[8]; }; };\n",
7046                "const union u c = { { \"1234\", \"567\" } };\n",
7047            ),
7048        );
7049        let text = result.text();
7050        assert_eq!(
7051            result.messages,
7052            ["/main.c:1:24: warning: initializer-string for array of 'const char' is too long \
7053              (5 chars into 3 available) [E0637]"]
7054        );
7055        assert!(text.contains("global @a : bytes 6 = { bytes \"123\", bytes \"xyz\" }"), "{text}");
7056        assert!(
7057            text.contains(
7058                "global @b : bytes 15 = { bytes \"12345\", bytes \"678\\00\", zero 1, \
7059                 bytes \"9\\00\", zero 3 }"
7060            ),
7061            "{text}"
7062        );
7063        // The eight bytes are four, three and a terminator, and then the byte the shorter
7064        // literal left for the string in the other member of the union to end at.
7065        assert!(
7066            text.contains("global @c : bytes 8 = { bytes \"1234\", bytes \"567\\00\" }"),
7067            "{text}"
7068        );
7069    }
7070
7071    #[test]
7072    fn a_cast_of_a_record_to_its_own_type_is_the_object_that_was_cast() {
7073        // gcc accepts one and does nothing with it, which sema already had. Lowering asked for
7074        // the object under it and had no arm for a cast, so `(struct s)x` in an initializer was
7075        // refused with E0519. It is one copy out of the object named, not two.
7076        let text = body(concat!(
7077            "struct s { int a, b; };\nstruct v { struct s s; int t; };\n",
7078            "void g(struct v *);\n",
7079            "void f(struct s *p) { struct v w = { (struct s)*p, 5 }; g(&w); }\n",
7080        ));
7081        assert_eq!(text.matches("memcpy").count(), 1, "{text}");
7082    }
7083
7084    #[test]
7085    fn a_compound_literal_read_in_a_static_initializer_lays_its_bytes_into_the_image() {
7086        // C 6.7.11p4 says a compound literal at file scope has static storage duration, which
7087        // makes it a constant element, and tcc and c-testsuite both write one. Sema used to call
7088        // it a non constant because reading it is a node of its own and the read was what it
7089        // looked at, and lowering had no way to put an object where it wanted a number.
7090        let text = ir(concat!(
7091            "struct s { int x; };\n",
7092            "struct t { struct s s; int o; } a = { (struct s){ 2 }, 3 };\n",
7093            "int n = (int){ 7 };\n",
7094            "struct u { struct s p; struct s q; } b = { (struct s){ 1 }, (struct s){ } };\n",
7095        ));
7096        assert!(text.contains("global @a : bytes 8 = { i32 2, i32 3 }"), "{text}");
7097        assert!(text.contains("global @n : i32 = 7,"), "{text}");
7098        // The second literal names nothing, so what it puts in is the zeros of its own size and
7099        // not the tail of the object it went in, which would have been the same bytes by luck.
7100        assert!(text.contains("global @b : bytes 8 = { i32 1, zero 4 }"), "{text}");
7101    }
7102
7103    #[test]
7104    fn the_address_of_a_compound_literal_asks_for_the_object_it_points_at() {
7105        // Nothing declares a compound literal, so the reference is the only thing that can ask
7106        // for it to be emitted. The image named `.Lanon.0` and the module defined no such
7107        // symbol, which the link would have been the first to find out.
7108        let text = ir("struct s { int x; };\nstruct s *q = &(struct s){ 9 };\n");
7109        assert!(text.contains("global @.Lanon.0 : i32 = 9, align 4, linkage(internal)"), "{text}");
7110        assert!(text.contains("global @q : bytes 8 = { addr.8 @.Lanon.0 }"), "{text}");
7111    }
7112
7113    #[test]
7114    fn an_object_of_no_size_at_all_has_an_image_with_nothing_in_it() {
7115        // A zero length array, which gcc allows and real code uses as the tail of a structure.
7116        // The image is there and holds nothing, which is not the global that has no image at
7117        // all, and the IR reader used to stop on the empty one.
7118        let text = ir("unsigned char foo[1][0];\n");
7119        assert!(text.contains("global @foo : bytes 0 = {}, align 1"), "{text}");
7120    }
7121
7122    #[test]
7123    fn a_null_pointer_in_an_image_is_the_bits_an_address_has_room_for() {
7124        // `NULL` in a static initializer, which every program has. The IR type is `ptr` and a
7125        // `ptr` has no width of its own, so the width the bits are cut to is the target's.
7126        let text = ir("void *p = 0;\nchar *q = (char *) 4096;\n");
7127        assert!(text.contains("global @p : i64 = 0, align 8"), "{text}");
7128        assert!(text.contains("global @q : i64 = 4096, align 8"), "{text}");
7129    }
7130
7131    #[test]
7132    fn an_object_another_module_defines_may_be_one_that_cannot_be_written_through() {
7133        // Which the verifier used to refuse, having read a declaration as a definition with
7134        // nothing in it. `extern const` is how a program names something in the library's read
7135        // only data, and glibc and Darwin both have one in a header a real program includes.
7136        let text = ir("extern const int limit;\nint f(void) { return limit; }\n");
7137        assert!(
7138            text.contains("global @limit : bytes 4, align 4, linkage(external), constant"),
7139            "{text}"
7140        );
7141    }
7142
7143    #[test]
7144    fn a_conditional_whose_value_is_an_object_answers_where_the_object_is() {
7145        // A structure is not a value in the IR, so the two arms cannot be joined as one. The
7146        // addresses can, and the answer is the address of whichever arm was taken rather than
7147        // a copy of it into a third place: both arms outlive the expression, so a copy would
7148        // be one nothing could observe. SQLite's parser writes one of these.
7149        let text = body(
7150            "\
7151struct s { int a, b; };
7152struct s pick(int c, struct s x, struct s y) { return c ? x : y; }
7153",
7154        );
7155        // The join takes an address, each arm hands it the one it has, and nothing is copied.
7156        assert!(text.contains("block3(%7: ptr)"), "{text}");
7157        assert!(text.contains("jump block3(%3)") && text.contains("jump block3(%4)"), "{text}");
7158        assert!(!text.contains("memcpy"), "the arms are joined rather than copied: {text}");
7159    }
7160
7161    /// GNU's `a ?: b` evaluates `a` once, and the arm answers the value that was tested.
7162    ///
7163    /// The checking keeps one node for `a` and converts it in two directions, to the bit the
7164    /// branch is taken on and to the type the whole expression has. Walking into the arm used to
7165    /// reach that node a second time and build a second copy of whatever it says, so `++i ?: 10`
7166    /// incremented twice and `f() ?: 10` called twice. Measured against gcc 16.2.0, which
7167    /// increments once.
7168    #[test]
7169    fn the_left_side_of_a_conditional_with_no_middle_is_evaluated_once() {
7170        let text = body("int f(int i) { return ++i ?: 10; }\n");
7171        assert!(text.contains("jump block3(%2)"), "the arm is the value that was tested: {text}");
7172        assert_eq!(text.matches("add.nsw").count(), 1, "incremented once: {text}");
7173
7174        // The arm still converts, since what the whole expression is worth is a `long` here and
7175        // the node under it is an `int`. What it converts is the value in hand.
7176        let text = body("long f(int i) { return ++i ?: 10L; }\n");
7177        assert!(text.contains("%5 = sext.i64 %2"), "the arm widens what was tested: {text}");
7178        assert_eq!(text.matches("add.nsw").count(), 1, "incremented once: {text}");
7179
7180        // A call, which is where evaluating twice is a wrong answer rather than a slow one.
7181        let text = body("int g(void);\nint f(void) { return g() ?: 10; }\n");
7182        assert_eq!(text.matches("call @g").count(), 1, "called once: {text}");
7183
7184        // Written out in full it is two reads of `i`, which is what C says it is, so the middle
7185        // operand being absent is the whole of the difference.
7186        let text = body("int f(int i) { return ++i ? ++i : 10; }\n");
7187        assert_eq!(text.matches("add.nsw").count(), 2, "incremented twice: {text}");
7188    }
7189
7190    #[test]
7191    fn a_structure_that_fits_in_registers_travels_as_the_registers_it_fits_in() {
7192        // `struct pair` is two eightbytes on SysV, one of them integer, so the signature says
7193        // one `i64` in each direction and the body takes the object apart and puts it back
7194        // together around the call.
7195        let text = ir("\
7196struct pair { int a, b; };
7197struct pair make(int a, int b);
7198struct pair twice(struct pair p) { return make(p.a, p.b); }
7199");
7200        assert!(text.contains("func @make(i32, i32) -> i64"), "{text}");
7201        assert!(text.contains("func @twice(i64) -> i64"), "{text}");
7202    }
7203
7204    #[test]
7205    fn a_structure_too_large_for_the_registers_travels_as_where_its_bytes_are() {
7206        // Over two eightbytes the caller passes the bytes in the argument area, which is
7207        // `byval`, and passes somewhere to write the return value, which is `sret`. Neither is
7208        // a parameter the program wrote and both are parameters the function has.
7209        let text = ir("\
7210struct big { double v[8]; };
7211struct big grow(struct big b);
7212struct big twice(struct big b) { return grow(grow(b)); }
7213");
7214        assert!(
7215            text.contains("func @grow(ptr sret(64, align 8), ptr byval(64, align 8))"),
7216            "{text}"
7217        );
7218        assert!(text.contains("block0(%0: ptr, %1: ptr):"), "{text}");
7219        // The inner call writes into a slot and the outer one reads the same slot, so the
7220        // object between the two calls is never copied anywhere.
7221        assert_eq!(text.matches("call @grow").count(), 2, "{text}");
7222    }
7223
7224    #[test]
7225    fn a_structure_passed_to_a_variadic_function_says_so_at_the_call() {
7226        // The bytes travel in the argument area the same way they would for a parameter, and
7227        // `printf` has no parameter there to say it on, so the call says it instead. The one
7228        // that fits in registers says nothing, because travelling as the registers it fits in
7229        // is what an argument does when nothing says otherwise.
7230        let text = ir("\
7231struct big { double v[8]; };
7232struct pair { int a, b; };
7233int p(const char *, ...);
7234int f(struct big b, struct pair q) { return p(\"\", 1, b, q); }
7235");
7236        assert!(
7237            text.contains("call @p(%4, %5, %2 byval(64, align 8), %6) : (ptr, ...) -> i32"),
7238            "{text}"
7239        );
7240    }
7241
7242    #[test]
7243    fn what_a_call_produced_is_somewhere_before_anything_is_read_out_of_it() {
7244        // `make(1, 2).b` has no object to read a member of until one is made, and what makes it
7245        // is a slot the returned registers are written to.
7246        let body = body(
7247            "\
7248struct pair { int a, b; };
7249struct pair make(int a, int b);
7250int second(void) { return make(1, 2).b; }
7251",
7252        );
7253        assert!(body.starts_with("block0:\n    %0 = alloca, size 8, align 4\n"), "{body}");
7254        assert!(body.contains("store %3 -> %0, align 4\n"), "{body}");
7255    }
7256
7257    #[test]
7258    fn a_structure_of_floats_travels_in_floating_point_registers_on_aarch64() {
7259        // The same declaration, classified by a different ABI: three `float` members are an
7260        // eightbyte of two of them and a half eightbyte of the third on SysV, and three vector
7261        // registers on AAPCS64.
7262        let source = "\
7263struct hfa { float x, y, z; };
7264int take(struct hfa h);
7265int give(struct hfa h) { return take(h); }
7266";
7267        assert!(ir(source).contains("func @take(f64, f32) -> i32"), "{}", ir(source));
7268        let mut opts = options();
7269        opts.emit = EmitKind::Ir;
7270        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
7271        let result = run(&opts, source);
7272        assert_eq!(result.messages, Vec::<String>::new());
7273        assert!(result.text().contains("func @take(f32, f32, f32) -> i32"), "{}", result.text());
7274    }
7275
7276    #[test]
7277    fn an_array_whose_length_is_not_a_constant_is_a_slot_made_where_its_declaration_is() {
7278        // The size is a multiplication rather than a number, the slot is taken from the stack
7279        // where the declaration is, and the scope it was declared in gives it back.
7280        let source = "\
7281int use(int *);
7282void f(int n) {
7283  {
7284    int a[n];
7285    use(a);
7286  }
7287  use(0);
7288}
7289";
7290        let body = body(source);
7291        assert!(body.contains("mul.nsw"), "{body}");
7292        assert!(body.contains("stacksave"), "{body}");
7293        assert!(body.contains("alloca %"), "{body}");
7294        assert!(body.contains("stackrestore"), "{body}");
7295    }
7296
7297    #[test]
7298    fn a_goto_out_of_the_scope_of_one_gives_its_stack_back_on_the_way() {
7299        // The label is outside the block the array is in, so arriving there means the array is
7300        // gone, and the restore that says so goes in front of the branch. The `goto` is written
7301        // before the walk knows where the label is, which is why the restore is put there at
7302        // the end rather than built where the branch was.
7303        let source = "\
7304int use(int *);
7305int f(int n) {
7306  {
7307    int a[n];
7308    if (use(a)) goto out;
7309    use(0);
7310  }
7311out:
7312  return 0;
7313}
7314";
7315        let body = body(source);
7316        // Two ways out of the block and a restore on each: the jump and the end of the block.
7317        assert_eq!(body.matches("stackrestore").count(), 2, "{body}");
7318        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
7319        assert!(after.starts_with(" %4\n    jump block"), "{body}");
7320    }
7321
7322    #[test]
7323    fn a_goto_to_a_label_the_array_is_still_alive_at_leaves_the_stack_alone() {
7324        // The label is after the declaration and in the same block, so control that arrives
7325        // there arrives somewhere the array exists. Giving it back would be giving back an
7326        // object the next statement reads.
7327        let source = "\
7328int use(int *);
7329int f(int n) {
7330  int a[n];
7331again:
7332  if (use(a)) goto again;
7333  return 0;
7334}
7335";
7336        let body = body(source);
7337        assert!(body.contains("stacksave"), "{body}");
7338        assert!(!body.contains("stackrestore"), "{body}");
7339    }
7340
7341    #[test]
7342    fn a_goto_back_to_a_label_in_front_of_one_gives_it_back_every_time_round() {
7343        // A loop written out of a `goto`, with the array made inside it. The label is in the
7344        // same block as the declaration and before it, which is a place where the array does
7345        // not exist yet, so the jump there leaves its scope and has to give the stack back. A
7346        // compiler that skips this restore grows the stack once per iteration.
7347        let source = "\
7348int use(int *);
7349int f(int n) {
7350again:
7351  {
7352    int a[n];
7353    if (use(a)) goto again;
7354  }
7355  return 0;
7356}
7357";
7358        let body = body(source);
7359        assert_eq!(body.matches("stacksave").count(), 1, "{body}");
7360        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
7361        assert!(after.starts_with(" %4\n    jump block1\n"), "{body}");
7362    }
7363
7364    #[test]
7365    fn the_head_of_a_for_loop_is_a_scope_that_closes_where_the_loop_is_left() {
7366        // The scope opened for `for (int a[n];;)` used to stay open, and a scope left open is
7367        // not one mark nobody reads. The marks are a stack, so the next close took this one
7368        // instead of its own, and the body of the loop gave back nothing while the block after
7369        // the loop restored a pointer saved inside it. The verifier refused that, which is how
7370        // it was found.
7371        let source = "\
7372int f(void);
7373void t(void) {
7374  int count = 10;
7375  for (; count--;) {
7376    int b[f()];
7377    int i;
7378    for (i = 0; i < f(); i++) {
7379      b[i] = count;
7380    }
7381  }
7382}
7383";
7384        let body = body(source);
7385        // One save, in the body, and one restore for it, also in the body: the block the
7386        // restore is in is the one the inner loop leaves through, and it goes back round the
7387        // outer loop rather than out of it.
7388        assert_eq!(body.matches("stacksave").count(), 1, "{body}");
7389        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
7390        // The rest of the block the restore is in, which is the last block here, so there is not
7391        // always another one after it to split on.
7392        let next = after.split("\n\n").next().expect("the block the restore is in");
7393        assert!(next.contains("jump block1("), "{body}");
7394    }
7395
7396    #[test]
7397    fn how_long_one_of_those_is_was_decided_where_it_was_declared_and_not_where_it_is_asked() {
7398        // What C says about the length being evaluated once: `sizeof a` after `n` changed is
7399        // still as long as the array is, which is what `n` was when the array came into being.
7400        let source = "\
7401unsigned long f(int n) {
7402  int a[n];
7403  n = 0;
7404  return sizeof a;
7405}
7406";
7407        let body = body(source);
7408        // One read of the parameter, at the declaration, and the answer is built out of it.
7409        assert_eq!(body.matches("sext.i64 %0").count(), 2, "{body}");
7410    }
7411
7412    #[test]
7413    fn a_block_in_the_middle_of_an_expression_is_walked_where_the_expression_is() {
7414        // GNU's statement expression: the statements happen where they are written and the last
7415        // one is the value, so the temporary in it never becomes a slot and never is copied.
7416        let source = "\
7417int use(int);
7418int f(int x) {
7419  return ({
7420    int t = use(x);
7421    t * t;
7422  });
7423}
7424";
7425        let expected = "\
7426block0(%0: i32):
7427    %1 = call @use(%0) : (i32) -> i32
7428    %2 = mul.nsw %1, %1
7429    return %2
7430";
7431        assert_eq!(body(source), expected);
7432    }
7433
7434    #[test]
7435    fn a_comma_whose_value_is_an_object_names_the_object_the_right_side_named() {
7436        // What janet writes, which is a call that does not return and then a value after it so
7437        // that the arm is worth something. The left side happens for what it did and the answer
7438        // is where the right side is, so there is nothing to copy and no temporary for a copy.
7439        let source = "\
7440struct pair { int a, b; };
7441void bail(void);
7442int f(struct pair p) {
7443  return (bail(), p).b;
7444}
7445";
7446        let expected = "\
7447block0(%0: i64):
7448    %1 = alloca, size 8, align 4
7449    store %0 -> %1, align 4
7450    call @bail() : ()
7451    %2 = iconst.i64 4
7452    %3 = ptr_add %1, %2
7453    %4 = load.i32 %3, align 4, tbaa !1
7454    return %4
7455";
7456        assert_eq!(body(source), expected);
7457    }
7458
7459    #[test]
7460    fn one_of_those_that_control_never_leaves_is_lowered_and_what_follows_it_is_dropped() {
7461        // A macro that always jumps, which is what this shape is in real code. The value is
7462        // never taken, and the block the rest of the expression would have been built in is
7463        // one nothing branches to, so it goes with the other unreachable blocks.
7464        let source = "int f(int x) { return ({ return x; 0; }); }\n";
7465        assert_eq!(body(source), "block0(%0: i32):\n    return %0\n");
7466    }
7467
7468    #[test]
7469    fn one_argument_off_a_variable_argument_list_stays_an_intrinsic() {
7470        // What it becomes is the target's answer, and this is not where the target's answers
7471        // are, so the walk writes down which list and which type and leaves it at that. Two of
7472        // them are two instructions, since each moves the list on.
7473        let source = "double f(__builtin_va_list ap) { return __builtin_va_arg(ap, double) + __builtin_va_arg(ap, double); }\n";
7474        let expected = "\
7475block0(%0: ptr):
7476    %1 = va_arg.f64 %0
7477    %2 = va_arg.f64 %0
7478    %3 = fadd %1, %2
7479    return %3
7480";
7481        assert_eq!(body(source), expected);
7482    }
7483
7484    #[test]
7485    fn one_that_reads_a_structure_answers_where_the_object_is() {
7486        // An aggregate is not a value, so there is nothing for the result of `va_arg` to be and
7487        // the object form is a second instruction. What it answers is an address, so it is a
7488        // place already and the walk copies nothing out of it: the copy here is the one the
7489        // initializer asks for, into the variable being declared. The size and the alignment
7490        // travel with it because they are what steps the list on and what a target that has to
7491        // put registers somewhere needs to know. So does the classification, which says the two
7492        // halves of this one arrived in general purpose registers: that is an answer about a C
7493        // type, and this is the last place that still has one.
7494        //
7495        // The slot is aligned to sixteen and the copy into it to eight, which is not a
7496        // disagreement. Sixteen is what a local aggregate of sixteen bytes gets whatever its
7497        // members ask for, and eight is what the type asks for and so what the copy may assume
7498        // about the object it is reading from.
7499        let source = "\
7500struct s { int a; long b; };
7501long f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.b; }
7502";
7503        let expected = "\
7504block0(%0: ptr):
7505    %1 = alloca, size 16, align 16
7506    %2 = va_object %0, size 16, align 8, in(int 8 at 0, int 8 at 8)
7507    memcpy %1, %2, size 16, align 8
7508    %3 = iconst.i64 8
7509    %4 = ptr_add %1, %3
7510    %5 = load.i64 %4, align 8, tbaa !1
7511    return %5
7512";
7513        assert_eq!(body(source), expected);
7514    }
7515
7516    /// Which register file each eightbyte arrived in is the whole of what the classification adds,
7517    /// and an object with no slots at all is one it sent to the caller's argument area, which is
7518    /// what everything over two eightbytes is whatever its members are.
7519    #[test]
7520    fn the_classification_says_which_registers_the_object_arrived_in() {
7521        let source = "\
7522struct s { double a; double b; };
7523double f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.a; }
7524";
7525        assert!(
7526            body(source)
7527                .contains("va_object %0, size 16, align 8, in(float f64 at 0, float f64 at 8)"),
7528            "{}",
7529            body(source)
7530        );
7531
7532        let big = "\
7533struct s { long a[4]; };
7534long f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.a[0]; }
7535";
7536        assert!(body(big).contains("va_object %0, size 32, align 8\n"), "{}", body(big));
7537    }
7538
7539    #[test]
7540    fn a_jump_to_an_address_branches_to_every_label_the_function_takes_the_address_of() {
7541        // GNU's computed goto. Which label the address holds is not known here, so all of them
7542        // are listed, and the values arriving at one are passed on every edge the same way they
7543        // are on an ordinary branch.
7544        let source = "\
7545int f(int c) {
7546  void *p = c ? &&one : &&two;
7547  goto *p;
7548one:
7549  return 1;
7550two:
7551  return 2;
7552}
7553";
7554        let expected = "\
7555block0(%0: i32):
7556    %1 = iconst.i32 0
7557    %2 = icmp ne %0, %1
7558    br_if %2, block1, block2
7559
7560block1:
7561    %3 = block_addr block3
7562    jump block4(%3)
7563
7564block2:
7565    %4 = block_addr block5
7566    jump block4(%4)
7567
7568block3:
7569    %5 = iconst.i32 1
7570    return %5
7571
7572block4(%6: ptr):
7573    indirect_br %6, block3, block5
7574
7575block5:
7576    %7 = iconst.i32 2
7577    return %7
7578";
7579        assert_eq!(body(source), expected);
7580    }
7581
7582    /// An interpreter, cut down to the shape that matters: a table of labels, a few values the
7583    /// loop keeps in hand, and a jump through the table at the end of every one of them.
7584    fn dispatch(labels: usize) -> String {
7585        let mask = labels - 1;
7586        let mut source = String::from("int spin(int n)\n{\n\tstatic void *table[] = {");
7587        for index in 0..labels {
7588            source.push_str(&format!(" &&a{index},"));
7589        }
7590        source.push_str(" };\n\tint w = n, x = n + 1, y = n + 2, z = n + 3;\n");
7591        source.push_str(&format!("\tif (n < 0) return 0;\n\tgoto *table[n & {mask}];\n"));
7592        for index in 0..labels {
7593            let step = match index % 4 {
7594                0 => "w += x;",
7595                1 => "x += y;",
7596                2 => "y += z;",
7597                _ => "z += w;",
7598            };
7599            source.push_str(&format!("a{index}:\n\t{step}\n"));
7600            source.push_str("\tif (--n <= 0) return w + x + y + z;\n");
7601            source.push_str(&format!("\tgoto *table[n & {mask}];\n"));
7602        }
7603        source.push_str("}\n");
7604        source
7605    }
7606
7607    /// How many moves are written in front of the first jump through a register.
7608    fn in_front_of_the_jump(text: &str) -> usize {
7609        let (before, _) = text.split_once("\tjmp\t*%").expect("a jump through a register");
7610        before.lines().rev().take_while(|line| line.starts_with("\tmov")).count()
7611    }
7612
7613    /// What a branch writes in front of its jump is what it carries, not what every label it can
7614    /// reach would like to be handed.
7615    ///
7616    /// A label an indirect branch reaches is given its values in registers the branch writes
7617    /// before it goes, because the moves cannot go after a jump and cannot go across the register
7618    /// the jump reads. Writing a register for each parameter of each label costs the table's
7619    /// length on every dispatch, which is a few moves in a program with two labels and five
7620    /// hundred in an interpreter with seventy. The values are the same values, so the registers
7621    /// are the same registers, and the cost stays where the number of values puts it.
7622    #[test]
7623    fn a_jump_through_a_register_writes_what_it_carries_and_not_the_whole_table() {
7624        let small = in_front_of_the_jump(&asm(&dispatch(4)));
7625        let large = in_front_of_the_jump(&asm(&dispatch(32)));
7626        assert_eq!(small, large, "eight times the labels and the same values in hand");
7627        assert!(large <= 8, "the values the loop keeps, and not a set of them per label: {large}");
7628    }
7629
7630    /// The same interpreter with more values in hand than there are registers, which is what makes
7631    /// the allocator send some of them to the stack at every label.
7632    fn crowded(labels: usize) -> String {
7633        const VALUES: usize = 24;
7634        let mask = labels - 1;
7635        let mut source = String::from("int spin(int n)\n{\n\tstatic void *table[] = {");
7636        for index in 0..labels {
7637            source.push_str(&format!(" &&a{index},"));
7638        }
7639        source.push_str(" };\n\t");
7640        for value in 0..VALUES {
7641            source.push_str(&format!("int v{value} = n + {value}; "));
7642        }
7643        let sum: Vec<String> = (0..VALUES).map(|value| format!("v{value}")).collect();
7644        source.push_str(&format!("\n\tif (n < 0) return 0;\n\tgoto *table[n & {mask}];\n"));
7645        for index in 0..labels {
7646            let (to, from) = (index % VALUES, (index + 1) % VALUES);
7647            source.push_str(&format!("a{index}:\n\tv{to} += v{from};\n"));
7648            source.push_str(&format!("\tif (--n <= 0) return {};\n", sum.join(" + ")));
7649            source.push_str(&format!("\tgoto *table[n & {mask}];\n"));
7650        }
7651        source.push_str("}\n");
7652        source
7653    }
7654
7655    /// How many bytes of frame the first function in a listing opens.
7656    fn the_frame(text: &str) -> u64 {
7657        text.lines()
7658            .find_map(|line| {
7659                let (size, _) = line.strip_prefix("\tsubq\t$")?.split_once(", %rsp")?;
7660                size.parse().ok()
7661            })
7662            .expect("a function that opens a frame")
7663    }
7664
7665    /// A frame holds what a function wants at once, and an interpreter does not want the whole
7666    /// table at once.
7667    ///
7668    /// Every label a dispatch table reaches is handed the values the loop keeps, and what the
7669    /// allocator has no register for goes on the stack. They are the same few values one label at
7670    /// a time, so they are the same bytes. A slot each put forty kilobytes on the frame of lua's
7671    /// interpreter and ran the C stack out at a depth lua's own limit was supposed to catch,
7672    /// which is tamnd/rucc#1630.
7673    #[test]
7674    fn a_frame_holds_what_is_wanted_at_once_and_not_a_slot_for_every_label() {
7675        let small = the_frame(&asm(&crowded(16)));
7676        let large = the_frame(&asm(&crowded(64)));
7677        assert_eq!(small, large, "four times the labels and the same values: {small}, {large}");
7678    }
7679
7680    #[test]
7681    fn a_jump_to_an_address_no_label_in_the_function_has_arrives_nowhere() {
7682        // The address came from outside the function, and a jump to a label in another function
7683        // is undefined. The expression is still evaluated, since a call in it has to happen.
7684        let source = "void **next(void);
7685void f(void) { goto *next(); }
7686";
7687        let expected = "\
7688block0:
7689    %0 = call @next() : () -> ptr
7690    unreachable
7691";
7692        assert_eq!(body(source), expected);
7693    }
7694
7695    #[test]
7696    fn an_asm_with_no_operands_is_volatile_and_the_clobbers_are_the_whole_of_what_it_says() {
7697        // Nothing reads a result, so the only thing that keeps it is that it is volatile, which
7698        // a basic asm implies.
7699        let source = "void f(void) { __asm__(\"mfence\" ::: \"memory\"); }\n";
7700        let expected = "\
7701block0:
7702    inline_asm.volatile \"mfence\", \"\", \"memory\"()
7703    return
7704";
7705        assert_eq!(body(source), expected);
7706    }
7707
7708    #[test]
7709    fn the_constraints_are_one_list_in_the_order_the_template_counts_the_operands() {
7710        // The outputs first and then the inputs, which is the numbering `%0` and `%1` use. An
7711        // output in a register is a result, and one that is read as well is an argument too.
7712        let source = "\
7713int f(int x, int y) {
7714  int r;
7715  __asm__(\"addl %2, %0\" : \"=r\"(r), \"+r\"(y) : \"r\"(x));
7716  return r + y;
7717}
7718";
7719        let expected = "\
7720block0(%0: i32, %1: i32):
7721    %2, %3 = inline_asm.(i32, i32) \"addl %2, %0\", \"=r,+r,r\", \"\"(%1, %0)
7722    %4 = add.nsw %2, %3
7723    return %4
7724";
7725        assert_eq!(body(source), expected);
7726    }
7727
7728    #[test]
7729    fn a_memory_operand_travels_as_the_address_of_an_object_that_is_given_a_slot() {
7730        // The assembly is handed a pointer, so the object cannot live in a value, and the scan
7731        // that runs before the walk has to have known that or there would be nothing to point
7732        // at. A structure travels this way whatever else its constraint allows, since there is
7733        // no register that holds one.
7734        let source = "\
7735struct pair { int a, b; };
7736int f(int x) {
7737  int slot = x;
7738  struct pair p = { x, x };
7739  __asm__(\"incl %0\" : \"+m\"(slot), \"=m\"(p));
7740  return slot + p.a;
7741}
7742";
7743        let text = body(source);
7744        assert!(text.contains("inline_asm \"incl %0\", \"+m,=m\", \"\"(%1, %2)\n"), "{text}");
7745        assert!(text.contains("%1 = alloca, size 4, align 4\n"), "{text}");
7746        assert!(text.contains("%2 = alloca, size 8, align 4\n"), "{text}");
7747    }
7748
7749    #[test]
7750    fn an_asm_goto_falls_through_to_its_first_target_and_writes_its_outputs_there() {
7751        // The output is only in scope where the instruction dominates, which is the fall through
7752        // block, so the edge to the label carries the value the object had before the assembly
7753        // ran. That is what document 11 asks for and it is what putting the fall through first
7754        // buys.
7755        let source = "\
7756int f(int x) {
7757  int r = 7;
7758  __asm__ goto(\"cbnz %0, %l1\" : \"=r\"(r) : \"r\"(x) :: away);
7759  return r;
7760away:
7761  return r;
7762}
7763";
7764        let expected = "\
7765block0(%0: i32):
7766    %1 = iconst.i32 7
7767    %2 = inline_asm.volatile \"cbnz %0, %l1\", \"=r,r\", \"\"(%0), labels [block1, block2]
7768
7769block1:
7770    return %2
7771
7772block2:
7773    return %1
7774";
7775        assert_eq!(body(source), expected);
7776    }
7777
7778    #[test]
7779    fn an_asm_statement_that_is_not_well_formed_is_reported_in_the_words_gcc_uses() {
7780        // The operands are checked here rather than by the assembler, because by the time the
7781        // assembler sees the template the operands have become registers and it has nothing left
7782        // to say about the C that named them.
7783        let mut opts = options();
7784        opts.emit = EmitKind::Ir;
7785        for (source, expected) in [
7786            (
7787                "void f(int x) { __asm__(\"\" : \"r\"(x)); }\n",
7788                "output operand constraint lacks '='",
7789            ),
7790            (
7791                "void f(int x) { __asm__(\"\" : \"=r\"(x + 1)); }\n",
7792                "lvalue required in 'asm' statement",
7793            ),
7794            (
7795                "const int g = 1;\nvoid f(void) { __asm__(\"\" : \"=r\"(g)); }\n",
7796                "read-only variable 'g' used as 'asm' output",
7797            ),
7798            (
7799                "void f(int x) { __asm__(\"\" : : \"=r\"(x)); }\n",
7800                "input operand constraint contains '='",
7801            ),
7802            (
7803                "void f(void) { __asm__(\"\" : : \"m\"(1)); }\n",
7804                "memory input 0 is not directly addressable",
7805            ),
7806            ("void f(void) { __asm__(L\"\"); }\n", "wide string literal in 'asm'"),
7807            (
7808                "void f(int x, int y) { __asm__(\"\" : [a] \"=r\"(x) : [a] \"r\"(y)); }\n",
7809                "duplicate asm operand name 'a'",
7810            ),
7811            ("void f(int x) { __asm__(\"%[in]\" : \"=r\"(x)); }\n", "undefined named operand 'in'"),
7812        ] {
7813            let result = run(&opts, source);
7814            assert!(result.failed(), "expected this to be reported:\n{source}");
7815            assert!(
7816                result.messages.iter().any(|m| m.contains(expected)),
7817                "{expected}\n{:?}",
7818                result.messages
7819            );
7820        }
7821    }
7822
7823    /// An `asm` at file scope whose template is directives is the whole of what the incbin
7824    /// header, an alias table and a hand written jump table each write, and what it says is a
7825    /// section holding named bytes. So it becomes the globals it names, in the order it names
7826    /// them, which is what `spec/11-asm-objects-debug.md` section 11.2 asks for.
7827    #[test]
7828    fn an_asm_at_file_scope_that_is_directives_becomes_the_objects_it_defines() {
7829        let text = ir(concat!(
7830            "__asm__(\n",
7831            "  \".section .rodata\\n\"\n",
7832            "  \".globl first\\n\"\n",
7833            "  \".balign 8\\n\"\n",
7834            "  \"first:\\n\"\n",
7835            "  \".long 1\\n\"\n",
7836            "  \".long 2\\n\"\n",
7837            "  \".globl last\\n\"\n",
7838            "  \"last:\\n\"\n",
7839            "  \".quad last - first\\n\");\n",
7840            "extern const int first[];\n",
7841            "extern const long last;\n",
7842        ));
7843        assert!(text.contains("global @first : bytes 8 = { i32 1, i32 2 }, align 8"), "{text}");
7844        assert!(text.contains("global @last : i64 = 8"), "{text}");
7845    }
7846
7847    /// The distance between two labels is what the incbin header hands a program as the size of
7848    /// the data, so a declaration of one of the names has to find the definition the template
7849    /// made rather than turn it back into something the linker is asked for.
7850    #[test]
7851    fn a_name_an_asm_at_file_scope_defined_is_not_undone_by_a_declaration_of_it() {
7852        let text = ir(concat!(
7853            "__asm__(\".data\\n.globl counter\\ncounter:\\n.long 7\\n\");\n",
7854            "extern int counter;\n",
7855            "int read(void) { return counter; }\n",
7856        ));
7857        assert!(text.contains("global @counter : i32 = 7"), "{text}");
7858    }
7859
7860    /// Bytes written before any label are a global with a name minted for them, in front of the
7861    /// label written under them, which is what makes the first byte of the name the one written
7862    /// under it. The block is the one tcc's test file writes, without the line of it that measures
7863    /// from one section to another.
7864    #[test]
7865    fn bytes_under_no_label_at_file_scope_are_a_global_in_front_of_the_label() {
7866        let text = ir(concat!(
7867            "__asm__(\".data\\n.byte 41\\nstuff:\\n661:\\n.byte 42\\n662:\\n",
7868            ".pushsection .data.ignore\\n.byte 7\\n.popsection\\n.byte 662b - 661b\\n\");\n",
7869            "extern unsigned char stuff[];\n",
7870            "int read(void) { return stuff[0]; }\n",
7871        ));
7872        let under = text.find("global @.Lasm.0 : i8 = 41").expect(&text);
7873        let named = text.find("global @stuff : i8 = 42").expect(&text);
7874        assert!(under < named, "the bytes under no label come first: {text}");
7875        assert!(text.contains("global @.Lasm.1 : i8 = 7, align 1, linkage(internal), section"));
7876        // The byte after the pop is a run of its own, because coming back to a section finishes
7877        // what was being written to it the way a label does. It is the next global of that
7878        // section all the same, so the byte lands where the template put it, which is the one
7879        // after the byte under `stuff`.
7880        let after = text.find("global @.Lasm.2 : i8 = 1").expect(&text);
7881        assert!(named < after, "{text}");
7882    }
7883
7884    /// How far a place is from the bytes holding the answer, which is what tcc's test file writes
7885    /// last and what the alternative instruction tables in a kernel header are made of. It is the
7886    /// linker's answer rather than the compiler's, because the two sections are placed by the
7887    /// linker, so the image holds a hole and a name for it.
7888    #[test]
7889    fn a_distance_from_here_at_file_scope_is_a_hole_naming_the_global_it_measures_to() {
7890        let text = ir(concat!(
7891            "__asm__(\".data\\n.byte 41\\nstuff:\\n661:\\n.byte 42\\n",
7892            ".pushsection .data.ignore\\n.long 661b - .\\n.popsection\\n\");\n",
7893            "extern unsigned char stuff[];\n",
7894            "int read(void) { return stuff[0]; }\n",
7895        ));
7896        // The label the template measured to is a local one and no symbol, so what the hole names
7897        // is the global it stands inside, which is the byte under `stuff`, and nothing further on
7898        // since it is the first byte of it.
7899        assert!(text.contains("global @.Lasm.1 : bytes 4 = { away.4 @stuff }"), "{text}");
7900    }
7901
7902    /// A `.set` says one name stands for another, which is a second symbol at the first one's
7903    /// address and is an alias and nothing else. What the directives around it said about the
7904    /// name is what the name gets, and a name the file defines itself keeps its own definition,
7905    /// which is what gcc's symbol table shows for the block tcc's test file writes.
7906    #[test]
7907    fn a_set_at_file_scope_is_a_second_name_for_what_it_names() {
7908        let text = ir(concat!(
7909            "void base(void) {}\n",
7910            "__asm__(\".weak one\\n.set one, base\");\n",
7911            "__asm__(\".globl two\\n.set two, base\");\n",
7912            "__asm__(\".set three, base\");\n",
7913            "void three(void) {}\n",
7914        ));
7915        assert!(text.contains("alias @one = @base, linkage(weak)"), "{text}");
7916        assert!(text.contains("alias @two = @base"), "{text}");
7917        assert!(!text.contains("alias @three"), "a definition of the name wins: {text}");
7918        assert!(text.contains("func @three"), "{text}");
7919    }
7920
7921    /// The target has to be something this file defines, because an alias is a symbol at an
7922    /// address in this object and a name only declared here has none to be at. The same rule and
7923    /// the same words as for `__attribute__((alias))`, since it is the same thing written another
7924    /// way.
7925    #[test]
7926    fn a_set_of_a_name_this_file_does_not_define_says_so() {
7927        let messages = errors("__asm__(\".set here, elsewhere\");\n");
7928        assert!(
7929            messages
7930                .iter()
7931                .any(|m| m.contains("'here' is aliased to undefined symbol 'elsewhere'")
7932                    && m.contains("E0697")),
7933            "{messages:?}"
7934        );
7935    }
7936
7937    /// `.incbin` is the one directive that reads something, and what it reads comes through the
7938    /// same file system the sources did.
7939    #[test]
7940    fn an_incbin_at_file_scope_is_the_bytes_of_the_file_it_names() {
7941        let mut opts = options();
7942        opts.emit = EmitKind::Ir;
7943        let mut fs = MemoryFileSystem::new();
7944        fs.insert(
7945            "/main.c",
7946            b"__asm__(\".data\\n.globl blob\\nblob:\\n.incbin \\\"seed\\\"\\n\");\n".to_vec(),
7947        );
7948        fs.insert("seed", b"hi".to_vec());
7949        let result = compile(&opts, "/main.c", &fs);
7950        assert_eq!(result.messages, Vec::<String>::new());
7951        let text = result.text();
7952        assert!(text.contains("global @blob : bytes 2 = { bytes \"hi\" }"), "{text}");
7953    }
7954
7955    /// A file that is not there is the mistake a build makes when it runs the compiler from the
7956    /// wrong directory, and it is worth saying which file rather than saying the template failed.
7957    #[test]
7958    fn an_incbin_naming_a_file_that_is_not_there_says_which_file() {
7959        let messages = errors("__asm__(\".data\\nb:\\n.incbin \\\"nowhere\\\"\\n\");\n");
7960        assert!(
7961            messages
7962                .iter()
7963                .any(|m| m.contains("cannot open 'nowhere' for reading") && m.contains("E0702")),
7964            "{messages:?}"
7965        );
7966    }
7967
7968    /// The line drawn is the same one the `asm` inside a function draws: directives are read and
7969    /// an instruction waits for an assembler. Refusing by name is what makes the wait visible.
7970    #[test]
7971    fn an_instruction_in_an_asm_at_file_scope_is_refused_rather_than_ignored() {
7972        for source in [
7973            "__asm__(\".text\\n.globl f\\nf:\\n  ret\\n\");\n",
7974            "__asm__(\".data\\n.set alias, 4\\n\");\n",
7975        ] {
7976            let messages = errors(source);
7977            assert!(
7978                messages
7979                    .iter()
7980                    .any(|m| m.contains("not supported yet")
7981                        && m.contains("in an `asm` at file scope")),
7982                "{source}\n{messages:?}"
7983            );
7984        }
7985    }
7986
7987    #[test]
7988    fn what_the_walk_cannot_build_yet_is_reported_rather_than_mislowered() {
7989        let mut opts = options();
7990        opts.emit = EmitKind::Ir;
7991        for source in [
7992            "int f(int n) { void *p = &&out; if (n) goto *p; { int a[n]; out: return 1; } }\n",
7993            "int f(int n) { int a[n]; __asm__ goto(\"\" ::::out); out: return a[0]; }\n",
7994        ] {
7995            let result = run(&opts, source);
7996            assert!(result.failed(), "expected this to be reported:\n{source}");
7997            assert!(
7998                result.messages.iter().any(|m| m.contains("not supported yet")),
7999                "{:?}",
8000                result.messages
8001            );
8002        }
8003    }
8004
8005    /// Compiles `source` to IR, reads that back as an input, and gives back both texts.
8006    fn round_trip(source: &str) -> (String, String) {
8007        let printed = ir(source);
8008        let mut opts = options();
8009        opts.emit = EmitKind::Ir;
8010        let mut fs = MemoryFileSystem::new();
8011        fs.insert("/main.ir", printed.clone().into_bytes());
8012        let result = compile_ir(&opts, "/main.ir", &fs);
8013        assert_eq!(result.messages, Vec::<String>::new(), "expected this to read back:\n{printed}");
8014        (printed, result.text().to_owned())
8015    }
8016
8017    #[test]
8018    fn ir_that_arrives_as_an_input_is_read_back_and_written_out_the_same() {
8019        // The other half of the round trip test below, through the driver rather than through
8020        // the library, which is what makes the property something to run over a real program
8021        // rather than over the modules a test builds.
8022        let (printed, again) = round_trip(
8023            "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",
8024        );
8025        assert_eq!(printed, again);
8026    }
8027
8028    #[test]
8029    fn ir_that_is_not_ir_says_which_line_stopped_it() {
8030        let mut opts = options();
8031        opts.emit = EmitKind::Ir;
8032        let mut fs = MemoryFileSystem::new();
8033        let text = "\
8034; ModuleID = 'a.c'
8035; format 0
8036target triple = \"x86_64-unknown-linux-gnu\"
8037target datalayout = \"e-p:64:64-i64:64-S128\"
8038
8039func @f(), linkage(external) {
8040block0:
8041    frobnicate
8042}
8043";
8044        fs.insert("/main.ir", text.as_bytes().to_vec());
8045        let result = compile_ir(&opts, "/main.ir", &fs);
8046        assert!(result.failed());
8047        assert!(result.messages[0].contains("/main.ir:8"), "{:?}", result.messages);
8048    }
8049
8050    #[test]
8051    fn ir_that_reads_but_does_not_hold_together_is_reported_by_the_verifier() {
8052        // A module that a person edited has not been through the verifier, and the return of
8053        // an `i32` from a function that returns nothing is the kind of thing editing produces.
8054        let mut opts = options();
8055        opts.emit = EmitKind::Ir;
8056        let mut fs = MemoryFileSystem::new();
8057        let text = "\
8058; ModuleID = 'a.c'
8059; format 0
8060target triple = \"x86_64-unknown-linux-gnu\"
8061target datalayout = \"e-p:64:64-i64:64-S128\"
8062
8063func @f(), linkage(external) {
8064block0:
8065    %0 = iconst.i32 1
8066    return %0
8067}
8068";
8069        fs.insert("/main.ir", text.as_bytes().to_vec());
8070        let result = compile_ir(&opts, "/main.ir", &fs);
8071        assert!(result.failed());
8072        assert!(result.messages[0].contains("invalid IR"), "{:?}", result.messages);
8073    }
8074
8075    #[test]
8076    fn a_typed_tree_is_not_something_an_input_of_ir_can_produce() {
8077        // The C that became this is not here any more, so there is nothing to print a tree of.
8078        let mut fs = MemoryFileSystem::new();
8079        fs.insert("/main.ir", Vec::new());
8080        let result = compile_ir(&options(), "/main.ir", &fs);
8081        assert!(result.failed());
8082        assert!(result.messages[0].contains("can only be emitted as IR"), "{:?}", result.messages);
8083    }
8084
8085    #[test]
8086    fn the_printed_ir_reads_back_as_the_same_module() {
8087        // The M2 exit criterion: the text is the module and nothing about it is lost by
8088        // writing it down. Anything the printer invents or the parser drops shows up here.
8089        let text = ir("\
8090struct point { int x, y; };
8091static const char greeting[] = \"hi\";
8092int table[4] = { 1, 2, 3 };
8093int puts(const char *);
8094double half(double x) { return x / 2.0; }
8095int f(int n) {
8096  int total = 0;
8097  for (int i = 0; i < n; i++) {
8098    if (i == 3) continue;
8099    total += table[i];
8100  }
8101  switch (n) {
8102    case 0: total = 1;
8103    case 1: total++; break;
8104    default: total = -total;
8105  }
8106  struct point p = { total, 1 };
8107  int *q = &p.y;
8108  puts(greeting);
8109  return p.x + *q;
8110}
8111int dispatch(int c) {
8112  void *p = c ? &&one : &&two;
8113  goto *p;
8114one:
8115  return 1;
8116two:
8117  return 2;
8118}
8119int assembly(int x, int *p) {
8120  int r;
8121  __asm__ volatile(\"xadd %0, %2\" : \"=r\"(r), \"+m\"(*p) : \"0\"(x) : \"cc\");
8122  __asm__ goto(\"cbnz %0, %l1\" : : \"r\"(r) : : away);
8123  return r;
8124away:
8125  return 0;
8126}
8127");
8128        let mut names = Interner::new();
8129        let module = rucc_ir::parse(&text, &mut names).expect("the printer writes what it reads");
8130        assert_eq!(rucc_ir::print(&module, &names), text);
8131    }
8132
8133    #[test]
8134    fn what_save_temps_keeps_is_the_text_that_was_compiled_and_the_assembly_that_was_assembled() {
8135        // The point of the flag is that these two are the compilation rather than a description
8136        // of one, so both come out of the run that produced the object rather than out of a
8137        // second run under different flags.
8138        let mut opts = options();
8139        opts.emit = EmitKind::Object;
8140        opts.save_temps = rucc_session::SaveTemps::Object;
8141        let result = run(&opts, "#define N 2\nint a[N];\n");
8142        assert_eq!(result.messages, Vec::<String>::new());
8143        let text = result.temps.preprocessed.expect("the preprocessed text");
8144        assert!(text.contains("int a[2];"), "{text}");
8145        assert!(text.starts_with("# 1 \"/main.c\""), "{text}");
8146        let asm = result.temps.assembly.expect("the assembly");
8147        assert!(asm.contains("a:"), "{asm}");
8148        assert!(matches!(result.artifact, Artifact::Object { .. }), "{:?}", result.artifact);
8149    }
8150
8151    #[test]
8152    fn nothing_is_kept_unless_the_flag_asked_for_it() {
8153        // A compilation that was not asked to keep anything must not pay for printing text
8154        // nobody will read, and the empty value is what says so.
8155        let mut opts = options();
8156        opts.emit = EmitKind::Object;
8157        assert_eq!(run(&opts, "int a;\n").temps, Temps::default());
8158    }
8159
8160    #[test]
8161    fn a_compilation_that_stops_before_the_back_end_keeps_the_text_and_no_assembly() {
8162        // `--emit=ir` never produces any, and the text is worth keeping all the same: it is
8163        // what a report about the file being read wrongly has to have in it.
8164        let mut opts = options();
8165        opts.emit = EmitKind::Ir;
8166        opts.save_temps = rucc_session::SaveTemps::Cwd;
8167        let result = run(&opts, "int a;\n");
8168        assert!(result.temps.preprocessed.is_some());
8169        assert_eq!(result.temps.assembly, None);
8170    }
8171}