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_diag::{Diagnostic, Severity, Span};
22use rucc_ir::{FpContract, Pic as IrPic, Visibility as IrVisibility};
23use rucc_lex::{Convert, Keywords, PpToken, convert};
24use rucc_lower::Protector as LowerProtector;
25use rucc_sema::{Checker, Context as CheckContext};
26use rucc_session::{
27    Contract, EmitKind, FileSystem, Options, Padding, Pic, Protector, Session, Visibility,
28};
29use rucc_target::TargetInfo;
30use rucc_tuple::{Arch, ObjectFormat};
31
32use crate::preprocess::render;
33
34/// What a compilation produced, which is text for most of the kinds and bytes for one of them.
35///
36/// Two variants rather than a string, because an object file is not text and a `Vec<u8>` holding
37/// UTF-8 for six kinds and a file format for the seventh would leave every reader guessing which
38/// it had. [`Artifact::Nothing`] is what a compilation that stopped early gives back, and it is
39/// not the same as an empty file: nothing is written for it at all.
40#[derive(Debug, Clone, PartialEq, Eq, Default)]
41pub enum Artifact {
42    /// The compilation stopped before it produced anything, or the kind asked for produces
43    /// nothing yet.
44    #[default]
45    Nothing,
46    /// Text, which is every kind up to and including assembly.
47    Text(String),
48    /// An object file, which is `-c`, and the names a linker can find in it.
49    ///
50    /// The names travel with the bytes rather than beside them because what wants them is the
51    /// archive step, and an index entry that does not match the member is worse than no archive:
52    /// the linker searches the index, pulls the member out, and still reports the name undefined.
53    /// One value holding both is one value the two cannot disagree in.
54    Object {
55        /// The file.
56        bytes: Vec<u8>,
57        /// Every name another object can reach, as the object writer wrote them. Empty is a real
58        /// answer: a translation unit of nothing but `static` functions is a member an archive
59        /// carries and nothing ever pulls out.
60        defines: Vec<String>,
61    },
62}
63
64impl Artifact {
65    /// The bytes to write, which is nothing at all for [`Artifact::Nothing`].
66    #[must_use]
67    pub fn bytes(&self) -> &[u8] {
68        match self {
69            Artifact::Nothing => &[],
70            Artifact::Text(text) => text.as_bytes(),
71            Artifact::Object { bytes, .. } => bytes,
72        }
73    }
74}
75
76/// What compiling one file produced.
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct Compiled {
79    /// What to write, which is nothing when the compilation failed or produced nothing.
80    pub artifact: Artifact,
81    /// The diagnostics, already rendered, one per element, in the order they were reported.
82    pub messages: Vec<String>,
83    /// How many of them were errors.
84    pub errors: u32,
85    /// Which lowering rules this file fired, for `-Zrule-coverage`.
86    ///
87    /// Empty for a compilation that stopped before the back end, which every kind up to and
88    /// including `--emit=ir` does. That is not the same as a rule set nothing reaches and the
89    /// caller unions these rather than reading one, so a file that fired nothing adds nothing.
90    pub fired: Fired,
91    /// What the register allocator had to put on the stack, for `-Zregister-pressure`.
92    ///
93    /// Empty for the same compilations `fired` is empty for and for the same reason, since both
94    /// are written by the back end and neither is a fact a file that stopped before it has.
95    pub pressure: Pressure,
96    /// What the pre-selection lowering group did, for `-Zlowering`.
97    ///
98    /// Empty for the same compilations `fired` is empty for and for the same reason, since the
99    /// group runs in the back end and a file that stopped before it lowered nothing.
100    pub lowerings: Lowerings,
101    /// What `-fdump-ir=` asked to see, in the order the passes ran.
102    ///
103    /// The optimizer does not write files, because nothing below the driver in
104    /// `spec/18-package-layout.md` knows what a file is, so the text comes back here and the
105    /// caller decides where it goes.
106    pub dumps: Vec<rucc_opt::Dump>,
107    /// What `-fopt-info` asked to hear, already rendered, one remark per line.
108    ///
109    /// Empty when the flag was not given, and also empty when it was given and no pass had
110    /// anything of the kinds asked for to say. Those two are the same text and different facts,
111    /// which is why a misspelled keyword is an error rather than a quiet nothing.
112    pub remarks: String,
113    /// Every file an `#include` found, for the `-M` family.
114    ///
115    /// The same list `Preprocessed` carries and for the same reason. A `-MD` writes it beside
116    /// the object, so the compiling path needs it as much as the preprocessing one does.
117    pub deps: Vec<rucc_pp::Dependency>,
118    /// What `-save-temps` asked to be kept, which is nothing at all unless it was given.
119    ///
120    /// It comes back from here rather than being produced by a second run of the compiler under
121    /// different flags, because a second run is a second answer: the file a person reads has to
122    /// be the file that was compiled, and two runs of anything with a `__TIME__` in it are not
123    /// the same text.
124    pub temps: Temps,
125}
126
127/// The intermediate text a compilation went through, kept when `-save-temps` asked for it.
128///
129/// Both are `None` on a compilation that was not asked to keep anything, and the assembly is
130/// `None` on one that stopped before there was any. Holding the text rather than writing it is
131/// what keeps this function free of the file system, which is what lets it be tested against a
132/// map from path to bytes.
133#[derive(Debug, Clone, PartialEq, Eq, Default)]
134pub struct Temps {
135    /// Phase 4's output, the same text `-E` would have printed.
136    pub preprocessed: Option<String>,
137    /// The assembly the back end produced on the way to the object file.
138    pub assembly: Option<String>,
139}
140
141impl Compiled {
142    /// Whether anything went wrong badly enough that the output should not be used.
143    #[must_use]
144    pub fn failed(&self) -> bool {
145        self.errors > 0
146    }
147
148    /// The text that was produced, and the empty string for anything that is not text.
149    ///
150    /// A caller that asked for one of the text kinds knows which it asked for, so this saves it
151    /// matching on a variant it has already ruled out.
152    #[must_use]
153    pub fn text(&self) -> &str {
154        match &self.artifact {
155            Artifact::Text(text) => text,
156            _ => "",
157        }
158    }
159}
160
161/// Compiles one file as far as `opts.emit` asks for and renders the result.
162///
163/// `name` is the path as the user wrote it, which is the name every diagnostic about the file
164/// uses. Every kind but the executable produces something today, and that one runs the same front
165/// end and gives back nothing, so that a file with a mistake in it is reported the same way
166/// whichever kind was asked for, rather than compiling silently until the part that is written
167/// notices.
168///
169/// The checking is skipped when the parse reported an error. The two poisoning rules mean a
170/// diagnosed expression produces no further complaints, but a declaration the parser had to skip
171/// past leaves no declaration behind at all, and every later use of that name would be reported
172/// as undeclared. One mistake is worth one message.
173#[must_use]
174pub fn compile(opts: &Options, name: &str, fs: &dyn FileSystem) -> Compiled {
175    let mut sess = Session::new(opts.clone());
176    // Before anything else interns a name. The keyword symbols have to be one unbroken run for
177    // a lookup to be a subtraction, and the preprocessor interns every identifier it reads, so
178    // building this after the expansion would mean building it after `char` had been seen.
179    let keywords = Keywords::new(&mut sess.interner, opts.std, opts.gnu_extensions);
180    let mut diagnostics: Vec<Diagnostic> = Vec::new();
181    // Filled in by the back end when there is one, and empty for every kind that stops before it.
182    let mut fired = Fired::new();
183    // The same, and the other thing the back end is asked to record about itself.
184    let mut pressure = Pressure::new();
185    let mut lowerings = Lowerings::asked(opts.lowering_dump.is_some());
186    // Filled in by the optimizer, and only when `-fdump-ir=` asked for something.
187    let mut dumps = Vec::new();
188    let mut remarks = String::new();
189    // Filled in as the compilation goes past each of them, and only under `-save-temps`.
190    let mut temps = Temps::default();
191
192    let bytes = match fs.read(Path::new(name)) {
193        Ok(bytes) => bytes,
194        Err(e) => return failure(format!("{name}: {e}")),
195    };
196    let Ok(file) = sess.sources.add_shared(name, bytes, None) else {
197        return failure(format!("{name}: the source map has no room left for this file"));
198    };
199
200    // Phases 1 to 4. The expanded stream is turned into pp-tokens straight away, because the
201    // include context borrows the source map that rendering a diagnostic reads and the borrow
202    // has to end before anything is rendered.
203    let mut pp = rucc_pp::Preprocessor::with_prefix_map(opts.prefix_map.macros.clone());
204    let predef = rucc_pp::Predef::for_options(opts);
205    let expanded: Vec<PpToken> = {
206        let mut tokens = Vec::new();
207        // The inner block is the borrow. The printer under `-save-temps` reads the source map
208        // that the include context is holding, so the context has to be gone before it runs, and
209        // nothing happens in between, which is what makes the text it prints the text that is
210        // compiled below rather than a second answer to the same question.
211        {
212            let mut cx =
213                rucc_pp::Context::new(&mut sess.interner, &mut sess.sources, fs, &opts.search);
214            cx.lex = rucc_lex::Options::for_dialect(opts.std, opts.gnu_extensions);
215            if pp.predefine(&sess.target, &predef, &mut cx).is_err() {
216                return failure(format!(
217                    "{name}: the source map has no room for the built in macros"
218                ));
219            }
220            if pp.preinclude(&opts.preincludes, &mut tokens, &mut cx).is_err() {
221                return failure(format!("{name}: the source map has no room for the command line"));
222            }
223            tokens.append(&mut pp.run(file, &mut cx));
224        }
225        if opts.save_temps.wanted() {
226            temps.preprocessed = Some(rucc_pp::print(
227                file,
228                &tokens,
229                pp.line_directives(),
230                &sess.sources,
231                &sess.interner,
232                rucc_pp::PrintOptions { line_markers: opts.line_markers },
233            ));
234        }
235        tokens.iter().map(|token| token.to_pp()).collect()
236    };
237    diagnostics.extend(pp.take_diagnostics());
238    // Taken here rather than at the end, because the preprocessor is done with and everything
239    // after this is about the tree it produced.
240    let deps = pp.dependencies().to_vec();
241
242    // Phase 7, which is where a spelling becomes a keyword and a preprocessing number becomes
243    // a constant of a type.
244    let cx = Convert {
245        keywords: &keywords,
246        interner: &sess.interner,
247        target: &sess.target,
248        std: opts.std,
249        gnu: opts.gnu_extensions,
250        pedantic: opts.pedantic,
251    };
252    let (tokens, complaints) = convert(&expanded, &cx);
253    diagnostics.extend(complaints);
254
255    let parsed = rucc_parse::parse(
256        &tokens,
257        rucc_parse::Context {
258            interner: &sess.interner,
259            std: opts.std,
260            gnu: opts.gnu_extensions,
261            pedantic: opts.pedantic,
262            error_limit: opts.error_limit as usize,
263        },
264    );
265    let parse_failed = parsed.diagnostics.iter().any(|d| d.severity.is_fatal());
266    diagnostics.extend(parsed.diagnostics);
267
268    let mut artifact = Artifact::Nothing;
269    // Zero when nothing instruments, which is the truthful summary of a file built without
270    // `-fsafety`: no checks went in, so none is standing, and every call it makes is unmodelled.
271    let mut instrumented = Instrumented::default();
272    if !parse_failed {
273        let mut checker = Checker::new(
274            &parsed.ast,
275            CheckContext {
276                names: &sess.interner,
277                target: &sess.target,
278                std: opts.std,
279                gnu: opts.gnu_extensions,
280                pedantic: opts.pedantic,
281                permissive: opts.permissive,
282                gnu89_inline: opts.gnu89_inline,
283                error_limit: opts.error_limit as usize,
284                // A freestanding program has no C library, so a name that is the library's
285                // everywhere else is the program's own here and means whatever it defined.
286                builtins: opts.builtins && opts.hosted,
287                no_builtin: &opts.no_builtin,
288                short_enums: opts.short_enums,
289                trapping_math: opts.trapping_math,
290            },
291        );
292        checker.check_unit();
293        let checked = checker.finish();
294        if !checked.failed() {
295            match opts.emit {
296                EmitKind::Tast => {
297                    artifact = Artifact::Text(rucc_sema::print(
298                        &checked.tast,
299                        &checked.types,
300                        &sess.interner,
301                    ));
302                }
303                // Nothing past the checker, because a granule is a fact about a layout and a
304                // layout is settled the moment the closing brace is seen. Lowering the
305                // function bodies would take minutes on an amalgamation and answer nothing.
306                EmitKind::TypeGranules => {
307                    artifact = Artifact::Text(rucc_types::granule_report(
308                        &checked.types,
309                        &sess.interner,
310                        &sess.target,
311                    ));
312                }
313                EmitKind::Ir
314                | EmitKind::MirFinal
315                | EmitKind::Asm
316                | EmitKind::Object
317                | EmitKind::Archive
318                | EmitKind::Executable
319                | EmitKind::SafetySummary => {
320                    // What a `.incbin` in an `asm` at file scope names is read through the same
321                    // file system the sources came through, and from where the compiler was run
322                    // rather than from beside the source, because that is where an assembler
323                    // looks for it.
324                    let mut read = |named: &str| {
325                        fs.read(Path::new(named))
326                            .map(|bytes| bytes.as_slice().to_vec())
327                            .map_err(|why| why.to_string())
328                    };
329                    let mut lowered = rucc_lower::lower(
330                        name,
331                        rucc_lower::Context {
332                            tast: &checked.tast,
333                            types: &checked.types,
334                            target: &sess.target,
335                            names: &mut sess.interner,
336                            visibility: match opts.visibility {
337                                Visibility::Default => IrVisibility::Default,
338                                Visibility::Hidden => IrVisibility::Hidden,
339                                Visibility::Protected => IrVisibility::Protected,
340                            },
341                            protector: match opts.protector {
342                                Protector::None => LowerProtector::None,
343                                Protector::Buffers => LowerProtector::Buffers,
344                                Protector::Strong => LowerProtector::Strong,
345                                Protector::All => LowerProtector::All,
346                            },
347                            wrapping: rucc_lower::Wrapping {
348                                signed: opts.wrapping.signed,
349                                pointer: opts.wrapping.pointer,
350                                trap: opts.wrapping.trap,
351                            },
352                            aliasing: opts.strict_aliasing,
353                            padding: opts.padding == Padding::Ignored,
354                            contract: match opts.fp_contract {
355                                Contract::Off => FpContract::Off,
356                                Contract::On => FpContract::On,
357                                Contract::Fast => FpContract::Fast,
358                            },
359                            read: &mut read,
360                        },
361                    );
362                    // The walk reports what it cannot build, and what it did build is printed
363                    // anyway: a file with one construct missing from it is more use to read
364                    // than nothing at all, and the errors are what stop it being compiled.
365                    let failed = lowered.diagnostics.iter().any(|d| d.severity.is_fatal());
366                    if !failed {
367                        // The verifier runs on everything the walk builds, always. It is the
368                        // one check that a bug in the walk cannot talk its way past, and a
369                        // wrong instruction found here costs a message rather than an hour
370                        // in front of a debugger over the assembly it turned into.
371                        if let Err(errors) = rucc_ir::verify(&lowered.module, &sess.interner) {
372                            for error in errors {
373                                diagnostics.push(internal(&format!("invalid IR, {error}")));
374                            }
375                        } else if let Err(complaints) =
376                            instrument(&mut lowered.module, &mut sess.interner, opts)
377                                .map(|done| instrumented = done)
378                        {
379                            diagnostics.extend(complaints);
380                        } else if let Err(complaints) = optimize(
381                            &mut lowered.module,
382                            &sess.interner,
383                            &sess.target,
384                            opts,
385                            name,
386                            &mut dumps,
387                            &mut remarks,
388                        ) {
389                            diagnostics.extend(complaints);
390                        } else if opts.emit == EmitKind::SafetySummary {
391                            // After the optimizer, because the number that matters is how many
392                            // checks are still standing and there is no way to know that before it
393                            // has run. Before the back end, because the back end turns a check into
394                            // a call and a summary of calls is not a summary of checks.
395                            artifact = Artifact::Text(
396                                rucc_safety::summarize(
397                                    &lowered.module,
398                                    &sess.interner,
399                                    name,
400                                    opts.safety.as_str(),
401                                    instrumented.checks,
402                                    instrumented.interposed,
403                                    instrumented.crossings,
404                                )
405                                .render(),
406                            );
407                        } else if opts.emit == EmitKind::Ir {
408                            // After the optimizer rather than before it, so that `--emit=ir -O2`
409                            // is the IR the back end will be given rather than the IR it would
410                            // have been given at `-O0`. There is no other way to see what a pass
411                            // did without reading the assembly it turned into.
412                            artifact =
413                                Artifact::Text(rucc_ir::print(&lowered.module, &sess.interner));
414                        } else {
415                            // The back end, which is every pass after the IR and which is
416                            // where a construct nothing has a rule for is finally noticed.
417                            match generate(
418                                &mut lowered.module,
419                                &mut sess.interner,
420                                &sess.target,
421                                opts,
422                                &mut Recording {
423                                    fired: &mut fired,
424                                    pressure: &mut pressure,
425                                    lowerings: &mut lowerings,
426                                },
427                                &mut temps.assembly,
428                            ) {
429                                Ok(made) => artifact = made,
430                                Err(complaints) => diagnostics.extend(complaints),
431                            }
432                        }
433                    }
434                    diagnostics.extend(lowered.diagnostics);
435                }
436                _ => {}
437            }
438        }
439        diagnostics.extend(checked.diagnostics);
440    }
441
442    let mut messages = Vec::with_capacity(diagnostics.len());
443    let mut errors = 0;
444    for diag in &diagnostics {
445        // `-w` drops the warning here rather than at the several hundred places one is raised,
446        // and it drops it before the count, so `-w -Werror` compiles. A warning that was never
447        // raised is not a warning there is anything to promote.
448        if !opts.warnings && diag.severity == Severity::Warning {
449            continue;
450        }
451        if diag.severity.is_fatal()
452            || (diag.severity == Severity::Warning && opts.warnings_are_errors)
453        {
454            errors += 1;
455        }
456        messages.push(render(diag, &sess.sources, opts.warnings_are_errors));
457    }
458    if errors > 0 {
459        // A tree built from a file that did not compile is not a tree anything should read.
460        artifact = Artifact::Nothing;
461    }
462    // Kept even when the compilation failed, because a rule that fired did fire and a report about
463    // which rules a corpus reaches should not lose the ones a file with a mistake in it reached.
464    Compiled { artifact, messages, errors, fired, pressure, lowerings, dumps, remarks, deps, temps }
465}
466
467/// Reads one file of IR, checks it, and prints it back.
468///
469/// This is the compiler's own textual IR arriving as an input rather than leaving as an output,
470/// which is what makes the round trip in the M2 exit criterion something to run rather than
471/// something to believe: what the printer wrote is read back, verified, and written again, and
472/// the two files are either the same bytes or they are not.
473///
474/// The verifier runs here for the reason it runs after the walk. A module that was printed by
475/// this compiler has been through it once already, and one that a person edited has not.
476#[must_use]
477pub fn compile_ir(opts: &Options, name: &str, fs: &dyn FileSystem) -> Compiled {
478    let mut sess = Session::new(opts.clone());
479    if opts.emit != EmitKind::Ir {
480        return failure(format!(
481            "{name}: an input of IR can only be emitted as IR, and `--emit={}` asks for what \
482             the C in front of it became",
483            opts.emit.as_str()
484        ));
485    }
486    let bytes = match fs.read(Path::new(name)) {
487        Ok(bytes) => bytes,
488        Err(e) => return failure(format!("{name}: {e}")),
489    };
490    let Ok(text) = std::str::from_utf8(bytes.as_slice()) else {
491        return failure(format!("{name}: this is not text, so it is not IR"));
492    };
493
494    let module = match rucc_ir::parse(text, &mut sess.interner) {
495        Ok(module) => module,
496        Err(error) => {
497            return failure(format!("{name}:{}: {}", error.line, error.message));
498        }
499    };
500    let mut diagnostics: Vec<Diagnostic> = Vec::new();
501    if let Err(errors) = rucc_ir::verify(&module, &sess.interner) {
502        for error in errors {
503            diagnostics.push(invalid(&format!("invalid IR, {error}")));
504        }
505    }
506    let mut messages = Vec::with_capacity(diagnostics.len());
507    for diag in &diagnostics {
508        messages.push(render(diag, &sess.sources, opts.warnings_are_errors));
509    }
510    let errors = u32::try_from(messages.len()).unwrap_or(u32::MAX);
511    let artifact = if errors > 0 {
512        Artifact::Nothing
513    } else {
514        Artifact::Text(rucc_ir::print(&module, &sess.interner))
515    };
516    // Nothing here reaches the back end, so no rule fired and there is nothing to record.
517    Compiled {
518        artifact,
519        messages,
520        errors,
521        fired: Fired::new(),
522        pressure: Pressure::new(),
523        lowerings: Lowerings::new(),
524        dumps: Vec::new(),
525        remarks: String::new(),
526        deps: Vec::new(),
527        temps: Temps::default(),
528    }
529}
530
531/// Puts the memory safety checks in and redirects the calls that cross the boundary, when
532/// `-fsafety=` asked for them.
533///
534/// Between the walk and the optimizer, which is where section 15.3 of
535/// `spec/safe-memory/15-integration.md` puts it and which is the whole design in one line: the
536/// checks go in while the addresses the program computes still exist, and the optimizer then
537/// discharges the ones it can prove. Every sanitizer that came before instruments after the
538/// optimizer so that its checks cannot be deleted, and pays for all of them forever.
539///
540/// The calls to the C library are redirected here too, and in the same window and for a related
541/// reason. `spec/safe-memory/10-boundaries.md` section 10.3 wants a `memcpy` modelled by a wrapper
542/// that performs the judgements, and `rucc_safety::wrap` is why that has to happen before the
543/// optimizer sees the call rather than after.
544///
545/// The verifier runs again afterwards, for the reason it runs after the walk. This pass rewrites
546/// every function in the module, and a pass that produced IR nothing else accepts should say so
547/// here rather than in the assembly it turned into.
548///
549/// # Errors
550///
551/// When the inserted checks left the module in a state the verifier refuses, which is a bug in
552/// this compiler and not in the program being compiled.
553fn instrument(
554    module: &mut rucc_ir::Module,
555    names: &mut Interner,
556    opts: &Options,
557) -> Result<Instrumented, Vec<Diagnostic>> {
558    if !opts.safety.instruments() {
559        return Ok(Instrumented::default());
560    }
561    let checks = rucc_safety::run(module, opts.subobject, opts.promise, opts.races);
562    // Before the optimizer rather than beside the check lowering, which is what
563    // `rucc_safety::wrap` argues out: `memcpy` is a name an optimizer knows things about, and a
564    // pass that turns a short copy into a pair of loads and stores would leave behind accesses the
565    // check insertion has already finished walking past.
566    let interposed = rucc_safety::redirect(module, names);
567    // After the redirection, so that a call this build models with a wrapper is not also counted
568    // as a crossing it did not model.
569    let crossings = rucc_safety::witness(module, names);
570    match rucc_ir::verify(module, names) {
571        Ok(()) => Ok(Instrumented { checks, interposed, crossings }),
572        Err(errors) => Err(errors
573            .iter()
574            .map(|e| internal(&format!("invalid IR after check insertion, {e}")))
575            .collect()),
576    }
577}
578
579/// What the instrumentation did, which nothing but the summary reads.
580///
581/// Carried out of [`instrument`] rather than recovered from the module afterwards because neither
582/// number survives the optimizer: a check that was discharged leaves nothing behind saying it was
583/// ever there, and a call that was pointed at a wrapper looks like a call that always named one.
584#[derive(Clone, Copy, Debug, Default)]
585struct Instrumented {
586    /// How many checks of each class went in.
587    checks: rucc_safety::Counts,
588    /// How many calls were pointed at an interposition wrapper.
589    interposed: usize,
590    /// How many places a pointer crosses to or from code this build did not instrument.
591    crossings: rucc_safety::Sites,
592}
593
594/// Runs the optimizer over the module, and collects whatever the dumps asked for.
595///
596/// The level chooses a pipeline, the `-f` flags edit it, and at `-O0` there is nothing in it, so
597/// this is a walk over an empty list rather than a branch on the level. See section 9.1 of
598/// `spec/09-optimizer.md` for why the pipelines are written out rather than assembled.
599///
600/// # Errors
601///
602/// When a pass left the module in a state the verifier refuses, which is a bug in the pass and
603/// not in the program being compiled, so it is reported as an internal error the way a bad
604/// lowering is.
605fn optimize(
606    module: &mut rucc_ir::Module,
607    names: &Interner,
608    target: &TargetInfo,
609    opts: &Options,
610    file: &str,
611    dumps: &mut Vec<rucc_opt::Dump>,
612    remarks: &mut String,
613) -> Result<(), Vec<Diagnostic>> {
614    let mut settings = rucc_opt::Options::for_level(opts.opt_level);
615    // What the analyses that read a body may believe about it. The same question the back end asks
616    // about addresses, with one thing on top: `-fno-semantic-interposition` is the build promising
617    // that a name it exports is the one that will run, which is what every distribution builds a
618    // library with. It says nothing about how an address is reached, and gcc does not change that
619    // under the flag either, so the back end is not given this value.
620    settings.interposition = match opts.interposition {
621        true => replaceable(target, opts),
622        false => IrPic::Executable,
623    };
624    settings.toggles.clone_from(&opts.passes);
625    settings.fuel = opts.pass_fuel.iter().cloned().collect();
626    settings.global_fuel = opts.pass_fuel_global;
627    settings.verify |= opts.verify_each;
628    for (on, spec) in &opts.pass_gates {
629        // Same argument as the dumps below: every spelling in here was checked while the
630        // arguments were parsed, so a rejection now is this compiler disagreeing with itself.
631        if let Err(why) = settings.gates.add(*on, spec) {
632            return Err(vec![internal(&why)]);
633        }
634    }
635    for spec in &opts.dump_ir {
636        // Every spelling in here was checked while the arguments were parsed, so a rejection
637        // now is this compiler disagreeing with itself rather than the command line being wrong.
638        if let Err(why) = settings.dumps.add(spec) {
639            return Err(vec![internal(&why)]);
640        }
641    }
642    let mut wants = rucc_opt::Wants::none();
643    for spec in &opts.opt_info {
644        // Same argument as the dumps above: every spelling was checked while the arguments were
645        // parsed, so a rejection now is the compiler disagreeing with itself.
646        if let Err(why) = wants.add(spec) {
647            return Err(vec![internal(&why)]);
648        }
649    }
650    let report = rucc_opt::run(module, names, &settings);
651    remarks.push_str(&rucc_opt::optinfo::render(file, &report, names, wants));
652    dumps.extend(report.dumps);
653    match report.broke.is_empty() {
654        true => Ok(()),
655        false => Err(report.broke.iter().map(|why| internal(why)).collect()),
656    }
657}
658
659/// Runs the back end over every function in `module` and writes what came out.
660///
661/// One machine function per definition in the module, in the order the module holds them, every
662/// register physical and every frame offset a constant. A declaration has no body and is skipped,
663/// because there is nothing in it to compile.
664///
665/// What the last step is, is the only thing `--emit=mir-final`, `-S` and `-c` disagree about. The
666/// three read the same functions and differ in whether they are printed as machine IR, printed as
667/// assembly, or encoded and put in a file, which is the point of section 11.1 of
668/// `spec/11-asm-objects-debug.md`: a listing that disagrees with the object file beside it is
669/// worse than no listing, and the way to make that impossible is to have one description of an
670/// instruction and two ways of writing it down.
671///
672/// # Errors
673///
674/// One diagnostic per function the back end could not compile, or one about the target when no
675/// back end covers it at all. Every function is attempted rather than stopping at the first, so a
676/// file with three constructs missing from the rule set reports three rather than one at a time.
677///
678/// `assembly` is where `-save-temps` gets its listing from on the path that does not print one,
679/// which is the same functions written the other way rather than a second compilation of the same
680/// file. A listing that disagrees with the object beside it would be worse than none.
681/// Whether a name this file exports is one another object may define or replace.
682///
683/// The link that reads the object decides half of what is in it, and the command line is where that
684/// is said, which is why the flag reaches this far down. See #756.
685///
686/// ELF only, because it is a question about a format rather than about a machine and the other two
687/// answer it differently. Mach-O has a two level namespace, so a name a library defines is bound to
688/// that library and is not replaced by a definition loaded earlier, and it has no copy relocations,
689/// so a variable defined elsewhere needs the table whichever link is coming. COFF decides what
690/// leaves a DLL by an export table the linker is handed. Neither has an object writer here yet, so
691/// what this does is decline to say the ELF answer about them.
692fn replaceable(target: &TargetInfo, opts: &Options) -> IrPic {
693    match (target.tuple.os().object_format(), opts.pic) {
694        (Some(ObjectFormat::Elf), Pic::Library) => IrPic::Library,
695        _ => IrPic::Executable,
696    }
697}
698
699fn generate(
700    module: &mut rucc_ir::Module,
701    names: &mut Interner,
702    target: &TargetInfo,
703    opts: &Options,
704    recording: &mut Recording<'_>,
705    assembly: &mut Option<String>,
706) -> Result<Artifact, Vec<Diagnostic>> {
707    let Some(machine) = Machine::for_target(target) else {
708        return Err(vec![unsupported(&format!(
709            "there is no back end for {} in this compiler yet, so there is nothing to generate",
710            target.tuple
711        ))]);
712    };
713    // Refused rather than dropped. A command line that asks for a stack protector on a target
714    // that has nowhere to keep the word one is compared against would otherwise get code with no
715    // protection in it and no indication that the flag did nothing, which is the one outcome worse
716    // than the error. Windows is the case: it has a protector and it is a different mechanism.
717    if opts.protector != Protector::None && machine.conv.guard.is_none() {
718        return Err(vec![unsupported(&format!(
719            "{} is not supported for {} yet, because the stack protector on that target is not \
720             the one this compiler writes",
721            opts.protector, target.tuple
722        ))]);
723    }
724    // The same answer for the same reason. What says a file was built to have its control flow
725    // checked is a note, the note is an ELF one, and a target whose objects are not ELF has nowhere
726    // to put it: the landing pads would go in and nothing would ever turn the check on. Windows has
727    // the same hardware and asks for it a different way, which is a bit in the image the linker is
728    // told to set rather than anything a compiler writes into an object.
729    if opts.control.any() && target.tuple.os().object_format() != Some(ObjectFormat::Elf) {
730        return Err(vec![unsupported(&format!(
731            "-fcf-protection={} is not supported for {} yet, because what says a file was built \
732             for it there is not the note this compiler writes",
733            opts.control, target.tuple
734        ))]);
735    }
736    // And once more. A profiled build is one whose functions call a routine the runtime provides,
737    // and a target whose runtime provides no such routine would get a call to a name nothing
738    // defines, which is a link error a long way from the flag that caused it. Windows profiles a
739    // build by calling something else, asked for a different way and taking its argument in a
740    // register, so it is not this hook spelled differently.
741    let profile = match machine.conv.trace {
742        Some(trace) => opts.profile.then(|| opts.hook.early(trace.fentry)),
743        None if opts.profile => {
744            return Err(vec![unsupported(&format!(
745                "-pg is not supported for {} yet, because the profiler's hook on that target is \
746                 not the one this compiler calls",
747                target.tuple
748            ))]);
749        }
750        None => None,
751    };
752    // And once more. The room a patcher was promised is only half the feature: the other half is a
753    // section listing where every function's room is, and both the section's shape and the way it
754    // points at the text it belongs to are ELF's. A format that has no such section would take the
755    // nops and quietly lose the list, which is a build that looks patchable and is not.
756    if opts.patchable.any() && target.tuple.os().object_format() != Some(ObjectFormat::Elf) {
757        return Err(vec![unsupported(&format!(
758            "-fpatchable-function-entry= is not supported for {} yet, because what records where \
759             the room is there is not the section this compiler writes",
760            target.tuple
761        ))]);
762    }
763    let flags = pipeline::Flags {
764        frame_pointer: opts.frame_pointer,
765        red_zone: opts.red_zone,
766        stack_clash: opts.stack_clash,
767        landing: opts.control.branch(),
768        profile: match profile {
769            None => pipeline::Profile::No,
770            Some(true) => pipeline::Profile::Early,
771            Some(false) => pipeline::Profile::Late,
772        },
773        patch: pipeline::Room { after: opts.patchable.after(), before: opts.patchable.before },
774        // On at every level above `-O0`, which is where gcc turns `-freorder-blocks` on
775        // (`gcc/opts.cc:604`) and what `spec/optimizer/38-scheduling-and-layout.md` section 38.3
776        // reads off that: it is one of the earliest optimizations there is, it is nearly free,
777        // and it helps every target. `-O0` keeps the order the shape of the graph gives, so that
778        // the blocks come out in the order they were written and a person stepping through the
779        // code walks down the screen.
780        reorder: opts.reorder_blocks.unwrap_or_else(|| opts.opt_level.runs_optimizer()),
781        // On at every level above `-O0`, for the reason the line above is off at it. Sharing one
782        // run of bytes between two locals is a smaller frame and a worse debugger: a variable that
783        // is out of scope reads as whatever took its place, which is what `-O0` exists not to do.
784        // Above it the frame is the win, and `-fstack-reuse=` says either answer at any level.
785        reuse: opts.stack_reuse.unwrap_or_else(|| opts.opt_level.runs_optimizer()),
786        // On from `-O2`, which is where gcc turns `-fschedule-insns2` on and what
787        // `spec/optimizer/38-scheduling-and-layout.md` section 38.6 asks for. Not at `-O1`,
788        // because a schedule is a whole dependence graph per block and `-O1` is the level whose
789        // budget is roughly `-O0`'s. Not at `-O0` for the reason nothing else is.
790        schedule: opts.schedule_insns.unwrap_or_else(|| opts.opt_level.schedules()),
791        // Whatever the command line said, and the model's own answer when it said nothing.
792        accurate: opts.cycle_accurate_model,
793    };
794
795    // The checks become calls here rather than beside the insertion, because the id each one
796    // carries is an index into a table and a row for a check the optimizer deleted is a row nothing
797    // will ever name. Section 6.3.1 of `spec/safe-memory/06-instrumentation.md` is what this
798    // eventually becomes and `rucc_safety::lower` says why it is not that yet.
799    //
800    // It is inside the back end rather than beside the optimizer so that `--emit=ir` still shows
801    // the checks. The IR a person reads should say what the compiler decided, not how it spelled it
802    // for the machine.
803    if opts.safety.instruments() {
804        // Which calls hand back storage, which the lowering needs and `-O0` has not worked out.
805        // `rucc_opt::pipeline` runs this only when some pass in the run reads the summaries, since a
806        // flag nothing reads is noise in a dump, and at `-O0` nothing did. Something does now: the
807        // capability for a pointer an allocator just returned is the one capability that is exact
808        // and costs a load, and `rucc_safety::slot` finds those sites by the flag. The safety suite
809        // runs at `-O0`, so without this the cheap case would be the one case that never happens.
810        //
811        // Safe to run twice and safe to run late, because it only ever sets the flag and never
812        // clears one, so a build that had it already gets the same module back.
813        rucc_opt::heap::annotate(module, names);
814        // Which calls hand their capabilities to the callee and which say there are none. Here and
815        // not beside the insertion, because the rule is what each function still has left to check
816        // and the optimizer is what makes that small: running before it would give every callee a
817        // frame for checks that are about to be discharged. `rucc_safety::handover` is the rule and
818        // the pass both, and the census in `--emit=safety-summary` reads the same rule, so the
819        // buckets it prints describe the code that was actually built.
820        rucc_safety::handover::arrange(module);
821        rucc_safety::lower(module, names);
822        if let Err(errors) = rucc_ir::verify(module, names) {
823            return Err(errors
824                .iter()
825                .map(|e| internal(&format!("invalid IR after check lowering, {e}")))
826                .collect());
827        }
828    }
829
830    // Worked out before the loop and not inside it, because it reads the whole module and the loop
831    // is holding one function of it. It has to be after the check lowering above, since that adds
832    // calls to the runtime and so can add a name this file does not define.
833    //
834    // The link that reads the object decides half of what is in it, and the command line is where
835    // that is said, which is why the flag reaches this far down. See #756.
836    //
837    let elsewhere = Elsewhere::of(module, replaceable(target, opts));
838
839    let mut funcs = Vec::new();
840    let mut complaints = Vec::new();
841    for id in module.funcs() {
842        if module[id].is_declaration() {
843            continue;
844        }
845        match pipeline::compile_recording(
846            &mut module[id],
847            names,
848            &machine,
849            &elsewhere,
850            flags,
851            recording,
852        ) {
853            Ok(func) => funcs.push(func),
854            Err(why) => {
855                let name = names.resolve(module[id].name).to_owned();
856                // The function knows where the instruction came from, so the message lands on
857                // the line somebody wrote rather than on the file as a whole.
858                let span = why.inst().map_or(Span::DUMMY, |inst| module[id].span(inst));
859                let said = format!("cannot generate code for '{name}': {why}");
860                complaints.push(unsupported_at(&said, span));
861            }
862        }
863    }
864    if !complaints.is_empty() {
865        return Err(complaints);
866    }
867    // The variables the file defines, which go through the back end the way the functions did not:
868    // there is nothing in a variable to select instructions for, so the module is what says what
869    // one is right up to the point where it is written down.
870    // The second names go the same way and for the same reason, and they are neither a function
871    // nor a variable: an alias is an entry in the symbol table and no bytes of anything.
872    let (globals, aliases) = match opts.emit {
873        EmitKind::Asm | EmitKind::Object | EmitKind::Archive | EmitKind::Executable => (
874            rucc_asm::globals(module, names, target.object_format).map_err(refused)?,
875            rucc_asm::aliases(module, names).map_err(refused)?,
876        ),
877        _ => (rucc_asm::Globals::default(), Vec::new()),
878    };
879    // A failure in either of the last two is a bug here rather than a program this compiler is
880    // behind on, because every instruction in a function that got this far came out of the same
881    // description both of them read and every register in it has been allocated.
882    let unwind = opts.unwinds();
883    match opts.emit {
884        EmitKind::Asm => {
885            rucc_asm::print(&funcs, &globals, &aliases, names, target, unwind, output(opts, target))
886                .map(Artifact::Text)
887                .map_err(refused)
888        }
889        // An executable is an object as far as this gets: one is what each file of a link
890        // contributes, and the linker is what turns them into the other. An archive is the same
891        // again, with the archive writer in place of the linker.
892        EmitKind::Object | EmitKind::Archive | EmitKind::Executable => {
893            if opts.save_temps.wanted() {
894                let listing = rucc_asm::print(
895                    &funcs,
896                    &globals,
897                    &aliases,
898                    names,
899                    target,
900                    unwind,
901                    output(opts, target),
902                );
903                *assembly = Some(listing.map_err(refused)?);
904            }
905            let text = rucc_asm::assemble(&funcs, names, target, unwind).map_err(refused)?;
906            let data = globals.image();
907            // A format with no writer is a target this compiler is behind on and anything else
908            // the writer refused is a bug here, and the two are not the same news to get.
909            let bytes = rucc_object::write(&text, &data, &aliases, target, output(opts, target))
910                .map_err(wrote)?;
911            // Asked of the writer rather than worked out from the same three values here, so that
912            // what the archive's index says and what is in the member cannot come apart. It is
913            // wanted only by `--emit=archive` and is cheap enough that the other two kinds are not
914            // worth a second path.
915            let defines = rucc_object::defines(&text, &data, &aliases, target).map_err(wrote)?;
916            Ok(Artifact::Object { bytes, defines })
917        }
918        _ => Ok(Artifact::Text(rucc_mir::print(&funcs, names, target.regs))),
919    }
920}
921
922/// What the command line decided about the file being written, in the words the assembler and the
923/// object writer use.
924///
925/// Two spellings of the same facts, because the flags are the command line's and the answer the two
926/// writers want is the object format's. The conversion is here rather than in either of them so
927/// that the two output paths are handed the same thing and cannot come to disagree about what is
928/// in a file.
929///
930/// The feature word is empty on a machine whose bits these are not. It is the x86 one, and a target
931/// that wanted its control flow checked would want a property of its own with a key of its own, so
932/// writing this one there would be recording something untrue rather than recording nothing.
933fn output(opts: &Options, target: &TargetInfo) -> rucc_object::Output {
934    let mut features = 0;
935    if target.tuple.arch() == Arch::X86_64 {
936        if opts.control.branch() {
937            features |= rucc_object::Property::IBT;
938        }
939        if opts.control.ret() {
940            features |= rucc_object::Property::SHSTK;
941        }
942    }
943    rucc_object::Output {
944        sections: rucc_object::Sections {
945            functions: opts.function_sections,
946            data: opts.data_sections,
947        },
948        property: rucc_object::Property { features },
949    }
950}
951
952/// What the object writer said, as the kind of news it is.
953///
954/// A format with no writer is a target this compiler is behind on, which is a program nobody can
955/// compile today and not a mistake in the one being compiled. Anything else it refused is a bug
956/// here, because every value it was handed came out of this compiler.
957fn wrote(why: rucc_object::Error) -> Vec<Diagnostic> {
958    match why {
959        rucc_object::Error::Format { .. } => vec![unsupported(&why.to_string())],
960        rucc_object::Error::Refused { .. } => vec![internal(&why.to_string())],
961    }
962}
963
964/// What the assembler said, as the kind of news it is.
965///
966/// Two of these are about a program and the rest are about this compiler. A thread-local variable
967/// and an ifunc are both valid C that the back end does not build yet, and everything else the
968/// assembler refuses is something that should never have reached it.
969fn refused(why: rucc_asm::Error) -> Vec<Diagnostic> {
970    match why {
971        rucc_asm::Error::Thread { .. } | rucc_asm::Error::IFunc { .. } => {
972            vec![unsupported(&why.to_string())]
973        }
974        _ => vec![internal(&why.to_string())],
975    }
976}
977
978/// A diagnostic about a program this compiler is not finished enough to compile.
979///
980/// Not an internal error, because nothing here is wrong: the program is valid C and the part of
981/// the back end that would handle it has not been written. The note says so, so that a report
982/// about one of these is filed against the milestone rather than as a miscompilation.
983fn unsupported(message: &str) -> Diagnostic {
984    unsupported_at(message, Span::DUMMY)
985}
986
987/// The same, about somewhere in the file rather than about the file.
988///
989/// The note names the issue tracker rather than `spec/17-milestones.md`, which is a document
990/// about the plan: a reader who follows it wants to know whether the construct in front of them
991/// is already written down as work, and the milestone list does not answer that.
992fn unsupported_at(message: &str, span: Span) -> Diagnostic {
993    Diagnostic::error(message.to_owned(), span)
994        .with_code("E0653")
995        .note("this construct is not lowered yet, see https://github.com/tamnd/rucc/issues", span)
996}
997
998/// A diagnostic about IR that was handed to us rather than built by us.
999fn invalid(message: &str) -> Diagnostic {
1000    Diagnostic::error(message.to_owned(), Span::DUMMY).with_code("E0661")
1001}
1002
1003/// A diagnostic about this compiler rather than about the program it was given.
1004fn internal(message: &str) -> Diagnostic {
1005    Diagnostic::error(format!("internal error: {message}"), Span::DUMMY)
1006        .with_code("E0652")
1007        .note("this is a bug in rucc rather than in the program, please report it", Span::DUMMY)
1008}
1009
1010/// A result that is nothing but one message, for the failures that happen before there is
1011/// anything to compile.
1012fn failure(message: String) -> Compiled {
1013    Compiled {
1014        artifact: Artifact::Nothing,
1015        messages: vec![format!("rucc: error: {message}")],
1016        errors: 1,
1017        fired: Fired::new(),
1018        pressure: Pressure::new(),
1019        lowerings: Lowerings::new(),
1020        dumps: Vec::new(),
1021        remarks: String::new(),
1022        deps: Vec::new(),
1023        temps: Temps::default(),
1024    }
1025}
1026
1027#[cfg(test)]
1028mod tests {
1029    use rucc_session::{MemoryFileSystem, Std};
1030    use rucc_target::Triple;
1031
1032    use super::*;
1033
1034    fn options() -> Options {
1035        let mut opts = Options::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
1036        opts.emit = EmitKind::Tast;
1037        opts
1038    }
1039
1040    fn run(opts: &Options, source: &str) -> Compiled {
1041        let mut fs = MemoryFileSystem::new();
1042        fs.insert("/main.c", source.to_owned().into_bytes());
1043        compile(opts, "/main.c", &fs)
1044    }
1045
1046    /// Options with the compiler's own headers on the search path and nothing else, which is
1047    /// what a freestanding compilation is. There is no file system underneath these tests,
1048    /// so a header that reached for one would fail to resolve and say so.
1049    fn freestanding() -> Options {
1050        let mut opts = options();
1051        opts.hosted = false;
1052        opts.search.push_system(rucc_session::runtime::DIR);
1053        opts
1054    }
1055
1056    /// The typed tree of a freestanding `source`, insisting that it compiled cleanly.
1057    fn shipped(source: &str) -> String {
1058        let result = run(&freestanding(), source);
1059        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
1060        result.text().to_owned()
1061    }
1062
1063    /// The typed tree of `source`, insisting that it compiled cleanly.
1064    fn tast(source: &str) -> String {
1065        let result = run(&options(), source);
1066        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
1067        result.text().to_owned()
1068    }
1069
1070    #[test]
1071    fn the_shipped_stdarg_declares_a_list_and_the_four_operators() {
1072        let text = shipped(concat!(
1073            "#include <stdarg.h>\n",
1074            "int sum(int n, ...) {\n",
1075            "  va_list ap, copy;\n",
1076            "  va_start(ap, n);\n",
1077            "  va_copy(copy, ap);\n",
1078            "  int total = va_arg(ap, int) + va_arg(copy, int);\n",
1079            "  va_end(ap);\n",
1080            "  va_end(copy);\n",
1081            "  return total;\n",
1082            "}\n",
1083        ));
1084        assert!(text.contains("va-start"), "{text}");
1085        assert!(text.contains("va-copy"), "{text}");
1086        assert!(text.contains("va-arg"), "{text}");
1087        assert!(text.contains("va-end"), "{text}");
1088    }
1089
1090    /// glibc includes `<stdarg.h>` this way from every header that declares a `vprintf`, and
1091    /// what it wants is the type without the four macro names. Answering the whole header
1092    /// would put `va_start` in the way of a program that has its own.
1093    #[test]
1094    fn stdarg_hands_out_the_type_alone_when_that_is_all_that_was_asked_for() {
1095        let text = shipped(concat!(
1096            "#define __need___va_list\n",
1097            "#include <stdarg.h>\n",
1098            "int vprint(const char *f, __gnuc_va_list ap);\n",
1099            "#ifdef va_start\n",
1100            "#error va_start should not be defined\n",
1101            "#endif\n",
1102            "#ifdef _VA_LIST_DEFINED\n",
1103            "#error va_list should not have been made\n",
1104            "#endif\n",
1105        ));
1106        assert!(text.contains("vprint"), "{text}");
1107    }
1108
1109    /// The same protocol on `<stddef.h>`, which glibc uses far more heavily: `<stdio.h>` asks
1110    /// for `size_t` and `NULL` and would be wrong to receive `offsetof` as well.
1111    #[test]
1112    fn stddef_answers_one_piece_at_a_time_and_the_next_request_still_gets_through() {
1113        let text = shipped(concat!(
1114            "#define __need_size_t\n",
1115            "#include <stddef.h>\n",
1116            "#ifdef offsetof\n",
1117            "#error offsetof should not be defined yet\n",
1118            "#endif\n",
1119            "#define __need_ptrdiff_t\n",
1120            "#include <stddef.h>\n",
1121            "#include <stddef.h>\n",
1122            "size_t a;\n",
1123            "ptrdiff_t b;\n",
1124            "wchar_t c;\n",
1125            "max_align_t d;\n",
1126            "void *e = NULL;\n",
1127            "struct P { int x; long y; };\n",
1128            "size_t f = offsetof(struct P, y);\n",
1129        ));
1130        assert!(text.contains("decl #0 a : unsigned long"), "{text}");
1131        assert!(text.contains("decl #1 b : long"), "{text}");
1132    }
1133
1134    #[test]
1135    fn the_shipped_limits_and_float_are_the_targets_own_answers() {
1136        let text = shipped(concat!(
1137            "#include <limits.h>\n",
1138            "#include <float.h>\n",
1139            "int bits = CHAR_BIT;\n",
1140            "long big = LONG_MAX;\n",
1141            "int low = INT_MIN;\n",
1142            "int radix = FLT_RADIX;\n",
1143            "int digits = DBL_MANT_DIG;\n",
1144        ));
1145        assert!(text.contains("const 8 : int"), "{text}");
1146        assert!(text.contains("const 9223372036854775807 : long"), "{text}");
1147        assert!(text.contains("const 2 : int"), "{text}");
1148        assert!(text.contains("const 53 : int"), "{text}");
1149    }
1150
1151    /// Freestanding, so there is no library header to chain to and `<stdint.h>` writes the
1152    /// whole set out itself. The widths are the ones the target picked, which is the only
1153    /// reason this header is the compiler's.
1154    #[test]
1155    fn the_shipped_stdint_writes_the_whole_set_when_there_is_no_library_to_defer_to() {
1156        let text = shipped(concat!(
1157            "#include <stdint.h>\n",
1158            "int64_t a = INT64_C(1);\n",
1159            "uint_least16_t b;\n",
1160            "intptr_t c;\n",
1161            "uintmax_t d = UINTMAX_MAX;\n",
1162            "int wide = sizeof(int_fast64_t);\n",
1163        ));
1164        assert!(text.contains("decl #0 a : long"), "{text}");
1165        assert!(text.contains("decl #1 b : unsigned short"), "{text}");
1166        assert!(text.contains("decl #2 c : long"), "{text}");
1167    }
1168
1169    /// `<mmintrin.h>` is the base of the vector header chain and the first one whose contents
1170    /// are C rather than declarations, so what this checks is that the C in it compiles: a
1171    /// header that is nothing but definitions fails as a whole or not at all.
1172    ///
1173    /// What the intrinsics answer is not checked here and cannot be, because the answer is
1174    /// only interesting next to another compiler's. Every intrinsic in the header was built
1175    /// and run against GCC 16.2.0 on the same inputs, at `-O0`, `-O1`, `-O2` and `-Os`, and
1176    /// gave the same bytes in all four. Carrying that comparison rather than repeating it by
1177    /// hand needs a facet in `tamnd/rucc-corpus` that works out the expected bytes itself,
1178    /// which is a second implementation of MMX and is `tamnd/rucc#1150`.
1179    #[test]
1180    fn the_shipped_mmintrin_defines_the_mmx_type_and_the_operations_over_it() {
1181        let text = shipped(concat!(
1182            "#include <mmintrin.h>\n",
1183            "__m64 add(__m64 a, __m64 b) { return _mm_add_pi16(a, b); }\n",
1184            "__m64 pack(__m64 a, __m64 b) { return _m_packsswb(a, b); }\n",
1185            "__m64 shift(__m64 a) { return _mm_srai_pi32(a, 3); }\n",
1186            "int low(__m64 a) { return _mm_cvtsi64_si32(a); }\n",
1187            "void done(void) { _mm_empty(); }\n",
1188        ));
1189        assert!(text.contains("add"), "{text}");
1190        assert!(text.contains("pack"), "{text}");
1191        assert!(text.contains("shift"), "{text}");
1192    }
1193
1194    /// The allocator beside the vector headers, which is the one piece of the family that is
1195    /// not a vector operation. It reaches for `<stddef.h>` and for three names out of the
1196    /// library, and the point of the test is that the reach resolves with nothing on the
1197    /// search path but the compiler's own directory.
1198    #[test]
1199    fn the_shipped_mm_malloc_asks_for_aligned_memory_and_gives_it_back() {
1200        let text = shipped(concat!(
1201            "#include <mm_malloc.h>\n",
1202            "void *get(void) { return _mm_malloc(64, 16); }\n",
1203            "void put(void *p) { _mm_free(p); }\n",
1204        ));
1205        assert!(text.contains("get"), "{text}");
1206        assert!(text.contains("put"), "{text}");
1207    }
1208
1209    /// `<xmmintrin.h>` is the next rung of the chain and pulls the other two in behind it, so a
1210    /// program that includes this one alone has to get all three. What the intrinsics answer is
1211    /// checked the same way `<mmintrin.h>` next door is checked and for the same reason: a
1212    /// hundred and forty eight lines of answers over nans, infinities, both zeros and values
1213    /// that do not fit in the integer they convert to, identical to GCC 16.2.0 at `-O0`, `-O1`,
1214    /// `-O2` and `-Os`.
1215    ///
1216    /// `_mm_rcp_ps` is the one answer in that run that is not identical, and is not meant to be.
1217    /// The instruction approximates a reciprocal and this computes one exactly, so the bits
1218    /// differ while both sit inside the relative error Intel documents, which the same program
1219    /// checks directly rather than by comparing bits.
1220    #[test]
1221    fn the_shipped_xmmintrin_defines_the_sse_type_and_the_operations_over_it() {
1222        let text = shipped(concat!(
1223            "#include <xmmintrin.h>\n",
1224            "__m128 add(__m128 a, __m128 b) { return _mm_add_ps(a, b); }\n",
1225            "__m128 one(__m128 a, __m128 b) { return _mm_max_ss(a, b); }\n",
1226            "__m128 mask(__m128 a, __m128 b) { return _mm_cmpnle_ps(a, b); }\n",
1227            "__m128 pick(__m128 a, __m128 b) { return _mm_shuffle_ps(a, b, _MM_SHUFFLE(0,1,2,3)); }\n",
1228            "int bits(__m128 a) { return _mm_movemask_ps(a); }\n",
1229            "int near(__m128 a) { return _mm_cvtss_si32(a); }\n",
1230            "__m128 wide(__m64 a) { return _mm_cvtpi16_ps(a); }\n",
1231            "void *room(void) { return _mm_malloc(64, 16); }\n",
1232            "void hint(const float *p) { _mm_prefetch(p, _MM_HINT_T0); _mm_sfence(); }\n",
1233        ));
1234        assert!(text.contains("add"), "{text}");
1235        assert!(text.contains("mask"), "{text}");
1236        assert!(text.contains("pick"), "{text}");
1237        assert!(text.contains("wide"), "{text}");
1238    }
1239
1240    /// The six names of gcc's header this one leaves out, each of which is an instruction whose
1241    /// answer no plain C reproduces exactly. Leaving them out is what turns a program that wants
1242    /// one into a diagnostic naming the function it called, rather than into a wrong answer, and
1243    /// this is what notices if one is ever quietly defined to something close.
1244    ///
1245    /// `tamnd/rucc#1157` is the square root, which brings the first four back.
1246    #[test]
1247    fn the_shipped_xmmintrin_leaves_out_the_names_that_need_an_instruction() {
1248        let text = rucc_session::runtime::header("xmmintrin.h").expect("xmmintrin.h is shipped");
1249        for absent in [
1250            "_mm_sqrt_ps",
1251            "_mm_sqrt_ss",
1252            "_mm_rsqrt_ps",
1253            "_mm_rsqrt_ss",
1254            "_mm_getcsr",
1255            "_mm_setcsr",
1256        ] {
1257            let defined = text.contains(&format!("{absent}("));
1258            assert!(!defined, "{absent} is defined and the header says it is not");
1259            assert!(text.contains(absent), "{absent} is absent and unexplained");
1260        }
1261    }
1262
1263    #[test]
1264    fn the_shipped_emmintrin_defines_both_sse2_types_and_the_operations_over_them() {
1265        let text = shipped(concat!(
1266            "#include <emmintrin.h>\n",
1267            "__m128i add(__m128i a, __m128i b) { return _mm_add_epi64(a, b); }\n",
1268            "__m128i wide(__m128i a, __m128i b) { return _mm_mul_epu32(a, b); }\n",
1269            "__m128i pick(__m128i a) { return _mm_shuffle_epi32(a, _MM_SHUFFLE(0,1,2,3)); }\n",
1270            "__m128i up(__m128i a) { return _mm_slli_epi64(a, 13); }\n",
1271            "__m128i down(__m128i a) { return _mm_srli_si128(a, 3); }\n",
1272            "__m128i pack(__m128i a, __m128i b) { return _mm_packus_epi16(a, b); }\n",
1273            "int bits(__m128i a) { return _mm_movemask_epi8(a); }\n",
1274            "__m128d sum(__m128d a, __m128d b) { return _mm_add_sd(a, b); }\n",
1275            "__m128d mask(__m128d a, __m128d b) { return _mm_cmpunord_pd(a, b); }\n",
1276            "__m128i near(__m128d a) { return _mm_cvtpd_epi32(a); }\n",
1277            "__m128d over(__m128 a) { return _mm_cvtps_pd(a); }\n",
1278            "__m128i half(__m64 a) { return _mm_movpi64_epi64(a); }\n",
1279            "__m128i grab(void const *p) { return _mm_loadu_si128(p); }\n",
1280            "void wall(void) { _mm_lfence(); _mm_mfence(); }\n",
1281        ));
1282        assert!(text.contains("wide"), "{text}");
1283        assert!(text.contains("pack"), "{text}");
1284        assert!(text.contains("near"), "{text}");
1285        assert!(text.contains("half"), "{text}");
1286    }
1287
1288    /// The umbrella header reaches the three underneath it. This is brotli's use of it, from
1289    /// `c/enc/matching_tag_mask.h`, which is the whole of what `tamnd/rucc#1236` was about: four
1290    /// SSE2 names that were already shipped and no way to get at them by the name gcc uses.
1291    #[test]
1292    fn the_shipped_immintrin_reaches_the_names_the_headers_under_it_define() {
1293        let text = shipped(concat!(
1294            "#include <immintrin.h>\n",
1295            "unsigned long long matching(unsigned char tag, unsigned char const *bucket) {\n",
1296            "  __m128i const want = _mm_set1_epi8((char)tag);\n",
1297            "  __m128i const chunk = _mm_loadu_si128((__m128i const *)(void const *)bucket);\n",
1298            "  __m128i const same = _mm_cmpeq_epi8(chunk, want);\n",
1299            "  return (unsigned long long)_mm_movemask_epi8(same);\n",
1300            "}\n",
1301            "__m64 narrow(__m64 a, __m64 b) { return _mm_add_pi32(a, b); }\n",
1302            "__m128 single(__m128 a, __m128 b) { return _mm_add_ps(a, b); }\n",
1303        ));
1304        assert!(text.contains("matching"), "{text}");
1305        assert!(text.contains("narrow"), "the MMX header is not reached: {text}");
1306        assert!(text.contains("single"), "the SSE header is not reached: {text}");
1307    }
1308
1309    /// Including it twice is the same as including it once, and so is including it beside the
1310    /// header it reaches. A program that includes both spellings is the usual case rather than an
1311    /// odd one, because one of its own headers includes the umbrella and another includes SSE2.
1312    #[test]
1313    fn the_umbrella_and_the_header_under_it_can_both_be_included() {
1314        let text = shipped(concat!(
1315            "#include <immintrin.h>\n",
1316            "#include <emmintrin.h>\n",
1317            "#include <immintrin.h>\n",
1318            "__m128i twice(__m128i a, __m128i b) { return _mm_add_epi32(a, b); }\n",
1319        ));
1320        assert!(text.contains("twice"), "{text}");
1321    }
1322
1323    /// The float header omits four square roots and SSE2 omits the matching two, for the reason
1324    /// both headers write down. A later change that quietly defines one as an approximation
1325    /// would be a wrong answer nobody sees, so the absence is held in place here.
1326    #[test]
1327    fn the_shipped_emmintrin_leaves_out_the_two_square_roots() {
1328        let text = rucc_session::runtime::header("emmintrin.h").expect("emmintrin.h is shipped");
1329        for absent in ["_mm_sqrt_pd", "_mm_sqrt_sd"] {
1330            let defined = text.contains(&format!("{absent}("));
1331            assert!(!defined, "{absent} is defined and the header says it is not");
1332            assert!(text.contains(absent), "{absent} is absent and unexplained");
1333        }
1334    }
1335
1336    #[test]
1337    fn the_three_formality_headers_still_have_to_work() {
1338        let text = shipped(concat!(
1339            "#include <stdbool.h>\n",
1340            "#include <stdalign.h>\n",
1341            "#include <iso646.h>\n",
1342            "#include <stdnoreturn.h>\n",
1343            "int t = true and not false;\n",
1344            "_Alignas(16) char buf[16];\n",
1345            "int a = alignof(long);\n",
1346        ));
1347        assert!(text.contains("decl #0 t : int"), "{text}");
1348        assert!(text.contains("const 8 : unsigned long"), "{text}");
1349    }
1350
1351    /// Including everything twice has to change nothing, because that is what happens in any
1352    /// program large enough to matter and a guard that is wrong shows up nowhere else.
1353    ///
1354    /// Stated as the two trees being the same rather than as a fact about what is in either
1355    /// one. A header that carries definitions puts them in the tree and moves everything
1356    /// after them along, so an assertion about where the program's own declaration landed is
1357    /// an assertion about how much `<mmintrin.h>` defines, which is not what is being asked.
1358    #[test]
1359    fn every_shipped_header_can_be_included_twice() {
1360        let once: String = rucc_session::runtime::names()
1361            .iter()
1362            .map(|name| format!("#include <{name}>\n"))
1363            .collect();
1364        let twice = once.repeat(2);
1365        assert_eq!(shipped(&format!("{once}int x;\n")), shipped(&format!("{twice}int x;\n")));
1366    }
1367
1368    #[test]
1369    fn a_file_that_is_not_there_says_so_and_produces_nothing() {
1370        let fs = MemoryFileSystem::new();
1371        let result = compile(&options(), "/nope.c", &fs);
1372        assert!(result.failed());
1373        assert!(result.messages[0].contains("/nope.c"), "{:?}", result.messages);
1374        assert!(result.text().is_empty());
1375    }
1376
1377    #[test]
1378    fn an_object_comes_out_with_its_type_its_linkage_and_how_much_of_a_definition_it_is() {
1379        let text = tast("int x = 1;\n");
1380        let expected = "\
1381decl #0 x : int object external static defined
1382  init
1383    +0
1384      const 1 : int
1385";
1386        assert_eq!(text, expected);
1387    }
1388
1389    #[test]
1390    fn the_macros_are_expanded_before_anything_is_parsed() {
1391        // The whole pipeline in one line. The bound came out of a macro, so it was expanded,
1392        // converted from a preprocessing number to a constant of a type, parsed as an
1393        // expression, and folded to the number the array type carries.
1394        let text = tast("#define N 2\nint a[N];\n");
1395        assert!(text.starts_with("decl #0 a : int[2] object external static tentative"), "{text}");
1396    }
1397
1398    /// A pragma survives the preprocessor on purpose, since what one means is not its
1399    /// business, and nothing after it has a place for a `#` in the grammar. `pack` is the one
1400    /// the parser reads and every other line is walked past. Both spellings are here because
1401    /// they arrive by different routes and only one of them was ever on a line of its own in
1402    /// the source.
1403    #[test]
1404    fn a_pragma_is_not_a_declaration_and_the_parse_walks_past_the_ones_it_does_not_read() {
1405        let text = tast(concat!(
1406            "#pragma pack(4)\n",
1407            "struct s { int a; };\n",
1408            "#pragma pack()\n",
1409            "int b;\n",
1410            "_Pragma(\"GCC visibility push(default)\") int c;\n",
1411        ));
1412        assert!(text.contains("decl #0 b : int"), "{text}");
1413        assert!(text.contains("decl #1 c : int"), "{text}");
1414    }
1415
1416    /// Every number in these two tests was read off gcc 16 on x86-64 under `-std=gnu23`
1417    /// rather than reasoned about, which is why they are written as assertions the program
1418    /// makes about itself: a compilation with no messages is every one of them holding.
1419    ///
1420    /// This half is the attributes. `packed` takes the padding out, on the record or on one
1421    /// member, `aligned` raises and never lowers, and the two written together are the
1422    /// combination that packs and then aligns the whole thing.
1423    #[test]
1424    fn the_layout_attributes_move_the_members_and_the_record_the_way_gcc_lays_them_out() {
1425        tast(concat!(
1426            "struct A { char c; int i; } __attribute__((packed));\n",
1427            "_Static_assert(sizeof(struct A) == 5 && _Alignof(struct A) == 1, \"A\");\n",
1428            "_Static_assert(__builtin_offsetof(struct A, i) == 1, \"A.i\");\n",
1429            // `aligned` with nothing in the parentheses is the largest alignment the target
1430            // has, which gcc calls BIGGEST_ALIGNMENT and which is sixteen everywhere here.
1431            "struct B { char c; int i; } __attribute__((aligned));\n",
1432            "_Static_assert(sizeof(struct B) == 16 && _Alignof(struct B) == 16, \"B\");\n",
1433            "struct C { char c; int i __attribute__((packed)); };\n",
1434            "_Static_assert(sizeof(struct C) == 5 && _Alignof(struct C) == 1, \"C\");\n",
1435            "_Static_assert(__builtin_offsetof(struct C, i) == 1, \"C.i\");\n",
1436            "struct D { char c; int i; } __attribute__((packed, aligned(4)));\n",
1437            "_Static_assert(sizeof(struct D) == 8 && _Alignof(struct D) == 4, \"D\");\n",
1438            "_Static_assert(__builtin_offsetof(struct D, i) == 1, \"D.i\");\n",
1439            "struct E { char c; _Alignas(8) int i; };\n",
1440            "_Static_assert(sizeof(struct E) == 16 && _Alignof(struct E) == 8, \"E\");\n",
1441            "_Static_assert(__builtin_offsetof(struct E, i) == 8, \"E.i\");\n",
1442            "struct F { char c; int i __attribute__((aligned(8))); };\n",
1443            "_Static_assert(sizeof(struct F) == 16 && _Alignof(struct F) == 8, \"F\");\n",
1444            // Two the record already had, so the attribute asks for nothing new, and two
1445            // where four was already there, so the attribute is ignored rather than obeyed.
1446            "struct G { char c; short s; } __attribute__((aligned(2)));\n",
1447            "_Static_assert(sizeof(struct G) == 4 && _Alignof(struct G) == 2, \"G\");\n",
1448            "struct H { char c; int i; } __attribute__((aligned(2)));\n",
1449            "_Static_assert(sizeof(struct H) == 8 && _Alignof(struct H) == 4, \"H\");\n",
1450            // `packed` on a member takes the padding out in front of that member alone, so on
1451            // the first one it does nothing and on the second one it does all of it.
1452            "struct I { [[gnu::packed]] char c; int i; };\n",
1453            "_Static_assert(sizeof(struct I) == 8 && _Alignof(struct I) == 4, \"I\");\n",
1454            "struct J { char c; [[gnu::packed]] int i; };\n",
1455            "_Static_assert(sizeof(struct J) == 5 && _Alignof(struct J) == 1, \"J\");\n",
1456            "struct M { char c; int i : 5; int j : 20; } __attribute__((packed));\n",
1457            "_Static_assert(sizeof(struct M) == 5 && _Alignof(struct M) == 1, \"M\");\n",
1458            "struct N { char c; long long l; } __attribute__((aligned(32)));\n",
1459            "_Static_assert(sizeof(struct N) == 32 && _Alignof(struct N) == 32, \"N\");\n",
1460            "union L { char c; int i; } __attribute__((packed));\n",
1461            "_Static_assert(sizeof(union L) == 4 && _Alignof(union L) == 1, \"L\");\n",
1462            // The armoured spellings, which are the ones a system header writes, since a
1463            // program is entitled to a macro called `packed` and is not entitled to one called
1464            // `__packed__`. The two names are one attribute and the layout is the same one.
1465            "struct O { char c; int i; } __attribute__((__packed__));\n",
1466            "_Static_assert(sizeof(struct O) == 5 && _Alignof(struct O) == 1, \"O\");\n",
1467            "struct P { char c; int i; } __attribute__((__aligned__(8)));\n",
1468            "_Static_assert(sizeof(struct P) == 8 && _Alignof(struct P) == 8, \"P\");\n",
1469        ));
1470    }
1471
1472    /// The attribute that changes what a call means rather than what a record lays out.
1473    ///
1474    /// Both halves are here. A call hands a value to a parameter of the union type and the value
1475    /// goes into the member that takes it, which is a compound literal of the union and is the
1476    /// same object the GNU cast to a union builds. And a declaration written with a member's type
1477    /// declares the same function as one written with the union, which is what lets a pointer to
1478    /// either be assigned from the other, and is what gnulib's signature checks do.
1479    ///
1480    /// The `void *` member is last on purpose: the search takes a member whose type the value
1481    /// already has wherever it sits, and falls back to a pointer member that would take the value
1482    /// silently only when there is no such member, so `char *` reaches the catch-all past two
1483    /// members that are not it.
1484    #[test]
1485    fn a_transparent_union_takes_the_member_a_value_fits_and_is_declared_either_way() {
1486        let text = tast(concat!(
1487            "struct one { int x; };\n",
1488            "struct two { long y; };\n",
1489            "typedef union { struct one *a; struct two *b; void *any; }\n",
1490            "  __attribute__((__transparent_union__)) arg;\n",
1491            "int takes(arg v);\n",
1492            "int f(struct one *p, struct two *q, char *c) {\n",
1493            "  return takes(p) + takes(q) + takes(c) + takes(0);\n",
1494            "}\n",
1495            // The other half, which is about declarations and not about values.
1496            "int takes(struct one *p);\n",
1497            "int (*as_a_member)(struct one *) = takes;\n",
1498            "int (*as_the_union)(arg) = takes;\n",
1499        ));
1500        assert!(text.contains("compound-literal"), "{text}");
1501    }
1502
1503    /// The other place glibc writes it, which is the one that matters.
1504    ///
1505    /// `sys/socket.h` puts the attribute on the declarator of the typedef rather than after the
1506    /// closing brace, so a compiler that reads only the second position reads nothing at all of
1507    /// the eleven pointer union that `bind` and `connect` and five others take.
1508    #[test]
1509    fn the_attribute_on_the_declarator_of_a_typedef_is_the_one_glibc_writes() {
1510        let text = tast(concat!(
1511            "struct sockaddr { int family; };\n",
1512            "struct sockaddr_in { int family; int addr; };\n",
1513            "typedef union { struct sockaddr *plain; struct sockaddr_in *inet; }\n",
1514            "  addr_arg __attribute__((__transparent_union__));\n",
1515            "int bind_to(int fd, addr_arg where);\n",
1516            "int f(struct sockaddr_in *where) { return bind_to(0, where); }\n",
1517        ));
1518        assert!(text.contains("compound-literal"), "{text}");
1519    }
1520
1521    /// What the attribute promises has to be a promise this can keep, and is checked rather than
1522    /// believed.
1523    ///
1524    /// A union wider than its first member is not passed the way that member is, and a structure
1525    /// has no members that are alternatives to each other at all. gcc drops the attribute in both
1526    /// cases with a warning and compiles the program, because the type is still a perfectly good
1527    /// type and only the extra rule is gone.
1528    #[test]
1529    fn a_transparent_union_that_cannot_keep_the_promise_is_dropped_with_a_word_about_it() {
1530        let result = run(
1531            &options(),
1532            concat!(
1533                "union wider { int small; double large; } __attribute__((transparent_union));\n",
1534                "struct plain { int x; } __attribute__((transparent_union));\n",
1535            ),
1536        );
1537        assert_eq!(result.messages.len(), 2, "{:?}", result.messages);
1538        assert!(!result.failed(), "{:?}", result.messages);
1539        for message in &result.messages {
1540            assert!(message.contains("'transparent_union' attribute ignored"), "{message}");
1541        }
1542        assert!(result.messages[0].contains("first member"), "{:?}", result.messages);
1543        assert!(result.messages[1].contains("only a union"), "{:?}", result.messages);
1544    }
1545
1546    /// What an access to a packed member is allowed to assume about where it starts.
1547    ///
1548    /// C 6.2.8 gives an object of type `int` four byte alignment and `packed` takes it away: the
1549    /// member goes wherever the members in front of it ended, and an `int` one byte into a record
1550    /// is aligned to one. The number on the access has to say so, because it is what the back end
1551    /// picks instructions from and what judgement J1 of `spec/safe-memory/04-safety-model.md`
1552    /// tests at run time. Four on an address that is a multiple of one is the compiler refusing a
1553    /// program that is doing nothing wrong.
1554    #[test]
1555    fn an_access_to_a_packed_member_says_the_alignment_the_layout_left_it() {
1556        let packed = body(concat!(
1557            "struct P { char c; int v; } __attribute__((packed));\n",
1558            "int f(struct P *p) { return p->v; }\n",
1559        ));
1560        assert!(packed.contains("load.i32 %2, align 1,"), "{packed}");
1561        // The same record without the attribute, which is where the type's own answer is right.
1562        let plain = body(concat!(
1563            "struct P { char c; int v; };\n",
1564            "int f(struct P *p) { return p->v; }\n",
1565        ));
1566        assert!(plain.contains("load.i32 %2, align 4,"), "{plain}");
1567    }
1568
1569    /// The same, for the two ways of being further in than the member itself.
1570    ///
1571    /// An array member is stepped through rather than offset to, and a record member is offset to
1572    /// twice, and both have to carry the outer record's alignment with them. A step of a whole
1573    /// number of elements leaves what the element width and the address had in common, which for
1574    /// a one byte aligned base is one byte however wide the elements are.
1575    #[test]
1576    fn what_is_inside_a_packed_member_is_no_more_aligned_than_the_member_is() {
1577        let stepped = body(concat!(
1578            "struct P { char c; int v[4]; } __attribute__((packed));\n",
1579            "int f(struct P *p, int i) { return p->v[i]; }\n",
1580        ));
1581        assert!(stepped.contains(", align 1,"), "{stepped}");
1582        assert!(!stepped.contains(", align 4,"), "{stepped}");
1583        let nested = body(concat!(
1584            "struct Inner { int v; };\n",
1585            "struct P { char c; struct Inner in; } __attribute__((packed));\n",
1586            "int f(struct P *p) { return p->in.v; }\n",
1587        ));
1588        assert!(nested.contains(", align 1,"), "{nested}");
1589        assert!(!nested.contains(", align 4,"), "{nested}");
1590    }
1591
1592    /// The same attribute on a declaration rather than on a type, which asks that this object or
1593    /// this function be at a multiple of that, and which is where a program that has to hand a
1594    /// buffer to hardware or keep two counters off one cache line writes it.
1595    ///
1596    /// A raise and never a lower, which is the one place it does not agree with `_Alignas`: below
1597    /// what the type already has, `_Alignas` is a constraint violation and this is ignored without
1598    /// a word. `__alignof__` of the object answers what the object got and not what its type has,
1599    /// because that is the question a program asking it is asking.
1600    #[test]
1601    fn the_aligned_attribute_on_a_declaration_raises_what_that_one_object_is_aligned_to() {
1602        tast(concat!(
1603            "int v __attribute__((aligned(64)));\n",
1604            "_Static_assert(__alignof__(v) == 64, \"v\");\n",
1605            // Written on the specifiers rather than after the declarator, which asks the same
1606            // thing and is the spelling a header is more likely to use.
1607            "__attribute__((aligned(32))) int w;\n",
1608            "_Static_assert(__alignof__(w) == 32, \"w\");\n",
1609            "[[gnu::aligned(16)]] int x;\n",
1610            "_Static_assert(__alignof__(x) == 16, \"x\");\n",
1611            // Two below the four an `int` already has, so nothing is asked for and nothing is
1612            // said, and the type still answers for the object.
1613            "int y __attribute__((aligned(2)));\n",
1614            "_Static_assert(__alignof__(y) == 4, \"y\");\n",
1615            // A local, which is the same question one scope down.
1616            "void f(void) { int a __attribute__((aligned(128)));\n",
1617            "_Static_assert(__alignof__(a) == 128, \"a\"); (void)a; }\n",
1618            // The type is untouched by any of it: `aligned` on a declaration says where that
1619            // declaration goes and says nothing about every other `int` in the program.
1620            "_Static_assert(__alignof__(int) == 4, \"int\");\n",
1621            // A function, which has no alignment of its own for this to be measured against and
1622            // takes whatever was asked for.
1623            "void g(void) __attribute__((aligned(256)));\n",
1624            "void g(void) {}\n",
1625            "_Static_assert(__alignof__(g) == 256, \"g\");\n",
1626        ));
1627    }
1628
1629    /// And what the object file says, which is the half that makes the answer above true. A
1630    /// function is at a fixed offset inside the text section, so it is at a multiple of two
1631    /// hundred and fifty six only if the section is at one too.
1632    #[test]
1633    fn what_a_declaration_asked_to_be_aligned_to_is_what_the_assembler_is_told() {
1634        let text = asm(concat!(
1635            "int v __attribute__((aligned(64)));\n",
1636            "void g(void) __attribute__((aligned(256)));\n",
1637            "void g(void) {}\n",
1638            "void plain(void) {}\n",
1639        ));
1640        assert!(text.contains("\t.p2align\t6\n\t.type\tv, @object\n"), "{text}");
1641        assert!(text.contains("\t.p2align\t8, 0x90\n\t.globl\tg\n"), "{text}");
1642        assert!(text.contains("\t.p2align\t4, 0x90\n\t.globl\tplain\n"), "{text}");
1643    }
1644
1645    /// And the one position where the attribute means something else. On a declaration it raises
1646    /// what that one object is aligned to, and on a typedef it says what the type is aligned to,
1647    /// which gcc lets it lower as well: `typedef int L __attribute__((aligned(2)))` really is an
1648    /// `int` at a multiple of two and a record with one in it really is smaller for it.
1649    ///
1650    /// The size is left alone, which is gcc's answer rather than an omission here. An aligned
1651    /// typedef whose alignment is larger than what it stands for keeps the size it stands for,
1652    /// and gcc refuses an array of one rather than padding the elements out to fit.
1653    #[test]
1654    fn an_aligned_typedef_says_what_an_object_of_it_is_aligned_to_and_may_lower_it() {
1655        tast(concat!(
1656            "typedef int L __attribute__((aligned(2)));\n",
1657            "_Static_assert(__alignof__(L) == 2, \"L\");\n",
1658            "_Static_assert(_Alignof(L) == 2, \"L alignof\");\n",
1659            // Below what an `int` has, which is the half a declaration cannot ask for.
1660            "_Static_assert(sizeof(L) == 4, \"L size\");\n",
1661            "struct T { char c; L x; };\n",
1662            "_Static_assert(sizeof(struct T) == 6, \"T\");\n",
1663            "_Static_assert(__builtin_offsetof(struct T, x) == 2, \"T.x\");\n",
1664            // And upwards, which is the ordinary direction and the one a header writes.
1665            "typedef int H __attribute__((aligned(16)));\n",
1666            "_Static_assert(__alignof__(H) == 16, \"H\");\n",
1667            "_Static_assert(sizeof(H) == 4, \"H size\");\n",
1668            "struct U { char c; H x; };\n",
1669            "_Static_assert(sizeof(struct U) == 32, \"U\");\n",
1670            "_Static_assert(__builtin_offsetof(struct U, x) == 16, \"U.x\");\n",
1671            // A typedef of a typedef, where the nearer one is the one the declaration was
1672            // written with and is the one that answers.
1673            "typedef L M __attribute__((aligned(8)));\n",
1674            "_Static_assert(__alignof__(M) == 8, \"M\");\n",
1675            // And one that asked for nothing, which still has whatever the one behind it asked
1676            // for because it is the same type spelled again.
1677            "typedef L N;\n",
1678            "_Static_assert(__alignof__(N) == 2, \"N\");\n",
1679            // The type it stands for is untouched by any of it.
1680            "_Static_assert(__alignof__(int) == 4, \"int\");\n",
1681        ));
1682        let text = asm(concat!(
1683            "typedef int L __attribute__((aligned(2)));\n",
1684            "typedef int H __attribute__((aligned(16)));\n",
1685            "L low;\n",
1686            "H high;\n",
1687        ));
1688        assert!(text.contains("\t.p2align\t1\n\t.type\tlow, @object\n"), "{text}");
1689        assert!(text.contains("\t.p2align\t4\n\t.type\thigh, @object\n"), "{text}");
1690    }
1691
1692    /// The attribute that builds a type rather than changing a layout. `vector_size(n)` says the
1693    /// declared type is `n` bytes of what was written, taken as lanes, and every operator over
1694    /// one is that operator over each lane.
1695    ///
1696    /// The size is in bytes and not in lanes, which is the part a reader gets backwards: sixteen
1697    /// of `int` is four lanes and sixteen of `char` is sixteen. A vector is aligned to its own
1698    /// size, which is what a machine that has the registers wants and what gcc gives one here.
1699    #[test]
1700    fn the_vector_size_attribute_builds_a_type_of_lanes_and_measures_it_in_bytes() {
1701        tast(concat!(
1702            "typedef int __attribute__((vector_size(16))) v4si;\n",
1703            "_Static_assert(sizeof(v4si) == 16 && _Alignof(v4si) == 16, \"v4si\");\n",
1704            "typedef char __attribute__((vector_size(16))) v16qi;\n",
1705            "_Static_assert(sizeof(v16qi) == 16, \"v16qi\");\n",
1706            // One lane, which is a power of two and is a vector rather than the type it was
1707            // written on: the operators it takes are the vector's and not the scalar's.
1708            "typedef int __attribute__((vector_size(4))) v1si;\n",
1709            "_Static_assert(sizeof(v1si) == 4, \"v1si\");\n",
1710            // The armoured spelling and the bracket one, which are the same attribute.
1711            "typedef float __attribute__((__vector_size__(8))) v2sf;\n",
1712            "_Static_assert(sizeof(v2sf) == 8, \"v2sf\");\n",
1713            "typedef short [[gnu::vector_size(8)]] v4hi;\n",
1714            "_Static_assert(sizeof(v4hi) == 8, \"v4hi\");\n",
1715            // A lane is what a subscript answers with, and a vector is not a pointer: there is
1716            // nothing to decay and the lane type is the one the arithmetic happens in.
1717            "v4si g;\n",
1718            "_Static_assert(sizeof(g[0]) == 4, \"lane\");\n",
1719            "_Static_assert(sizeof(g + g) == 16, \"whole\");\n",
1720            // A scalar beside a vector stands for itself in every lane, so the answer is still
1721            // the vector and not the wider of the two types.
1722            "_Static_assert(sizeof(g + 1) == 16, \"broadcast\");\n",
1723            // An array of them, which is the ordinary way a program holds several.
1724            "_Static_assert(sizeof(v4si[3]) == 48, \"array\");\n",
1725        ));
1726    }
1727
1728    /// A whole vector written into an array of them, and a vector named by a type name rather
1729    /// than by a typedef.
1730    ///
1731    /// Both are the same question asked twice. A vector is filled like an array of its lanes when
1732    /// a list is written into it, so a braced element that is itself a vector has to be taken
1733    /// whole rather than started as the first lane, and the type of what was written is the only
1734    /// thing that says which was meant. And a type name is where a compound literal and a cast
1735    /// spell the type out, which a macro taking a lane type and a lane count does, so the
1736    /// attribute has to be read there and not only on a declaration.
1737    #[test]
1738    fn a_vector_is_written_whole_into_an_array_of_them_and_named_by_a_type_name() {
1739        tast(concat!(
1740            "typedef int __attribute__((vector_size(8))) v2si;\n",
1741            "v2si table[] = { (v2si){ 1, 2 }, (v2si){ 3, 4 } };\n",
1742            "_Static_assert(sizeof(table) == 16, \"two of them and not eight lanes\");\n",
1743            // The size written out rather than named, which is the spelling a macro expands to.
1744            "v2si written = (int __attribute__((vector_size(8)))){ 5, 6 };\n",
1745            "_Static_assert(sizeof((int __attribute__((vector_size(16)))){ 0 }) == 16, \"named\");\n",
1746            // A lane is still a lane, so a list of them fills the vector the way it always did
1747            // and the rule above did not turn brace elision off.
1748            "v2si lanes[2] = { 1, 2, 3, 4 };\n",
1749            "_Static_assert(sizeof(lanes) == 16, \"still elided\");\n",
1750        ));
1751    }
1752
1753    /// A lane written rather than read, and a shift whose two vectors are not the same type.
1754    ///
1755    /// Both are places where a vector is not the aggregate it looks like. A subscript of one is
1756    /// an lvalue because the vector it came from is an object, so a lane can be assigned to and
1757    /// has an address, and a qualifier written on the vector reaches every lane the way it does
1758    /// on an array. And a shift is the one lanewise operator whose sides are not brought to a
1759    /// single type, since the right side counts rather than computes.
1760    #[test]
1761    fn a_lane_is_assignable_and_a_shift_takes_a_count_of_its_own_lane() {
1762        let result = run(
1763            &options(),
1764            concat!(
1765                "typedef int __attribute__((vector_size(16))) v4si;\n",
1766                "typedef unsigned __attribute__((vector_size(16))) v4ui;\n",
1767                "void write(v4si *out, v4ui a, v4si b, int n) {\n",
1768                "  v4si v = { 1, 2, 3, 4 };\n",
1769                "  v[0] = n;\n",
1770                "  v[1] += n;\n",
1771                "  v[2]++;\n",
1772                "  *&v[3] = n;\n",
1773                // The count is signed and the value is not, which no other operator allows.
1774                "  v4ui shifted = a >> b;\n",
1775                "  shifted <<= b;\n",
1776                // A scalar stands in every lane on either side of a shift, which is the half
1777                // that looks wrong: the shape of the answer comes off the count here.
1778                "  *out = v + (v4si)shifted + (1 << b);\n",
1779                "}\n",
1780                // A qualifier on the vector is a qualifier on the lane, so there is nothing here
1781                // to write to.
1782                "void refused(const v4si c) {\n",
1783                "  c[0] = 1;\n",
1784                "}\n",
1785            ),
1786        );
1787        assert_eq!(result.messages.len(), 1, "{:?}", result.messages);
1788        assert!(result.messages[0].contains("assignment of read-only"), "{:?}", result.messages);
1789    }
1790
1791    /// The third layout attribute, and the one that is refused rather than read. Reversing the
1792    /// byte order of every scalar in a record is not something a compiler can do half of, and a
1793    /// compilation that ignored it would lay the record out in the host's order and hand back
1794    /// every field with its bytes the wrong way round. Both spellings are here because a header
1795    /// writes the armoured one, and the member is here because the refusal has to arrive before
1796    /// the layout is used rather than after.
1797    #[test]
1798    fn a_record_that_asks_for_the_other_byte_order_is_refused_rather_than_laid_out_in_this_one() {
1799        let opts = options();
1800        let big = "struct s { int i; } __attribute__((scalar_storage_order(\"big-endian\")));\n";
1801        assert_eq!(
1802            run(&opts, big).messages,
1803            ["/main.c:1:36: error: 'scalar_storage_order' is not implemented yet [E0688]\n\
1804              /main.c:1:36: note: every scalar in this record would be read in the wrong byte \
1805              order"]
1806        );
1807
1808        let armoured =
1809            "struct s { int i; } __attribute__((__scalar_storage_order__(\"little-endian\")));\n";
1810        let messages = run(&opts, armoured).messages;
1811        assert!(messages[0].contains("[E0688]"), "{messages:?}");
1812
1813        // The attribute in front of the body reaches the same list as the one behind it, and
1814        // the C23 spelling in gcc's namespace is the same attribute written a third way.
1815        let front = "struct __attribute__((scalar_storage_order(\"big-endian\"))) s { int i; };\n";
1816        assert!(run(&opts, front).messages[0].contains("[E0688]"), "{front}");
1817        let standard = "struct s { int i; } [[gnu::scalar_storage_order(\"big-endian\")]];\n";
1818        assert!(run(&opts, standard).messages[0].contains("[E0688]"), "{standard}");
1819    }
1820
1821    /// Where a bit-field goes, which packing decides and which is the part of all this that
1822    /// is not what the names suggest. A bit-field goes at the next free bit unless that would
1823    /// make it span more storage than its own type occupies, and then it moves to the next
1824    /// boundary of its alignment. Any packing at all takes that rule out, and `#pragma pack`
1825    /// counts even where it lowers nothing, which is the fourth and seventh cases here.
1826    ///
1827    /// Nothing in the language can be asked where a bit-field is, since `offsetof` refuses one
1828    /// and every size below comes out the same either way, so what is asked is the byte a read
1829    /// of the field loads from.
1830    #[test]
1831    fn packing_is_what_decides_whether_a_bit_field_may_straddle_its_own_storage() {
1832        // A `char` field after twelve bits, which will not straddle unpacked and does packed.
1833        assert_eq!(bit_field_byte("struct s { int x : 12; char y : 6; };"), 2);
1834        assert_eq!(
1835            bit_field_byte("struct s { int x : 12; char y : 6; } __attribute__((packed));"),
1836            1
1837        );
1838        assert_eq!(
1839            bit_field_byte("struct s { int x : 12; __attribute__((packed)) char y : 6; };"),
1840            1
1841        );
1842        assert_eq!(bit_field_byte("#pragma pack(4)\nstruct s { int x : 12; char y : 6; };"), 1);
1843        // A thirty bit field after a byte, which is the case the rule was written for.
1844        assert_eq!(bit_field_byte("struct s { char x; int y : 30; };"), 4);
1845        assert_eq!(bit_field_byte("struct s { char x; int y : 30; } __attribute__((packed));"), 1);
1846        // Four is what an `int` asked for anyway, so this caps nothing and still counts.
1847        assert_eq!(bit_field_byte("#pragma pack(4)\nstruct s { char x; int y : 30; };"), 1);
1848        assert_eq!(bit_field_byte("#pragma pack(2)\nstruct s { char x; int y : 30; };"), 1);
1849    }
1850
1851    /// The byte a read of `s.y` loads from, which is where the bit-field was placed.
1852    fn bit_field_byte(record: &str) -> u64 {
1853        let source = format!("{record}\nint f(struct s *p) {{ return p->y; }}\n");
1854        let body = body(&source);
1855        let Some((before, _)) = body.split_once("ptr_add") else { return 0 };
1856        let (_, constant) = before.rsplit_once("iconst.i64 ").expect("an offset constant");
1857        constant.lines().next().expect("a line").trim().parse().expect("a byte offset")
1858    }
1859
1860    /// An attribute in the middle of a specifier list, which is where a member usually carries
1861    /// one and which was read and then thrown away. The `[[...]]` spelling and whatever was
1862    /// written in front of the declaration are collected as the list is walked and the
1863    /// `__attribute__` spelling is put straight on the specifiers, and the two were assigned
1864    /// over each other rather than joined.
1865    #[test]
1866    fn an_attribute_among_the_specifiers_is_kept_beside_the_ones_written_in_front() {
1867        tast(concat!(
1868            "struct a { char c; __attribute__((aligned(8))) int i; };\n",
1869            "_Static_assert(sizeof(struct a) == 16 && _Alignof(struct a) == 8, \"a\");\n",
1870            "_Static_assert(__builtin_offsetof(struct a, i) == 8, \"a.i\");\n",
1871            "struct b { char c; __attribute__((packed)) int i; };\n",
1872            "_Static_assert(sizeof(struct b) == 5 && _Alignof(struct b) == 1, \"b\");\n",
1873            "_Static_assert(__builtin_offsetof(struct b, i) == 1, \"b.i\");\n",
1874            "typedef struct { char c; int i; } __attribute__((packed)) c;\n",
1875            "_Static_assert(sizeof(c) == 5 && _Alignof(c) == 1, \"c\");\n",
1876        ));
1877    }
1878
1879    /// The other half, which is `#pragma pack`. It caps a member's alignment where `packed`
1880    /// drops it, so `pack(2)` leaves a `short` where it was and moves an `int`, and it caps a
1881    /// member the program asked to align as well, which is where the two differ. It is read
1882    /// at the closing brace of the body, so a line written in the middle of one settles the
1883    /// whole record rather than the members after it, and `push` and `pop` nest.
1884    #[test]
1885    fn pragma_pack_caps_every_member_and_is_read_where_the_body_closes() {
1886        tast(concat!(
1887            "#pragma pack(1)\n",
1888            "struct A { char c; int i; };\n",
1889            "_Static_assert(sizeof(struct A) == 5 && _Alignof(struct A) == 1, \"A\");\n",
1890            "_Static_assert(__builtin_offsetof(struct A, i) == 1, \"A.i\");\n",
1891            "#pragma pack()\n",
1892            "struct B { char c; int i; };\n",
1893            "_Static_assert(sizeof(struct B) == 8 && _Alignof(struct B) == 4, \"B\");\n",
1894            "#pragma pack(2)\n",
1895            "struct C { char c; int i; double d; };\n",
1896            "_Static_assert(sizeof(struct C) == 14 && _Alignof(struct C) == 2, \"C\");\n",
1897            "_Static_assert(__builtin_offsetof(struct C, d) == 6, \"C.d\");\n",
1898            // A member the program aligned, which `pack` caps and `packed` would not.
1899            "struct K { char c; int i __attribute__((aligned(8))); };\n",
1900            "_Static_assert(sizeof(struct K) == 6 && _Alignof(struct K) == 2, \"K\");\n",
1901            "_Static_assert(__builtin_offsetof(struct K, i) == 2, \"K.i\");\n",
1902            // The record's own `aligned` is not a member's, so it is not capped.
1903            "struct J { char c; int i; } __attribute__((aligned(8)));\n",
1904            "_Static_assert(sizeof(struct J) == 8 && _Alignof(struct J) == 8, \"J\");\n",
1905            "#pragma pack()\n",
1906            "#pragma pack(push, 1)\n",
1907            "struct D { char c; short s; };\n",
1908            "_Static_assert(sizeof(struct D) == 3 && _Alignof(struct D) == 1, \"D\");\n",
1909            "#pragma pack(pop)\n",
1910            "struct E { char c; short s; };\n",
1911            "_Static_assert(sizeof(struct E) == 4 && _Alignof(struct E) == 2, \"E\");\n",
1912            // Written in the middle of a body, and it still settles the whole record.
1913            "struct H { char c;\n",
1914            "#pragma pack(1)\n",
1915            "  int i; };\n",
1916            "_Static_assert(sizeof(struct H) == 5 && _Alignof(struct H) == 1, \"H\");\n",
1917            "#pragma pack(1)\n",
1918            "struct I { char c;\n",
1919            "#pragma pack()\n",
1920            "  int i; };\n",
1921            "_Static_assert(sizeof(struct I) == 8 && _Alignof(struct I) == 4, \"I\");\n",
1922            "#pragma pack()\n",
1923            // Nested pushes, each one giving back what the one under it had.
1924            "#pragma pack(push, 8)\n",
1925            "#pragma pack(push, 1)\n",
1926            "struct P { char c; int i; };\n",
1927            "_Static_assert(sizeof(struct P) == 5 && _Alignof(struct P) == 1, \"P\");\n",
1928            "#pragma pack(pop)\n",
1929            "struct Q { char c; int i; };\n",
1930            "_Static_assert(sizeof(struct Q) == 8 && _Alignof(struct Q) == 4, \"Q\");\n",
1931            "#pragma pack(pop)\n",
1932            // A cap above what every member already asks for changes nothing at all.
1933            "#pragma pack(16)\n",
1934            "struct R { char c; int i; };\n",
1935            "_Static_assert(sizeof(struct R) == 8 && _Alignof(struct R) == 4, \"R\");\n",
1936            "#pragma pack()\n",
1937            "#pragma pack(1)\n",
1938            "struct S { char c; int i : 5; int j : 20; };\n",
1939            "_Static_assert(sizeof(struct S) == 5 && _Alignof(struct S) == 1, \"S\");\n",
1940            "union T { char c; int i; };\n",
1941            "_Static_assert(sizeof(union T) == 4 && _Alignof(union T) == 1, \"T\");\n",
1942            "#pragma pack()\n",
1943        ));
1944    }
1945
1946    /// A line the reader cannot make sense of is a warning and the line is dropped, which is
1947    /// what GCC does with one, and these are its words for each of them. The last line is the
1948    /// one nothing else would reach, since it stands after every record in the file.
1949    #[test]
1950    fn a_pack_line_that_is_not_one_is_reported_in_the_words_gcc_uses() {
1951        let result = run(
1952            &options(),
1953            concat!(
1954                "#pragma pack 4\n",
1955                "#pragma pack(pop)\n",
1956                "#pragma pack(3)\n",
1957                "#pragma pack(1) junk\n",
1958                "#pragma pack(push, 1\n",
1959                "#pragma pack(x)\n",
1960                // These two are well formed and say nothing. Zero is how a line asks for the
1961                // target's own alignments back without writing empty parentheses.
1962                "#pragma pack(0)\n",
1963                "#pragma pack(push)\n",
1964                "struct s { char c; int i; };\n",
1965                "#pragma pack(pop)\n",
1966                "#pragma pack(pop, foo)\n",
1967            ),
1968        );
1969        let expected = [
1970            "missing `(` after `#pragma pack` - ignored",
1971            "`#pragma pack (pop)` encountered without matching `#pragma pack (push)`",
1972            "alignment must be a small power of two, not 3",
1973            "junk at end of `#pragma pack`",
1974            "malformed `#pragma pack(push[, id][, <n>])` - ignored",
1975            "unknown action `x` for `#pragma pack` - ignored",
1976            "`#pragma pack(pop, foo)` encountered without matching `#pragma pack(push, foo)`",
1977        ];
1978        assert_eq!(result.messages.len(), expected.len(), "{:?}", result.messages);
1979        for (message, want) in result.messages.iter().zip(expected) {
1980            assert!(message.contains(want), "expected {want:?} in {message:?}");
1981        }
1982    }
1983
1984    /// The two typedef spellings of the 128 bit types. gcc offers them as keywords rather
1985    /// than as typedefs in a header, which is the only way a program that includes nothing at
1986    /// all can still use them, and Apple's `<mach/arm/_structs.h>` is one such program.
1987    #[test]
1988    fn the_wide_integer_answers_to_all_three_of_its_names() {
1989        let text = tast("__uint128_t a; __int128_t b; unsigned __int128 c;\n");
1990        assert!(text.contains("decl #0 a : unsigned __int128"), "{text}");
1991        assert!(text.contains("decl #1 b : __int128"), "{text}");
1992        assert!(text.contains("decl #2 c : unsigned __int128"), "{text}");
1993    }
1994
1995    #[test]
1996    fn every_conversion_the_language_performs_is_a_node_in_the_output() {
1997        // The point of a typed tree. The source has one operator and the output has the
1998        // widening that operator asked for, spelled out, so that nothing downstream has to
1999        // work out the conversion rules a second time.
2000        let text = tast("long f(int a, long b) { return a + b; }\n");
2001        assert!(text.contains("convert arithmetic"), "{text}");
2002    }
2003
2004    #[test]
2005    fn a_mistake_in_each_phase_reaches_the_caller_and_writes_no_tree() {
2006        for source in [
2007            "#error stop\n",
2008            "int f(void) { return 1 + ; }\n",
2009            "int f(void) { return undeclared; }\n",
2010        ] {
2011            let result = run(&options(), source);
2012            assert!(result.failed(), "expected this to fail:\n{source}");
2013            assert!(
2014                result.text().is_empty(),
2015                "a file that did not compile wrote a tree:\n{source}"
2016            );
2017        }
2018    }
2019
2020    #[test]
2021    fn one_undeclared_name_is_one_message_and_not_one_per_use() {
2022        // The poisoning rule from `spec/06-lexer-and-parser.md` section 6.8, seen from the
2023        // outside. Three uses of a name that was never declared, and the operators over them
2024        // say nothing at all.
2025        let result = run(&options(), "int f(void) { return nope + nope * nope; }\n");
2026        assert_eq!(result.errors, 1, "{:?}", result.messages);
2027    }
2028
2029    #[test]
2030    fn a_declaration_the_parser_skipped_does_not_become_an_undeclared_name_as_well() {
2031        // The reason the checking is skipped after a failed parse. The parser gave up on the
2032        // first line and there is no `x` in the tree, so a checker run over it would report
2033        // every use of `x` below as undeclared, which is a second message about one mistake.
2034        let result = run(&options(), "int x = ;\nint f(void) { return x; }\n");
2035        assert_eq!(result.errors, 1, "{:?}", result.messages);
2036    }
2037
2038    #[test]
2039    fn werror_turns_a_warning_into_an_error_in_the_count_and_in_the_word() {
2040        let source = "int f(void) { char c = 300; return c; }\n";
2041        let plain = run(&options(), source);
2042        assert_eq!(plain.errors, 0, "{:?}", plain.messages);
2043        assert_eq!(plain.messages.len(), 1, "expected a warning about the narrowed constant");
2044        assert!(!plain.text().is_empty(), "a warning is not a reason to write nothing");
2045
2046        let mut opts = options();
2047        opts.warnings_are_errors = true;
2048        let strict = run(&opts, source);
2049        assert!(strict.failed());
2050        assert!(strict.text().is_empty(), "and under -Werror it is a reason to write nothing");
2051        for message in &strict.messages {
2052            assert!(!message.contains("warning:"), "{message}");
2053        }
2054    }
2055
2056    #[test]
2057    fn w_drops_the_warning_before_werror_can_promote_it() {
2058        let source = "int f(void) { char c = 300; return c; }\n";
2059        let mut opts = options();
2060        opts.warnings = false;
2061        let quiet = run(&opts, source);
2062        assert_eq!(quiet.messages, Vec::<String>::new());
2063        assert_eq!(quiet.errors, 0);
2064        assert!(!quiet.text().is_empty(), "and the file still compiles");
2065
2066        // A build that passes both means it wants neither, and the order it wrote them in is not
2067        // something to make it think about.
2068        opts.warnings_are_errors = true;
2069        let both = run(&opts, source);
2070        assert_eq!(both.messages, Vec::<String>::new());
2071        assert!(!both.failed(), "-w -Werror is not an error about a warning nobody saw");
2072    }
2073
2074    #[test]
2075    fn the_dialect_reaches_the_keywords_and_the_checking() {
2076        // `typeof` is C23's and GNU's, so the same source is a declaration under one dialect
2077        // and a mistake under the other, which is the keyword table being built per dialect.
2078        let source = "typeof(1) x;\n";
2079        let mut opts = options();
2080        opts.std = Std::C23;
2081        opts.gnu_extensions = false;
2082        assert!(!run(&opts, source).failed(), "{:?}", run(&opts, source).messages);
2083
2084        opts.std = Std::C17;
2085        assert!(run(&opts, source).failed());
2086    }
2087
2088    #[test]
2089    fn asking_for_a_kind_that_is_not_written_yet_runs_the_front_end_and_writes_nothing() {
2090        let mut opts = options();
2091        opts.emit = EmitKind::Object;
2092        let result = run(&opts, "int x = 1;\n");
2093        assert!(!result.failed(), "{:?}", result.messages);
2094        assert!(result.text().is_empty());
2095        // And it still finds what the checking finds, so a later kind on a broken file is not
2096        // a silent success.
2097        assert!(run(&opts, "int f(void) { return undeclared; }\n").failed());
2098    }
2099
2100    /// The machine code of `source`, insisting that it compiled cleanly.
2101    fn mir(source: &str) -> String {
2102        let mut opts = options();
2103        opts.emit = EmitKind::MirFinal;
2104        let result = run(&opts, source);
2105        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
2106        result.text().to_owned()
2107    }
2108
2109    /// The whole compiler in one assertion, which is what this emit kind is for.
2110    ///
2111    /// C in, machine instructions out, every register a real one and every frame offset a
2112    /// number. Everything between the two is checked somewhere else, one pass at a time. What is
2113    /// checked here is that the passes are joined up and that the driver runs them.
2114    #[test]
2115    fn a_function_goes_from_c_to_instructions_with_real_registers_in_them() {
2116        let text = mir("int add(int a, int b) { return a + b; }\n");
2117        assert!(text.starts_with("mfunc @add {"), "{text}");
2118        assert!(text.contains("x64.add_rr_32"), "{text}");
2119        assert!(text.contains("x64.ret"), "{text}");
2120        // A virtual register is what the allocator was there to remove, so one left in the
2121        // output is the difference between code and something that looks like code.
2122        assert!(!text.contains('%'), "{text}");
2123    }
2124
2125    /// A declaration has no body, so there is nothing to generate for one and nothing is.
2126    #[test]
2127    fn a_function_with_no_body_produces_no_machine_function() {
2128        let text = mir("int g(int);\nint f(int a) { return g(a); }\n");
2129        assert_eq!(text.matches("mfunc @").count(), 1, "{text}");
2130        assert!(text.contains("mfunc @f {"), "{text}");
2131        assert!(text.contains("x64.call"), "{text}");
2132    }
2133
2134    /// Two functions come out in the order the module holds them, which is source order.
2135    #[test]
2136    fn every_definition_in_the_file_is_generated_and_they_keep_their_order() {
2137        let text = mir("int a(int x) { return x; }\nint b(int x) { return x; }\n");
2138        let first = text.find("mfunc @a").expect("the first function");
2139        let second = text.find("mfunc @b").expect("the second function");
2140        assert!(first < second, "{text}");
2141    }
2142
2143    /// The target reaches the back end, so the same C is different instructions on Windows.
2144    #[test]
2145    fn the_target_decides_which_convention_the_generated_code_follows() {
2146        let mut opts = options();
2147        opts.emit = EmitKind::MirFinal;
2148        let linux = run(&opts, "int f(int a) { return a; }\n").text().to_owned();
2149        assert!(linux.contains("$rdi"), "{linux}");
2150
2151        opts.target = "x86_64-pc-windows-msvc".parse::<Triple>().unwrap();
2152        let windows = run(&opts, "int f(int a) { return a; }\n").text().to_owned();
2153        assert!(windows.contains("$rcx"), "{windows}");
2154        assert!(!windows.contains("$rdi"), "{windows}");
2155    }
2156
2157    /// A target with no back end says so rather than generating something for another machine.
2158    #[test]
2159    fn a_target_this_has_no_back_end_for_is_reported_rather_than_generated() {
2160        let mut opts = options();
2161        opts.emit = EmitKind::MirFinal;
2162        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
2163        let result = run(&opts, "int f(int a) { return a; }\n");
2164        assert!(result.failed());
2165        assert!(result.messages[0].contains("no back end for aarch64"), "{:?}", result.messages);
2166        assert!(result.text().is_empty());
2167    }
2168
2169    /// A construct the rule set does not reach yet is named, along with the function it is in.
2170    ///
2171    /// The message is about this compiler being unfinished rather than about the program, which
2172    /// is valid C either way, so it carries the note that says where the work is tracked. Both
2173    /// functions are attempted, so a file that is ahead of the back end in three places says so
2174    /// three times rather than one recompilation at a time.
2175    #[test]
2176    fn a_construct_the_back_end_cannot_reach_yet_is_reported_against_its_function() {
2177        let mut opts = options();
2178        opts.emit = EmitKind::MirFinal;
2179        let source = "void a(int n) { int v[n] __attribute__((aligned(32))); v[0] = 1; }\n\
2180                      void b(int n) { int v[n] __attribute__((aligned(32))); v[0] = 1; }\n";
2181        let result = run(&opts, source);
2182        assert!(result.failed());
2183        assert_eq!(result.messages.len(), 2, "{:?}", result.messages);
2184        assert!(result.messages[0].contains("cannot generate code for 'a'"), "{:?}", result);
2185        assert!(result.messages[0].contains("wants more alignment"), "{:?}", result);
2186        assert!(result.messages[1].contains("cannot generate code for 'b'"), "{:?}", result);
2187        assert!(result.text().is_empty());
2188    }
2189
2190    /// A variable length array walks its pages under the flag that says every page is touched.
2191    ///
2192    /// The pages the prologue takes are touched by the prologue. The pages the array takes are
2193    /// however many the size worked out to, so touching them is a loop written around the
2194    /// declaration rather than anything a prologue can do. What says the loop is there is the
2195    /// ordered comparison it ends each step with, which nothing else in a function writes, and the
2196    /// touch behind it. Without the flag the declaration is still the one subtraction it always was.
2197    #[test]
2198    fn a_variable_length_array_walks_its_pages_where_every_page_of_the_frame_is_to_be_touched() {
2199        let mut opts = options();
2200        opts.emit = EmitKind::MirFinal;
2201        let source = "void a(int n) { int v[n]; v[0] = 1; }\n";
2202        let plain = run(&opts, source);
2203        assert!(!plain.failed(), "{:?}", plain.messages);
2204        assert!(!plain.text().contains("cmp_set_a_64"), "{}", plain.text());
2205
2206        opts.stack_clash = true;
2207        let result = run(&opts, source);
2208        assert!(!result.failed(), "{:?}", result.messages);
2209        assert!(result.text().contains("cmp_set_a_64"), "{}", result.text());
2210        assert!(result.text().contains("or_mi_8"), "{}", result.text());
2211    }
2212
2213    /// An opcode the rule language has no word for is named anyway, and pointed at.
2214    ///
2215    /// The rule language's spelling is the better name when there is one, but an opcode it has
2216    /// no word for is exactly the opcode no rule lowers, so falling back to the opcode and the
2217    /// type is what makes the message say anything at all in the cases that happen. The span is
2218    /// the instruction's own, so the message lands on the line rather than on the file.
2219    ///
2220    /// The width of the float is what keeps the program refused. Everything else here is split into
2221    /// halves by `rucc_codegen::wide`, including the divisions and the conversions to a `float` and
2222    /// a `double`, which became calls into the compiler runtime. A `long double` is the eighty bit
2223    /// float on this target, the runtime has no conversion at that width because the back end has no
2224    /// register that holds one, which is tamnd/rucc#326, so a function converting to it is left with
2225    /// its wide values and reaches the selector the way every function of this width used to.
2226    #[test]
2227    fn an_opcode_with_no_name_in_the_rule_language_is_named_by_its_own_spelling() {
2228        let mut opts = options();
2229        opts.emit = EmitKind::MirFinal;
2230        let source =
2231            "long double f(int a) {\n  __int128 wide = a;\n  return (long double) wide;\n}\n";
2232        let result = run(&opts, source);
2233        assert!(result.failed());
2234        assert!(
2235            result.messages[0].contains("no rule lowers a `sext` producing a `i128`"),
2236            "{result:?}"
2237        );
2238        assert!(result.messages[0].contains(":2:"), "the line the widening is on: {result:?}");
2239        assert!(!result.messages[0].contains("this instruction"), "{result:?}");
2240    }
2241
2242    /// The note names the issue tracker, which is where a reader finds out whether it is known.
2243    #[test]
2244    fn the_note_on_unfinished_work_points_at_the_issues_rather_than_at_the_plan() {
2245        let mut opts = options();
2246        opts.emit = EmitKind::MirFinal;
2247        let source = "long double f(int a) { __int128 wide = a; return (long double) wide; }\n";
2248        let result = run(&opts, source);
2249        assert!(result.failed());
2250        let note = result.messages.iter().find(|line| line.contains("note:")).expect("a note");
2251        assert!(note.contains("https://github.com/tamnd/rucc/issues"), "{note}");
2252        assert!(!note.contains("spec/17-milestones.md"), "{note}");
2253    }
2254
2255    /// The two frame flags reach the frame, which is the only thing either of them does.
2256    #[test]
2257    fn the_frame_flags_on_the_command_line_reach_the_generated_frame() {
2258        let source = "int f(int a) { return a; }\n";
2259        assert!(!mir(source).contains("$rbp"), "a leaf needs no frame pointer by default");
2260
2261        let mut opts = options();
2262        opts.emit = EmitKind::MirFinal;
2263        opts.frame_pointer = true;
2264        let kept = run(&opts, source).text().to_owned();
2265        assert!(kept.contains("x64.push_64 $rbp"), "{kept}");
2266    }
2267
2268    /// The assembly of `source`, insisting that it compiled cleanly.
2269    fn asm(source: &str) -> String {
2270        let mut opts = options();
2271        opts.emit = EmitKind::Asm;
2272        let result = run(&opts, source);
2273        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
2274        result.text().to_owned()
2275    }
2276
2277    /// `-S`, which is the same compiler as the kind above it with a different last step.
2278    ///
2279    /// What the assembly says is checked in `rucc-asm`, one instruction at a time and against the
2280    /// target's own description of what an instruction is. What is checked here is that a C file
2281    /// goes all the way to a listing an assembler would take, which means the directives around
2282    /// the function as well as the instructions in it.
2283    #[test]
2284    fn a_function_goes_from_c_to_assembly_an_assembler_would_take() {
2285        let text = asm("int add(int a, int b) { return a + b; }\n");
2286        assert!(text.contains("\t.globl\tadd\n"), "{text}");
2287        assert!(text.contains("\t.type\tadd, @function\n"), "{text}");
2288        assert!(text.contains("\nadd:\n"), "{text}");
2289        assert!(text.contains("\taddl\t"), "{text}");
2290        assert!(text.contains("\tret\n"), "{text}");
2291        assert!(text.contains("\t.size\tadd, .-add\n"), "{text}");
2292        // Without this the stack the program runs on is executable, which is not a default
2293        // anybody chose and is not a thing a reader would notice missing.
2294        assert!(text.contains(".note.GNU-stack"), "{text}");
2295    }
2296
2297    /// A call through a function pointer, which is a different instruction from a call to a name.
2298    ///
2299    /// Both are in the one function on purpose. What is being read is that the two calls are told
2300    /// apart all the way down: one carries a name the linker resolves and one carries a register,
2301    /// and neither turns into the other on the way.
2302    #[test]
2303    fn a_call_through_a_function_pointer_goes_through_the_register_it_is_in() {
2304        let text = asm("int g(int);\nint f(int (*p)(int), int a) { return p(a) + g(a); }\n");
2305        assert!(text.contains("\tcall\t*%"), "{text}");
2306        assert!(text.contains("\tcall\tg\n"), "{text}");
2307        // The address arrived in the first argument register and the argument the call passes has
2308        // to end up there, so the two cannot be the same register and the compiler has to have
2309        // moved one of them.
2310        assert!(text.contains("%rdi"), "{text}");
2311    }
2312
2313    /// A name at file scope, which is the one address a function cannot compute for itself. The
2314    /// `lea` that computes it is folded into the load that reads through it, so what is left to
2315    /// read is the addressing mode, which is where the instruction pointer shows up.
2316    #[test]
2317    fn the_address_of_a_global_is_read_from_the_instruction_pointer() {
2318        let text = asm("extern int counter;\nint f(void) { return counter; }\n");
2319        assert!(text.contains("\tmovl\tcounter(%rip), %eax\n"), "{text}");
2320    }
2321
2322    /// Every comparison a branch can be on, which the machine jumps on without keeping a byte.
2323    ///
2324    /// Ten conditions, and each of them comes out as its opposite because the block falls into the
2325    /// arm the comparison is true for and jumps to the other one. That is the half of this most
2326    /// worth pinning: a jump on the condition rather than on its opposite compiles, encodes and
2327    /// runs, and gets every one of these ten functions backwards. The unsigned four and the signed
2328    /// four are separate for the same reason, since `jl` where `jb` was meant is a program that
2329    /// works until an address is above two gigabytes.
2330    #[test]
2331    fn a_branch_on_a_comparison_jumps_on_the_opposite_of_what_it_compared() {
2332        let arms = "return 1; return 2;";
2333        let signed = [("==", "jne"), ("!=", "je"), ("<", "jge"), ("<=", "jg"), (">", "jle")];
2334        for (operator, jump) in signed.into_iter().chain([(">=", "jl")]) {
2335            let text = asm(&format!("int f(int a, int b) {{ if (a {operator} b) {arms} }}\n"));
2336            assert!(
2337                text.contains(&format!("\tcmpl\t%esi, %edi\n\t{jump}\t")),
2338                "{operator}: {text}"
2339            );
2340            assert!(!text.contains("\tset"), "{operator}: {text}");
2341            assert!(!text.contains("\ttest"), "{operator}: {text}");
2342        }
2343        let unsigned = [("<", "jae"), ("<=", "ja"), (">", "jbe"), (">=", "jb")];
2344        for (operator, jump) in unsigned {
2345            let source =
2346                format!("int f(unsigned a, unsigned b) {{ if (a {operator} b) {arms} }}\n");
2347            let text = asm(&source);
2348            assert!(
2349                text.contains(&format!("\tcmpl\t%esi, %edi\n\t{jump}\t")),
2350                "{operator}: {text}"
2351            );
2352        }
2353
2354        // And against a constant, which is four comparisons in five and is where the saving
2355        // mostly is, since the byte that goes was the only reason the constant was in a register.
2356        let text = asm("int f(int a) { if (a < 7) return 1; return 2; }\n");
2357        assert!(text.contains("\tcmpl\t$7, %edi\n\tjge\t"), "{text}");
2358    }
2359
2360    /// The comparison whose answer is a value rather than a branch, which keeps its byte.
2361    ///
2362    /// The one that goes is the byte nothing but the branch reads. A comparison the program asked
2363    /// for the answer of is not that, and there is no branch behind it to fold into in any case,
2364    /// so this is here to say that what was taken out was taken out of one place and not two.
2365    #[test]
2366    fn a_comparison_whose_answer_the_program_wanted_still_writes_a_byte() {
2367        let text = asm("int f(int a, int b) { return a < b; }\n");
2368        assert!(text.contains("\tsetl\t"), "{text}");
2369    }
2370
2371    /// The same source at `-O2`, which is where the optimizer's passes are in the list.
2372    fn optimized(source: &str) -> String {
2373        let mut opts = options();
2374        opts.emit = EmitKind::Asm;
2375        opts.opt_level = rucc_session::OptLevel::O2;
2376        let result = run(&opts, source);
2377        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
2378        result.text().to_owned()
2379    }
2380
2381    /// A dense `switch` whose arms are a function of the label, which is arithmetic.
2382    ///
2383    /// Sixteen labels, and the arm for label `k` gives `k + 1`. What came out of this was a
2384    /// comparison and a jump for every one of them, which is tamnd/rucc#728. What comes out now is
2385    /// one comparison and one addition, and the count is the whole of the claim: it does not grow
2386    /// with the number of labels, so sixteen and a hundred and sixty compile to the same thing.
2387    ///
2388    /// The comparison is unsigned because the range check is the label minus the lowest one, which
2389    /// is a count and not a number the program wrote.
2390    #[test]
2391    fn a_switch_whose_arms_are_a_function_of_the_label_is_a_range_check_and_arithmetic() {
2392        let arms: String =
2393            (0..16).map(|k| format!("case {k}: return {};", k + 1)).collect::<Vec<_>>().join(" ");
2394        let text = optimized(&format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
2395        assert!(text.contains("\tcmpl\t$15, %edi\n\tja\t"), "{text}");
2396        assert!(text.contains("\taddl\t$1, %edi"), "{text}");
2397        assert_eq!(text.matches("\tcmp").count(), 1, "{text}");
2398    }
2399
2400    /// The same `switch` with one arm off the line, which keeps every comparison it had.
2401    ///
2402    /// The answers being a line is what licenses the range check, since a range check answers for
2403    /// every label in the range at once. One label whose arm disagrees is a label the check would
2404    /// answer wrongly, so this is here to say that the pass is reading the arms and not counting
2405    /// the labels.
2406    #[test]
2407    fn a_dense_switch_whose_arms_are_not_a_line_keeps_its_comparisons() {
2408        let arms: String = (0..16)
2409            .map(|k| format!("case {k}: return {};", if k == 9 { 100 } else { k + 1 }))
2410            .collect::<Vec<_>>()
2411            .join(" ");
2412        let text = optimized(&format!("int f(int x) {{ switch (x) {{ {arms} }} return 0; }}\n"));
2413        assert!(text.matches("\tcmp").count() > 1, "{text}");
2414    }
2415
2416    /// A cast between a pointer and an integer as wide as one, which is every one C writes here.
2417    #[test]
2418    fn a_cast_between_a_pointer_and_an_integer_leaves_the_value_where_it_is() {
2419        let text = asm("long f(void *p) { return (long)p; }\n");
2420        // Every instruction in the body is a full width move or the return. The copies are the
2421        // allocator taking no hints, and what matters here is what is not among them: nothing
2422        // narrows the value and nothing widens it again, which is what a cast that did something
2423        // would look like.
2424        for line in text.lines().filter(|line| line.starts_with('\t') && !line.contains('.')) {
2425            let mnemonic = line.split_whitespace().next().unwrap_or("");
2426            assert!(matches!(mnemonic, "movq" | "ret"), "{line} in\n{text}");
2427        }
2428    }
2429
2430    /// The arguments past the sixth arrive in the caller's memory rather than in a register, and
2431    /// where that memory is depends on what the prologue did, so this is checked at the end of the
2432    /// pipeline rather than in the middle of it.
2433    #[test]
2434    fn an_argument_past_the_last_register_is_read_out_of_the_caller_s_stack() {
2435        let six = "long a, long b, long c, long d, long e, long f";
2436        let text = asm(&format!("long f({six}, long g, long h) {{ return g + h; }}\n"));
2437
2438        // Nothing is pushed and no frame is taken, so the only thing between the stack pointer and
2439        // the caller's arguments is the return address the call pushed. Which is where gcc 16.2.0
2440        // reads them from too, at `-O0`, though it reads them in three instructions where this
2441        // reads them in two: the second read is the addition's own memory operand, which is
2442        // `rucc_codegen::combine`, and the offset in it is the one the frame layout wrote into the
2443        // load before the two were put together.
2444        assert!(text.contains("\tmovq\t8(%rsp), "), "{text}");
2445        assert!(text.contains("\taddq\t16(%rsp), "), "{text}");
2446
2447        // A narrower one is read at its own width, because the bits above it are bits the
2448        // convention says nothing about, and one in the other register file with the other file's
2449        // instruction.
2450        let narrow = asm(&format!("int f({six}, int g) {{ return g; }}\n"));
2451        assert!(narrow.contains("\tmovl\t8(%rsp), "), "{narrow}");
2452        let eight =
2453            "double a, double b, double c, double d, double e, double f, double g, double h";
2454        let float = asm(&format!("double f({eight}, double i) {{ return i; }}\n"));
2455        assert!(float.contains("\tmovsd\t8(%rsp), "), "{float}");
2456    }
2457
2458    /// The other end of the same thing. What the caller writes is at the stack pointer, because
2459    /// that is the bottom of its frame and the bottom of its frame is where the callee looks.
2460    #[test]
2461    fn a_call_writes_the_arguments_with_no_register_left_at_the_stack_pointer() {
2462        let six = "1, 2, 3, 4, 5, 6";
2463        let decl = "long g(long, long, long, long, long, long, long, long);\n";
2464        let text = asm(&format!("{decl}long f(void) {{ return g({six}, 7, 8); }}\n"));
2465
2466        assert!(text.contains("\tmovq\t%"), "{text}");
2467        assert!(text.contains(", (%rsp)\n"), "{text}");
2468        assert!(text.contains(", 8(%rsp)\n"), "{text}");
2469        // And it reserved the bytes it wrote into, so nothing else in the frame is on top of them.
2470        assert!(text.contains("\tsubq\t$"), "{text}");
2471
2472        // A narrower one is written at its own width, matching what the callee reads it back with.
2473        let narrow = "int g(int, int, int, int, int, int, int);\n";
2474        let text = asm(&format!("{narrow}int f(void) {{ return g({six}, 7); }}\n"));
2475        assert!(text.contains("\tmovl\t%"), "{text}");
2476        assert!(text.contains(", (%rsp)\n"), "{text}");
2477    }
2478
2479    /// The count a variadic callee on this convention reads is a count of vector registers, so a
2480    /// float that ran out of them and went to memory is not in it.
2481    #[test]
2482    fn a_variadic_call_counts_registers_and_not_arguments() {
2483        let nine = "1., 2., 3., 4., 5., 6., 7., 8., 9.";
2484        let decl = "int g(int, ...);\n";
2485        let text = asm(&format!("{decl}int f(void) {{ return g(0, {nine}); }}\n"));
2486
2487        assert!(text.contains("\tmovl\t$8, "), "eight registers, not nine: {text}");
2488        assert!(text.contains("\tmovsd\t%"), "{text}");
2489        assert!(text.contains(", (%rsp)\n"), "{text}");
2490    }
2491
2492    /// The callee's half of the same convention. Every argument register it was handed is written
2493    /// into its frame on the way in, because which of them hold anything is a thing only the caller
2494    /// knew, and the ones the signature does name are left out because `va_start` sets the offsets
2495    /// past them and nothing ever reads their slots.
2496    #[test]
2497    fn a_variadic_function_writes_the_argument_registers_it_was_handed_into_its_frame() {
2498        let body =
2499            "__builtin_va_list ap; __builtin_va_start(ap, n); __builtin_va_end(ap); return n;";
2500        let text = asm(&format!("int f(int n, ...) {{ {body} }}\n"));
2501
2502        // Five general purpose registers and eight vector ones, since the one parameter the
2503        // signature names took the first of the six.
2504        let stores = |mnemonic: &str| text.matches(&format!("\t{mnemonic}\t%")).count();
2505        assert!(text.contains(", 8(%r"), "the second slot, not the first: {text}");
2506        assert!(!text.contains(", 0(%r"), "{text}");
2507        // All sixteen bytes of each vector register, which is what gcc writes and what a `va_arg`
2508        // of a `_Float128` reads back, so the mnemonic is the one that moves a whole register.
2509        assert_eq!(stores("movaps"), 8, "every vector register: {text}");
2510        assert_eq!(stores("movsd"), 0, "and the whole of each one: {text}");
2511
2512        // And the area is one of the function's own stack objects, so the frame holds it.
2513        assert!(text.contains("\tsubq\t$"), "{text}");
2514    }
2515
2516    /// What `va_start` writes is the four fields of the list, and the two numbers among them are
2517    /// where the arguments the signature names left the walk over each file's registers.
2518    #[test]
2519    fn va_start_writes_the_four_fields_the_psabi_describes() {
2520        let start = "__builtin_va_list ap; __builtin_va_start(ap, d);";
2521        let params = "int a, int b, int c, double d";
2522        let text = asm(&format!("int f({params}, ...) {{ {start} return a; }}\n"));
2523
2524        // Three integers took three of the six general purpose registers, and one double took one
2525        // of the eight vector ones, so the walk starts at twenty four bytes into the first half and
2526        // sixteen bytes into the second, which begins at forty eight.
2527        assert!(text.contains("	movl	$24, "), "{text}");
2528        assert!(text.contains("	movl	$64, "), "{text}");
2529        // The other two fields are addresses rather than numbers, so each is stored as a word and
2530        // each is a `lea` away. One of them reaches above the frame, which is where the caller's
2531        // arguments are and is the only thing in this function that is not below the stack pointer.
2532        assert!(text.contains(", 8(%r"), "{text}");
2533        assert!(text.contains(", 16(%r"), "{text}");
2534        let frame: u32 = text
2535            .lines()
2536            .find_map(|line| line.trim().strip_prefix("subq	$")?.split(',').next()?.parse().ok())
2537            .expect("a variadic function takes a frame for the save area");
2538        let above = |line: &str| {
2539            let at: u32 = line.trim().strip_prefix("leaq	")?.split('(').next()?.parse().ok()?;
2540            Some(at > frame)
2541        };
2542        assert!(text.lines().filter_map(above).any(|it| it), "{frame}: {text}");
2543    }
2544
2545    /// A `va_arg` is a branch on whether the argument it wants is still in the save area, and which
2546    /// of the two halves it walks is the type's answer.
2547    #[test]
2548    fn va_arg_branches_on_whether_the_argument_is_still_in_the_save_area() {
2549        let read = "__builtin_va_list ap; __builtin_va_start(ap, n);";
2550        let ints = format!("int f(int n, ...) {{ {read} return __builtin_va_arg(ap, int); }}\n");
2551        let text = asm(&ints);
2552
2553        // The last general purpose slot begins at forty, so an offset above it is an argument the
2554        // caller left in its own memory instead.
2555        assert!(text.contains("$40, "), "{text}");
2556        assert!(text.contains("	cmpl	"), "{text}");
2557        // The jump is the unsigned one, since an offset is a count of bytes. It is the opposite
2558        // of the comparison the front end wrote, because the block falls into the half taken when
2559        // the argument is still in the save area and jumps to the other one.
2560        assert!(text.contains("	ja	"), "{text}");
2561
2562        let arg = "__builtin_va_arg(ap, double)";
2563        let text = asm(&format!("double f(int n, ...) {{ {read} return {arg}; }}\n"));
2564        assert!(text.contains("$160, "), "the last vector slot: {text}");
2565    }
2566
2567    /// A structure assigned is a copy of a known size, and a copy of a known size is a run of
2568    /// moves rather than a call to a library this compiler has no way to reach yet.
2569    #[test]
2570    fn a_structure_assignment_is_a_move_for_each_word_of_it() {
2571        let decl = "struct pair { long a, b; };\n";
2572        let body = "struct pair p = *q; return p.a + p.b;";
2573        let text = asm(&format!("{decl}long f(struct pair *q) {{ {body} }}\n"));
2574
2575        assert!(!text.contains("memcpy"), "nothing calls the library: {text}");
2576        assert!(!text.contains("\tcall"), "{text}");
2577        // Sixteen bytes aligned to eight is two words, and each is a load and a store.
2578        assert!(text.matches("\tmovq\t").count() >= 4, "two words each way: {text}");
2579    }
2580
2581    /// A word is as wide as the object is aligned to and no wider, so a character array is copied
2582    /// a byte at a time and a structure of longs eight bytes at a time.
2583    #[test]
2584    fn how_wide_a_word_of_a_copy_is_follows_the_alignment() {
2585        let decl = "struct bytes { char a[8]; };\n";
2586        let body = "struct bytes p = *q; return p.a[0];";
2587        let text = asm(&format!("{decl}int f(struct bytes *q) {{ {body} }}\n"));
2588
2589        // Eight bytes aligned to one is eight words, and each is a load and a store.
2590        assert!(text.matches("\tmovb\t").count() >= 16, "a byte at a time: {text}");
2591    }
2592
2593    /// What an initialiser does not name is zero, which the front end writes as a fill and this
2594    /// writes as the byte spread across each word.
2595    #[test]
2596    fn the_part_of_an_initialiser_that_names_nothing_is_stored_as_zero() {
2597        let decl = "struct wide { long a, b, c; };\n";
2598        let text = asm(&format!("{decl}long f(void) {{ struct wide w = {{ 7 }}; return w.c; }}\n"));
2599
2600        assert!(!text.contains("memset"), "nothing calls the library: {text}");
2601        assert!(text.contains("\tmovq\t$0, ") || text.contains("$0, %"), "the zero: {text}");
2602    }
2603
2604    /// A copy too large to be worth unrolling is a call to the runtime, which is the C library on
2605    /// a hosted target and `rucc-builtins` on a freestanding one.
2606    #[test]
2607    fn a_copy_too_large_to_unroll_calls_the_runtime() {
2608        let decl = "struct huge { char a[4096]; };\n";
2609        let mut opts = options();
2610        opts.emit = EmitKind::Asm;
2611        let source = format!("{decl}void f(struct huge *p, struct huge *q) {{ *p = *q; }}\n");
2612        let result = run(&opts, &source);
2613        assert!(!result.failed(), "{:?}", result.messages);
2614        let text = result.text();
2615        assert!(text.contains("call") && text.contains("memcpy"), "{text}");
2616        // The size in the register the convention passes the third argument in, which is what
2617        // says the call was built from the convention and not from the shape of the IR.
2618        assert!(text.contains("4096"), "the size travels: {text}");
2619    }
2620
2621    /// A frame that had to force its own alignment cannot say how far away the caller's stack
2622    /// pointer was, so it reaches back through the frame pointer instead.
2623    #[test]
2624    fn a_realigned_frame_reads_them_through_the_frame_pointer() {
2625        let six = "long a, long b, long c, long d, long e, long f";
2626        let body = "_Alignas(32) long wide[4]; wide[0] = g; return wide[0];";
2627        let text = asm(&format!("long f({six}, long g) {{ {body} }}\n"));
2628
2629        // The frame pointer is saved and pointed at where it was saved before the alignment is
2630        // forced, so the caller's arguments stay a constant distance from it: one word for the
2631        // saved frame pointer and one for the return address.
2632        assert!(text.contains("\tandq\t$-32, %rsp"), "{text}");
2633        assert!(text.contains("\tmovq\t16(%rbp), "), "{text}");
2634        assert!(!text.contains("\tmovq\t16(%rsp), "), "{text}");
2635    }
2636
2637    /// The object format decides the directives, and the target decides the object format.
2638    #[test]
2639    fn the_target_decides_how_the_assembly_is_spelled() {
2640        let mut opts = options();
2641        opts.emit = EmitKind::Asm;
2642        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
2643        let text = run(&opts, "int f(void) { return 0; }\n").text().to_owned();
2644        assert!(text.contains("__TEXT,__text"), "{text}");
2645        assert!(text.contains("\n_f:\n"), "{text}");
2646        assert!(!text.contains(".note.GNU-stack"), "{text}");
2647    }
2648
2649    /// The object file of `source`, insisting that it compiled cleanly.
2650    fn obj(source: &str) -> Vec<u8> {
2651        let mut opts = options();
2652        opts.emit = EmitKind::Object;
2653        let result = run(&opts, source);
2654        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
2655        match result.artifact {
2656            Artifact::Object { bytes, .. } => bytes,
2657            other => panic!("expected an object, got {other:?}"),
2658        }
2659    }
2660
2661    /// `-c`, which is the last step of the three the back end can end with.
2662    ///
2663    /// What is in the file is checked in `rucc-object`, a field at a time. What is checked here is
2664    /// that a C file goes all the way to one, which is the whole compiler in one line and the
2665    /// thing that stops working when a layer between them changes its mind about something.
2666    #[test]
2667    fn a_function_goes_from_c_to_an_object_a_linker_would_take() {
2668        let bytes = obj("int add(int a, int b) { return a + b; }\n");
2669        assert_eq!(&bytes[..4], b"\x7fELF", "an object file starts by saying it is one");
2670        let text = asm("int add(int a, int b) { return a + b; }\n");
2671        assert!(
2672            text.contains("\taddl\t"),
2673            "and the listing of it is the same instructions:\n{text}"
2674        );
2675    }
2676
2677    /// A variable this file defines, which is what a reference to one has to resolve against.
2678    #[test]
2679    fn a_variable_goes_from_c_to_the_section_it_belongs_in() {
2680        let text = asm("int counter = 42;\nstatic int hidden;\nconst int fixed = 7;\n");
2681        assert!(text.contains("\t.data\n\t.globl\tcounter\n"), "{text}");
2682        assert!(text.contains("\ncounter:\n\t.long\t42\n"), "{text}");
2683        assert!(text.contains("\t.size\tcounter, .-counter\n"), "{text}");
2684        // A zeroed variable carries its size and none of its bytes, and a `static` one is not
2685        // announced to the linker at all, which is the whole of what `static` means here.
2686        assert!(text.contains("\t.bss\n\t.p2align\t2\n"), "{text}");
2687        assert!(text.contains("\nhidden:\n\t.space\t4\n"), "{text}");
2688        assert!(!text.contains(".globl\thidden"), "{text}");
2689        // Nothing writes through it, so it goes in a page the loader can map read only and every
2690        // process running the program can share.
2691        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
2692    }
2693
2694    /// A bit-field with a value in it, which is written as the bytes the value lands in.
2695    ///
2696    /// The interesting one is the field whose lowest byte is zero. The bytes a bit-field
2697    /// initializer makes are put together first and then taken back out as the run they make,
2698    /// and taking them out starts at the byte the field starts at, so a zero byte at the front
2699    /// used to end the object up in `.bss` with the rest of its value thrown away.
2700    #[test]
2701    fn a_bit_field_initializer_writes_every_byte_of_the_value_and_not_only_the_ones_that_are_set() {
2702        let text = asm("struct s { unsigned f : 20; } x = { 0x12300 };\n");
2703        assert!(text.contains("\t.data\n"), "there is something to write: {text}");
2704        assert!(text.contains("\nx:\n\t.ascii\t\"\\000#\\001\"\n"), "and it is the value: {text}");
2705
2706        // Two fields, the first of them zero, which is the same thing said with the zero byte
2707        // inside the run rather than at the front of it.
2708        let text = asm("struct s { unsigned a : 8; unsigned b : 8; } x = { 0, 3 };\n");
2709        assert!(text.contains("\nx:\n\t.ascii\t\"\\000\\003\"\n"), "{text}");
2710
2711        // Wider than an `int`, which is the same code and is worth saying because the value no
2712        // longer fits in the thirty two bits a bit-field used to be read at.
2713        let text = asm("struct s { unsigned long long f : 40; } x = { 0x100000 };\n");
2714        assert!(text.contains("\nx:\n\t.ascii\t\"\\000\\000\\020\"\n\t.space\t5\n"), "{text}");
2715
2716        // Nothing in it, which still costs no bytes in the file.
2717        let text = asm("struct s { unsigned f : 20; } x = { 0 };\n");
2718        assert!(text.contains("\t.bss\n"), "an object of zeroes is zeroes: {text}");
2719        assert!(text.contains("\nx:\n\t.space\t4\n"), "{text}");
2720    }
2721
2722    /// A string literal, which is a variable the program never named.
2723    #[test]
2724    fn a_string_literal_is_a_variable_with_a_name_no_program_could_write() {
2725        let text = asm("const char *f(void) { return \"hi\"; }\n");
2726        assert!(text.contains("\t.ascii\t\"hi\\000\"\n"), "{text}");
2727        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
2728        let label = text
2729            .lines()
2730            .find(|line| line.starts_with(".Lstr"))
2731            .unwrap_or_else(|| panic!("a label for the literal in\n{text}"));
2732        assert!(!text.contains(&format!(".globl\t{}", label.trim_end_matches(':'))), "{text}");
2733    }
2734
2735    /// A variable holding the address of another one, which is the only hole an image has in it.
2736    #[test]
2737    fn an_address_in_an_initializer_is_left_to_the_linker() {
2738        let source = "int counter;\nint *p = &counter;\n";
2739        let text = asm(source);
2740        assert!(text.contains("\np:\n\t.quad\tcounter\n"), "{text}");
2741        // And in the object it is eight zero bytes and a relocation, which is what the two paths
2742        // being one description is for.
2743        let bytes = obj(source);
2744        assert!(bytes.windows(8).any(|w| w == b"counter\0"), "the object has to name it");
2745    }
2746
2747    /// A const table of function pointers, which is the shape that made SQLite link with a warning.
2748    ///
2749    /// The table is const so nothing in the program writes it, but the addresses in it are not
2750    /// numbers a link knows, so the loader writes it once at startup. Putting it in `.rodata`
2751    /// leaves a relocation in a section that is never writable, and what the linker does about
2752    /// that is set `DT_TEXTREL` on the whole image and say so. `.data.rel.ro` is writable for
2753    /// exactly as long as the loader is writing it and read only afterwards, which is what the
2754    /// program asked for in the first place.
2755    #[test]
2756    fn a_constant_holding_an_address_goes_in_the_section_the_loader_may_write_once() {
2757        // Both names are `static` and both are defined here, so nothing else can be the one that
2758        // defines them and the linker may lay the table out in the first pages of the segment.
2759        let text = asm("static void a(void) {}\nstatic void b(void) {}\n\
2760             struct m { void (*x)(void); void (*y)(void); };\n\
2761             const struct m t = { a, b };\n");
2762        assert!(text.contains("\t.section\t.data.rel.ro.local,\"aw\",@progbits\n"), "{text}");
2763        assert!(text.contains("\nt:\n\t.quad\ta\n\t.quad\tb\n"), "{text}");
2764
2765        // One name this file only declares is enough to lose the `.local` half, because a name the
2766        // link resolves from somewhere else is one another object may turn out to define.
2767        let text =
2768            asm("void a(void);\nstruct m { void (*x)(void); };\nconst struct m t = { a };\n");
2769        assert!(text.contains("\t.section\t.data.rel.ro,\"aw\",@progbits\n"), "{text}");
2770
2771        // And a constant with no address in it stays exactly where it was.
2772        let text = asm("const int fixed = 7;\n");
2773        assert!(text.contains("\t.section\t.rodata\n"), "{text}");
2774    }
2775
2776    /// A thread-local variable, which is the whole of one: the storage and the way to reach it.
2777    ///
2778    /// The two halves are in one test on purpose. Either one alone is worse than neither: a
2779    /// definition with no way to reach it is a variable nothing can read, and a reference with no
2780    /// definition behind it is the bug this pair was written to prevent, where a thread-local is
2781    /// read as though it were an ordinary global and every thread quietly shares one copy.
2782    #[test]
2783    fn a_thread_local_variable_is_storage_a_thread_gets_a_copy_of_and_an_offset_into_it() {
2784        let text = asm("_Thread_local int x = 1;\nint read(void) { return x; }\n");
2785        // The storage: the section the loader makes a copy of for every thread, and the symbol
2786        // type that makes a linker refuse an ordinary relocation aimed at it.
2787        assert!(text.contains("\t.section\t.tdata,\"awT\",@progbits\n"), "{text}");
2788        assert!(text.contains("\t.type\tx, @tls_object\n"), "{text}");
2789        // The way to reach it: how far into a thread's block it sits, out of the table, plus where
2790        // this thread's block is, out of the segment register.
2791        assert!(text.contains("x@GOTTPOFF(%rip)"), "{text}");
2792        assert!(text.contains("%fs:0"), "{text}");
2793    }
2794
2795    /// The second half of that on its own, which is what a program asks for when the number it
2796    /// wants is the thread rather than anything in it.
2797    ///
2798    /// rpmalloc writes this to find its per thread cache, and it is the whole of what stood
2799    /// between that library and a build. gcc 16 writes the same one instruction.
2800    #[test]
2801    fn the_address_of_this_thread_s_own_storage_is_read_out_of_the_segment_register() {
2802        let text = asm("void *here(void) { return __builtin_thread_pointer(); }\n");
2803        assert!(text.contains("movq\t%fs:0, "), "{text}");
2804        // No table slot and no addition, because there is no variable to find inside the block.
2805        assert!(!text.contains("GOTTPOFF"), "{text}");
2806    }
2807
2808    /// The four hints and the one thing that decides between them, which is the locality.
2809    ///
2810    /// A prefetch promises nothing, so what is checked here is the instruction rather than any
2811    /// effect: the program runs the same whichever of the four it gets, and the whole point of
2812    /// writing one is which. The four spellings are what gcc 16.2.0 writes for the same four
2813    /// programs, measured on x86-64 rather than read off a manual.
2814    ///
2815    /// The write hint is not one of them. `prefetchw` is not in the base instruction set and gcc
2816    /// writes it only when the command line says the part has it, so a prefetch for a write is the
2817    /// same instruction as a prefetch for a read, which is the fourth line here.
2818    #[test]
2819    fn a_prefetch_is_one_of_four_instructions_and_the_locality_is_what_picks() {
2820        for (locality, wanted) in
2821            [(0, "prefetchnta"), (1, "prefetcht2"), (2, "prefetcht1"), (3, "prefetcht0")]
2822        {
2823            let source =
2824                format!("void warm(void *p) {{ __builtin_prefetch(p, 0, {locality}); }}\n");
2825            let text = asm(&source);
2826            assert!(text.contains(&format!("\t{wanted}\t")), "locality {locality}: {text}");
2827        }
2828        // The one argument form, which means a read that wants all of the data afterwards.
2829        let text = asm("void warm(void *p) { __builtin_prefetch(p); }\n");
2830        assert!(text.contains("\tprefetcht0\t"), "{text}");
2831        // A prefetch for a write, which on a part nobody said has `prefetchw` is the same
2832        // instruction as the read above.
2833        let text = asm("void warm(void *p) { __builtin_prefetch(p, 1); }\n");
2834        assert!(text.contains("\tprefetcht0\t"), "{text}");
2835        assert!(!text.contains("prefetchw"), "{text}");
2836    }
2837
2838    /// The stop, which is the one instruction the machine is promised never to have a meaning for.
2839    ///
2840    /// What is checked is the instruction and not any effect, because the effect is a fault and a
2841    /// unit test has nowhere to take one. gcc 16.2.0 writes the same instruction for the same
2842    /// program, and it is not a call, which is the half that matters in a kernel and in a
2843    /// freestanding program: neither has an `abort` for a call to reach.
2844    ///
2845    /// The second half is the block going on after it. A statement written under a stop is
2846    /// compiled the way it would have been without one, so the addition is still there, and that
2847    /// is the front end declining to treat a stop as the end of a path.
2848    #[test]
2849    fn a_trap_is_the_instruction_the_machine_has_no_meaning_for() {
2850        let text = asm("void stop(void) { __builtin_trap(); }\n");
2851        assert!(text.contains("\tud2\n"), "{text}");
2852        assert!(!text.contains("\tcall"), "a stop is not a call to anything: {text}");
2853
2854        let text = asm("int stop(int a) { __builtin_trap(); return a + 1; }\n");
2855        assert!(text.contains("\tud2\n"), "{text}");
2856        assert!(text.contains("\taddl\t"), "the block goes on after a stop: {text}");
2857    }
2858
2859    /// The promise about the low bits of an address, whose value is the address.
2860    ///
2861    /// Nothing here reads an alignment fact about a value yet, so what the call leaves behind is
2862    /// its first argument and no instruction at all. The claim worth checking end to end is that
2863    /// the name is gone: a builtin nothing lowers reaches the assembler as a call to a name no
2864    /// object file defines, which is how this one used to fail to link out of glibc's string
2865    /// headers.
2866    ///
2867    /// The arguments behind the address are still evaluated, because gcc 16.2.0 evaluates them at
2868    /// every optimization level even though it has folded the call away. A constant has nothing to
2869    /// run and is dropped, and a call does, so the second half asks for the callee by name.
2870    #[test]
2871    fn assume_aligned_is_its_first_argument_and_keeps_the_rest() {
2872        let text = asm("void *aligned(char *p) { return __builtin_assume_aligned(p, 16); }\n");
2873        assert!(!text.contains("assume_aligned"), "{text}");
2874        assert!(!text.contains("\tcall"), "nothing is called for an alignment fact: {text}");
2875
2876        let source = "unsigned long width(void);\n\
2877                      void *aligned(char *p) { return __builtin_assume_aligned(p, width()); }\n";
2878        let text = asm(source);
2879        assert!(!text.contains("assume_aligned"), "{text}");
2880        assert!(text.contains("width"), "the argument that is not the answer still runs: {text}");
2881    }
2882
2883    /// Where a frame is, which on this machine is what the frame pointer holds.
2884    ///
2885    /// The first half is a function that would have kept no frame pointer at all, since it is a
2886    /// leaf with no locals, and keeps one because it asked where its frame is. The answer being
2887    /// `%rbp` rather than an offset off `%rsp` is the whole of the builtin at a depth of zero.
2888    ///
2889    /// The second half is the walk. Each link above zero is one load through the register the last
2890    /// one wrote, so a depth of two is two loads and a depth of three is three, which is what gcc
2891    /// 16.2.0 writes for the same programs at `-O2`.
2892    #[test]
2893    fn the_frame_address_is_the_frame_pointer_after_walking_that_many_links() {
2894        let text = asm("void *here(void) { return __builtin_frame_address(0); }\n");
2895        assert!(text.contains("pushq\t%rbp"), "a function that asks keeps a frame pointer: {text}");
2896        assert!(text.contains("movq\t%rbp, %rax"), "{text}");
2897        assert!(!text.contains("\tcall"), "a frame address is not a call to anything: {text}");
2898
2899        let walk = |depth: u32| {
2900            let source = format!("void *up(void) {{ return __builtin_frame_address({depth}); }}\n");
2901            asm(&source).matches("movq\t(%r").count()
2902        };
2903        assert_eq!(walk(1), 1, "one link is one load");
2904        assert_eq!(walk(3), 3, "three links are three loads");
2905    }
2906
2907    /// The address a frame returns to, which is one word above the frame the walk ended at.
2908    ///
2909    /// A word is eight bytes here and the `8(...)` is the whole claim: the call instruction pushed
2910    /// the return address and the prologue pushed the caller's frame pointer under it, so what the
2911    /// frame pointer points at is the link and what is above it is where control goes back to.
2912    /// gcc 16.2.0 writes `movq 8(%rbp), %rax` for the first of these, measured at `-O2`.
2913    ///
2914    /// The second half is the same walk the frame address does, with the load at the end of it
2915    /// reading one word further along rather than the register itself being the answer.
2916    #[test]
2917    fn the_return_address_is_one_word_above_the_frame_the_walk_ended_at() {
2918        let text = asm("void *back(void) { return __builtin_return_address(0); }\n");
2919        assert!(text.contains("pushq\t%rbp"), "a function that asks keeps a frame pointer: {text}");
2920        assert!(text.contains("movq\t8(%rbp), %rax"), "{text}");
2921        assert!(!text.contains("\tcall"), "a return address is not a call to anything: {text}");
2922
2923        let text = asm("void *back(void) { return __builtin_return_address(2); }\n");
2924        assert_eq!(text.matches("movq\t(%r").count(), 2, "two links are two loads: {text}");
2925        assert!(text.contains("movq\t8(%r"), "and the answer is above the last of them: {text}");
2926    }
2927
2928    /// A depth that is not a constant is refused, and so is one past the limit.
2929    ///
2930    /// The first is gcc's rule and not a convenience: what the call becomes is a walk that many
2931    /// links long, written out, so a number that is not known until the program runs has nothing
2932    /// to walk. gcc 16.2.0 says `invalid argument to '__builtin_return_address'` for the same
2933    /// program.
2934    ///
2935    /// The second is where this and gcc part company. gcc writes the walk however long it is, and
2936    /// this refuses a depth no program has a use for rather than filling an object file with loads
2937    /// that fault part way up.
2938    #[test]
2939    fn a_depth_that_is_not_a_small_constant_is_refused() {
2940        let mut opts = options();
2941        opts.emit = EmitKind::Ir;
2942        for source in [
2943            "void *up(int n) { return __builtin_return_address(n); }\n",
2944            "void *up(void) { return __builtin_frame_address(1000); }\n",
2945        ] {
2946            let messages = run(&opts, source).messages;
2947            let named = messages.iter().any(|m| m.contains("E0705"));
2948            assert!(named, "expected a refusal in {messages:?}");
2949        }
2950    }
2951
2952    /// Bytes off the frame, which is the stack pointer moving down and the answer being where it
2953    /// moved to.
2954    ///
2955    /// The rounding is the alignment: the size is taken up to the next sixteen before it is
2956    /// subtracted, so the pointer suits anything the program puts behind it. gcc 16.2.0 rounds the
2957    /// same way at `-O0` and spends a division doing it, which is the one place the two differ and
2958    /// is about how the rounding is written rather than about what it answers.
2959    ///
2960    /// There is no call anywhere in either program. An alloca that had reached the linker would
2961    /// have found the C library's, which is a real function with a real frame and is not what a
2962    /// program writing the builtin asked for.
2963    #[test]
2964    fn an_alloca_takes_the_bytes_off_the_stack_pointer_and_answers_where_they_are() {
2965        let text =
2966            asm("void use(void *p); void f(unsigned long n) { use(__builtin_alloca(n)); }\n");
2967        assert!(text.contains("andq\t$-16"), "the size is rounded up to sixteen: {text}");
2968        assert!(text.contains("subq\t%rdi, %rsp"), "and taken off the stack pointer: {text}");
2969        assert_eq!(text.matches("\tcall").count(), 1, "the only call is the one written: {text}");
2970
2971        // The plain name, which a program that declares it the way the C library does means the
2972        // same thing by. `gcc.c-torture/execute/20010122-1.c` is exactly this program.
2973        let plain = concat!(
2974            "extern void *alloca(__SIZE_TYPE__);\n",
2975            "void use(void *p);\n",
2976            "void f(unsigned long n) { use(alloca(n)); }\n",
2977        );
2978        let text = asm(plain);
2979        assert!(text.contains("subq\t%rdi, %rsp"), "the plain name is the same bytes: {text}");
2980        assert_eq!(text.matches("\tcall").count(), 1, "and is not a call either: {text}");
2981
2982        // And a program that means something of its own by the name keeps it, which is what the
2983        // declaration is looked at for.
2984        let own = concat!(
2985            "static void *alloca(unsigned long n) { return 0; }\n",
2986            "void *f(unsigned long n) { return alloca(n); }\n",
2987        );
2988        assert!(asm(own).contains("\tcall"), "a name the program took back is a call");
2989    }
2990
2991    /// The bytes an alloca took live until the function returns and not until the end of the block
2992    /// the call was written in.
2993    ///
2994    /// That is what makes it different from a variable length array, and the way it is kept is that
2995    /// every scope open where the call was written stops giving the stack back. The second program
2996    /// is the mixed case: an array in the outer block and an alloca in the inner one, where the
2997    /// inner block gives nothing back either even though an array is in scope that ordinarily
2998    /// would. gcc 16.2.0 at `-O0` writes no restore at the end of either block, measured rather
2999    /// than read off the manual.
3000    #[test]
3001    fn the_bytes_an_alloca_took_are_still_there_at_the_end_of_the_block_that_took_them() {
3002        let inner = "{ use(__builtin_alloca(n)); }";
3003        for body in [inner.to_owned(), format!("int a[n]; {inner} use(a);")] {
3004            let source = format!("void use(void *p);\nvoid f(unsigned long n) {{ {body} }}\n");
3005            let text = asm(&source);
3006            // Every instruction that writes the stack pointer, which in a function that gives
3007            // nothing back is the alloca taking bytes and the epilogue putting the frame pointer
3008            // there. A restore would be a third kind, a move out of a register the save wrote.
3009            for line in text.lines().filter(|line| line.trim_end().ends_with(", %rsp")) {
3010                let taking = line.contains("subq");
3011                let leaving = line.contains("%rbp");
3012                assert!(taking || leaving, "nothing puts the stack back: {line} in {text}");
3013            }
3014        }
3015    }
3016
3017    /// Not a rewording of the check above: what the two paths agree about is the point.
3018    #[test]
3019    fn the_object_and_the_listing_are_two_spellings_of_one_compilation() {
3020        // A call, because it is the one thing whose spelling in the two differs completely: the
3021        // listing writes a name and the object writes four zero bytes and a relocation asking the
3022        // linker for the same name. If either path had lost the callee, one of these would fail.
3023        let source = "int callee(void); int g(void) { return callee(); }\n";
3024        let bytes = obj(source);
3025        assert!(
3026            bytes.windows(7).any(|w| w == b"callee\0"),
3027            "the object has to name the callee for the linker to find it"
3028        );
3029        let text = asm(source);
3030        assert!(text.contains("\tcall\tcallee\n"), "{text}");
3031    }
3032
3033    /// What a file of a link contributes is an object, and the default emit is a link.
3034    ///
3035    /// This is here because getting it wrong is silent in the worst way: an empty file is a valid
3036    /// empty linker script, so a link fed one gets as far as reporting every symbol of the file as
3037    /// undefined and says nothing about the compilation that produced nothing.
3038    #[test]
3039    fn compiling_for_an_executable_produces_an_object_and_not_a_dump() {
3040        let mut opts = options();
3041        // What a command line with no `-c` and no `-S` on it asks for.
3042        opts.emit = EmitKind::Executable;
3043        let result = run(&opts, "int main(void) { return 0; }\n");
3044        assert_eq!(result.messages, Vec::<String>::new());
3045        match result.artifact {
3046            Artifact::Object { bytes, .. } => assert_eq!(&bytes[..4], b"\x7fELF"),
3047            other => panic!("expected an object, got {other:?}"),
3048        }
3049    }
3050
3051    /// A target with a back end but no object writer says so rather than writing the wrong file.
3052    #[test]
3053    fn a_platform_with_no_object_writer_is_said_so_rather_than_written_as_elf() {
3054        let mut opts = options();
3055        opts.emit = EmitKind::Object;
3056        opts.target = "x86_64-apple-darwin".parse::<Triple>().unwrap();
3057        let result = run(&opts, "int f(void) { return 0; }\n");
3058        assert!(result.failed(), "an object nobody can read is worse than a message");
3059        assert!(
3060            result.messages.iter().any(|m| m.contains("no object writer")),
3061            "{:?}",
3062            result.messages
3063        );
3064    }
3065
3066    /// The IR of `source`, insisting that it compiled cleanly.
3067    fn ir(source: &str) -> String {
3068        let mut opts = options();
3069        opts.emit = EmitKind::Ir;
3070        let result = run(&opts, source);
3071        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3072        result.text().to_owned()
3073    }
3074
3075    /// What was said about `source`, insisting that something was.
3076    fn errors(source: &str) -> Vec<String> {
3077        let mut opts = options();
3078        opts.emit = EmitKind::Ir;
3079        let result = run(&opts, source);
3080        assert!(result.failed(), "expected this to be refused:\n{source}");
3081        result.messages
3082    }
3083
3084    /// The body of the one function in `source`, which is what most of these are about.
3085    fn body(source: &str) -> String {
3086        let text = ir(source);
3087        let (_, rest) = text.split_once("{\n").expect("a function definition");
3088        let (body, _) = rest.rsplit_once("}\n").expect("a function definition");
3089        body.to_owned()
3090    }
3091
3092    /// What `-fgnu89-inline` is for, seen at the only place it shows: whether a body reached the
3093    /// module or only a declaration did.
3094    ///
3095    /// The C99 reading is the one an inline definition is written for and is not being changed
3096    /// here. What the flag is for is a program written before C99 swapped the two, which relies on
3097    /// `inline` alone leaving something behind for another unit to call, and there are twelve of
3098    /// those in the GCC torture suite alone.
3099    #[test]
3100    fn gnu89_inline_is_what_decides_whether_a_bare_inline_definition_reaches_the_module() {
3101        let source = "inline int f(int x) { return x + 1; }\n";
3102        let with = |flag: bool| {
3103            let mut opts = options();
3104            opts.emit = EmitKind::Ir;
3105            opts.gnu89_inline = flag;
3106            let result = run(&opts, source);
3107            assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
3108            result.text().to_owned()
3109        };
3110
3111        // Under C's reading the module holds the declaration and the calls in this unit go to
3112        // whatever definition another unit has, which is C 6.7.4p7 and is what gcc does too.
3113        assert!(!with(false).contains("block0"), "no body: {}", with(false));
3114
3115        // Under GNU's it is an ordinary external definition, so the body is there and the symbol
3116        // is one the linker can resolve against.
3117        assert!(with(true).contains("block0"), "a body: {}", with(true));
3118    }
3119
3120    /// Every shape that reads or writes through a C type names that type.
3121    ///
3122    /// The tree itself is `rucc_lower::aliasing`'s and is tested there. What this is about is that
3123    /// the walk reaches it from every shape a program actually writes, since a node on the scalar
3124    /// load and nothing on the member load would be a layer that answers for a third of the
3125    /// accesses in a program and is not worth having.
3126    #[test]
3127    fn an_access_through_a_type_names_the_type_it_went_through() {
3128        let source = "\
3129struct s { int a; float b; };\n\
3130union u { int i; float f; };\n\
3131int scalar(int *p) { return *p; }\n\
3132float member(struct s *p) { p->a = 1; return p->b; }\n\
3133int element(int *a, long i) { return a[i]; }\n\
3134float through_a_union(union u *p) { p->i = 1; return p->f; }\n";
3135        let text = ir(source);
3136        assert!(text.contains(r#"!0 = tbaa "char""#), "the root: {text}");
3137        assert!(text.contains(r#"tbaa "int", parent !0"#), "int under it: {text}");
3138        assert!(text.contains(r#"tbaa "float", parent !0"#), "float under it: {text}");
3139        // One per access, and a function whose accesses all go through one type says so once per
3140        // access rather than once per function.
3141        let named = text.lines().filter(|line| line.contains(", tbaa !")).count();
3142        assert_eq!(named, 6, "six accesses: {text}");
3143    }
3144
3145    /// `-fno-strict-aliasing` is the front end leaving the name off.
3146    ///
3147    /// Nothing asks the alias analysis anything yet, so no program compiles differently for having
3148    /// passed this today. What this test is for is the day one does: the flag has to be the
3149    /// absence of the names rather than a condition somewhere downstream, since that is the only
3150    /// version of it that a pass added later cannot forget about.
3151    #[test]
3152    fn turning_strict_aliasing_off_leaves_the_type_off_every_access() {
3153        let source = "int punned(float *f, int *i) { *i = 1; *f = 2.0f; return *i; }\n";
3154        let mut opts = options();
3155        opts.emit = EmitKind::Ir;
3156        opts.strict_aliasing = false;
3157        let result = run(&opts, source);
3158        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile");
3159        let text = result.text().to_owned();
3160        assert!(!text.contains("tbaa"), "not even the root: {text}");
3161    }
3162
3163    /// `return;` from a function that promised a value, which only C89 lets through and which
3164    /// therefore only reaches the IR builder under that dialect.
3165    ///
3166    /// Zero goes back. The alternatives are worse: an empty return list builds a `ret` the
3167    /// verifier refuses, which is what a torture case found, and `unreachable` would be a claim
3168    /// that the branch reaching this never runs, which is a claim about the program rather than
3169    /// about the value and lets the optimizer delete the path that led here.
3170    #[test]
3171    fn a_bare_return_from_a_function_that_promised_a_value_gives_back_a_zero() {
3172        let mut opts = options();
3173        opts.emit = EmitKind::Ir;
3174        opts.std = Std::C89;
3175        let compiled = |source: &str| {
3176            let result = run(&opts, source);
3177            assert_eq!(result.messages, Vec::<String>::new(), "C89 has nothing to say about this");
3178            result.text().to_owned()
3179        };
3180
3181        let text = compiled("int f(int x) { if (x) return; return 3; }\n");
3182        assert!(text.contains("iconst.i32 0\n    return"), "zero goes back: {text}");
3183        assert!(!text.contains("unreachable"), "the branch that reached it is kept: {text}");
3184
3185        // A floating point return needs the constant of its own kind rather than an integer one.
3186        let text = compiled("double f(int x) { if (x) return; return 1.0; }\n");
3187        assert!(text.contains("fconst.f64 0x0\n    return"), "a float zero goes back: {text}");
3188    }
3189
3190    /// What C89 6.3.2.2 declares for a call to a name nothing declared, seen in the IR rather than
3191    /// in what was said about it.
3192    ///
3193    /// `extern int f();`, so the call gives back an `int` and its arguments are promoted rather
3194    /// than converted to parameters there are none of. The declaration lasts for the file, which
3195    /// is what makes a second call to the same name ordinary and is why gcc says this once per
3196    /// file rather than once per call.
3197    #[test]
3198    fn a_call_to_a_name_nothing_declared_declares_it_as_c89_said_to() {
3199        let mut opts = options();
3200        opts.emit = EmitKind::Ir;
3201        opts.std = Std::C89;
3202        let compiled = |source: &str| {
3203            let result = run(&opts, source);
3204            assert_eq!(result.messages, Vec::<String>::new(), "C89 has nothing to say about this");
3205            result.text().to_owned()
3206        };
3207
3208        // An `int` back, which is the whole of what the implicit declaration says.
3209        let text = compiled("int f(void) { return g(); }\n");
3210        assert!(text.contains("call @g"), "the call is to the name that was written: {text}");
3211        assert!(text.contains("i32"), "and it gives back an int: {text}");
3212
3213        // No prototype, so a `char` argument arrives promoted to `int` the way an argument to a
3214        // function whose parameters are unspecified does.
3215        let text = compiled("int f(char c) { return g(c); }\n");
3216        assert!(text.contains("sext.i32"), "the argument is promoted: {text}");
3217
3218        // A name written as a value rather than called is still undeclared, since the rule is
3219        // about a call and nothing else.
3220        let mut opts = options();
3221        opts.std = Std::C89;
3222        let said = run(&opts, "int f(void) { return h; }\n").messages.join("\n");
3223        assert!(said.contains("'h' undeclared"), "not a call, so not declared: {said}");
3224    }
3225
3226    /// A file that calls a name above the definition of it, which is the shape the implicit
3227    /// declaration has to survive rather than swallow.
3228    ///
3229    /// The definition merges into the declaration the call already made rather than making a
3230    /// second one, so a declaration the tree does not carry at the top level takes the definition
3231    /// down with it: the body is attached to a node nothing walks and no function comes out.
3232    /// Nothing about the call itself looks wrong when that happens, and the program gets to the
3233    /// linker before anyone finds out, which is where `execute/cmpsi-1.c` in the torture suite
3234    /// found it, as an undefined reference to a name defined eleven lines further down.
3235    #[test]
3236    fn a_name_called_before_it_is_defined_still_gets_its_definition() {
3237        let mut opts = options();
3238        opts.emit = EmitKind::Ir;
3239        opts.std = Std::C89;
3240        let text = run(&opts, "int f(void) { return dummy(); }\ndummy () { return 7; }\n")
3241            .text()
3242            .to_owned();
3243        assert!(text.contains("func @f()"), "the caller is there: {text}");
3244        assert!(text.contains("func @dummy"), "and so is what it calls: {text}");
3245        assert!(text.contains("iconst.i32 7"), "with the body it was given: {text}");
3246    }
3247
3248    /// An old style definition whose parameter is narrower than what a call passes it.
3249    ///
3250    /// There is no prototype for a call to convert its argument to, so the argument is promoted
3251    /// and an `int` arrives for a parameter the body reads as an `unsigned char`. The entry block
3252    /// is where the two meet, and gcc writes the same pair of instructions there: store the low
3253    /// byte, read it back widened. `execute/950605-1.c` in the torture suite calls `f(-1)` and
3254    /// checks the parameter against `0xFF`, which is the difference between converting and not.
3255    #[test]
3256    fn an_old_style_parameter_is_converted_from_what_the_call_promoted_it_to() {
3257        let mut opts = options();
3258        opts.emit = EmitKind::Ir;
3259        opts.std = Std::C89;
3260        let compiled = |source: &str| run(&opts, source).text().to_owned();
3261
3262        let text = compiled("f (c) unsigned char c; { return c; }\n");
3263        assert!(text.contains("func @f(i32"), "an int arrives: {text}");
3264        assert!(text.contains("trunc.i8"), "and is cut down to what was declared: {text}");
3265        assert!(text.contains("zext.i32"), "then read back unsigned: {text}");
3266
3267        // A `short` is the same shape and signed, so it comes back the other way.
3268        let text = compiled("f (s) short s; { return s; }\n");
3269        assert!(text.contains("trunc.i16"), "cut down: {text}");
3270        assert!(text.contains("sext.i32"), "and read back signed: {text}");
3271
3272        // A `float` parameter is promoted to `double`, and without the conversion the multiply
3273        // below has one f64 operand and one f32, which the verifier refuses as invalid IR.
3274        let text = compiled("f (x) float x; { return x * 2; }\n");
3275        assert!(text.contains("func @f(f64"), "a double arrives: {text}");
3276        assert!(text.contains("fptrunc.f32"), "and is narrowed to the float: {text}");
3277
3278        // A parameter a prototype named arrives as itself and nothing is converted, which is the
3279        // case this must not have changed.
3280        let text = compiled("int f(unsigned char c) { return c; }\n");
3281        assert!(text.contains("func @f(i8)"), "the declared type arrives: {text}");
3282        assert!(!text.contains("trunc"), "so there is nothing to cut down: {text}");
3283    }
3284
3285    /// The six rules gcc 14 turned from a warning into an error, and the three answers each one
3286    /// gets depending on the dialect and on `-fpermissive`.
3287    ///
3288    /// The table is a measurement rather than a reading of the release notes. Six files, one per
3289    /// rule, put through gcc 16.2.0 on x86-64 Linux under each of the four command lines below
3290    /// with no `-W` flags on any of them, and what came back is what is written here. The three
3291    /// rules that say nothing under C89 are the three C89 did not have, and the three that warn
3292    /// there were constraint violations then as well.
3293    #[test]
3294    fn the_rules_gcc_promoted_are_decided_by_the_dialect_and_by_fpermissive() {
3295        // `-std=gnu89`, `-std=gnu17`, `-std=gnu17 -fpermissive`, and `-std=gnu23`.
3296        let modes = [(Std::C89, false), (Std::C17, false), (Std::C17, true), (Std::C23, false)];
3297        let cases = [
3298            ("static counted;\n", ["", "error", "warning", "error"]),
3299            ("int f(void) { return g(); }\n", ["", "error", "warning", "error"]),
3300            ("int f(x) { return x; }\n", ["", "error", "warning", "error"]),
3301            ("int *p;\nvoid h(void) { p = 1; }\n", ["warning", "error", "warning", "error"]),
3302            (
3303                "char *q;\nint *r;\nvoid k(void) { r = q; }\n",
3304                ["warning", "error", "warning", "error"],
3305            ),
3306            ("int f(void) { return; }\n", ["", "error", "warning", "error"]),
3307            ("void g(void) { return 1; }\n", ["warning", "error", "warning", "error"]),
3308        ];
3309
3310        for (source, wanted) in cases {
3311            for (&(std, permissive), wanted) in modes.iter().zip(wanted) {
3312                let mut opts = options();
3313                opts.std = std;
3314                opts.permissive = permissive;
3315                let said = run(&opts, source).messages.join("\n");
3316                let severity = if said.contains(": error: ") {
3317                    "error"
3318                } else if said.contains(": warning: ") {
3319                    "warning"
3320                } else {
3321                    ""
3322                };
3323                let how = if permissive { " -fpermissive" } else { "" };
3324                assert_eq!(
3325                    severity,
3326                    wanted,
3327                    "under -std={}{how}, {source} was answered with `{said}`",
3328                    std.as_str()
3329                );
3330                if wanted.is_empty() {
3331                    assert!(said.is_empty(), "nothing to say, but said `{said}`");
3332                }
3333            }
3334        }
3335    }
3336
3337    /// A first argument that is not a list, which the four variadic operators answer in two ways.
3338    ///
3339    /// gcc has `va_arg` as an operator, since it takes a type name and no function can, and the
3340    /// other three as builtin functions taking the address of a list. The difference is not a
3341    /// naming one: the operator's complaint is its own and is an error under every dialect, and
3342    /// the three functions go through the ordinary rule about an argument of the wrong type,
3343    /// which is one of the rules the table above is about. The same four command lines through
3344    /// gcc 16.2.0 on x86-64 Linux is where these came from.
3345    #[test]
3346    fn the_three_variadic_builtins_answer_a_bad_list_the_way_a_call_answers_a_bad_argument() {
3347        let modes = [(Std::C89, false), (Std::C17, false), (Std::C17, true), (Std::C23, false)];
3348        let cases = [
3349            (
3350                "int f(int n, ...) { char *p; return __builtin_va_arg(p, int); }\n",
3351                "first argument to 'va_arg' not of type 'va_list'",
3352                ["error", "error", "error", "error"],
3353            ),
3354            (
3355                "void f(int n, ...) { char *p; __builtin_va_start(p, n); }\n",
3356                "passing argument 1 of '__builtin_va_start' from incompatible pointer type",
3357                ["warning", "error", "warning", "error"],
3358            ),
3359            (
3360                "void f(int n, ...) { int x; __builtin_va_end(x); }\n",
3361                "passing argument 1 of '__builtin_va_end' makes pointer from integer without a \
3362                 cast",
3363                ["warning", "error", "warning", "error"],
3364            ),
3365            (
3366                "void f(int n, ...) { __builtin_va_list a; char *p; __builtin_va_copy(a, p); }\n",
3367                "passing argument 2 of '__builtin_va_copy' from incompatible pointer type",
3368                ["warning", "error", "warning", "error"],
3369            ),
3370        ];
3371
3372        for (source, message, wanted) in cases {
3373            for (&(std, permissive), wanted) in modes.iter().zip(wanted) {
3374                let mut opts = options();
3375                opts.std = std;
3376                opts.permissive = permissive;
3377                let said = run(&opts, source).messages.join("\n");
3378                let how = if permissive { " -fpermissive" } else { "" };
3379                assert!(
3380                    said.contains(&format!(": {wanted}: {message}")),
3381                    "under -std={}{how}, {source} was answered with `{said}`",
3382                    std.as_str()
3383                );
3384            }
3385        }
3386    }
3387
3388    /// The IR of `source` at one safety tier, insisting that it compiled cleanly.
3389    fn safe_ir(tier: rucc_session::Safety, source: &str) -> String {
3390        let mut opts = options();
3391        opts.emit = EmitKind::Ir;
3392        opts.safety = tier;
3393        let result = run(&opts, source);
3394        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3395        result.text().to_owned()
3396    }
3397
3398    const READS_THROUGH_A_POINTER: &str = "int read(int *p) { return p[1]; }\n";
3399
3400    /// The IR for a source built with a tier and a padding mode.
3401    fn padded_ir(padding: Padding, source: &str) -> String {
3402        let mut opts = options();
3403        opts.emit = EmitKind::Ir;
3404        opts.safety = rucc_session::Safety::Detect;
3405        opts.padding = padding;
3406        let result = run(&opts, source);
3407        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3408        result.text().to_owned()
3409    }
3410
3411    const FILLS_A_RECORD_A_MEMBER_AT_A_TIME: &str = "struct padded { char tag; int value; };\n\
3412         void fill(struct padded *p) { p->tag = 1; p->value = 2; }\n";
3413
3414    #[test]
3415    fn a_record_filled_a_member_at_a_time_comes_out_whole_when_padding_does_not_participate() {
3416        // Section 9.3 of document 09, and the reason the default is the one it gives library code.
3417        // Four bytes from the `char` and four from the `int` is the whole of an eight byte record,
3418        // so the `memcmp` or the hash or the `write` that reads it back is not refused.
3419        let text = padded_ir(Padding::Ignored, FILLS_A_RECORD_A_MEMBER_AT_A_TIME);
3420        assert_eq!(text.matches("owns 4").count(), 2, "{text}");
3421    }
3422
3423    #[test]
3424    fn a_store_says_only_what_it_wrote_when_padding_does_participate() {
3425        // The kernel profile's default, which is section 9.3's actual rule: the padding stays
3426        // unwritten and the read of the record that would leak it is the one that reports.
3427        let text = padded_ir(Padding::Tracked, FILLS_A_RECORD_A_MEMBER_AT_A_TIME);
3428        assert!(!text.contains("owns"), "{text}");
3429    }
3430
3431    #[test]
3432    fn a_member_of_a_union_owns_nothing_after_it() {
3433        // The bytes after a short member of a union belong to a longer member rather than to
3434        // padding, and saying a store through the short one wrote them would be saying the longer
3435        // one holds a value nobody put there.
3436        let text = padded_ir(
3437            Padding::Ignored,
3438            "union u { char tag; long wide; };\nvoid fill(union u *p) { p->tag = 1; }\n",
3439        );
3440        assert!(!text.contains("owns"), "{text}");
3441    }
3442
3443    #[test]
3444    fn an_inner_records_trailing_padding_reaches_the_outer_records() {
3445        // The composition. `in` owns four bytes of `outer` because `x` starts there, and `c` is
3446        // the last member of `in`, so what it owns is what `in` owns rather than its own one byte.
3447        // Without that the three bytes between them would stay unwritten and a read of the whole
3448        // thing would report.
3449        let text = padded_ir(
3450            Padding::Ignored,
3451            "struct inner { char c; };\n\
3452             struct outer { struct inner in; int x; };\n\
3453             void fill(struct outer *p) { p->in.c = 1; p->x = 2; }\n",
3454        );
3455        assert_eq!(text.matches("owns 4").count(), 2, "{text}");
3456    }
3457
3458    #[test]
3459    fn a_build_that_did_not_ask_for_the_monitor_is_compiled_the_way_it_always_was() {
3460        // This is the load bearing test of the whole flag. The monitor is being built in the open
3461        // and every build in the world is compiled by this compiler with the flag absent, so a
3462        // check that leaked into that path would be a regression for everybody.
3463        let text = ir(READS_THROUGH_A_POINTER);
3464        assert!(!text.contains("check_"), "{text}");
3465        assert!(!text.contains("cap_of"), "{text}");
3466    }
3467
3468    #[test]
3469    fn asking_for_a_tier_puts_the_checks_in_before_the_optimizer_sees_them() {
3470        let text = safe_ir(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
3471        assert!(text.contains("cap_of"), "{text}");
3472        assert!(text.contains("check_bounds"), "{text}");
3473        assert!(text.contains("check_live"), "{text}");
3474        // The subscript is address arithmetic, so J2 applies to it as well as J1.
3475        assert!(text.contains("check_deriv"), "{text}");
3476        // And the read names a type, so it asks the type plane about the bytes as well.
3477        assert!(text.contains("check_type"), "{text}");
3478    }
3479
3480    #[test]
3481    fn the_three_tiers_that_are_not_off_all_check_the_same_accesses_so_far() {
3482        // What separates them is the reporter and the boundary, which are milestones S2 and S3.
3483        // Pinning it here means the day they stop agreeing, this test says so rather than the
3484        // difference going unnoticed.
3485        let detect = safe_ir(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
3486        for tier in [rucc_session::Safety::Enforce, rucc_session::Safety::Kernel] {
3487            assert_eq!(safe_ir(tier, READS_THROUGH_A_POINTER), detect, "{tier}");
3488        }
3489    }
3490
3491    /// The safety summary of `source` at one tier, insisting that it compiled cleanly.
3492    fn summary(tier: rucc_session::Safety, source: &str) -> String {
3493        let mut opts = options();
3494        opts.emit = EmitKind::SafetySummary;
3495        opts.safety = tier;
3496        let result = run(&opts, source);
3497        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3498        result.text().to_owned()
3499    }
3500
3501    #[test]
3502    fn the_summary_counts_the_checks_that_went_in_and_the_ones_still_standing() {
3503        let text = summary(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
3504        assert!(text.contains("\"tier\": \"detect\""), "{text}");
3505        // One load, so one of each of the two access checks, and the subscript is a derivation.
3506        assert!(
3507            text.contains("\"bounds\": { \"emitted\": 1, \"remaining\": 1, \"discharged\": 0 }"),
3508            "{text}"
3509        );
3510        assert!(
3511            text.contains(
3512                "\"derivation\": { \"emitted\": 1, \"remaining\": 1, \"discharged\": 0 }"
3513            ),
3514            "{text}"
3515        );
3516    }
3517
3518    #[test]
3519    fn a_build_without_the_monitor_summarises_as_a_build_with_no_checks_in_it() {
3520        // Which is the honest summary rather than an error. A build system that emits a summary
3521        // for every unit should get one for the units nobody asked to instrument too, and the
3522        // zeroes are what say that the guarantee over that file is nothing at all.
3523        let text = summary(rucc_session::Safety::Off, READS_THROUGH_A_POINTER);
3524        assert!(text.contains("\"tier\": \"off\""), "{text}");
3525        assert!(
3526            text.contains("\"bounds\": { \"emitted\": 0, \"remaining\": 0, \"discharged\": 0 }"),
3527            "{text}"
3528        );
3529    }
3530
3531    #[test]
3532    fn a_call_the_boundary_models_is_counted_apart_from_one_it_does_not() {
3533        let text = summary(
3534            rucc_session::Safety::Detect,
3535            "void *memcpy(void *, const void *, unsigned long);\n\
3536             int puts(const char *);\n\
3537             void f(char *d, char *s) { memcpy(d, s, 4); puts(d); }\n",
3538        );
3539        assert!(text.contains("\"interposed\": 1"), "{text}");
3540        assert!(text.contains("\"puts\""), "{text}");
3541        // The wrapper it was pointed at is ours, so it is not on the list of things this build
3542        // failed to model. Counting it there would make instrumenting a file look worse than
3543        // leaving it alone.
3544        assert!(!text.contains("__rucc_wrap_memcpy\""), "{text}");
3545    }
3546
3547    #[test]
3548    fn the_two_directions_a_pointer_crosses_the_boundary_are_counted_apart() {
3549        // `f` is a name the linker can bind to and takes a pointer, so a pointer arrives there.
3550        // `notes_open` is a library this build did not instrument, so a pointer comes back from
3551        // it. Both are crossings and neither is the other, which is why there are two numbers.
3552        let text = summary(
3553            rucc_session::Safety::Detect,
3554            "void *notes_open(void);\n\
3555             char *f(char *p) { char *q = notes_open(); return q ? q : p; }\n",
3556        );
3557        assert!(text.contains("\"crossings\": { \"entered\": 1, \"returned\": 1 }"), "{text}");
3558        assert!(text.contains("\"notes_open\""), "{text}");
3559    }
3560
3561    #[test]
3562    fn a_static_function_nobody_takes_the_address_of_is_not_a_crossing() {
3563        // Nothing outside the file can reach it, so a witness on its parameters would be counting
3564        // a crossing that does not happen.
3565        let text = summary(
3566            rucc_session::Safety::Detect,
3567            "static int len(const char *p) { return p ? 1 : 0; }\n\
3568             int f(void) { return len(\"x\"); }\n",
3569        );
3570        assert!(text.contains("\"crossings\": { \"entered\": 0, \"returned\": 0 }"), "{text}");
3571    }
3572
3573    /// The granule report for `source`, insisting that it compiled cleanly.
3574    fn granules(source: &str) -> String {
3575        let mut opts = options();
3576        opts.emit = EmitKind::TypeGranules;
3577        let result = run(&opts, source);
3578        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3579        result.text().to_owned()
3580    }
3581
3582    #[test]
3583    fn the_granule_report_names_every_record_and_both_keyings() {
3584        let text = granules(
3585            "struct hot { char *p; int a; int b; };\n\
3586             int f(struct hot *h) { return h->a; }\n",
3587        );
3588        assert!(text.contains("struct hot"), "{text}");
3589        // Both keyings are reported because which types count as one is a decision the design
3590        // has not made yet, and a report that picked one would be hiding the cost of the other.
3591        assert!(text.contains("every type distinct"), "{text}");
3592        assert!(text.contains("every pointer one type"), "{text}");
3593        assert!(text.contains("budget"), "{text}");
3594    }
3595
3596    #[test]
3597    fn a_record_nothing_uses_is_still_measured() {
3598        // The measurement is about what a program declares, not about what it runs, so a type
3599        // that is only ever declared still costs the plane whatever its layout costs.
3600        let text = granules("struct unused { long a; double b; };\nint f(void) { return 0; }\n");
3601        assert!(text.contains("struct unused"), "{text}");
3602    }
3603
3604    #[test]
3605    fn the_granule_report_stops_before_anything_is_lowered() {
3606        // A layout is settled at the closing brace, so lowering the function bodies would take
3607        // minutes on an amalgamation and answer nothing. The evidence that it stops is that a
3608        // body the back end has no way to compile still produces a report.
3609        let text = granules(
3610            "struct wide { long double d; };\n\
3611             long double f(long double x) { return x * x; }\n",
3612        );
3613        assert!(text.contains("struct wide"), "{text}");
3614    }
3615
3616    #[test]
3617    fn a_witness_reaches_the_assembler_as_a_call_to_the_runtime() {
3618        // The count only means anything if the call is really there, and a summary saying one is
3619        // there is not evidence that the back end emitted it.
3620        let text = safe_asm(rucc_session::Safety::Detect, "char *f(char *p) { return p; }\n");
3621        assert!(text.contains("\tcall\t__rucc_cap_witness\n"), "{text}");
3622    }
3623
3624    #[test]
3625    fn a_pointer_turned_into_an_integer_is_on_the_trust_set() {
3626        let text = summary(
3627            rucc_session::Safety::Detect,
3628            "unsigned long f(int *p) { return (unsigned long) p; }\n",
3629        );
3630        assert!(text.contains("\"exposed\": 1"), "{text}");
3631    }
3632
3633    /// The assembly of `source` at one safety tier, insisting that it compiled cleanly.
3634    fn safe_asm(tier: rucc_session::Safety, source: &str) -> String {
3635        let mut opts = options();
3636        opts.emit = EmitKind::Asm;
3637        opts.safety = tier;
3638        let result = run(&opts, source);
3639        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
3640        result.text().to_owned()
3641    }
3642
3643    #[test]
3644    fn a_check_reaches_the_assembler_as_a_call_to_the_runtime() {
3645        let text = safe_asm(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
3646        assert!(text.contains("\tcall\t__rucc_check_bounds\n"), "{text}");
3647        assert!(text.contains("\tcall\t__rucc_check_live\n"), "{text}");
3648        assert!(text.contains("\tcall\t__rucc_check_deriv\n"), "{text}");
3649        assert!(text.contains("\tcall\t__rucc_check_type\n"), "{text}");
3650        assert!(text.contains("\tcall\t__rucc_check_init\n"), "{text}");
3651    }
3652
3653    #[test]
3654    fn every_check_that_reached_the_assembler_has_a_row_describing_it() {
3655        // Five checks and five descriptors, each in the section the runtime's reporter reads.
3656        // The width is `rucc_safety::lower::WIDTH` and the row is `rucc_safe_rt::fail::Descriptor`,
3657        // and the two agreeing is what makes the address a check is handed mean anything.
3658        let text = safe_asm(rucc_session::Safety::Detect, READS_THROUGH_A_POINTER);
3659        let section = format!("\t.section\t{},", rucc_safety::SECTION);
3660        assert_eq!(text.matches(&section).count(), 5, "{text}");
3661        for index in 0..5 {
3662            let name = format!("__rucc_safety_desc_{index}");
3663            // Defined once and referenced once, because a descriptor nothing points at describes
3664            // nothing and a reference with no definition does not link.
3665            assert!(text.contains(&format!("{name}:\n")), "{text}");
3666            assert!(text.contains(&format!("{name}(%rip)")), "{text}");
3667        }
3668        assert!(!text.contains("__rucc_safety_desc_5"), "{text}");
3669    }
3670
3671    /// `__builtin_constant_p` is answered in the front end and never reaches the IR.
3672    ///
3673    /// gcc folds it after optimization, so its answer for an argument that is not written as a
3674    /// constant can differ between `-O0` and `-O2`. What is checked here is the front end's
3675    /// answer, which is the same at every level, and the four cases where gcc gives the same
3676    /// answer at both levels are the ones measured on gcc 16: a literal is one, a variable is
3677    /// zero, a string literal is one and the address of an object is zero.
3678    #[test]
3679    fn builtin_constant_p_is_folded_where_it_is_written_rather_than_called() {
3680        let text = ir(concat!(
3681            "int g;\n",
3682            "int a = __builtin_constant_p(1);\n",
3683            "int b = __builtin_constant_p(g);\n",
3684            "int c = __builtin_constant_p(\"abc\");\n",
3685            "int d = __builtin_constant_p(&g);\n",
3686            "int e = __builtin_constant_p(1.5);\n",
3687            "int h = __builtin_choose_expr(__builtin_constant_p(3), 11, 22);\n",
3688        ));
3689        assert!(text.contains("global @a : i32 = 1,"), "{text}");
3690        assert!(text.contains("global @b : i32 = 0,"), "{text}");
3691        assert!(text.contains("global @c : i32 = 1,"), "{text}");
3692        assert!(text.contains("global @d : i32 = 0,"), "{text}");
3693        assert!(text.contains("global @e : i32 = 1,"), "{text}");
3694        assert!(text.contains("global @h : i32 = 11,"), "{text}");
3695        assert!(!text.contains("__builtin_constant_p"), "it is not a call to anything:\n{text}");
3696
3697        // The argument is not evaluated, which is what gcc does with it as well, so `i` is
3698        // still zero. The second constant is the answer, which nothing reads and which the
3699        // first pass that looks for dead code will take out.
3700        let text = body("int f(void) { int i = 0; __builtin_constant_p(i++); return i; }\n");
3701        assert_eq!(text, "block0:\n    %0 = iconst.i32 0\n    %1 = iconst.i32 0\n    return %0\n");
3702    }
3703
3704    /// A library builtin is the library function of the same name, and the call says so.
3705    ///
3706    /// A program writes `__builtin_strlen` rather than `strlen` to reach the function the C
3707    /// library promises where its own name has been taken by a macro, and to say that the usual
3708    /// meaning is the one intended. So the name in the program and the name in the object file
3709    /// are two different names and the call carries the second one. gcc folds several of these
3710    /// when the arguments allow it, which is an optimization on top of a call that is already
3711    /// right rather than instead of it, so nothing here depends on any folding happening.
3712    #[test]
3713    fn a_call_to_a_library_builtin_reaches_the_library_function() {
3714        let text = body("void f(void) { __builtin_abort(); }\n");
3715        assert_eq!(text, "block0:\n    call @abort() : ()\n    return\n");
3716
3717        // Nothing declared either of these and nothing had to: the prefix is what says the name
3718        // belongs to the implementation, and the type comes out of `features.toml`.
3719        let text = ir("int f(const char *s) { return __builtin_puts(s) + __builtin_strlen(s); }\n");
3720        assert!(text.contains("call @puts(%0) : (ptr) -> i32"), "{text}");
3721        assert!(text.contains("call @strlen(%0) : (ptr) -> i64"), "{text}");
3722        assert!(!text.contains("__builtin_"), "the prefix is not part of any name here:\n{text}");
3723    }
3724
3725    /// The absolute value family is four instructions and not a call, whoever declared the name.
3726    ///
3727    /// `abs`, `labs` and `llabs` are reserved to the implementation, so a program that writes one
3728    /// means the one the C library promises and the compiler is allowed to know what it does. The
3729    /// program in `gcc.c-torture/execute/20021127-1.c` is the one that insists: it defines `llabs`
3730    /// to abort and expects the call not to reach it. Measured against gcc 16.2.0, which writes a
3731    /// `neg` and a `cmovns` and never calls the definition either.
3732    ///
3733    /// The most negative value comes back as itself, which is what the arithmetic gives and what
3734    /// gcc's pair of instructions gives, and C says the answer is undefined there.
3735    #[test]
3736    fn the_absolute_value_family_is_the_magnitude_and_not_a_call() {
3737        let text = body(concat!(
3738            "long long llabs(long long);\n",
3739            "long long f(long long x) { return llabs(x); }\n",
3740        ));
3741        assert!(text.contains("%1 = iconst.i64 63"), "{text}");
3742        assert!(text.contains("%2 = ashr %0, %1"), "{text}");
3743        assert!(text.contains("%3 = xor %0, %2"), "{text}");
3744        assert!(text.contains("%4 = sub %3, %2"), "{text}");
3745        assert!(!text.contains("call"), "the call does not happen:\n{text}");
3746
3747        // The narrower two, whose width comes from the type the library gives the name and not
3748        // from anything at the call.
3749        let text = body("int abs(int);\nint f(int x) { return abs(x); }\n");
3750        assert!(text.contains("iconst.i32 31"), "{text}");
3751        let text = body("long labs(long);\nlong f(long x) { return labs(x); }\n");
3752        assert!(text.contains("iconst.i64 63"), "{text}");
3753
3754        // The prefixed spelling is the same node, and it is what a program writes to reach the
3755        // library's meaning where the plain name has been taken.
3756        let text = body("long long f(long long x) { return __builtin_llabs(x); }\n");
3757        assert!(!text.contains("call"), "{text}");
3758
3759        // A definition of the name in the same file changes nothing, which is the whole point.
3760        let text = ir(concat!(
3761            "long long llabs(long long b);\n",
3762            "long long g(long long x) { return llabs(x); }\n",
3763            "long long llabs(long long b) { return 7; }\n",
3764        ));
3765        assert!(!text.contains("call @llabs"), "{text}");
3766    }
3767
3768    /// A byte swap is one instruction and not a call, and nothing had to declare it.
3769    ///
3770    /// SQLite writes these for its page headers and glibc's `<endian.h>` defines `htobe32` and its
3771    /// neighbours as exactly these, so a program that reads a file format reaches one without ever
3772    /// naming it. There is no object file anywhere that defines `__builtin_bswap32`, so a call left
3773    /// standing here would not link.
3774    #[test]
3775    fn a_byte_swap_is_arithmetic_and_not_a_call() {
3776        let text = body("unsigned f(unsigned x) { return __builtin_bswap32(x); }\n");
3777        assert_eq!(text, "block0(%0: i32):\n    %1 = bswap %0\n    return %1\n");
3778
3779        // The argument is converted by the prototype the way any other call's would be, so the
3780        // swap happens at the width the name says and not at the width the program wrote.
3781        let text = body("unsigned f(unsigned char c) { return __builtin_bswap32(c); }\n");
3782        assert!(text.contains("zext.i32 %0"), "widened first: {text}");
3783        assert!(text.contains("bswap %1"), "and swapped at four bytes: {text}");
3784    }
3785
3786    /// Each of the three reverses in the width its name says, which is the type of the node.
3787    ///
3788    /// The width matters more here than it looks. `__builtin_bswap16` is the two bytes of a
3789    /// `uint16_t` exchanged, and if the node came out at the machine's width instead then the bits
3790    /// above the value would be dragged into the answer and the result would be zero.
3791    #[test]
3792    fn the_byte_swaps_reverse_at_the_width_their_name_says() {
3793        for (name, ty, width) in [
3794            ("__builtin_bswap16", "unsigned short", "i16"),
3795            ("__builtin_bswap32", "unsigned", "i32"),
3796            ("__builtin_bswap64", "unsigned long long", "i64"),
3797        ] {
3798            let source = format!("{ty} f({ty} x) {{ return {name}(x); }}\n");
3799            let text = body(&source);
3800            assert_eq!(
3801                text,
3802                format!("block0(%0: {width}):\n    %1 = bswap %0\n    return %1\n"),
3803                "{name}"
3804            );
3805        }
3806    }
3807
3808    /// The three bit counts the IR has an instruction for are that instruction and not a call.
3809    ///
3810    /// Eighteen rows of `features.toml` come out of six questions, and three of the six are one
3811    /// instruction each. The kernel's bitmap search is built on them, ffmpeg counts leading zeroes
3812    /// in its bitstream reader and SQLite uses one to size a page, so a call left standing here
3813    /// would not link against anything and would be slow if it did.
3814    #[test]
3815    fn the_bit_counts_are_instructions_and_not_calls() {
3816        let text = body("int f(unsigned x) { return __builtin_clz(x); }\n");
3817        assert_eq!(text, "block0(%0: i32):\n    %1 = ctlz %0\n    return %1\n");
3818
3819        let text = body("int f(unsigned x) { return __builtin_ctz(x); }\n");
3820        assert_eq!(text, "block0(%0: i32):\n    %1 = cttz %0\n    return %1\n");
3821
3822        let text = body("int f(unsigned x) { return __builtin_popcount(x); }\n");
3823        assert_eq!(text, "block0(%0: i32):\n    %1 = ctpop %0\n    return %1\n");
3824    }
3825
3826    /// The width counted is the operand's and the width answered is `int`, which are two different
3827    /// things at every spelling but the narrowest.
3828    ///
3829    /// This is the mistake the family invites. `__builtin_clz` of a value counts the leading zeroes
3830    /// of it narrowed to `unsigned int` and `__builtin_clzll` counts them at sixty four bits, and
3831    /// those are different numbers for the same value. What decides it is the prototype the row
3832    /// carries, so the count happens after the conversion and the narrowing back to `int` happens
3833    /// after the count.
3834    #[test]
3835    fn the_bit_counts_ask_about_the_width_their_name_says() {
3836        let text = body("int f(unsigned long long x) { return __builtin_clzll(x); }\n");
3837        assert!(text.starts_with("block0(%0: i64):"), "counted at eight bytes: {text}");
3838        assert!(text.contains("%1 = ctlz %0"), "{text}");
3839        assert!(text.contains("trunc.i32 %1"), "and answered in an int: {text}");
3840
3841        // The same value asked about at the narrower width, which converts first and so counts
3842        // something else.
3843        let text = body("int f(unsigned long long x) { return __builtin_clz(x); }\n");
3844        assert!(text.contains("trunc.i32 %0"), "narrowed to what was asked about: {text}");
3845        assert!(text.contains("ctlz %1"), "and counted there: {text}");
3846
3847        let text = body("int f(unsigned long x) { return __builtin_popcountl(x); }\n");
3848        assert!(text.contains("%1 = ctpop %0"), "{text}");
3849        assert!(!text.contains("call"), "{text}");
3850    }
3851
3852    /// A parity is whether the count of set bits is odd, which is that count and its low bit.
3853    ///
3854    /// Not the machine's parity flag, which on x86-64 is over the low byte of a result and so is a
3855    /// different question, and not the count itself, since C says the answer is zero or one.
3856    #[test]
3857    fn a_parity_is_the_low_bit_of_the_set_bit_count() {
3858        let text = body("int f(unsigned x) { return __builtin_parity(x); }\n");
3859        assert!(text.contains("%1 = ctpop %0"), "{text}");
3860        assert!(text.contains("iconst.i32 1"), "{text}");
3861        assert!(text.contains("and %1, %2"), "the low bit of it: {text}");
3862    }
3863
3864    /// `__builtin_ffs` is the trailing zero count and one, kept only when there was a bit to find.
3865    ///
3866    /// The one in the family defined at zero, where it answers zero. Written as a mask rather than
3867    /// as a branch: the count and the comparison do not depend on each other and both are cheap, so
3868    /// a branch would buy nothing and cost two blocks and a join.
3869    #[test]
3870    fn the_first_set_bit_is_one_based_and_zero_for_a_zero() {
3871        let text = body("int f(int x) { return __builtin_ffs(x); }\n");
3872        assert!(text.contains("%1 = cttz %0"), "{text}");
3873        assert!(text.contains("%4 = add %1, %2"), "one more than the count: {text}");
3874        assert!(text.contains("%5 = icmp ne %0, %3"), "whether there was a bit at all: {text}");
3875        assert!(text.contains("%7 = sub %3, %6"), "spread to a mask: {text}");
3876        assert!(text.contains("%8 = and %4, %7"), "and kept only then: {text}");
3877        assert!(!text.contains("br_if"), "no branch: {text}");
3878    }
3879
3880    /// `__builtin_clrsb` is how many bits below the sign bit repeat it, which is a leading zero
3881    /// count of the value folded onto its own sign.
3882    ///
3883    /// Exclusive or with the sign spread over every bit turns a negative value into its complement
3884    /// and leaves one that is not negative alone, so in both cases the top bit is clear and there
3885    /// is one zero above the highest bit that does not repeat the sign. The answer is one less
3886    /// than that count, and the shift left is what takes the one off, with the low bit set on the
3887    /// way so that zero and minus one have something to count: both of them fold to a word with no
3888    /// bits in it, which is the one input a leading zero count says nothing about.
3889    #[test]
3890    fn the_redundant_sign_bit_count_is_instructions_and_not_a_call() {
3891        let text = body("int f(int x) { return __builtin_clrsb(x); }\n");
3892        assert!(text.contains("%1 = iconst.i32 31"), "{text}");
3893        assert!(text.contains("%2 = ashr %0, %1"), "the sign over every bit: {text}");
3894        assert!(text.contains("%3 = xor %0, %2"), "folded onto it: {text}");
3895        assert!(text.contains("%5 = shl %3, %4"), "one less than the count: {text}");
3896        assert!(text.contains("%6 = or %5, %4"), "with something to count at zero: {text}");
3897        assert!(text.contains("%7 = ctlz %6"), "{text}");
3898        assert!(!text.contains("call"), "{text}");
3899        assert!(!text.contains("br_if"), "no branch: {text}");
3900    }
3901
3902    /// The unsigned four are the same four instructions answering in the unsigned type.
3903    ///
3904    /// Which on a two's complement machine is the same bits, so what this checks is that the type
3905    /// of the answer is the unsigned one. The reason the family exists is the most negative value,
3906    /// whose magnitude is not representable in the signed type and is representable in this one.
3907    #[test]
3908    fn the_unsigned_absolute_value_family_answers_in_the_unsigned_type() {
3909        let text = body("unsigned f(int x) { return __builtin_uabs(x); }\n");
3910        assert!(text.contains("%1 = iconst.i32 31"), "{text}");
3911        assert!(text.contains("%4 = sub %3, %2"), "{text}");
3912        assert!(!text.contains("call"), "nothing declares uabs, so a call would not link: {text}");
3913
3914        let text = body("unsigned long long f(long long x) { return __builtin_ullabs(x); }\n");
3915        assert!(text.contains("iconst.i64 63"), "at the width the name says: {text}");
3916
3917        // The answer is the unsigned type and not the signed one, which is what a comparison
3918        // against it is decided by.
3919        let text = body("int f(int x) { return __builtin_uabs(x) > 2147483647u; }\n");
3920        assert!(text.contains("icmp ugt"), "compared unsigned: {text}");
3921    }
3922
3923    /// `intmax_t` is not a fixed type, so the two widest spellings ask the target what it is.
3924    ///
3925    /// `long` where that is sixty four bits wide and `long long` where it is not, which is the rule
3926    /// `rucc_pp::predef` writes `__INTMAX_TYPE__` out of. The three targets here are all LP64, so
3927    /// the answer is `long` and the shift is sixty three, and the point of the test is that the
3928    /// signature was understood at all rather than refused for naming a type the table could not
3929    /// spell.
3930    #[test]
3931    fn the_widest_absolute_value_is_whichever_type_the_target_makes_intmax_t() {
3932        let text = body("long f(long x) { return __builtin_imaxabs(x); }\n");
3933        assert!(text.contains("iconst.i64 63"), "{text}");
3934        assert!(text.contains("%4 = sub %3, %2"), "{text}");
3935        assert!(!text.contains("call"), "{text}");
3936
3937        let text = body("unsigned long f(long x) { return __builtin_umaxabs(x); }\n");
3938        assert!(text.contains("iconst.i64 63"), "{text}");
3939        assert!(!text.contains("call"), "{text}");
3940    }
3941
3942    /// The `_p` spellings ask the same question, write nothing, and do not evaluate the third
3943    /// argument.
3944    ///
3945    /// gcc says the third argument is there for its type alone, so a call is two operands and a
3946    /// type by the time it reaches the IR. What the type decides is the same thing it decides for
3947    /// the three that write: whether the exact answer would have fit there, which is why the
3948    /// second call below is done at a wider width than the first.
3949    #[test]
3950    fn an_overflow_predicate_writes_nothing_and_answers_the_bit_the_check_would() {
3951        let text =
3952            body("int f(int a, int b) { return __builtin_add_overflow_p(a, b, (int) 0); }\n");
3953        assert!(text.contains("%2, %3 = sadd_overflow.(i32, i1) %0, %1"), "{text}");
3954        assert!(!text.contains("store"), "nothing is written: {text}");
3955        assert!(!text.contains("call"), "{text}");
3956
3957        // A wider destination is a wider arithmetic, and the narrowing test that goes with it is
3958        // what says whether the answer got there, exactly as for the spelling that stores.
3959        let text =
3960            body("int f(int a, int b) { return __builtin_mul_overflow_p(a, b, (long long) 0); }\n");
3961        assert!(text.contains("smul_overflow.(i64, i1)"), "{text}");
3962        assert!(!text.contains("store"), "{text}");
3963
3964        // The third argument is a value and not a pointer, and a side effect written in it does
3965        // not happen, because what the argument is there for is its type.
3966        let text = body(concat!(
3967            "int g(void);\n",
3968            "int f(int a, int b) { return __builtin_sub_overflow_p(a, b, g()); }\n",
3969        ));
3970        assert!(!text.contains("call @g"), "the third argument is not evaluated: {text}");
3971    }
3972
3973    /// The three overflow checks are arithmetic and a flag, and not a call to anything.
3974    ///
3975    /// gcc has emitted these since 5.0 and there is no object file that defines one, so a call left
3976    /// standing here would not link. SQLite reaches all three within twenty lines of each other, in
3977    /// `sqlite3AddInt64` and its two neighbours, which is the reason they were done now.
3978    ///
3979    /// The IR instruction answers two things at once, the wrapped value and whether it wrapped,
3980    /// which is a shape nothing else in the IR has. The store is the builtin writing the answer
3981    /// through the pointer it was handed.
3982    #[test]
3983    fn an_overflow_check_is_arithmetic_and_not_a_call() {
3984        let text =
3985            body("int f(int a, int b, int *r) { return __builtin_add_overflow(a, b, r); }\n");
3986        assert!(text.contains("%3, %4 = sadd_overflow.(i32, i1) %0, %1"), "{text}");
3987        assert!(text.contains("store %3 -> %2"), "{text}");
3988        assert!(!text.contains("call"), "{text}");
3989
3990        let text =
3991            body("int f(int a, int b, int *r) { return __builtin_sub_overflow(a, b, r); }\n");
3992        assert!(text.contains("ssub_overflow.(i32, i1) %0, %1"), "{text}");
3993
3994        let text =
3995            body("int f(int a, int b, int *r) { return __builtin_mul_overflow(a, b, r); }\n");
3996        assert!(text.contains("smul_overflow.(i32, i1) %0, %1"), "{text}");
3997
3998        // Unsigned operands get the unsigned form, which is a different question about the same
3999        // arithmetic: an unsigned sum wraps where a signed one of the same bits does not.
4000        let text = body(
4001            "int f(unsigned a, unsigned b, unsigned *r) { return __builtin_add_overflow(a, b, r); }\n",
4002        );
4003        assert!(text.contains("uadd_overflow.(i32, i1) %0, %1"), "{text}");
4004    }
4005
4006    /// The arithmetic happens at a type that holds every value all three written types can hold.
4007    ///
4008    /// That is what makes the check exact. `unsigned int` and `int` in one call need thirty three
4009    /// bits between them, so the add is done at sixty four with each operand extended the way its
4010    /// own signedness says: the unsigned one zero extended, the signed one sign extended. Sign
4011    /// extending the unsigned one would turn three billion into a negative number before the
4012    /// addition ever saw it.
4013    #[test]
4014    fn an_overflow_check_is_done_at_a_type_that_holds_every_operand() {
4015        let text = body(
4016            "int f(unsigned a, int b, long long *r) { return __builtin_add_overflow(a, b, r); }\n",
4017        );
4018        assert!(text.contains("%3 = zext.i64 %0"), "the unsigned operand keeps its value: {text}");
4019        assert!(text.contains("%4 = sext.i64 %1"), "and so does the signed one: {text}");
4020        assert!(text.contains("sadd_overflow.(i64, i1) %3, %4"), "{text}");
4021
4022        // Three types that agree need no extension at all, which is what nearly every real call
4023        // is written as.
4024        let text = body(
4025            "int f(long long a, long long b, long long *r) { return __builtin_mul_overflow(a, b, r); }\n",
4026        );
4027        assert!(text.contains("smul_overflow.(i64, i1) %0, %1"), "{text}");
4028        assert!(!text.contains("sext."), "{text}");
4029        // The one widening left is the answer, which is a bit becoming the `int` C says it is.
4030        assert!(!text.contains("zext.i64"), "{text}");
4031    }
4032
4033    /// The wrapped answer is written through the pointer whether or not it fit.
4034    ///
4035    /// That is gcc's rule and it is what makes the builtin usable as a wrapping add with a flag on
4036    /// the side. A destination narrower than the arithmetic is narrowed and widened back, and the
4037    /// answer being different is the second half of the test: the instruction says whether the
4038    /// arithmetic itself needed more room, and the round trip says whether what came out survived
4039    /// the trip down to where it was going.
4040    #[test]
4041    fn an_overflow_check_writes_the_wrapped_answer_whether_or_not_it_fit() {
4042        let text =
4043            body("int f(int a, int b, char *r) { return __builtin_sub_overflow(a, b, r); }\n");
4044        assert!(text.contains("%3, %4 = ssub_overflow.(i32, i1) %0, %1"), "{text}");
4045        assert!(text.contains("%5 = trunc.i8 %3"), "narrowed to where it goes: {text}");
4046        assert!(text.contains("%6 = sext.i32 %5"), "and back: {text}");
4047        assert!(text.contains("%7 = icmp ne %6, %3"), "which is whether it fit: {text}");
4048        assert!(text.contains("store %5 -> %2"), "the narrowed value is stored either way: {text}");
4049        assert!(text.contains("%8 = or %4, %7"), "and either bit is an overflow: {text}");
4050    }
4051
4052    /// A call needing more than the widest type there is compiles, by not asking for such a type.
4053    ///
4054    /// One way to reach it: an unsigned `__int128` mixed with a signed type, which needs a hundred
4055    /// and twenty nine bits to represent both and so has nowhere left to go. That used to be refused
4056    /// by name. It is done now by carrying the sign of each operand alongside its value rather than
4057    /// inside it, which is what gcc does, so all three of the family compile for that mix.
4058    #[test]
4059    fn a_call_needing_more_than_the_widest_type_still_compiles() {
4060        for name in ["add", "sub", "mul"] {
4061            let source = format!(
4062                "int f(unsigned __int128 a, long long b, __int128 *r) {{\n    \
4063                 return __builtin_{name}_overflow(a, b, r);\n}}\n"
4064            );
4065            let mut opts = options();
4066            opts.emit = EmitKind::MirFinal;
4067            assert!(!run(&opts, &source).failed(), "{name} was refused or stopped the back end");
4068        }
4069    }
4070
4071    /// An operand that is not an integer at all is the older message, from the type checking every
4072    /// type generic builtin shares.
4073    #[test]
4074    fn an_overflow_check_over_something_that_is_not_an_integer_says_so() {
4075        let messages =
4076            errors("int f(double a, int b, int *r) { return __builtin_add_overflow(a, b, r); }\n");
4077        assert!(messages.iter().any(|line| line.contains("E0671")), "{messages:?}");
4078
4079        let messages =
4080            errors("int f(int a, int b, double *r) { return __builtin_add_overflow(a, b, r); }\n");
4081        assert!(messages.iter().any(|line| line.contains("E0671")), "{messages:?}");
4082    }
4083
4084    /// An ordered access is an ordered access in the IR, with the ordering the program wrote.
4085    ///
4086    /// Which is the point of the node existing at all. An ordering is not an argument anything is
4087    /// passed, it is a thing the IR says about an access, so the number in the source is read once
4088    /// in the front end and after that the ordering travels on the instruction where every pass
4089    /// that moves code can see it.
4090    ///
4091    /// SQLite is why these are done: `AtomicLoad` and `AtomicStore` in `sqlite3.c` are
4092    /// `__atomic_load_n` and `__atomic_store_n` at the relaxed ordering, and there are thirty five
4093    /// calls to the pair.
4094    #[test]
4095    fn an_ordered_access_is_ordered_in_the_ir() {
4096        let text = body("int f(int *p) { return __atomic_load_n(p, 0); }\n");
4097        assert!(text.contains("atomic_load.i32 %0, align 4, relaxed"), "{text}");
4098
4099        let text = body("long f(long *p) { return __atomic_load_n(p, 2); }\n");
4100        assert!(text.contains("atomic_load.i64 %0, align 8, acquire"), "{text}");
4101
4102        let text = body("void f(int *p, int v) { __atomic_store_n(p, v, 3); }\n");
4103        assert!(text.contains("atomic_store %1 -> %0, align 4, release"), "{text}");
4104
4105        let text = body("void f(int *p, int v) { __atomic_store_n(p, v, 5); }\n");
4106        assert!(text.contains("atomic_store %1 -> %0, align 4, seq_cst"), "{text}");
4107
4108        // The value is converted to what the pointer points at before it is stored, which is what
4109        // the call would have done if it had a prototype to convert against.
4110        let text = body("void f(char *p, int v) { __atomic_store_n(p, v, 0); }\n");
4111        assert!(text.contains("trunc.i8 %1"), "{text}");
4112        assert!(text.contains("atomic_store %2 -> %0, align 1, relaxed"), "{text}");
4113    }
4114
4115    /// On this machine the ordered access is the plain instruction, except at the strongest
4116    /// ordering of a store.
4117    ///
4118    /// x86-64 is total store order: every load is already an acquire and every store is already a
4119    /// release, and an aligned access no wider than a word is indivisible whether or not anybody
4120    /// asked. So the whole family is `mov` and the one thing the machine does not give away is a
4121    /// store staying in front of a later load, which is `mfence` behind the store. Every line below
4122    /// is what gcc 16.2.0 writes for the same function.
4123    #[test]
4124    fn an_ordered_access_is_the_plain_instruction_on_this_machine() {
4125        let text = asm("int f(int *p) { return __atomic_load_n(p, 5); }\n");
4126        assert!(text.contains("movl\t(%rdi), %eax"), "{text}");
4127        assert!(!text.contains("mfence"), "a load needs no barrier here: {text}");
4128
4129        let text = asm("void f(int *p, int v) { __atomic_store_n(p, v, 3); }\n");
4130        assert!(text.contains("movl\t%esi, (%rdi)"), "{text}");
4131        assert!(!text.contains("mfence"), "a release store needs no barrier here: {text}");
4132
4133        let text = asm("void f(int *p, int v) { __atomic_store_n(p, v, 5); }\n");
4134        let (before, after) = text.split_once("mfence").expect("a barrier: {text}");
4135        assert!(before.contains("movl\t%esi, (%rdi)"), "the store comes first: {text}");
4136        assert!(!after.contains("movl"), "and nothing else is between them: {text}");
4137    }
4138
4139    /// A barrier is one instruction at the strongest ordering and no instruction below it.
4140    ///
4141    /// The same reasoning the other way round. An acquire, a release and an acquire release fence
4142    /// are already true of every program running on this machine, and what a program wanted from
4143    /// one is that the compiler not move accesses across it, which is already so by the time any
4144    /// instruction is picked. Sequential consistency is the one that costs something.
4145    ///
4146    /// `__sync_synchronize` is the older family's spelling of the strongest one and compiles to
4147    /// exactly the same instruction, which is what SQLite calls twice in `sqlite3.c`.
4148    #[test]
4149    fn a_barrier_is_one_instruction_at_the_strongest_ordering_and_none_below_it() {
4150        assert!(asm("void f(void) { __atomic_thread_fence(5); }\n").contains("mfence"));
4151        assert!(asm("void f(void) { __sync_synchronize(); }\n").contains("mfence"));
4152
4153        for weaker in ["1", "2", "3", "4"] {
4154            let source = format!("void f(void) {{ __atomic_thread_fence({weaker}); }}\n");
4155            assert!(!asm(&source).contains("mfence"), "{weaker} costs nothing here");
4156        }
4157    }
4158
4159    /// The four compare and exchange names are one IR instruction producing two values.
4160    ///
4161    /// Which of the two the expression answers is the difference between three of the four names,
4162    /// and the fourth difference is the C11 pair writing what they found back through the pointer
4163    /// they were handed, which is the branch after the instruction.
4164    #[test]
4165    fn a_compare_and_exchange_is_one_instruction_answering_two_things() {
4166        // The older family, whose two names are the same instruction read two ways. Neither has a
4167        // memory order argument and both are a full barrier, which is what `seq_cst` says.
4168        let text =
4169            body("int f(int *p, int e, int d) { return __sync_val_compare_and_swap(p, e, d); }\n");
4170        assert!(text.contains("%3, %4 = cmpxchg.(i32, i1) %0, %1, %2, align 4, seq_cst"), "{text}");
4171        assert!(text.contains("return %3"), "the value it found: {text}");
4172
4173        let text =
4174            body("int f(int *p, int e, int d) { return __sync_bool_compare_and_swap(p, e, d); }\n");
4175        assert!(text.contains("%3, %4 = cmpxchg.(i32, i1) %0, %1, %2, align 4, seq_cst"), "{text}");
4176        assert!(text.contains("zext.i32 %4"), "whether it happened: {text}");
4177
4178        // The C11 form, whose value expected arrives by pointer and is read before the exchange,
4179        // and whose answer is whether it happened. The write back is on the path where it did not.
4180        let text = body(
4181            "int f(int *p, int *e, int d) { return __atomic_compare_exchange_n(p, e, d, 0, 4, 2); }\n",
4182        );
4183        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
4184        assert!(text.contains("%4, %5 = cmpxchg.(i32, i1) %0, %3, %2, align 4, acq_rel"), "{text}");
4185        assert!(text.contains("br_if %5, block2, block1"), "{text}");
4186        assert!(text.contains("store %4 -> %1, align 4"), "{text}");
4187
4188        // And the form that takes the value to put there by pointer as well, which is one more
4189        // read and is otherwise the same node.
4190        let text = body(
4191            "int f(int *p, int *e, int *d) { return __atomic_compare_exchange(p, e, d, 0, 5, 5); }\n",
4192        );
4193        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
4194        assert!(text.contains("%4 = load.i32 %2, align 4"), "{text}");
4195        assert!(text.contains("%5, %6 = cmpxchg.(i32, i1) %0, %3, %4, align 4, seq_cst"), "{text}");
4196    }
4197
4198    /// On this machine it is `lock cmpxchg`, at the width of the object and at every ordering.
4199    ///
4200    /// The `lock` is what makes the whole of it one step as far as every other processor is
4201    /// concerned, and it is also what makes the instruction a full barrier, which is why the
4202    /// ordering the program wrote changes nothing in what is written here. Every line below is what
4203    /// gcc 16.2.0 writes for the same function.
4204    #[test]
4205    fn a_compare_and_exchange_is_a_locked_instruction_at_the_width_of_the_object() {
4206        let widths = [("char", "b", "%dl"), ("short", "w", "%dx"), ("int", "l", "%edx")];
4207        for (ty, suffix, reg) in widths {
4208            let source = format!(
4209                "int f({ty} *p, {ty} e, {ty} d) {{ return __sync_bool_compare_and_swap(p, e, d); }}\n"
4210            );
4211            let text = asm(&source);
4212            assert!(text.contains("\tlock\n"), "{ty}: {text}");
4213            assert!(text.contains(&format!("cmpxchg{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
4214            assert!(text.contains("sete\t"), "{ty}: {text}");
4215        }
4216        let source =
4217            "int f(long *p, long e, long d) { return __sync_bool_compare_and_swap(p, e, d); }\n";
4218        assert!(asm(source).contains("cmpxchgq\t%rdx, (%rdi)"), "{}", asm(source));
4219
4220        // The ordering the program asked for changes nothing, because a locked instruction on this
4221        // machine orders everything whatever it was asked for, so there is never a barrier beside
4222        // it either.
4223        for order in ["0", "2", "3", "4", "5"] {
4224            let call = format!("__atomic_compare_exchange_n(p, e, d, 0, {order}, 0)");
4225            let source = format!("int f(int *p, int *e, int d) {{ return {call}; }}\n");
4226            let text = asm(&source);
4227            assert!(text.contains("cmpxchgl\t"), "{order}: {text}");
4228            assert!(!text.contains("mfence"), "{order} needs no barrier here: {text}");
4229        }
4230    }
4231
4232    /// A read modify write is one IR instruction, and a name that asks for the value afterwards is
4233    /// that instruction and one more operation.
4234    ///
4235    /// The instruction answers what was there before, which is the convention every machine and
4236    /// every language in this area uses. Half the names in the family ask for the value afterwards
4237    /// instead, and that is the answer and the operand put together again, which is arithmetic on
4238    /// two values already in registers rather than a second flavour of the instruction.
4239    ///
4240    /// The two lock names are here too. They are not read modify writes in the same sense: one is
4241    /// an exchange and the other is a store of a zero, and what makes them a pair is the ordering,
4242    /// which is the one place in the older family that is not sequential consistency.
4243    #[test]
4244    fn a_read_modify_write_is_one_instruction_and_the_arithmetic_a_name_asks_for() {
4245        let text = body("int f(int *p, int v) { return __atomic_fetch_add(p, v, 5); }\n");
4246        assert!(text.contains("%2 = atomic_rmw.i32 add %0, %1, align 4, seq_cst"), "{text}");
4247        assert!(text.contains("return %2"), "the value that was there: {text}");
4248
4249        let text = body("int f(int *p, int v) { return __atomic_add_fetch(p, v, 5); }\n");
4250        assert!(text.contains("%2 = atomic_rmw.i32 add %0, %1, align 4, seq_cst"), "{text}");
4251        assert!(text.contains("%3 = add %2, %1"), "and the value afterwards: {text}");
4252
4253        let text = body("int f(int *p, int v) { return __atomic_sub_fetch(p, v, 5); }\n");
4254        assert!(text.contains("%2 = atomic_rmw.i32 sub %0, %1, align 4, seq_cst"), "{text}");
4255        assert!(text.contains("%3 = sub %2, %1"), "{text}");
4256
4257        // The older family, which passes no ordering and is a full barrier.
4258        let text = body("int f(int *p, int v) { return __sync_fetch_and_sub(p, v); }\n");
4259        assert!(text.contains("%2 = atomic_rmw.i32 sub %0, %1, align 4, seq_cst"), "{text}");
4260
4261        // The exchange, and the older family's spelling of it, which is taking a lock and so is an
4262        // acquire rather than the full barrier the rest of that family is.
4263        let text = body("int f(int *p, int v) { return __atomic_exchange_n(p, v, 5); }\n");
4264        assert!(text.contains("%2 = atomic_rmw.i32 xchg %0, %1, align 4, seq_cst"), "{text}");
4265
4266        let text = body("int f(int *p, int v) { return __sync_lock_test_and_set(p, v); }\n");
4267        assert!(text.contains("%2 = atomic_rmw.i32 xchg %0, %1, align 4, acquire"), "{text}");
4268
4269        // Giving the lock back, which is one of the two names in the family that is handed no value
4270        // to put there, because what it puts there is a zero.
4271        let text = body("void f(int *p) { __sync_lock_release(p); }\n");
4272        assert!(text.contains("release"), "{text}");
4273        assert!(text.contains("%1 = iconst.i32 0"), "{text}");
4274
4275        // And with something after the pointer, which is the list of variables the call promises to
4276        // protect rather than a value to write. Reading it as a value would store whatever the
4277        // caller happened to name there, which is the one thing giving a lock back must not do.
4278        let text = body("void f(int *p, int guard) { __sync_lock_release(p, guard); }\n");
4279        assert!(text.contains("%2 = iconst.i32 0"), "{text}");
4280        assert!(text.contains("atomic_store %2 -> %0, align 4, release"), "{text}");
4281
4282        // The bitwise four, which look no different here from the arithmetic ones: what the machine
4283        // has an instruction for is a question further down and this level does not ask it.
4284        let text = body("int f(int *p, int v) { return __atomic_fetch_and(p, v, 5); }\n");
4285        assert!(text.contains("%2 = atomic_rmw.i32 and %0, %1, align 4, seq_cst"), "{text}");
4286
4287        let text = body("int f(int *p, int v) { return __sync_or_and_fetch(p, v); }\n");
4288        assert!(text.contains("%2 = atomic_rmw.i32 or %0, %1, align 4, seq_cst"), "{text}");
4289        assert!(text.contains("%3 = or %2, %1"), "and the value afterwards: {text}");
4290
4291        // The nand, which is the one of the six that is two operations. The flip is an exclusive or
4292        // against every bit set because the IR has no not and that is what one is.
4293        let text = body("int f(int *p, int v) { return __atomic_nand_fetch(p, v, 5); }\n");
4294        assert!(text.contains("%2 = atomic_rmw.i32 nand %0, %1, align 4, seq_cst"), "{text}");
4295        assert!(text.contains("%3 = and %2, %1"), "{text}");
4296        assert!(text.contains("%4 = iconst.i32 -1"), "{text}");
4297        assert!(text.contains("%5 = xor %3, %4"), "{text}");
4298    }
4299
4300    /// The four operations with no instruction on this machine are a loop around `lock cmpxchg`.
4301    ///
4302    /// The shape is the one every architecture manual writes out by hand: read the word, work out
4303    /// what should be there instead, put it back if nothing else got in first, and go round again
4304    /// when something did. What is checked is that the loop is there at every width, that the
4305    /// operation is inside it, and that no `xchg` or `xadd` got used for something neither of them
4306    /// does.
4307    ///
4308    /// gcc 16.2.0 writes the same loop for the same functions, down to which register holds the
4309    /// value that was read.
4310    #[test]
4311    fn a_bitwise_read_modify_write_is_a_loop_around_the_compare_and_exchange() {
4312        let widths = [("char", "b", "%dl"), ("short", "w", "%dx"), ("int", "l", "%edx")];
4313        for (ty, suffix, reg) in widths {
4314            for (name, call, insn) in [
4315                ("and", "__atomic_fetch_and(p, v, 5)", "and"),
4316                ("or", "__sync_fetch_and_or(p, v)", "or"),
4317                ("xor", "__atomic_xor_fetch(p, v, 5)", "xor"),
4318            ] {
4319                let source = format!("{ty} f({ty} *p, {ty} v) {{ return {call}; }}\n");
4320                let text = asm(&source);
4321                assert!(text.contains("\tlock\n"), "{ty} {name}: {text}");
4322                assert!(
4323                    text.contains(&format!("cmpxchg{suffix}\t{reg}, (%rdi)")),
4324                    "{ty} {name}: {text}"
4325                );
4326                assert!(text.contains(&format!("{insn}{suffix}\t")), "{ty} {name}: {text}");
4327                // The tab matters on the second of these, since `cmpxchg` ends in the other name.
4328                assert!(!text.contains("\txadd"), "{ty} {name} is not an add: {text}");
4329                assert!(!text.contains("\txchg"), "{ty} {name} is not an exchange: {text}");
4330            }
4331        }
4332        let source = "long f(long *p, long v) { return __atomic_fetch_or(p, v, 5); }\n";
4333        assert!(asm(source).contains("cmpxchgq\t%rdx, (%rdi)"), "{}", asm(source));
4334
4335        // The nand, which puts two instructions inside the loop rather than one. The flip is an
4336        // exclusive or against every bit set in the IR and the folder turns that into the `not` the
4337        // machine has, which is what gcc writes here too.
4338        let text = asm("int f(int *p, int v) { return __sync_fetch_and_nand(p, v); }\n");
4339        assert!(text.contains("cmpxchgl\t"), "{text}");
4340        assert!(text.contains("andl\t"), "{text}");
4341        assert!(text.contains("notl\t"), "{text}");
4342    }
4343
4344    /// The three names that pass a value through a pointer are the same access and one plain one.
4345    ///
4346    /// They exist for an object too big to come back in a register, and the front end takes them at
4347    /// their word rather than folding them into the `_n` spellings, because the extra access is real:
4348    /// the caller handed over somewhere to read from or write into and that is where the value has
4349    /// to come from or go. Both of those accesses are plain. The object at the end of the caller's
4350    /// pointer is the caller's own and no other thread has its address, which is what the whole
4351    /// shape is for.
4352    #[test]
4353    fn an_access_through_a_second_pointer_is_the_same_access_and_one_more() {
4354        let text = body("void f(int *p, int *r) { __atomic_load(p, r, 5); }\n");
4355        assert!(text.contains("%2 = atomic_load.i32 %0, align 4, seq_cst"), "{text}");
4356        assert!(text.contains("store %2 -> %1, align 4"), "and out through the place: {text}");
4357
4358        let text = body("void f(int *p, int *v) { __atomic_store(p, v, 3); }\n");
4359        assert!(text.contains("%2 = load.i32 %1, align 4"), "in through the place: {text}");
4360        assert!(text.contains("atomic_store %2 -> %0, align 4, release"), "{text}");
4361
4362        // The exchange, which reads through one pointer and writes through another and is the same
4363        // instruction in between as the spelling that takes and answers values.
4364        let text = body("void f(int *p, int *v, int *r) { __atomic_exchange(p, v, r, 5); }\n");
4365        assert!(text.contains("%3 = load.i32 %1, align 4"), "{text}");
4366        assert!(text.contains("%4 = atomic_rmw.i32 xchg %0, %3, align 4, seq_cst"), "{text}");
4367        assert!(text.contains("store %4 -> %2, align 4"), "{text}");
4368    }
4369
4370    /// The flag pair is an exchange of one byte and a store of a zero over the same byte.
4371    ///
4372    /// One byte whatever the pointer was written as, which is the standard's reading rather than a
4373    /// liberty: the object is an `atomic_flag`, there is no other way to read or write one, so the
4374    /// type the pointer carries says nothing about the access and the width is the implementation's
4375    /// to fix. gcc 16.2.0 writes `xchgb` here through an `int *` too.
4376    ///
4377    /// The answer is a comparison against zero rather than the byte itself, because the type of the
4378    /// call is `_Bool` and a byte that is neither zero nor one is not one. gcc answers the raw byte,
4379    /// and the two agree wherever the flag is only ever touched through this pair.
4380    #[test]
4381    fn a_flag_is_an_exchange_of_one_byte_and_a_store_of_a_zero_over_the_same_byte() {
4382        for pointer in ["char", "int", "void"] {
4383            let source = format!("int f({pointer} *p) {{ return __atomic_test_and_set(p, 5); }}\n");
4384            let text = body(&source);
4385            assert!(text.contains("%1 = iconst.i8 1"), "{pointer}: {text}");
4386            assert!(
4387                text.contains("%2 = atomic_rmw.i8 xchg %0, %1, align 1, seq_cst"),
4388                "{pointer}: {text}"
4389            );
4390            assert!(text.contains("%4 = icmp ne %2, %3"), "{pointer}: {text}");
4391
4392            let source = format!("void f({pointer} *p) {{ __atomic_clear(p, 3); }}\n");
4393            let text = body(&source);
4394            assert!(text.contains("atomic_store %2 -> %0, align 1, release"), "{pointer}: {text}");
4395        }
4396
4397        // And on this machine, where the exchange carries no `lock` because one with memory locks
4398        // the bus whether it was asked to or not. Both lines are what gcc 16.2.0 writes.
4399        let text = asm("int f(int *p) { return __atomic_test_and_set(p, 5); }\n");
4400        assert!(text.contains("xchgb\t%al, (%rdi)"), "{text}");
4401        assert!(text.contains("setne\t"), "{text}");
4402    }
4403
4404    /// On this machine it is `xchg` where the machine has an exchange and `lock xadd` where it has
4405    /// an add, at the width of the object.
4406    ///
4407    /// The exchange carries no prefix and the add carries one, which is the machine rather than an
4408    /// oversight: an exchange with memory locks the bus whether it is asked to or not. Both are
4409    /// therefore full barriers whatever ordering the program wrote, so no ordering costs an
4410    /// `mfence` beside them. Every line below is what gcc 16.2.0 writes for the same function.
4411    #[test]
4412    fn a_read_modify_write_is_an_exchange_or_a_locked_add_at_the_width_of_the_object() {
4413        let widths = [("char", "b", "%sil"), ("short", "w", "%si"), ("int", "l", "%esi")];
4414        for (ty, suffix, reg) in widths {
4415            let source =
4416                format!("{ty} f({ty} *p, {ty} v) {{ return __atomic_fetch_add(p, v, 5); }}\n");
4417            let text = asm(&source);
4418            assert!(text.contains("\tlock\n"), "{ty}: {text}");
4419            assert!(text.contains(&format!("xadd{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
4420
4421            let source =
4422                format!("{ty} f({ty} *p, {ty} v) {{ return __atomic_exchange_n(p, v, 5); }}\n");
4423            let text = asm(&source);
4424            assert!(text.contains(&format!("xchg{suffix}\t{reg}, (%rdi)")), "{ty}: {text}");
4425            assert!(!text.contains("\tlock\n"), "an exchange is locked already: {ty}: {text}");
4426        }
4427        let source = "long f(long *p, long v) { return __atomic_fetch_add(p, v, 5); }\n";
4428        assert!(asm(source).contains("xaddq\t%rsi, (%rdi)"), "{}", asm(source));
4429
4430        // A subtraction is the same instruction over the negated operand, which is right at every
4431        // width because the machine's arithmetic wraps.
4432        let source = "int f(int *p, int v) { return __atomic_fetch_sub(p, v, 5); }\n";
4433        let text = asm(source);
4434        assert!(text.contains("negl\t"), "{text}");
4435        assert!(text.contains("xaddl\t"), "{text}");
4436
4437        // The ordering changes nothing, for the reason it changes nothing for a compare and
4438        // exchange: a locked instruction on this machine orders everything whatever it was asked.
4439        for order in ["0", "2", "3", "4", "5"] {
4440            let source =
4441                format!("int f(int *p, int v) {{ return __atomic_fetch_add(p, v, {order}); }}\n");
4442            let text = asm(&source);
4443            assert!(text.contains("xaddl\t"), "{order}: {text}");
4444            assert!(!text.contains("mfence"), "{order} needs no barrier here: {text}");
4445        }
4446
4447        // And the lock pair, which is the exchange and a store of a zero. Neither is a barrier
4448        // instruction: the exchange is one already and the store is a release, which this machine
4449        // gives away.
4450        let text = asm("int f(int *p, int v) { return __sync_lock_test_and_set(p, v); }\n");
4451        assert!(text.contains("xchgl\t%esi, (%rdi)"), "{text}");
4452        // The zero goes through a register on the way, which is where every constant this
4453        // compiler stores goes: gcc writes the one instruction because it has a store that takes an
4454        // immediate and no rule here does. That is a rule this rule set is missing rather than
4455        // anything about the builtin, and it is the same two instructions a plain `*p = 0` makes.
4456        let text = asm("void f(int *p) { __sync_lock_release(p); }\n");
4457        assert!(text.contains("movl\t$0, %eax"), "{text}");
4458        assert!(text.contains("movl\t%eax, (%rdi)"), "{text}");
4459        assert!(!text.contains("mfence"), "a release store needs no barrier here: {text}");
4460    }
4461
4462    /// The two lock free questions are numbers in the program rather than calls to anything.
4463    ///
4464    /// Both answer from the size, which has to be a power of two no wider than the widest access
4465    /// this compiler writes, and from what the pointer says about the alignment. Sixteen bytes is
4466    /// no here and is no in gcc without `-mcx16`, because `cmpxchg16b` is not in the baseline and
4467    /// nothing here writes it. Three bytes is no because there is no three byte access at all.
4468    ///
4469    /// The whole point of both names is that the answer is available before the program runs, so
4470    /// what is checked is that a `mov` of a constant is the whole function and that no call was
4471    /// left behind. A call would be to `__atomic_is_lock_free` in libatomic, which is not a library
4472    /// this links against.
4473    #[test]
4474    fn the_lock_free_questions_are_answered_as_constants() {
4475        for size in ["1", "2", "4", "8"] {
4476            let source =
4477                format!("int f(void) {{ return __atomic_always_lock_free({size}, 0); }}\n");
4478            let text = asm(&source);
4479            assert!(text.contains("movb\t$1, %al"), "{size} bytes is lock free: {text}");
4480            assert!(!text.contains("call"), "and is not a call: {text}");
4481        }
4482        for size in ["3", "16", "sizeof(long double)"] {
4483            let source = format!("int f(void) {{ return __atomic_is_lock_free({size}, 0); }}\n");
4484            let text = asm(&source);
4485            assert!(text.contains("movb\t$0, %al"), "{size} bytes is not: {text}");
4486            assert!(!text.contains("call"), "and is not a call either: {text}");
4487        }
4488
4489        // A size the compiler cannot work out, which is no rather than a refusal, and an object
4490        // whose type is aligned under the size asked about, which is the whole of what the second
4491        // argument is for.
4492        let text = asm("int f(int n) { return __atomic_is_lock_free(n, 0); }\n");
4493        assert!(text.contains("movb\t$0, %al"), "a size nobody knows is not lock free: {text}");
4494        let text = asm("int f(int *p) { return __atomic_always_lock_free(8, p); }\n");
4495        assert!(text.contains("movb\t$0, %al"), "eight bytes at four is not: {text}");
4496        let text = asm("int f(long *p) { return __atomic_always_lock_free(8, p); }\n");
4497        assert!(text.contains("movb\t$1, %al"), "and at eight it is: {text}");
4498    }
4499
4500    /// A memory order an operation cannot carry is read as the strongest one, and said so about.
4501    ///
4502    /// There are three ways the number is not one the operation can take: it is not a constant at
4503    /// all, it is not one of the six the headers define, or it is one of them and means nothing for
4504    /// this operation, which is a release load or an acquire store. All three become sequential
4505    /// consistency, which is stronger than anything the program could have meant, so a program that
4506    /// wrote nonsense gets a correct answer rather than a fast one. gcc does the same.
4507    ///
4508    /// The last two also warn, because the number was written down and is wrong. The first does
4509    /// not: gcc takes a computed order, and so does the C11 spelling, so a warning there would fire
4510    /// on correct programs.
4511    #[test]
4512    fn a_memory_order_an_operation_cannot_carry_is_read_as_the_strongest() {
4513        let mut opts = options();
4514        opts.emit = EmitKind::Ir;
4515
4516        let acquire_store = run(&opts, "void f(int *p, int v) { __atomic_store_n(p, v, 2); }\n");
4517        assert!(acquire_store.text().contains("seq_cst"), "{:?}", acquire_store.text());
4518        assert!(acquire_store.messages[0].contains("[W0333]"), "{:?}", acquire_store.messages);
4519
4520        let nonsense = run(&opts, "int f(int *p) { return __atomic_load_n(p, 99); }\n");
4521        assert!(nonsense.text().contains("seq_cst"), "{:?}", nonsense.text());
4522        assert!(nonsense.messages[0].contains("[W0333]"), "{:?}", nonsense.messages);
4523
4524        let computed = run(&opts, "int f(int *p, int n) { return __atomic_load_n(p, n); }\n");
4525        assert!(computed.text().contains("seq_cst"), "{:?}", computed.text());
4526        assert_eq!(computed.messages, Vec::<String>::new(), "a computed order is not a mistake");
4527    }
4528
4529    /// A conversion between a float and the widest unsigned integer, which the machine has not got.
4530    ///
4531    /// Every other conversion between a float and an integer is the signed one at some width with a
4532    /// widening in front or a narrowing behind. These two are not, because there is no signed width
4533    /// that holds every value of an unsigned sixty four bit integer, so each is the signed
4534    /// conversion with arithmetic around it that brings the value into range and puts it back.
4535    ///
4536    /// What is checked here is that the conversion happens at all and that it happens without a
4537    /// branch. gcc writes a branch for both; this writes the choice as a mask, because every rewrite
4538    /// in that pass stays inside the block it started in. The arithmetic itself is checked in
4539    /// `rucc-codegen`, where it can be run against the answer rather than read in the assembly.
4540    #[test]
4541    fn a_conversion_between_a_float_and_the_widest_unsigned_integer_is_written_without_a_branch() {
4542        let text = asm("double f(unsigned long long x) { return (double)x; }\n");
4543        assert!(text.contains("cvtsi2sdq"), "the signed conversion is what runs: {text}");
4544        assert!(text.contains("shrq"), "with the value halved first: {text}");
4545        assert!(text.contains("addsd"), "and doubled after: {text}");
4546        assert!(!text.contains("\tj"), "and no branch anywhere: {text}");
4547
4548        let text = asm("unsigned long long f(double d) { return (unsigned long long)d; }\n");
4549        assert!(text.contains("cvttsd2siq"), "the signed conversion is what runs: {text}");
4550        assert!(text.contains("subsd"), "with half the range taken off first: {text}");
4551        assert!(text.contains("shlq\t$63"), "and the top bit put back: {text}");
4552        assert!(!text.contains("\tj"), "and no branch anywhere: {text}");
4553    }
4554
4555    /// The plain names are the library's only where nothing else has taken them.
4556    ///
4557    /// Four ways a program says it means something else. A `static` definition is its own
4558    /// function and the name outside the file is somebody else's. A declaration of another type
4559    /// is another function. `-fno-builtin` and `-fno-builtin-<name>` say so outright, and
4560    /// `-ffreestanding` says there is no C library for the name to be the name of. Every one of
4561    /// these was measured against gcc 16.2.0, which calls the program's function in all of them.
4562    ///
4563    /// The `__builtin_` spelling goes on meaning the library's function through all of it, which
4564    /// is what the prefix is for and what lets a freestanding build reach one deliberately.
4565    #[test]
4566    fn a_plain_name_the_program_took_is_the_programs_own_function() {
4567        let taken = concat!(
4568            "static long long llabs(long long b) { return 7; }\n",
4569            "long long f(long long x) { return llabs(x); }\n",
4570        );
4571        assert!(ir(taken).contains("call @llabs"), "a static definition is the program's own");
4572
4573        let retyped = concat!("int llabs(int b);\n", "int f(int x) { return llabs(x); }\n",);
4574        assert!(ir(retyped).contains("call @llabs"), "another type is another function");
4575
4576        let plain = concat!(
4577            "long long llabs(long long b);\n",
4578            "long long f(long long x) { return llabs(x); }\n",
4579        );
4580        let mut opts = options();
4581        opts.emit = EmitKind::Ir;
4582        assert!(!run(&opts, plain).text().contains("call @llabs"), "the library's by default");
4583
4584        opts.builtins = false;
4585        assert!(run(&opts, plain).text().contains("call @llabs"), "-fno-builtin");
4586
4587        opts.builtins = true;
4588        opts.no_builtin = vec!["llabs".to_owned()];
4589        assert!(run(&opts, plain).text().contains("call @llabs"), "-fno-builtin-llabs");
4590        let one = "long labs(long b);\nlong f(long x) { return labs(x); }\n";
4591        assert!(!run(&opts, one).text().contains("call @labs"), "one name and not the family");
4592
4593        // `-ffreestanding` reaches the front end as the same answer, which is what the driver
4594        // does with it in `compile`, and the prefixed spelling is untouched by any of it.
4595        opts.no_builtin = Vec::new();
4596        opts.builtins = false;
4597        let prefixed = "long long f(long long x) { return __builtin_llabs(x); }\n";
4598        assert!(!run(&opts, prefixed).text().contains("call @llabs"), "the prefix is a promise");
4599    }
4600
4601    /// The hint builtins are their first argument, and nothing is left of the hint.
4602    ///
4603    /// Which way a branch is expected to go is the whole of what they say, and there is nothing
4604    /// here that reads a branch weight yet, so what reaches the IR is the value and the hint is
4605    /// gone. The one thing the prototype has to keep doing is converting: gcc gives both of them
4606    /// a `long` result, so `sizeof(__builtin_expect((char)1, 1))` is eight and a narrower argument
4607    /// widens before it is answered with.
4608    ///
4609    /// Whether a side effect in the hint happens depends on the first argument, which is gcc's
4610    /// answer rather than a rule anybody designed. A constant first argument folds the whole call
4611    /// where it is written and the hint goes with it, and a first argument that is not a constant
4612    /// leaves the hint standing. Both halves are below and both were measured on gcc 16.2.0.
4613    #[test]
4614    fn the_hint_builtins_are_their_first_argument_and_the_hint_leaves_no_trace() {
4615        let text = ir(concat!(
4616            "long a = __builtin_expect(7, 1);\n",
4617            "long b = __builtin_expect_with_probability(9, 1, 0.9);\n",
4618            "unsigned long c = sizeof(__builtin_expect((char)1, 1));\n",
4619        ));
4620        assert!(text.contains("global @a : i64 = 7,"), "{text}");
4621        assert!(text.contains("global @b : i64 = 9,"), "{text}");
4622        assert!(text.contains("global @c : i64 = 8,"), "{text}");
4623        assert!(!text.contains("__builtin_expect"), "it is not a call to anything:\n{text}");
4624
4625        // A narrower argument is widened by the prototype before it is handed back, and it is
4626        // widened with its sign, since the parameter is a signed `long`.
4627        let text = body("long f(char c) { return __builtin_expect(c, 1); }\n");
4628        assert!(text.contains("sext"), "{text}");
4629
4630        // The first argument is a constant, so the second is not evaluated and `i` is still zero,
4631        // and neither is the third. What is left of each statement is the first argument widened,
4632        // which nothing reads and which the first pass that looks for dead code will take out.
4633        let one = "block0:\n    %0 = iconst.i32 0\n    %1 = iconst.i32 1\n    %2 = sext.i64 %1\n    return %0\n";
4634        assert_eq!(body("int f(void) { int i = 0; __builtin_expect(1, i++); return i; }\n"), one);
4635        let source = "int g(void) { int i = 0; __builtin_expect_with_probability(1, i++, 0.5); return i; }\n";
4636        assert_eq!(body(source), one);
4637
4638        // The first argument is not a constant, so the hint runs and `i` comes back one. There is
4639        // an increment in the body and the value it returns is the load after it, which is what
4640        // gcc gives for the same program, and the whole of tamnd/rucc#584 is that this used to
4641        // come out the same as the pair above.
4642        let kept = body("int f(int n) { int i = 0; __builtin_expect(n, i++); return i; }\n");
4643        assert!(kept.contains("add.nsw"), "the hint still runs: {kept}");
4644        assert!(kept.ends_with("return %3\n"), "and the answer is what it left behind: {kept}");
4645        let both = "int g(int n) { int i = 0; __builtin_expect_with_probability(n, i++, 0.5); return i; }\n";
4646        assert!(body(both).contains("add.nsw"), "and so does the one with three arguments");
4647    }
4648
4649    /// A point control does not arrive at, in both of the ways the compiler has one.
4650    ///
4651    /// `__builtin_unreachable()` is the promise written down, and a function whose body can run
4652    /// off the bottom is the walk arriving at the same place on its own. Neither writes an
4653    /// instruction, which is what gcc 16.2.0 does at `-O0`: it emits the epilogue and the `ret`
4654    /// for both of the functions below and nothing else, and the two of them come out byte for
4655    /// byte the same there.
4656    ///
4657    /// The `ret` is the part worth holding on to. It is not there because anything runs it, it is
4658    /// there because a function whose last instruction is not a return is one that falls into
4659    /// whatever the assembler puts after it.
4660    #[test]
4661    fn a_promise_that_control_does_not_arrive_writes_no_instruction() {
4662        let promised = "int f(int x) { if (x) return 1; __builtin_unreachable(); }\n";
4663        let text = ir(promised);
4664        assert!(text.contains("    unreachable_hint\n"), "{text}");
4665        assert!(!text.contains("call"), "it is not a call to anything:\n{text}");
4666
4667        // The statement after it is still lowered. Continuing to translate a path the program
4668        // promised is dead is one of the things a compiler may do with undefined behaviour, and
4669        // it is the one that keeps a program built at `-O0` behaving the way it was watched to.
4670        let after = body("int g(int x) { __builtin_unreachable(); return x; }\n");
4671        assert!(after.contains("return"), "{after}");
4672
4673        // Both functions are the same instructions, because the hint writes none of them and the
4674        // terminator underneath it writes none either.
4675        let text = asm(promised);
4676        let mine = text.split_once("\nf:\n").expect("a definition").1;
4677        let mine = mine.split_once("\t.size").expect("a definition").0;
4678        let plain = asm("int f(int x) { if (x) return 1; }\n");
4679        let plain = plain.split_once("\nf:\n").expect("a definition").1;
4680        let plain = plain.split_once("\t.size").expect("a definition").0;
4681        assert_eq!(mine, plain);
4682        // The last instruction, rather than the last line, because the unwind record is closed
4683        // after it and a directive is not something the machine runs.
4684        let last = mine.lines().rfind(|line| !line.trim_start().starts_with('.'));
4685        assert_eq!(last.map(str::trim), Some("ret"), "{mine}");
4686        assert!(!mine.contains("ud2"), "{mine}");
4687    }
4688
4689    /// The two names stay apart, which is what having both of them is for.
4690    ///
4691    /// The one the program wrote is what the call is checked against and what a diagnostic about
4692    /// it says, and the one the library defines is what the call ends up carrying. A compiler
4693    /// that kept only the second would report this against `abort`, which is a function the
4694    /// program never mentions.
4695    #[test]
4696    fn a_library_builtin_is_diagnosed_under_the_name_the_program_wrote() {
4697        let mut opts = options();
4698        opts.emit = EmitKind::Ir;
4699        let messages = run(&opts, "void f(void) { __builtin_abort(1); }\n").messages;
4700        assert!(
4701            messages.iter().any(|m| m.contains("__builtin_abort")),
4702            "expected the written name in {messages:?}"
4703        );
4704    }
4705
4706    /// A builtin nothing lowers is refused where it is written, rather than at the link.
4707    ///
4708    /// One name is left, which is the last of the atomic family that is refused and is also the
4709    /// one whose prefix is not `__builtin_`; its older half has nothing left in it at all, and so
4710    /// does the half of the family that carries a prototype. What the message has to carry is the
4711    /// name, because the whole complaint about the link error this replaces is that the name in it
4712    /// was one the compiler chose.
4713    #[test]
4714    fn a_builtin_nothing_lowers_is_refused_by_name() {
4715        let mut opts = options();
4716        opts.emit = EmitKind::Ir;
4717        let builtin = "__atomic_signal_fence";
4718        let source = format!("int counter;\nint f(void) {{ return ({builtin}(5), 0); }}\n");
4719        let messages = run(&opts, &source).messages;
4720        let named = messages.iter().any(|m| m.contains(builtin) && m.contains("E0686"));
4721        assert!(named, "expected {builtin} to be refused by name in {messages:?}");
4722    }
4723
4724    /// The refusal is about a call and not about the name, so a program that defines the name
4725    /// itself gets the function it wrote.
4726    ///
4727    /// That is not the reason the refusal exists, but a definition in front of us is a definition
4728    /// and the call to it links. It works here because the name is one with no prototype and no
4729    /// meaning the front end knows, which is what is left once the rest of the family is
4730    /// implemented: a `__builtin_` name the front end does answer is answered whatever the program
4731    /// declares, the way gcc answers one.
4732    #[test]
4733    fn what_is_refused_is_the_call_and_not_the_name() {
4734        let text = ir(concat!(
4735            "void __atomic_signal_fence(int order) { (void)order; }\n",
4736            "void f(void) { __atomic_signal_fence(5); }\n",
4737        ));
4738        assert!(text.contains("call @__atomic_signal_fence"), "{text}");
4739    }
4740
4741    /// How many bytes are behind an address is read off the layout, for every shape the walk
4742    /// covers.
4743    ///
4744    /// This is what `_FORTIFY_SOURCE` runs on, so the numbers matter one at a time rather than in
4745    /// aggregate: a size too small turns a correct copy into an abort, and a size too large turns
4746    /// a checked copy back into an unchecked one. Every answer here was measured against gcc
4747    /// 16.2.0 first. They are written as initializers so that each one is a constant in the
4748    /// output and the test reads as the table it is.
4749    #[test]
4750    fn the_object_size_of_an_address_is_what_the_layout_leaves_in_front_of_it() {
4751        let text = ir(concat!(
4752            "struct S { char a[8]; int n; char b[12]; };\n",
4753            "char g[32];\n",
4754            "struct S gs;\n",
4755            "unsigned long whole = __builtin_object_size(g, 0);\n",
4756            "unsigned long moved = __builtin_object_size(g + 4, 0);\n",
4757            "unsigned long back = __builtin_object_size(g + 30 - 2, 0);\n",
4758            "unsigned long outer = __builtin_object_size(gs.a, 0);\n",
4759            "unsigned long inner = __builtin_object_size(gs.a, 1);\n",
4760            "unsigned long scalar = __builtin_object_size(&gs.n, 1);\n",
4761            "unsigned long after = __builtin_object_size(&gs.n, 0);\n",
4762            "unsigned long into = __builtin_object_size(&gs.b[2], 1);\n",
4763            "unsigned long text = __builtin_object_size(\"hello\", 0);\n",
4764            "unsigned long dyn = __builtin_dynamic_object_size(gs.b, 1);\n",
4765        ));
4766        for (name, size) in [
4767            ("whole", 32),
4768            ("moved", 28),
4769            ("back", 4),
4770            ("outer", 24),
4771            ("inner", 8),
4772            ("scalar", 4),
4773            ("after", 16),
4774            ("into", 10),
4775            ("text", 6),
4776            ("dyn", 12),
4777        ] {
4778            let said = format!("global @{name} : i64 = {size},");
4779            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
4780        }
4781    }
4782
4783    /// A local is as knowable as a global, which is the whole point of asking on the way into a
4784    /// copy.
4785    ///
4786    /// A fortified header expands around the destination the caller wrote, and the destination a
4787    /// program most wants checked is the buffer on its own stack. Nothing in the answer depends on
4788    /// storage duration, unlike in a constant expression, where the address of a local is exactly
4789    /// what is not allowed.
4790    #[test]
4791    fn the_object_behind_an_address_can_be_one_with_automatic_storage() {
4792        let text = body(concat!(
4793            "struct S { char a[8]; int n; char b[12]; };\n",
4794            "unsigned long f(void) {\n",
4795            "  char loc[20];\n",
4796            "  struct S ls;\n",
4797            "  return __builtin_object_size(loc + 3, 0) + __builtin_object_size(ls.b + 2, 1);\n",
4798            "}\n",
4799        ));
4800        assert!(text.contains("iconst.i64 17"), "twenty bytes with three used: {text}");
4801        assert!(text.contains("iconst.i64 10"), "twelve bytes with two used: {text}");
4802    }
4803
4804    /// An address whose object the walk cannot see answers at whichever end of the range the kind
4805    /// asks for.
4806    ///
4807    /// The two bits are a question and the answer has to fit it. A kind wanting the largest object
4808    /// the address could be in has to name a size nothing is bigger than, and a kind wanting the
4809    /// smallest has to name a size nothing is smaller than, so the unknown answers are all ones
4810    /// and zero. That pair is what a fortified header compares against to decide whether to check
4811    /// at all, and getting either of them the wrong way round turns every unknown copy into an
4812    /// abort.
4813    #[test]
4814    fn an_address_with_no_object_in_sight_answers_at_the_end_of_the_range_its_kind_asks_for() {
4815        let text = ir(concat!(
4816            "struct T { int n; char f[]; };\n",
4817            "extern char *p;\n",
4818            "extern struct T *t;\n",
4819            "unsigned long largest = __builtin_object_size(p, 0);\n",
4820            "unsigned long nearest = __builtin_object_size(p, 1);\n",
4821            "unsigned long least = __builtin_object_size(p, 2);\n",
4822            "unsigned long tight = __builtin_object_size(p, 3);\n",
4823            "unsigned long flex = __builtin_object_size(t->f, 1);\n",
4824            "int says = __builtin_object_size(p, 0) == (unsigned long)-1;\n",
4825        ));
4826        for name in ["largest", "nearest", "flex"] {
4827            // All ones, printed as the signed rendering of the sixty four bits it is held in.
4828            // `says` is what pins the pattern itself, since it is the comparison a fortified
4829            // header writes and it folds only if every bit is set.
4830            let said = format!("global @{name} : i64 = -1,");
4831            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
4832        }
4833        for name in ["least", "tight"] {
4834            let said = format!("global @{name} : i64 = 0,");
4835            assert!(text.contains(&said), "expected `{said}` in:\n{text}");
4836        }
4837        assert!(text.contains("global @says : i32 = 1,"), "{text}");
4838    }
4839
4840    /// The address is not evaluated, which is the rule `sizeof` follows and for the same reason.
4841    ///
4842    /// What the builtin reads is the shape of the expression rather than the value it would
4843    /// produce, so there is nothing to run. It matters because a fortified header writes the
4844    /// destination twice, once into the copy and once into the size, and a program whose
4845    /// destination is `*next()` would advance twice if this evaluated.
4846    #[test]
4847    fn the_address_an_object_size_is_asked_about_is_not_evaluated() {
4848        let text = body(concat!(
4849            "extern char *side(void);\n",
4850            "unsigned long f(void) { return __builtin_object_size(side(), 0); }\n",
4851        ));
4852        assert!(!text.contains("call"), "nothing is called: {text}");
4853    }
4854
4855    /// The kind has to be a constant in range, because it says which of four questions was asked.
4856    ///
4857    /// A number that is not known until the program runs decides nothing, and one outside the two
4858    /// bits names no question at all. gcc refuses both in one sentence and so does this.
4859    #[test]
4860    fn a_kind_that_is_not_one_of_the_four_is_refused() {
4861        for source in [
4862            "extern char *p;\nextern int k;\nunsigned long f(void) ".to_owned()
4863                + "{ return __builtin_object_size(p, k); }\n",
4864            "extern char *p;\nunsigned long f(void) { return __builtin_object_size(p, 4); }\n"
4865                .to_owned(),
4866            "extern char *p;\nunsigned long f(void) ".to_owned()
4867                + "{ return __builtin_dynamic_object_size(p, -1); }\n",
4868        ] {
4869            let messages = errors(&source);
4870            let named = messages.iter().any(|m| m.contains("E0709") && m.contains("0 to 3"));
4871            assert!(named, "expected a complaint about the kind in {messages:?}");
4872        }
4873    }
4874
4875    /// A `static` function nothing refers to is not emitted, and one that is refered to is.
4876    ///
4877    /// The pair is written as one program so that the two answers come out of one walk. What
4878    /// makes the difference is the call in `main` and nothing else about either definition.
4879    #[test]
4880    fn a_static_function_nothing_refers_to_is_not_emitted() {
4881        let text = ir("static int dropped(void) { return 1; }\n\
4882                       static int kept(void) { return 2; }\n\
4883                       int main(void) { return kept(); }\n");
4884        assert!(text.contains("func @kept"), "{text}");
4885        assert!(!text.contains("dropped"), "{text}");
4886    }
4887
4888    /// The set is transitive, so two of them that only call each other are both dropped.
4889    ///
4890    /// Counting the references to a name would keep this pair, since each is named once, and
4891    /// that is the mistake this is here to catch: what decides it is whether a root reaches the
4892    /// definition, and a root is something the file has a reason to emit on its own.
4893    #[test]
4894    fn two_static_functions_that_only_call_each_other_are_both_dropped() {
4895        let text = ir("static int ping(void);\n\
4896                       static int pong(void) { return ping(); }\n\
4897                       static int ping(void) { return pong(); }\n\
4898                       int main(void) { return 0; }\n");
4899        assert!(!text.contains("ping"), "{text}");
4900        assert!(!text.contains("pong"), "{text}");
4901    }
4902
4903    /// Everything that names a function keeps it, whether or not the name is being called.
4904    ///
4905    /// An address taken in a body, an image that holds one, and a body that is only reached
4906    /// through another `static` function are three different ways for a definition to be needed
4907    /// and none of them is a call at the top level of a reachable function.
4908    #[test]
4909    fn naming_a_static_function_anywhere_keeps_it() {
4910        let text = ir("static int by_address(void) { return 1; }\n\
4911                       static int in_an_image(void) { return 2; }\n\
4912                       static int deeper(void) { return 3; }\n\
4913                       static int reaches_deeper(void) { return deeper(); }\n\
4914                       static int (*table[1])(void) = {in_an_image};\n\
4915                       int main(void) {\n\
4916                         int (*p)(void) = by_address;\n\
4917                         return p() + table[0]() + reaches_deeper();\n\
4918                       }\n");
4919        for kept in ["by_address", "in_an_image", "deeper", "reaches_deeper"] {
4920            assert!(text.contains(&format!("func @{kept}")), "expected {kept} in:\n{text}");
4921        }
4922    }
4923
4924    /// An attribute that says something outside the file reaches it keeps the definition.
4925    ///
4926    /// None of the five is implemented as anything else yet, and this is the part of each of
4927    /// them that a program notices first: a symbol a linker script names or a function the
4928    /// run-up to `main` calls is not written about anywhere a C file can see.
4929    #[test]
4930    fn an_attribute_keeps_a_static_function_nothing_refers_to() {
4931        for attribute in ["used", "retain", "constructor", "destructor", "__used__"] {
4932            let source = format!(
4933                "__attribute__(({attribute})) static int kept(void) {{ return 1; }}\n\
4934                 int main(void) {{ return 0; }}\n"
4935            );
4936            let text = ir(&source);
4937            assert!(text.contains("func @kept"), "for {attribute}:\n{text}");
4938        }
4939    }
4940
4941    /// A function with external linkage is emitted whatever this file does with it, because
4942    /// another one may call it, and that is what external linkage is.
4943    #[test]
4944    fn a_function_anything_could_call_is_emitted_without_being_called() {
4945        let text =
4946            ir("int nobody_here_calls_it(void) { return 1; }\nint main(void) { return 0; }\n");
4947        assert!(text.contains("func @nobody_here_calls_it"), "{text}");
4948    }
4949
4950    /// Four of the classification builtins are operators C already has, and become those.
4951    ///
4952    /// What the standard's macro promises over the operator is that it does not raise the
4953    /// invalid operation exception on a quiet NaN. This compiler does not model floating point
4954    /// exceptions, so there is nothing left for a node of its own to carry and a second way of
4955    /// spelling a comparison would be a second thing every pass has to know about.
4956    #[test]
4957    fn a_classification_c_has_an_operator_for_is_that_operator() {
4958        for (builtin, operator) in [
4959            ("__builtin_isgreater", "binary >"),
4960            ("__builtin_isgreaterequal", "binary >="),
4961            ("__builtin_isless", "binary <"),
4962            ("__builtin_islessequal", "binary <="),
4963        ] {
4964            let source = format!("int f(double x, double y) {{ return {builtin}(x, y); }}\n");
4965            let text = tast(&source);
4966            assert!(text.contains(&format!("{operator} : int")), "for {builtin}:\n{text}");
4967        }
4968    }
4969
4970    /// The rest of the family are comparisons in the IR and never a call to anything.
4971    ///
4972    /// `math.h` defines the macro of each of these names as the builtin of the same name, so
4973    /// there is no function under any of them for a call to reach. `isunordered` and
4974    /// `islessgreater` are predicates the IR's comparison already has, `isnan` is the value that
4975    /// is unordered with itself, and the two that ask about a magnitude are written against the
4976    /// infinities. `signbit` is the one that is not a question about the value, since a negative
4977    /// zero compares equal to a positive one, so its answer comes from the bits.
4978    #[test]
4979    fn the_classification_builtins_are_comparisons_and_not_calls() {
4980        let text = body("int f(double x, double y) { return __builtin_isunordered(x, y); }\n");
4981        assert_eq!(
4982            text,
4983            "block0(%0: f64, %1: f64):\n    %2 = fcmp uno %0, %1\n    %3 = zext.i32 \
4984                          %2\n    return %3\n"
4985        );
4986
4987        // Not `x != y`, which is true when the two are unordered and so is true of a NaN.
4988        let text = body("int f(double x, double y) { return __builtin_islessgreater(x, y); }\n");
4989        assert!(text.contains("fcmp one %0, %1"), "{text}");
4990
4991        let text = body("int f(double x) { return __builtin_isnan(x); }\n");
4992        assert!(text.contains("fcmp uno %0, %0"), "{text}");
4993
4994        let text = body("int f(double x) { return __builtin_isinf(x); }\n");
4995        assert!(text.contains("fconst.f64 0x7ff0000000000000"), "{text}");
4996        assert!(text.contains("fconst.f64 0xfff0000000000000"), "{text}");
4997        assert!(text.contains("%3 = fcmp oeq %0, %1"), "{text}");
4998        assert!(text.contains("%4 = fcmp oeq %0, %2"), "{text}");
4999        assert!(text.contains("%5 = or %3, %4"), "{text}");
5000
5001        // Strictly between the two infinities, which a NaN is not, because an ordered comparison
5002        // against either of them is false. That is what makes this one test rather than two.
5003        let text = body("int f(double x) { return __builtin_isfinite(x); }\n");
5004        assert!(text.contains("%3 = fcmp olt %2, %0"), "{text}");
5005        assert!(text.contains("%4 = fcmp olt %0, %1"), "{text}");
5006        assert!(text.contains("%5 = and %3, %4"), "{text}");
5007
5008        let text = body("int f(double x) { return __builtin_signbit(x); }\n");
5009        assert!(text.contains("%1 = bitcast.i64 %0"), "{text}");
5010        assert!(text.contains("icmp slt %1, %2"), "{text}");
5011
5012        // The same question of a value in the target's widest format, where the bits are eighty
5013        // and the object they sit in is sixteen bytes.
5014        let text = body("int f(long double x) { return __builtin_signbitl(x); }\n");
5015        assert!(text.contains("%1 = bitcast.i80 %0"), "{text}");
5016
5017        // The operand is evaluated once however many times it is compared, which is the whole
5018        // reason these are nodes rather than a rewriting into the operators.
5019        let text = body("double g(void);\nint f(void) { return __builtin_isnan(g()); }\n");
5020        assert_eq!(text.matches("call @g()").count(), 1, "{text}");
5021    }
5022
5023    /// A spelling that names a width converts its argument before it asks.
5024    ///
5025    /// gcc gives `__builtin_isinff` a `float` parameter and `__builtin_isinf` no parameter type
5026    /// at all, and the difference is visible rather than academic: `1e300` does not fit in a
5027    /// `float`, so converting it first is an infinity and not converting it is not. Both numbers
5028    /// here are what gcc 16 gives.
5029    #[test]
5030    fn a_classification_spelling_that_names_a_width_converts_before_it_asks() {
5031        let text = ir(concat!(
5032            "int a = __builtin_isinff(1e300);\n",
5033            "int b = __builtin_isinf(1e300);\n",
5034            // Folded here rather than compared at run time, because a question about a value has
5035            // an answer as soon as the value is a constant, and an initializer for an object
5036            // with static storage duration has to have one.
5037            "int c = __builtin_isnan(0.0);\n",
5038            "int d = __builtin_signbit(-0.0);\n",
5039            "int e = __builtin_islessgreater(1.0, 2.0);\n",
5040        ));
5041        assert!(text.contains("global @a : i32 = 1,"), "{text}");
5042        assert!(text.contains("global @b : i32 = 0,"), "{text}");
5043        assert!(text.contains("global @c : i32 = 0,"), "{text}");
5044        assert!(text.contains("global @d : i32 = 1,"), "{text}");
5045        assert!(text.contains("global @e : i32 = 1,"), "{text}");
5046    }
5047
5048    /// An argument that is not floating point is refused, in gcc's words.
5049    #[test]
5050    fn a_classification_builtin_refuses_an_argument_that_is_not_floating_point() {
5051        let mut opts = options();
5052        opts.emit = EmitKind::Ir;
5053        let source = concat!(
5054            "int a(int x) { return __builtin_isnan(x); }\n",
5055            "int b(int x, int y) { return __builtin_isunordered(x, y); }\n",
5056            "int c(double x) { return __builtin_isnan(x, x); }\n",
5057        );
5058        let messages = run(&opts, source).messages;
5059        assert_eq!(
5060            messages,
5061            [
5062                "/main.c:1:23: error: non-floating-point argument in call to function \
5063                 '__builtin_isnan' [E0685]",
5064                "/main.c:2:30: error: non-floating-point arguments in call to function \
5065                 '__builtin_isunordered' [E0685]",
5066                "/main.c:3:26: error: too many arguments to function '__builtin_isnan' [E0511]",
5067            ]
5068        );
5069    }
5070
5071    /// The three of the family that need a constant of the format other than an infinity.
5072    ///
5073    /// `isnormal` is the one that needs the smallest normal, and it is asked of the magnitude, so
5074    /// the sign comes off first and what is left is the same shape as `isfinite`. `isinf_sign` is
5075    /// the one whose answer is a number: the two comparisons `isinf` builds, subtracted rather
5076    /// than combined. `fpclassify` is four questions of one value and five answers to pick from,
5077    /// and the picking is a mask because all five are constants and neither of them can have an
5078    /// effect.
5079    #[test]
5080    fn the_last_three_classification_builtins_are_comparisons_and_not_calls() {
5081        let text = body("int f(double x) { return __builtin_isnormal(x); }\n");
5082        // The sign off, which is the magnitude, and then the range, asked of the bits rather than
5083        // of the number, since the encoding of a value whose sign bit is clear rises with the
5084        // value in every format this compiles for.
5085        assert!(text.contains("%1 = bitcast.i64 %0"), "{text}");
5086        assert!(text.contains("%2 = iconst.i64 9223372036854775807"), "{text}");
5087        assert!(text.contains("%3 = and %1, %2"), "{text}");
5088        assert!(text.contains("%4 = iconst.i64 4503599627370496"), "{text}");
5089        assert!(text.contains("%5 = iconst.i64 9218868437227405312"), "{text}");
5090        assert!(text.contains("%6 = icmp uge %3, %4"), "{text}");
5091        assert!(text.contains("%7 = icmp ult %3, %5"), "{text}");
5092        assert!(text.contains("%8 = and %6, %7"), "{text}");
5093
5094        // The same question in the target's widest format, where the smallest normal has the
5095        // leading significand bit stored rather than implied, so its encoding is two bits and not
5096        // one.
5097        let text = body("int f(long double x) { return __builtin_isnormal(x); }\n");
5098        assert!(text.contains("%4 = iconst.i80 27670116110564327424"), "{text}");
5099        assert!(text.contains("%5 = iconst.i80 604453686435277732577280"), "{text}");
5100
5101        let text = body("int f(double x) { return __builtin_isinf_sign(x); }\n");
5102        assert!(text.contains("%3 = fcmp oeq %0, %1"), "{text}");
5103        assert!(text.contains("%4 = fcmp oeq %0, %2"), "{text}");
5104        assert!(text.contains("%7 = sub %5, %6"), "{text}");
5105
5106        let text = body("int f(double x) { return __builtin_fpclassify(0, 1, 2, 3, 4, x); }\n");
5107        assert!(text.contains("fcmp uno %0, %0"), "{text}");
5108        assert!(text.contains("fcmp oeq %0, %6"), "{text}");
5109        // Four questions, each of them a bit widened into the type of the answer and then spread
5110        // into a mask that picks between the answer and whatever the questions after it settled
5111        // on. Nothing sign extends, because no rule lowers a sign extension out of one bit.
5112        assert_eq!(text.matches(" = zext.i32 ").count(), 4, "{text}");
5113        assert_eq!(text.matches(" = xor ").count(), 4, "{text}");
5114        assert!(!text.contains("call"), "{text}");
5115
5116        // The value is evaluated once however many questions are asked of it, which is the whole
5117        // reason `fpclassify` is a node rather than the chain of tests it turns into.
5118        let text = body(concat!(
5119            "double g(void);\n",
5120            "int f(void) { return __builtin_fpclassify(0, 1, 2, 3, 4, g()); }\n",
5121        ));
5122        assert_eq!(text.matches("call @g()").count(), 1, "{text}");
5123    }
5124
5125    /// Each of the three answers a constant where its operand is one.
5126    ///
5127    /// glibc's `fpclassify` macro is exactly this builtin, so a program that writes
5128    /// `fpclassify(0.0)` in a static initializer is writing this, and it has to have a value at
5129    /// translation time or the program is refused rather than merely compiled slowly. Every
5130    /// number here is what gcc 16 gives.
5131    #[test]
5132    fn the_last_three_classification_builtins_fold_where_their_operand_is_a_constant() {
5133        let text = ir(concat!(
5134            "int a = __builtin_isnormal(1.0);\n",
5135            "int b = __builtin_isnormal(0.0);\n",
5136            "int c = __builtin_isnormal(1.0 / 0.0);\n",
5137            "int d = __builtin_isinf_sign(-1.0 / 0.0);\n",
5138            "int e = __builtin_isinf_sign(1.0);\n",
5139            "int g = __builtin_fpclassify(0, 1, 2, 3, 4, 0.0);\n",
5140            "int h = __builtin_fpclassify(0, 1, 2, 3, 4, 1.0);\n",
5141            "int i = __builtin_fpclassify(0, 1, 2, 3, 4, 1.0 / 0.0);\n",
5142        ));
5143        assert!(text.contains("global @a : i32 = 1,"), "{text}");
5144        assert!(text.contains("global @b : i32 = 0,"), "{text}");
5145        assert!(text.contains("global @c : i32 = 0,"), "{text}");
5146        assert!(text.contains("global @d : i32 = -1,"), "{text}");
5147        assert!(text.contains("global @e : i32 = 0,"), "{text}");
5148        assert!(text.contains("global @g : i32 = 4,"), "{text}");
5149        assert!(text.contains("global @h : i32 = 2,"), "{text}");
5150        assert!(text.contains("global @i : i32 = 1,"), "{text}");
5151    }
5152
5153    /// `fpclassify` refuses what gcc refuses, in gcc's words.
5154    ///
5155    /// The five answers have to be integer constant expressions, because what the builtin does is
5156    /// pick one of them and a pick between values that are not known here would be a chain of
5157    /// conditionals over expressions the call has already evaluated.
5158    #[test]
5159    fn fpclassify_refuses_an_answer_that_is_not_an_integer_constant() {
5160        let mut opts = options();
5161        opts.emit = EmitKind::Ir;
5162        let source = concat!(
5163            "int a(double x, int n) { return __builtin_fpclassify(0, 1, n, 3, 4, x); }\n",
5164            "int b(double x) { return __builtin_fpclassify(0, 1, 2, 3, x); }\n",
5165            "int c(int x) { return __builtin_fpclassify(0, 1, 2, 3, 4, x); }\n",
5166        );
5167        let messages = run(&opts, source).messages;
5168        assert_eq!(
5169            messages,
5170            [
5171                "/main.c:1:60: error: non-const integer argument 3 in call to function \
5172                 '__builtin_fpclassify' [E0687]",
5173                "/main.c:2:26: error: too few arguments to function '__builtin_fpclassify' \
5174                 [E0511]",
5175                "/main.c:3:23: error: non-floating-point argument in call to function \
5176                 '__builtin_fpclassify' [E0685]",
5177            ]
5178        );
5179    }
5180
5181    /// A builtin whose answer is a constant is one, and is not a call to the library.
5182    ///
5183    /// This is the reason the family is answered in the front end at all. `double x =
5184    /// __builtin_inf();` at file scope initializes an object with static storage duration, so
5185    /// there is no point in the program at which a call could be made, and a compiler that
5186    /// lowered it to one would reject a program gcc accepts. Every number here is the encoding
5187    /// gcc 16 gives on x86-64.
5188    #[test]
5189    fn a_builtin_whose_answer_is_a_constant_is_one_and_not_a_call() {
5190        let text = ir(concat!(
5191            "double a = __builtin_inf();\n",
5192            "float b = __builtin_huge_valf();\n",
5193            "long double c = __builtin_infl();\n",
5194            "double d = __builtin_huge_val();\n",
5195        ));
5196        assert!(text.contains("global @a : f64 = 0x7ff0000000000000,"), "{text}");
5197        assert!(text.contains("global @b : f32 = 0x7f800000,"), "{text}");
5198        assert!(text.contains("f80 0x7fff8000000000000000"), "{text}");
5199        assert!(text.contains("global @d : f64 = 0x7ff0000000000000,"), "{text}");
5200        assert!(!text.contains("call"), "{text}");
5201    }
5202
5203    /// A nan is written with the payload the program asked for.
5204    ///
5205    /// The string is read the way `strtoull` reads a number, which is what the library function
5206    /// of the same name does with it, and a string that is not one at all leaves the call for the
5207    /// library to answer at run time. A quiet nan has the high fraction bit set and a signalling
5208    /// one does not, except that a signalling nan with nothing in it would be an infinity, so it
5209    /// gets the next bit down instead. Every encoding here was measured against gcc 16, the two
5210    /// `long double` ones on a machine with the x87 format.
5211    #[test]
5212    fn a_nan_is_written_with_the_payload_the_program_asked_for() {
5213        let text = ir(concat!(
5214            "double a = __builtin_nan(\"\");\n",
5215            "double b = __builtin_nan(\"0x1\");\n",
5216            // Octal, since there is a leading zero, so this is eight and not ten.
5217            "double c = __builtin_nan(\"010\");\n",
5218            "double d = __builtin_nans(\"\");\n",
5219            "double e = __builtin_nans(\"0x1\");\n",
5220            "float f = __builtin_nanf(\"0x1\");\n",
5221            "float g = __builtin_nansf(\"\");\n",
5222            "long double h = __builtin_nansl(\"\");\n",
5223        ));
5224        assert!(text.contains("global @a : f64 = 0x7ff8000000000000,"), "{text}");
5225        assert!(text.contains("global @b : f64 = 0x7ff8000000000001,"), "{text}");
5226        assert!(text.contains("global @c : f64 = 0x7ff8000000000008,"), "{text}");
5227        assert!(text.contains("global @d : f64 = 0x7ff4000000000000,"), "{text}");
5228        assert!(text.contains("global @e : f64 = 0x7ff0000000000001,"), "{text}");
5229        assert!(text.contains("global @f : f32 = 0x7fc00001,"), "{text}");
5230        assert!(text.contains("global @g : f32 = 0x7fa00000,"), "{text}");
5231        assert!(text.contains("f80 0x7fffa000000000000000"), "{text}");
5232
5233        // A payload that is not a number, and one that is not known until run time, are both
5234        // left to the library, which is the same thing gcc emits for either of them.
5235        let text = ir(concat!(
5236            "double f(const char *p) { return __builtin_nan(p); }\n",
5237            "double g(void) { return __builtin_nans(\"1x\"); }\n",
5238        ));
5239        assert_eq!(text.matches("call @nan(").count(), 1, "{text}");
5240        assert_eq!(text.matches("call @nans(").count(), 1, "{text}");
5241    }
5242
5243    /// The length and the order of a string literal are known here.
5244    ///
5245    /// A program that asks for either of them is asking about something the translation already
5246    /// has in front of it, and folding is not only an optimization: `execute/921007-1.c` in the
5247    /// torture suite calls `__builtin_strcmp` in a file that defines its own `strcmp` with a
5248    /// different signature, so leaving the call behind is a name collision that gcc does not
5249    /// have. The comparison is over `unsigned char`, which is why the second one is negative.
5250    #[test]
5251    fn the_length_and_the_order_of_a_string_literal_are_known_here() {
5252        let text = ir(concat!(
5253            "unsigned long a = __builtin_strlen(\"hello\");\n",
5254            "unsigned long b = __builtin_strlen(\"a\\0bc\");\n",
5255            "int c = __builtin_strcmp(\"X\", \"X\\376\") < 0;\n",
5256            "int d = __builtin_strcmp(\"abc\", \"abc\");\n",
5257            "int e = __builtin_strcmp(\"abc\", \"ab\") > 0;\n",
5258        ));
5259        assert!(text.contains("global @a : i64 = 5,"), "{text}");
5260        assert!(text.contains("global @b : i64 = 1,"), "{text}");
5261        assert!(text.contains("global @c : i32 = 1,"), "{text}");
5262        assert!(text.contains("global @d : i32 = 0,"), "{text}");
5263        assert!(text.contains("global @e : i32 = 1,"), "{text}");
5264        assert!(!text.contains("call"), "{text}");
5265
5266        // An argument that is not a literal is the library's to answer, as it has to be.
5267        let text = ir("unsigned long f(const char *p) { return __builtin_strlen(p); }\n");
5268        assert!(text.contains("call @strlen("), "{text}");
5269    }
5270
5271    /// A sign builtin is a mask over the bits, and is not a call.
5272    ///
5273    /// `fabs` and `copysign` are in the math library rather than the C one, so a program that
5274    /// only ever wrote the prefixed spelling never asked for `-lm` and a call left behind here
5275    /// would not link. Neither needs anything the library has: one clears the sign bit and the
5276    /// other takes it from the second operand, and every other bit goes through untouched.
5277    #[test]
5278    fn a_sign_builtin_is_a_mask_over_the_bits_and_not_a_call() {
5279        let text = body("double f(double x) { return __builtin_fabs(x); }\n");
5280        assert!(text.contains("bitcast.i64 %0"), "{text}");
5281        assert!(text.contains("iconst.i64 9223372036854775807"), "{text}");
5282        assert!(text.contains("and %1, %2"), "{text}");
5283        assert!(text.contains("bitcast.f64 %3"), "{text}");
5284        assert!(!text.contains("call"), "{text}");
5285
5286        let text = body("double f(double x, double y) { return __builtin_copysign(x, y); }\n");
5287        assert!(text.contains("iconst.i64 -9223372036854775808"), "{text}");
5288        assert!(text.contains("%8 = or %4, %7"), "{text}");
5289        assert!(!text.contains("call"), "{text}");
5290
5291        // The x87 format, whose value is eighty bits sitting in an object of sixteen. The mask is
5292        // as wide as the value and not as wide as the object, so the padding is not part of it.
5293        let text = body("long double f(long double x) { return __builtin_fabsl(x); }\n");
5294        assert!(text.contains("bitcast.i80 %0"), "{text}");
5295        assert!(text.contains("bitcast.f80"), "{text}");
5296
5297        // The width a name does not spell out is `double`, so a `float` argument widens first and
5298        // the answer is a `double`, which is what gcc's declaration of it says.
5299        let text = body("double f(float x) { return __builtin_fabs(x); }\n");
5300        assert!(text.contains("fpext.f64 %0"), "{text}");
5301        assert!(text.contains("bitcast.i64 %1"), "{text}");
5302    }
5303
5304    /// The plain math library names are the same mask, which is what makes a program link.
5305    ///
5306    /// `math.h` declares `fabs` and never spells `__builtin_fabs`, so the plain name is the one
5307    /// every program that includes the header reaches. Recognising only the prefixed spelling
5308    /// leaves a call to the math library behind, and the math library is not on the link line
5309    /// unless the program asked for `-lm`. parson is the project that shows it: its makefile has
5310    /// no `-lm`, it does not need one under gcc, and `undefined reference to 'fabs'` is where the
5311    /// build stopped. That is issue 630.
5312    #[test]
5313    fn the_plain_math_names_are_the_same_mask_and_not_a_call() {
5314        let text =
5315            body(concat!("double fabs(double x);\n", "double f(double x) { return fabs(x); }\n",));
5316        assert!(text.contains("iconst.i64 9223372036854775807"), "{text}");
5317        assert!(!text.contains("call"), "{text}");
5318
5319        let text =
5320            body(concat!("float fabsf(float x);\n", "float f(float x) { return fabsf(x); }\n",));
5321        assert!(text.contains("bitcast.i32 %0"), "{text}");
5322        assert!(!text.contains("call"), "{text}");
5323
5324        let text = body(concat!(
5325            "double copysign(double x, double y);\n",
5326            "double f(double x, double y) { return copysign(x, y); }\n",
5327        ));
5328        assert!(text.contains("iconst.i64 -9223372036854775808"), "{text}");
5329        assert!(!text.contains("call"), "{text}");
5330
5331        let text = body(concat!(
5332            "float copysignf(float x, float y);\n",
5333            "float f(float x, float y) { return copysignf(x, y); }\n",
5334        ));
5335        assert!(!text.contains("call"), "{text}");
5336
5337        // The `long double` pair is left alone on purpose. The prefixed spelling of both stops in
5338        // the back end with `no rule lowers a bitcast producing an i80`, so expanding the plain
5339        // name would trade a link error for a worse one. They go in with issue 540.
5340        let text = ir(concat!(
5341            "long double fabsl(long double x);\n",
5342            "long double f(long double x) { return fabsl(x); }\n",
5343        ));
5344        assert!(text.contains("call @fabsl"), "{text}");
5345    }
5346
5347    /// A plain math name the program took is the program's own function.
5348    ///
5349    /// The same four ways as the absolute value family next door, asked again here because these
5350    /// two go through a different path: the plain names of this family are taken after the call
5351    /// has been checked against the declaration, and the declaration is the whole reason the
5352    /// question can be answered at all. Measured against gcc 16.2.0, which calls the program's
5353    /// function in every one of them.
5354    #[test]
5355    fn a_plain_math_name_the_program_took_is_the_programs_own_function() {
5356        let taken = concat!(
5357            "static double fabs(double b) { return 7; }\n",
5358            "double f(double x) { return fabs(x); }\n",
5359        );
5360        assert!(ir(taken).contains("call @fabs"), "a static definition is the program's own");
5361
5362        let retyped = concat!("int fabs(int b);\n", "int f(int x) { return fabs(x); }\n");
5363        assert!(ir(retyped).contains("call @fabs"), "another type is another function");
5364
5365        let plain = concat!("double fabs(double b);\n", "double f(double x) { return fabs(x); }\n");
5366        let mut opts = options();
5367        opts.emit = EmitKind::Ir;
5368        assert!(!run(&opts, plain).text().contains("call @fabs"), "the library's by default");
5369
5370        opts.builtins = false;
5371        assert!(run(&opts, plain).text().contains("call @fabs"), "-fno-builtin");
5372
5373        opts.builtins = true;
5374        opts.no_builtin = vec!["fabs".to_owned()];
5375        assert!(run(&opts, plain).text().contains("call @fabs"), "-fno-builtin-fabs");
5376        let one = concat!(
5377            "double copysign(double a, double b);\n",
5378            "double f(double x) { return copysign(x, 1.0); }\n",
5379        );
5380        assert!(!run(&opts, one).text().contains("call @copysign"), "one name and not the family");
5381
5382        // The prefixed spelling is untouched by any of it, which is what the prefix is for.
5383        opts.no_builtin = Vec::new();
5384        opts.builtins = false;
5385        let prefixed = "double f(double x) { return __builtin_fabs(x); }\n";
5386        assert!(!run(&opts, prefixed).text().contains("call @fabs"), "the prefix is not a library");
5387    }
5388
5389    /// The sign builtins answer a zero and a nan the way the bits say.
5390    ///
5391    /// This is why they are described over the bits rather than written with comparisons and
5392    /// negation. A negative zero compares equal to a positive one and has a sign bit to clear,
5393    /// and a nan compares equal to nothing at all and keeps its payload through both operations.
5394    /// `execute/ieee/copysign1.c` in the torture suite is the test that notices, because it
5395    /// compares its answers with `memcmp`. Every number here is what gcc 16 gives, the two in the
5396    /// x87 format measured on a machine that has it.
5397    #[test]
5398    fn the_sign_builtins_answer_a_zero_and_a_nan_the_way_the_bits_say() {
5399        let text = ir(concat!(
5400            "double a = __builtin_fabs(-3.5);\n",
5401            "double b = __builtin_copysign(1.0, -0.0);\n",
5402            "double c = __builtin_copysign(0.0, -2.0);\n",
5403            // The payload survives both, and only the sign bit moves.
5404            "double d = __builtin_copysign(-__builtin_nan(\"\"), 1.0);\n",
5405            "double e = __builtin_fabs(-__builtin_nan(\"0x1\"));\n",
5406            "float g = __builtin_copysignf(-0.0f, 2.0f);\n",
5407            "long double h = __builtin_copysignl(1.0L, -1.0L);\n",
5408            "long double i = __builtin_fabsl(-__builtin_infl());\n",
5409        ));
5410        assert!(text.contains("global @a : f64 = 0x400c000000000000,"), "{text}");
5411        assert!(text.contains("global @b : f64 = 0xbff0000000000000,"), "{text}");
5412        assert!(text.contains("global @c : f64 = 0x8000000000000000,"), "{text}");
5413        assert!(text.contains("global @d : f64 = 0x7ff8000000000000,"), "{text}");
5414        assert!(text.contains("global @e : f64 = 0x7ff8000000000001,"), "{text}");
5415        assert!(text.contains("global @g : f32 = 0x0,"), "{text}");
5416        assert!(text.contains("f80 0xbfff8000000000000000"), "{text}");
5417        assert!(text.contains("f80 0x7fff8000000000000000"), "{text}");
5418    }
5419
5420    /// The complex builtins are the halves of the value, and are not a call.
5421    ///
5422    /// `conj`, `creal` and `cimag` are `~`, `__real__` and `__imag__` under the names `complex.h`
5423    /// gives them, so there is nothing for the math library to do that the translation cannot do
5424    /// with the object in front of it. Leaving the call behind would not link either, since all
5425    /// three are in the math library and a program that wrote one never had a reason to ask for
5426    /// `-lm`. Measured against gcc 16.2.0, which emits no call for any of them even at `-O0`.
5427    #[test]
5428    fn the_complex_builtins_are_the_halves_of_the_value_and_not_a_call() {
5429        let text = body("double f(_Complex double z) { return __builtin_creal(z); }\n");
5430        assert!(!text.contains("call"), "{text}");
5431        let text = body("double f(_Complex double z) { return __builtin_cimag(z); }\n");
5432        assert!(!text.contains("call"), "{text}");
5433
5434        // The conjugate is the imaginary half negated and the real half as it stands, so there is
5435        // one negation in it. A complex negation is the one with two.
5436        let text = body("_Complex double f(_Complex double z) { return __builtin_conj(z); }\n");
5437        assert_eq!(text.matches("fneg").count(), 1, "{text}");
5438        assert!(!text.contains("call"), "{text}");
5439        let negated = body("_Complex double f(_Complex double z) { return -z; }\n");
5440        assert_eq!(negated.matches("fneg").count(), 2, "{negated}");
5441
5442        // `~` on a complex operand is the same operator, which is the spelling the language has
5443        // had all along and the one a program that never included the header writes.
5444        let written = body("_Complex double f(_Complex double z) { return ~z; }\n");
5445        assert_eq!(written, text, "the name and the operator are the same thing");
5446
5447        // The plain names, which are the ones the header declares and so the ones programs write.
5448        let text = body(concat!(
5449            "double creal(_Complex double z);\n",
5450            "double f(_Complex double z) { return creal(z); }\n",
5451        ));
5452        assert!(!text.contains("call"), "{text}");
5453        let text = body(concat!(
5454            "_Complex float conjf(_Complex float z);\n",
5455            "_Complex float f(_Complex float z) { return conjf(z); }\n",
5456        ));
5457        assert_eq!(text.matches("fneg").count(), 1, "{text}");
5458        assert!(!text.contains("call"), "{text}");
5459
5460        // A program that took the name means its own function, the same four ways the absolute
5461        // value family next door asks it.
5462        let taken = concat!(
5463            "static double creal(_Complex double z) { return 7; }\n",
5464            "double f(_Complex double z) { return creal(z); }\n",
5465        );
5466        assert!(ir(taken).contains("call @creal"), "a static definition is the program's own");
5467        let retyped = concat!("int cimag(int z);\n", "int f(int z) { return cimag(z); }\n");
5468        assert!(ir(retyped).contains("call @cimag"), "another type is another function");
5469        let plain = concat!(
5470            "double cimag(_Complex double z);\n",
5471            "double f(_Complex double z) { return cimag(z); }\n",
5472        );
5473        let mut opts = options();
5474        opts.emit = EmitKind::Ir;
5475        opts.builtins = false;
5476        assert!(run(&opts, plain).text().contains("call @cimag"), "-fno-builtin");
5477        opts.builtins = true;
5478        opts.no_builtin = vec!["cimag".to_owned()];
5479        assert!(run(&opts, plain).text().contains("call @cimag"), "-fno-builtin-cimag");
5480
5481        // A constant folds, which is what a static initializer written with one needs.
5482        let text = ir(concat!(
5483            "double a = __builtin_creal(1.5 + 2.5i);\n",
5484            "double b = __builtin_cimag(1.5 + 2.5i);\n",
5485            "_Complex double c = __builtin_conj(1.5 + 2.5i);\n",
5486        ));
5487        assert!(text.contains("global @a : f64 = 0x3ff8000000000000,"), "{text}");
5488        assert!(text.contains("global @b : f64 = 0x4004000000000000,"), "{text}");
5489        assert!(
5490            text.contains("{ f64 0x3ff8000000000000, f64 0xc004000000000000 }"),
5491            "the conjugate of a constant is the constant with the second half negated: {text}"
5492        );
5493        assert!(!text.contains("call"), "{text}");
5494    }
5495
5496    /// A math library builtin handed a constant is the answer, and is not a call.
5497    ///
5498    /// This is the reason the family is answered in the front end at all. `double x =
5499    /// __builtin_ceil(1.5);` at file scope initializes an object with static storage duration, so
5500    /// there is no point in the program at which a call could be made, and a compiler that lowered
5501    /// it to one would refuse a program gcc accepts. Every number here is the encoding gcc 16.2.0
5502    /// gives on x86-64, read out of the object file one initializer at a time.
5503    #[test]
5504    fn a_math_library_builtin_of_a_constant_is_the_answer_and_not_a_call() {
5505        let text = ir(concat!(
5506            "double a = __builtin_ceil(1.5);\n",
5507            "double b = __builtin_floor(1.5);\n",
5508            "double c = __builtin_trunc(-1.5);\n",
5509            // A half goes away from zero and not to even, which is where C and the default
5510            // rounding of IEEE 754 part company.
5511            "double d = __builtin_round(2.5);\n",
5512            // The sign survives a number that rounds away to nothing, so this is a negative zero.
5513            "double e = __builtin_ceil(-0.5);\n",
5514            "double f = __builtin_fmax(1.0, 2.0);\n",
5515            "double g = __builtin_fmin(1.0, 2.0);\n",
5516            "float h = __builtin_ceilf(1.25f);\n",
5517            // The plain name is the same answer, which is what a program that included `math.h`
5518            // and never wrote a prefix reaches.
5519            "double ceil(double x);\n",
5520            "double i = ceil(2.25);\n",
5521        ));
5522        assert!(text.contains("global @a : f64 = 0x4000000000000000,"), "{text}");
5523        assert!(text.contains("global @b : f64 = 0x3ff0000000000000,"), "{text}");
5524        assert!(text.contains("global @c : f64 = 0xbff0000000000000,"), "{text}");
5525        assert!(text.contains("global @d : f64 = 0x4008000000000000,"), "{text}");
5526        assert!(text.contains("global @e : f64 = 0x8000000000000000,"), "{text}");
5527        assert!(text.contains("global @f : f64 = 0x4000000000000000,"), "{text}");
5528        assert!(text.contains("global @g : f64 = 0x3ff0000000000000,"), "{text}");
5529        assert!(text.contains("global @h : f32 = 0x40000000,"), "{text}");
5530        assert!(text.contains("global @i : f64 = 0x4008000000000000,"), "{text}");
5531        assert!(!text.contains("call"), "{text}");
5532    }
5533
5534    /// A math library builtin handed anything else is a call to the library function it is.
5535    ///
5536    /// gcc emits `jmp ceil` for `__builtin_ceil` on x86-64 at the default architecture, measured
5537    /// on gcc 16.2.0, and reaches the `roundsd` instruction only under `-msse4.1`. So the call is
5538    /// what a program gets from gcc too, and the name on it is the plain one, which is the whole
5539    /// point of the prefixed spelling: a program writing it reaches the library's function even
5540    /// where a macro or a definition of its own has taken the short name.
5541    #[test]
5542    fn a_math_library_builtin_of_anything_else_is_a_call_to_the_library() {
5543        let text = ir(concat!(
5544            "double f(double x) { return __builtin_ceil(x); }\n",
5545            "float g(float x) { return __builtin_floorf(x); }\n",
5546            "double h(double x, double y) { return __builtin_fmax(x, y); }\n",
5547        ));
5548        assert!(text.contains("call @ceil("), "{text}");
5549        assert!(text.contains("call @floorf("), "{text}");
5550        assert!(text.contains("call @fmax("), "{text}");
5551
5552        // The two the rounding mode decides are calls even when the argument is a constant, since
5553        // what they answer is not known until the program runs. gcc refuses a static initializer
5554        // written with one for that reason, so there is nothing to fold here either.
5555        let text = ir(concat!(
5556            "double f(void) { return __builtin_rint(2.5); }\n",
5557            "double g(void) { return __builtin_nearbyint(2.5); }\n",
5558        ));
5559        assert!(text.contains("call @rint("), "{text}");
5560        assert!(text.contains("call @nearbyint("), "{text}");
5561
5562        // A nan operand is the library's rule rather than the machine's, 7.12.12.2 saying the
5563        // answer is the other operand, and gcc will not fold that one either.
5564        let text = ir("double f(void) { return __builtin_fmin(__builtin_nan(\"\"), 1.0); }\n");
5565        assert!(text.contains("call @fmin("), "{text}");
5566
5567        // `-fno-builtin-ceil` is a program saying it means its own `ceil`, and it leaves the
5568        // prefixed spelling alone, which is what writing the prefix is for.
5569        let plain = concat!("double ceil(double x);\n", "double f(void) { return ceil(2.25); }\n");
5570        let mut opts = options();
5571        opts.emit = EmitKind::Ir;
5572        opts.no_builtin = vec!["ceil".to_owned()];
5573        assert!(run(&opts, plain).text().contains("call @ceil("), "-fno-builtin-ceil");
5574    }
5575
5576    /// A `constexpr` object is a named constant, which is the whole reason the keyword exists.
5577    ///
5578    /// C23 6.6p8 puts two of them on the list an integer constant expression is built from: one
5579    /// of an arithmetic type, and a member of one of a structure or union type. A subscript of
5580    /// one is not on the list and is a variably modified type in gcc 16 as well, and every
5581    /// number here is what gcc 16 gives on x86-64.
5582    #[test]
5583    fn a_constexpr_object_is_a_constant_wherever_one_is_required() {
5584        let text = ir(concat!(
5585            "constexpr int side = 4;\n",
5586            "constexpr int wider = side + 1;\n",
5587            "constexpr double half = 1.5;\n",
5588            "struct point { int x; int y; };\n",
5589            "constexpr struct point origin = { 5, 6 };\n",
5590            "int square[side * side];\n",
5591            "int rectangle[wider];\n",
5592            "int rounded[(int)half * 2];\n",
5593            "int across[origin.y];\n",
5594            "enum named { four = side };\n",
5595            "int e = four;\n",
5596        ));
5597        assert!(text.contains("global @square : bytes 64 ="), "{text}");
5598        assert!(text.contains("global @rectangle : bytes 20 ="), "{text}");
5599        assert!(text.contains("global @rounded : bytes 8 ="), "{text}");
5600        assert!(text.contains("global @across : bytes 24 ="), "{text}");
5601        assert!(text.contains("global @e : i32 = 4,"), "{text}");
5602
5603        // A `const` object is not one of them, which is what makes `int a[n];` a variable
5604        // length array in C and is the distinction the keyword was added to draw.
5605        let mut opts = options();
5606        opts.emit = EmitKind::Ir;
5607        let konst = "const int n = 1;\nint a[n];\n";
5608        let message = "/main.c:2:5: error: variably modified 'a' at file scope [E0538]";
5609        assert_eq!(run(&opts, konst).messages, [message]);
5610
5611        // Nor is a subscript of one, which gcc 16 refuses in the same words.
5612        let subscript = "constexpr int t[3] = { 1, 2, 3 };\nint a[t[1]];\n";
5613        assert_eq!(run(&opts, subscript).messages, [message]);
5614
5615        // And `constexpr` implies `const`, so the address of one is an address of a `const`.
5616        let address = "constexpr int c = 3;\nint *p = &c;\n";
5617        let warning = "/main.c:2:6: warning: initialization discards 'const' qualifier from \
5618             pointer target type [E0514]";
5619        assert_eq!(run(&opts, address).messages, [warning]);
5620    }
5621
5622    /// A definition that names its parameters and then declares them under the list.
5623    ///
5624    /// The declarations say what the types are, 6.9.1p6, and what the function takes is those
5625    /// types with the default argument promotions over them, which is what a caller of an
5626    /// unprototyped function hands over. A prototype already in scope overrules the promoted
5627    /// types, since a header saying `int narrow(char);` over a definition written this way is
5628    /// the pairing all the code written this way relies on and 6.7.6.3p15 is read that way by
5629    /// every compiler.
5630    #[test]
5631    fn an_old_style_definition_takes_its_types_from_the_declarations_under_its_list() {
5632        // C17, since the default dialect is the one that warns about the form and this is
5633        // about what it means rather than about the warning.
5634        let mut opts = options();
5635        opts.std = Std::C17;
5636        let source = concat!(
5637            "int add(a, b)\n",
5638            "int a;\n",
5639            "int b;\n",
5640            "{ return a + b; }\n",
5641            "int promoted(c)\n",
5642            "char c;\n",
5643            "{ return c; }\n",
5644            "int narrow(char);\n",
5645            "int narrow(c)\n",
5646            "char c;\n",
5647            "{ return c; }\n",
5648            "int first(a)\n",
5649            "int a[4];\n",
5650            "{ return a[0]; }\n",
5651        );
5652        let result = run(&opts, source);
5653        assert_eq!(result.messages, Vec::<String>::new(), "expected this to compile:\n{source}");
5654        let text = result.text();
5655        assert!(text.contains("add : int(int, int) function external defined"), "{text}");
5656        assert!(text.contains("promoted : int(int) function external defined"), "{text}");
5657        // The body still sees the `char` it was declared as, whatever the caller hands over.
5658        assert!(text.contains("c : char object automatic defined"), "{text}");
5659        assert!(text.contains("narrow : int(char) function external defined"), "{text}");
5660        // An array parameter is a pointer here as much as it is in a prototype.
5661        assert!(text.contains("first : int(int *) function external defined"), "{text}");
5662    }
5663
5664    /// What the two halves of an old-style parameter list can disagree about.
5665    ///
5666    /// Each of these is a sentence gcc 16 has, and every message below is the one it prints,
5667    /// read off it on x86-64 rather than reasoned about. The last two are the dialect: a name
5668    /// with no declaration is an `int` in C89 and a diagnostic from C99 on, and the whole form
5669    /// left the language in C23, where gcc still takes it and warns.
5670    #[test]
5671    fn the_two_halves_of_an_old_style_parameter_list_have_to_agree() {
5672        let mut opts = options();
5673        opts.std = Std::C17;
5674        for (source, message) in [
5675            ("int f(a, a)\nint a;\n{ return a; }\n", "1:10: error: multiple parameters named 'a'"),
5676            (
5677                "int f(a)\nint a;\nint b;\n{ return a; }\n",
5678                "3:5: error: declaration for parameter 'b' but no such parameter",
5679            ),
5680            ("int f(a)\nint a;\nint a;\n{ return a; }\n", "3:5: error: redefinition of parameter"),
5681            ("int f(a)\nint a = 1;\n{ return a; }\n", "2:5: error: parameter 'a' is initialized"),
5682            (
5683                "int f(a)\nstatic int a;\n{ return a; }\n",
5684                "2:12: error: storage class specified for parameter 'a'",
5685            ),
5686            (
5687                "int f(char);\nint f(a)\nshort a;\n{ return a; }\n",
5688                "2:7: error: argument 'a' doesn't match prototype",
5689            ),
5690        ] {
5691            let result = run(&opts, source);
5692            assert!(result.failed(), "expected this to fail:\n{source}");
5693            assert!(result.messages[0].contains(message), "{:?}", result.messages);
5694        }
5695
5696        // A name the declarations never mention. C89 gave it an `int` and gcc still takes it
5697        // in that dialect, and every dialect after it made the same line a diagnostic.
5698        let implicit = "int f(a, b)\nint a;\n{ return a + b; }\n";
5699        let mut older = options();
5700        older.std = Std::C89;
5701        assert!(!run(&older, implicit).failed(), "{:?}", run(&older, implicit).messages);
5702        let result = run(&opts, implicit);
5703        assert!(
5704            result.messages[0].contains("1:10: error: type of 'b' defaults to 'int'"),
5705            "{:?}",
5706            result.messages
5707        );
5708
5709        // C23 took the form out of the language and gcc kept accepting it with a warning, and
5710        // a warning is what this is, because the code written this way is not going to be
5711        // rewritten and refusing it would put the compiler out of reach of it.
5712        let mut newer = options();
5713        newer.std = Std::C23;
5714        let plain = "int f(a)\nint a;\n{ return a; }\n";
5715        let result = run(&newer, plain);
5716        assert!(!result.failed(), "{:?}", result.messages);
5717        assert_eq!(
5718            result.messages,
5719            ["/main.c:1:5: warning: old-style function definition [E0412]"]
5720        );
5721        assert!(run(&opts, plain).messages.is_empty(), "and nothing to say in the dialects before");
5722    }
5723
5724    /// The two obsolete designators, which are silent until `-pedantic` asks about them.
5725    ///
5726    /// `[3] 7` is what GCC had for an array before C99 settled on `[3] = 7`, and `x: 7` is the
5727    /// same era's spelling for a member. Both are still in code written against a compiler of
5728    /// that era, and gcc 16 takes both without a word unless it is asked to be pedantic, which
5729    /// is where the columns below come from as well.
5730    #[test]
5731    fn the_obsolete_designators_are_taken_and_are_pedantic_warnings() {
5732        let array = "int a[8] = { [3] 7 };\n";
5733        let member = "struct s { int x; } v = { x: 7 };\n";
5734        for source in [array, member] {
5735            let result = run(&options(), source);
5736            assert!(!result.failed(), "{:?}", result.messages);
5737            assert!(result.messages.is_empty(), "nothing to say: {:?}", result.messages);
5738        }
5739
5740        let mut asked = options();
5741        asked.pedantic = true;
5742        assert_eq!(
5743            run(&asked, array).messages,
5744            ["/main.c:1:18: warning: obsolete designator, write `[i] =` instead [E0415]"]
5745        );
5746        assert_eq!(
5747            run(&asked, member).messages,
5748            ["/main.c:1:27: warning: obsolete designator, write `.field =` instead [E0413]"]
5749        );
5750    }
5751
5752    /// A type nothing is ever an object of is a type `sizeof` still has to answer about, which
5753    /// is what `991014-1.c` in the gcc.c-torture execution suite asks.
5754    ///
5755    /// The limit is `PTRDIFF_MAX` and it is the same one for an array and for a record, so a
5756    /// record of every byte an object may have is laid out and one byte more is refused. All
5757    /// four numbers are what gcc 16 gives on x86-64.
5758    #[test]
5759    fn a_type_is_refused_when_it_passes_the_largest_object_and_not_before() {
5760        let text = ir(concat!(
5761            "struct huge_struct { short buf[(1L << 62) - 256]; int a, b, c, d; };\n",
5762            "struct brim { char buf[9223372036854775807L]; };\n",
5763            "struct bitty { char buf[9223372036854775800L]; int x : 1; };\n",
5764            "unsigned long h = sizeof(struct huge_struct);\n",
5765            "unsigned long b = sizeof(struct brim);\n",
5766            "unsigned long y = sizeof(struct bitty);\n",
5767        ));
5768        assert!(text.contains("global @h : i64 = 9223372036854775312,"), "{text}");
5769        assert!(text.contains("global @b : i64 = 9223372036854775807,"), "{text}");
5770        assert!(text.contains("global @y : i64 = 9223372036854775804,"), "{text}");
5771
5772        let mut opts = options();
5773        opts.emit = EmitKind::Ir;
5774        let over = "struct over { char buf[9223372036854775800L]; char x[8]; };\n";
5775        let message = "/main.c:1:1: error: type 'struct over' is too large [E0560]";
5776        assert_eq!(run(&opts, over).messages, [message]);
5777        let array = "struct wide { short buf[1L << 62]; };\n";
5778        let message = "/main.c:1:25: error: size of array 'buf' exceeds \
5779             maximum object size '9223372036854775807' [E0537]";
5780        assert_eq!(run(&opts, array).messages[0], message);
5781    }
5782
5783    /// A byte in the source that is not part of a character, which only a literal may hold.
5784    ///
5785    /// The source cannot be a `&str` here, which is the whole point: a file is bytes and only
5786    /// mostly text.
5787    fn compile_bytes(source: &[u8]) -> Compiled {
5788        let mut opts = options();
5789        opts.emit = EmitKind::Ir;
5790        let mut fs = MemoryFileSystem::new();
5791        fs.insert("/main.c", source.to_vec());
5792        compile(&opts, "/main.c", &fs)
5793    }
5794
5795    /// A raw byte inside a string literal is that byte, which gcc has always taken and which is
5796    /// the only place in a source file where a byte does not have to be part of a character.
5797    /// Replacing it would give the object three bytes rather than one, since the replacement
5798    /// character is three bytes of UTF-8, so the object would not be the one that was written
5799    /// even where the diagnostic is ignored. Anywhere else the byte is still a mistake, which
5800    /// is where gcc draws the same line.
5801    #[test]
5802    fn a_byte_that_is_not_a_character_is_kept_in_a_literal_and_refused_outside_one() {
5803        let mut source = b"char s[] = \"a".to_vec();
5804        source.push(0xff);
5805        source.extend_from_slice(b"b\";\nchar c = '");
5806        source.push(0xff);
5807        source.extend_from_slice(b"';\n");
5808        let result = compile_bytes(&source);
5809        assert_eq!(result.messages, Vec::<String>::new(), "a raw byte in a literal is that byte");
5810        assert!(result.text().contains(r#"bytes "a\ffb\00""#), "{}", result.text());
5811        // Plain `char` is signed on this target, so the constant is minus one rather than 255.
5812        assert!(result.text().contains("global @c : i8 = -1,"), "{}", result.text());
5813
5814        let mut stray = b"int a".to_vec();
5815        stray.push(0xff);
5816        stray.extend_from_slice(b" = 1;\n");
5817        let result = compile_bytes(&stray);
5818        assert!(
5819            result.messages.iter().any(|m| m.contains("source is not valid UTF-8 here")),
5820            "{:?}",
5821            result.messages
5822        );
5823    }
5824
5825    #[test]
5826    fn an_object_becomes_a_global_with_an_image_and_a_function_becomes_a_func() {
5827        let text = ir("int x = 7;\nint add(int a, int b) { return a + b; }\n");
5828        assert!(text.contains("global @x : i32 = 7, align 4, linkage(external)\n"), "{text}");
5829        let expected = "\
5830func @add(i32, i32) -> i32, linkage(external) {
5831block0(%0: i32, %1: i32):
5832    %2 = add.nsw %0, %1
5833    return %2
5834}
5835";
5836        assert!(text.contains(expected), "{text}");
5837    }
5838
5839    #[test]
5840    fn a_local_nothing_takes_the_address_of_is_a_value_and_never_a_stack_slot() {
5841        let text = body("int f(int n) { int a = n + 1; int b = a * 2; return a + b; }\n");
5842        assert!(!text.contains("alloca"), "{text}");
5843        assert!(!text.contains("load"), "{text}");
5844        assert!(!text.contains("store"), "{text}");
5845    }
5846
5847    #[test]
5848    fn a_local_whose_address_is_taken_gets_a_slot_in_the_entry_block() {
5849        let text = body("int g(int *);\nint f(void) { int a = 1; return g(&a); }\n");
5850        let expected = "\
5851block0:
5852    %0 = alloca, size 4, align 4
5853    %1 = iconst.i32 1
5854    store %1 -> %0, align 4, tbaa !1
5855    %2 = call @g(%0) : (ptr) -> i32
5856    return %2
5857";
5858        assert_eq!(text, expected);
5859    }
5860
5861    #[test]
5862    fn a_loop_carries_what_it_changes_as_block_parameters() {
5863        // The whole point of building SSA during the walk rather than after it: `i` and
5864        // `total` are values that arrive on an edge, and neither has ever been in memory.
5865        let text = body(
5866            "int f(int n) {\n  int total = 0;\n  for (int i = 0; i < n; i++) total += i;\n  \
5867             return total;\n}\n",
5868        );
5869        assert!(!text.contains("alloca"), "{text}");
5870        assert!(text.contains("block1(%3: i32, %4: i32):"), "{text}");
5871        assert!(text.contains("jump block1("), "{text}");
5872    }
5873
5874    #[test]
5875    fn a_comparison_used_as_a_condition_is_not_widened_and_narrowed_again() {
5876        let text = body("int f(int a, int b) { if (a < b) return 1; return 0; }\n");
5877        assert!(text.contains("icmp slt %0, %1"), "{text}");
5878        assert!(!text.contains("zext"), "{text}");
5879    }
5880
5881    #[test]
5882    fn the_right_side_of_a_short_circuit_is_in_a_block_of_its_own() {
5883        let text = body("int f(int a, int b) { return a && b; }\n");
5884        let expected = "\
5885block0(%0: i32, %1: i32):
5886    %2 = iconst.i32 0
5887    %3 = icmp ne %0, %2
5888    %4 = iconst.i1 0
5889    br_if %3, block1, block2(%4)
5890
5891block1:
5892    %5 = iconst.i32 0
5893    %6 = icmp ne %1, %5
5894    jump block2(%6)
5895
5896block2(%7: i1):
5897    %8 = zext.i32 %7
5898    return %8
5899";
5900        assert_eq!(text, expected);
5901    }
5902
5903    #[test]
5904    fn code_after_a_return_is_not_built_and_does_not_leave_an_empty_block_behind() {
5905        let text = body("int f(int a) { if (a) return 1; else return 2; return 3; }\n");
5906        // Three blocks, the test and the two arms. The join the `return 3` would need is
5907        // never created, because a block nothing branches to is not a block.
5908        assert!(!text.contains("block3"), "{text}");
5909        assert!(!text.contains("iconst.i32 3"), "{text}");
5910    }
5911
5912    #[test]
5913    fn falling_off_the_end_returns_zero_from_main_and_nothing_from_a_void_function() {
5914        assert!(body("int main(void) { }\n").contains("iconst.i32 0\n    return"));
5915        assert_eq!(body("void f(void) { }\n"), "block0:\n    return\n");
5916        assert!(body("int f(void) { }\n").contains("unreachable"));
5917    }
5918
5919    #[test]
5920    fn a_structure_is_copied_rather_than_held_in_a_value() {
5921        let text = body(
5922            "struct point { int x, y; };\n\
5923             int f(void) { struct point p = { 1, 2 }; struct point q = p; return q.x; }\n",
5924        );
5925        assert!(text.contains("memcpy"), "{text}");
5926    }
5927
5928    #[test]
5929    fn an_initializer_that_leaves_part_of_an_object_unwritten_zeroes_it_first() {
5930        let text = body("int f(void) { int a[4] = { 1 }; return a[3]; }\n");
5931        assert!(text.contains("memset"), "{text}");
5932    }
5933
5934    #[test]
5935    fn a_switch_is_one_branch_and_a_case_that_falls_through_carries_what_it_wrote() {
5936        let text = body(
5937            "int f(int x) { int r = 0; switch (x) { case 1: r = 1; case 2: r += 2; break; \
5938             default: r = 4; } return r; }\n",
5939        );
5940        let expected = "\
5941block0(%0: i32):
5942    %1 = iconst.i32 0
5943    switch %0, block1, [1 => block2, 2 => block3(%1)]
5944
5945block1:
5946    %2 = iconst.i32 4
5947    jump block4(%2)
5948
5949block2:
5950    %3 = iconst.i32 1
5951    jump block3(%3)
5952
5953block3(%4: i32):
5954    %5 = iconst.i32 2
5955    %6 = add.nsw %4, %5
5956    jump block4(%6)
5957
5958block4(%7: i32):
5959    return %7
5960";
5961        assert_eq!(text, expected);
5962    }
5963
5964    #[test]
5965    fn a_case_range_is_tested_for_rather_than_put_in_the_table() {
5966        // GNU's `case 1 ... 9`. Nine table entries would be nine here and four billion for the
5967        // range a program is allowed to write, so it is a subtraction and one unsigned compare.
5968        let text = body("int f(int x) { switch (x) { case 1 ... 9: return 1; } return 0; }\n");
5969        assert!(text.contains("%2 = sub %0, %1"), "{text}");
5970        assert!(text.contains("icmp ule"), "{text}");
5971        assert!(!text.contains("switch"), "{text}");
5972    }
5973
5974    #[test]
5975    fn break_leaves_the_switch_and_continue_leaves_the_loop_around_it() {
5976        let text = body(
5977            "int f(int n) { int t = 0; for (int i = 0; i < n; i++) { switch (i) { \
5978             case 0: continue; case 1: break; default: t += i; } t++; } return t; }\n",
5979        );
5980        // The `continue` goes to the step and the `break` goes to the `t++` after the switch,
5981        // which is also where the default falls out to.
5982        assert!(text.contains("switch %3, block4, [0 => block5, 1 => block6]"), "{text}");
5983        assert!(text.contains("block5:\n    jump block7("), "{text}");
5984        assert!(text.contains("block6:\n    jump block8("), "{text}");
5985    }
5986
5987    #[test]
5988    fn a_switch_with_nothing_to_branch_on_still_runs_what_comes_after_it() {
5989        assert_eq!(body("void f(int x) { switch (x) { } }\n"), "block0(%0: i32):\n    return\n");
5990    }
5991
5992    #[test]
5993    fn a_label_a_loop_is_only_entered_through_builds_the_loop_around_it() {
5994        // A branch into the middle of a loop that nothing else reaches, the Duff's device shape.
5995        // The `while` is not reached in order, so the walk starts a block nothing branches to and
5996        // builds it from there. What comes out is the loop with an edge straight into its body,
5997        // and the header that nothing arrives at is pruned.
5998        let text = body(
5999            "int f(int x, int n) { switch (x) { case 1: break; while (n) { case 2: n--; } } \
6000             return n; }\n",
6001        );
6002        // `case 2` lands on the body, `case 1` and the default land on the return, and the test
6003        // at the bottom of the loop comes back round to the body.
6004        assert!(text.contains("switch %0, block1(%1), [1 => block2, 2 => block3(%1)]"), "{text}");
6005        assert!(text.contains("block3(%3: i32):\n    %4 = iconst.i32 1"), "{text}");
6006        assert!(text.contains("block4:\n    jump block3("), "{text}");
6007    }
6008
6009    #[test]
6010    fn a_goto_into_a_loop_body_enters_it_without_the_test() {
6011        // The same thing through a `goto`. The first pass through the body runs whatever the
6012        // label is on, and only then does the loop reach its own test.
6013        let text = body("int f(int x, int n) { goto in; while (n) { in: n--; } return n; }\n");
6014        assert!(text.starts_with("block0(%0: i32, %1: i32):\n    jump block1(%1)"), "{text}");
6015        assert!(text.contains("block1(%2: i32):\n    %3 = iconst.i32 1"), "{text}");
6016        assert!(text.contains("br_if %6, block2, block3"), "{text}");
6017    }
6018
6019    #[test]
6020    fn a_goto_is_a_jump_to_the_block_the_label_starts() {
6021        let text = body("int f(int x) { int r = 0; if (x) goto out; r = 1; out: return r; }\n");
6022        // Both edges into `out` carry what `r` holds on the way, and neither is a stack slot. The
6023        // block the `goto` jumps out of is empty and hands its edge on, which is what moves `out`
6024        // up the block list to second place.
6025        assert!(!text.contains("alloca"), "{text}");
6026        assert!(text.contains("block2(%4: i32):\n    return %4"), "{text}");
6027        assert_eq!(text.matches("jump block2(").count(), 2, "{text}");
6028    }
6029
6030    #[test]
6031    fn a_backward_goto_is_a_loop_and_carries_what_it_changes() {
6032        let text =
6033            body("int f(int n) { int i = 0; again: if (i < n) { i++; goto again; } return i; }\n");
6034        assert!(!text.contains("alloca"), "{text}");
6035        assert!(text.contains("block1(%2: i32):"), "{text}");
6036        assert!(text.contains("jump block1(%5)"), "{text}");
6037    }
6038
6039    #[test]
6040    fn a_label_nothing_reaches_is_taken_out_rather_than_left_for_the_verifier() {
6041        // A block nothing branches to is not a legal function, and which labels are dead is not
6042        // known until the last statement has been walked, since the `goto` is allowed to be it.
6043        assert_eq!(
6044            body("int f(int x) { return x; spare: return 0; }\n"),
6045            "block0(%0: i32):\n    return %0\n"
6046        );
6047    }
6048
6049    #[test]
6050    fn a_bit_field_is_read_by_loading_the_bytes_it_lies_in_and_shifting() {
6051        let text = body(
6052            "struct s { unsigned a : 3; signed b : 5; };\nint f(struct s *p) { return p->b; }\n",
6053        );
6054        // One byte holds both fields, and the signed one needs no mask: shifting it down
6055        // arithmetically is what says its top bit is a sign.
6056        assert_eq!(
6057            text,
6058            "\
6059block0(%0: ptr):
6060    %1 = load.i8 %0, align 1
6061    %2 = iconst.i8 3
6062    %3 = ashr %1, %2
6063    %4 = sext.i32 %3
6064    return %4
6065"
6066        );
6067    }
6068
6069    #[test]
6070    fn a_store_to_a_bit_field_does_not_write_a_byte_it_has_no_bit_in() {
6071        // C11 says an ordinary member beside a bit-field is a memory location of its own, so
6072        // the four byte store this would take is a data race in a program that has none. The
6073        // three bytes of `a` go in as two and one, and `c` is not touched.
6074        let text =
6075            body("struct s { int a : 24; char c; };\nvoid f(struct s *p, int v) { p->a = v; }\n");
6076        assert_eq!(
6077            text,
6078            "\
6079block0(%0: ptr, %1: i32):
6080    %2 = iconst.i32 16777215
6081    %3 = and %1, %2
6082    %4 = trunc.i16 %3
6083    store %4 -> %0, align 2
6084    %5 = iconst.i32 16
6085    %6 = lshr %3, %5
6086    %7 = trunc.i8 %6
6087    %8 = iconst.i64 2
6088    %9 = ptr_add %0, %8
6089    store %7 -> %9, align 1
6090    return
6091"
6092        );
6093    }
6094
6095    #[test]
6096    fn what_an_assignment_to_a_bit_field_is_worth_is_what_fits_in_it() {
6097        let text =
6098            body("struct s { unsigned b : 5; };\nunsigned f(struct s *p) { return p->b = 33; }\n");
6099        // 33 does not fit in five bits, and 1 is both what goes in the field and what the
6100        // assignment is worth.
6101        assert!(text.contains("%3 = iconst.i8 31\n    %4 = and %2, %3"), "{text}");
6102        assert!(text.ends_with("%9 = zext.i32 %4\n    return %9\n"), "{text}");
6103    }
6104
6105    #[test]
6106    fn an_assignment_a_statement_throws_away_builds_none_of_what_it_is_worth() {
6107        // The value of an assignment to a bit-field takes a shift to build, and a statement
6108        // has no use for it. Nothing here reads back what was stored.
6109        let text = body("struct s { signed b : 5; };\nvoid f(struct s *p) { p->b = 3; }\n");
6110        assert_eq!(text.matches("ashr").count(), 0, "{text}");
6111        assert!(text.ends_with("store %8 -> %0, align 1\n    return\n"), "{text}");
6112    }
6113
6114    #[test]
6115    fn a_bit_field_in_an_initializer_goes_in_over_bytes_that_were_zeroed_first() {
6116        // A bit-field writes part of a byte and leaves the rest of it alone, so the object has
6117        // to be zero before it goes in or what the initializer did not name is whatever the
6118        // stack held.
6119        let text = body(
6120            "struct s { int a : 3; int b; };\nint f(void) { struct s v = { 1 }; return v.b; }\n",
6121        );
6122        assert!(text.contains("memset %0, %1, size 8, align 4"), "{text}");
6123    }
6124
6125    #[test]
6126    fn the_image_of_a_static_bit_field_is_the_bytes_the_fields_share() {
6127        // Two fields in one byte are not two entries in the image, because an image is written
6128        // in bytes: they are the byte they are both in.
6129        let text = ir("struct s { unsigned a : 3; unsigned b : 5; } g = { 1, 2 };\n");
6130        assert!(
6131            text.contains("global @g : bytes 4 = { bytes \"\\11\", zero 3 }, align 4"),
6132            "{text}"
6133        );
6134    }
6135
6136    #[test]
6137    fn an_initialized_flexible_array_member_makes_the_object_larger_than_its_type() {
6138        // `sizeof` answers without the array and the definition has to hold what was written, so
6139        // the object is the size of its image. gcc 16 gives these four, three and two bytes and
6140        // so does this. The image used to be written at the size the type had, which left the
6141        // verifier looking at twenty bytes going into four.
6142        let text = ir(concat!(
6143            "struct a { int i; int j[]; } x = { 1, { 2, 0, 2, 3 } };\n",
6144            "struct b { char c; char p[]; } y = { 'o', \"wx\" };\n",
6145            "struct c { char c; char p[]; } z = { '9', { 'e', 'b' } };\n",
6146            "char s[2] = \"hi\";\n",
6147        ));
6148        assert!(
6149            text.contains("global @x : bytes 20 = { i32 1, i32 2, i32 0, i32 2, i32 3 }"),
6150            "{text}"
6151        );
6152        assert!(text.contains("global @y : bytes 4 = { i8 111, bytes \"wx\\00\" }"), "{text}");
6153        assert!(text.contains("global @z : bytes 3 = { i8 57, i8 101, i8 98 }"), "{text}");
6154        // The array with a length of its own still cuts the literal down to it, which is the
6155        // one case in C where a string initializer drops its terminator.
6156        assert!(text.contains("global @s : bytes 2 = { bytes \"hi\" }"), "{text}");
6157    }
6158
6159    #[test]
6160    fn a_definition_takes_a_parameter_it_left_unnamed() {
6161        // The entry block's parameters are the definition's, and one the front end dropped for
6162        // having no name left the two lists different lengths, which the walk read as an
6163        // old-style definition and refused. gcc has taken these for far longer than C23 has.
6164        let text = ir("int f(int a, int) { return a; }\n");
6165        assert!(text.contains("func @f(i32, i32) -> i32"), "{text}");
6166        assert!(text.contains("block0(%0: i32, %1: i32):"), "{text}");
6167
6168        // The unnamed one first, so that the named one is the second parameter of the entry
6169        // block and not the first: the list says the order and not only how many there are.
6170        let text = ir("int g(int, int n) { return n; }\n");
6171        assert!(text.contains("block0(%0: i32, %1: i32):\n    return %1\n"), "{text}");
6172    }
6173
6174    #[test]
6175    fn an_assignment_of_a_structure_is_the_object_it_wrote() {
6176        // `d = e = c` used to be refused, because the middle assignment is a value of structure
6177        // type and the walk had nowhere to read one from. What an assignment is worth is the
6178        // value it stored, so the object it stored into is the answer and the chain is three
6179        // copies out of the one source with no temporary in it.
6180        let text = body(concat!(
6181            "struct s { int f; int g; };\n",
6182            "void h(struct s *a, struct s *c, struct s *d, struct s *e)\n",
6183            "{ *d = *e = a[0] = *c; }\n",
6184        ));
6185        assert_eq!(text.matches("memcpy").count(), 3, "{text}");
6186        assert!(text.contains("memcpy %8, %1, size 8, align 4\n"), "{text}");
6187        assert!(text.contains("memcpy %3, %8, size 8, align 4\n"), "{text}");
6188        assert!(text.contains("memcpy %2, %3, size 8, align 4\n"), "{text}");
6189    }
6190
6191    #[test]
6192    fn a_string_literal_stops_at_the_end_of_the_array_it_is_filling() {
6193        // The excess used to be laid into the object anyway, so the row after was written over
6194        // and the image refused the entry that came to it. C 6.7.10p14 says the terminator goes
6195        // in only if there is room for it, and gcc discards the rest of a literal that is longer
6196        // still, which is what the first of these is and why it warns.
6197        let mut opts = options();
6198        opts.emit = EmitKind::Ir;
6199        let result = run(
6200            &opts,
6201            concat!(
6202                "const char a[2][3] = { \"1234\", \"xyz\" };\n",
6203                "static const char b[3][5] = { \"12345\", \"678\", \"9\" };\n",
6204                "union u { struct { char x[4]; char y[4]; }; struct { char z[8]; }; };\n",
6205                "const union u c = { { \"1234\", \"567\" } };\n",
6206            ),
6207        );
6208        let text = result.text();
6209        assert_eq!(
6210            result.messages,
6211            ["/main.c:1:24: warning: initializer-string for array of 'const char' is too long \
6212              (5 chars into 3 available) [E0637]"]
6213        );
6214        assert!(text.contains("global @a : bytes 6 = { bytes \"123\", bytes \"xyz\" }"), "{text}");
6215        assert!(
6216            text.contains(
6217                "global @b : bytes 15 = { bytes \"12345\", bytes \"678\\00\", zero 1, \
6218                 bytes \"9\\00\", zero 3 }"
6219            ),
6220            "{text}"
6221        );
6222        // The eight bytes are four, three and a terminator, and then the byte the shorter
6223        // literal left for the string in the other member of the union to end at.
6224        assert!(
6225            text.contains("global @c : bytes 8 = { bytes \"1234\", bytes \"567\\00\" }"),
6226            "{text}"
6227        );
6228    }
6229
6230    #[test]
6231    fn a_cast_of_a_record_to_its_own_type_is_the_object_that_was_cast() {
6232        // gcc accepts one and does nothing with it, which sema already had. Lowering asked for
6233        // the object under it and had no arm for a cast, so `(struct s)x` in an initializer was
6234        // refused with E0519. It is one copy out of the object named, not two.
6235        let text = body(concat!(
6236            "struct s { int a, b; };\nstruct v { struct s s; int t; };\n",
6237            "void g(struct v *);\n",
6238            "void f(struct s *p) { struct v w = { (struct s)*p, 5 }; g(&w); }\n",
6239        ));
6240        assert_eq!(text.matches("memcpy").count(), 1, "{text}");
6241    }
6242
6243    #[test]
6244    fn a_compound_literal_read_in_a_static_initializer_lays_its_bytes_into_the_image() {
6245        // C 6.7.11p4 says a compound literal at file scope has static storage duration, which
6246        // makes it a constant element, and tcc and c-testsuite both write one. Sema used to call
6247        // it a non constant because reading it is a node of its own and the read was what it
6248        // looked at, and lowering had no way to put an object where it wanted a number.
6249        let text = ir(concat!(
6250            "struct s { int x; };\n",
6251            "struct t { struct s s; int o; } a = { (struct s){ 2 }, 3 };\n",
6252            "int n = (int){ 7 };\n",
6253            "struct u { struct s p; struct s q; } b = { (struct s){ 1 }, (struct s){ } };\n",
6254        ));
6255        assert!(text.contains("global @a : bytes 8 = { i32 2, i32 3 }"), "{text}");
6256        assert!(text.contains("global @n : i32 = 7,"), "{text}");
6257        // The second literal names nothing, so what it puts in is the zeros of its own size and
6258        // not the tail of the object it went in, which would have been the same bytes by luck.
6259        assert!(text.contains("global @b : bytes 8 = { i32 1, zero 4 }"), "{text}");
6260    }
6261
6262    #[test]
6263    fn the_address_of_a_compound_literal_asks_for_the_object_it_points_at() {
6264        // Nothing declares a compound literal, so the reference is the only thing that can ask
6265        // for it to be emitted. The image named `.Lanon.0` and the module defined no such
6266        // symbol, which the link would have been the first to find out.
6267        let text = ir("struct s { int x; };\nstruct s *q = &(struct s){ 9 };\n");
6268        assert!(text.contains("global @.Lanon.0 : i32 = 9, align 4, linkage(internal)"), "{text}");
6269        assert!(text.contains("global @q : bytes 8 = { addr.8 @.Lanon.0 }"), "{text}");
6270    }
6271
6272    #[test]
6273    fn an_object_of_no_size_at_all_has_an_image_with_nothing_in_it() {
6274        // A zero length array, which gcc allows and real code uses as the tail of a structure.
6275        // The image is there and holds nothing, which is not the global that has no image at
6276        // all, and the IR reader used to stop on the empty one.
6277        let text = ir("unsigned char foo[1][0];\n");
6278        assert!(text.contains("global @foo : bytes 0 = {}, align 1"), "{text}");
6279    }
6280
6281    #[test]
6282    fn a_null_pointer_in_an_image_is_the_bits_an_address_has_room_for() {
6283        // `NULL` in a static initializer, which every program has. The IR type is `ptr` and a
6284        // `ptr` has no width of its own, so the width the bits are cut to is the target's.
6285        let text = ir("void *p = 0;\nchar *q = (char *) 4096;\n");
6286        assert!(text.contains("global @p : i64 = 0, align 8"), "{text}");
6287        assert!(text.contains("global @q : i64 = 4096, align 8"), "{text}");
6288    }
6289
6290    #[test]
6291    fn an_object_another_module_defines_may_be_one_that_cannot_be_written_through() {
6292        // Which the verifier used to refuse, having read a declaration as a definition with
6293        // nothing in it. `extern const` is how a program names something in the library's read
6294        // only data, and glibc and Darwin both have one in a header a real program includes.
6295        let text = ir("extern const int limit;\nint f(void) { return limit; }\n");
6296        assert!(
6297            text.contains("global @limit : bytes 4, align 4, linkage(external), constant"),
6298            "{text}"
6299        );
6300    }
6301
6302    #[test]
6303    fn a_conditional_whose_value_is_an_object_answers_where_the_object_is() {
6304        // A structure is not a value in the IR, so the two arms cannot be joined as one. The
6305        // addresses can, and the answer is the address of whichever arm was taken rather than
6306        // a copy of it into a third place: both arms outlive the expression, so a copy would
6307        // be one nothing could observe. SQLite's parser writes one of these.
6308        let text = body(
6309            "\
6310struct s { int a, b; };
6311struct s pick(int c, struct s x, struct s y) { return c ? x : y; }
6312",
6313        );
6314        // The join takes an address, each arm hands it the one it has, and nothing is copied.
6315        assert!(text.contains("block3(%7: ptr)"), "{text}");
6316        assert!(text.contains("jump block3(%3)") && text.contains("jump block3(%4)"), "{text}");
6317        assert!(!text.contains("memcpy"), "the arms are joined rather than copied: {text}");
6318    }
6319
6320    /// GNU's `a ?: b` evaluates `a` once, and the arm answers the value that was tested.
6321    ///
6322    /// The checking keeps one node for `a` and converts it in two directions, to the bit the
6323    /// branch is taken on and to the type the whole expression has. Walking into the arm used to
6324    /// reach that node a second time and build a second copy of whatever it says, so `++i ?: 10`
6325    /// incremented twice and `f() ?: 10` called twice. Measured against gcc 16.2.0, which
6326    /// increments once.
6327    #[test]
6328    fn the_left_side_of_a_conditional_with_no_middle_is_evaluated_once() {
6329        let text = body("int f(int i) { return ++i ?: 10; }\n");
6330        assert!(text.contains("jump block3(%2)"), "the arm is the value that was tested: {text}");
6331        assert_eq!(text.matches("add.nsw").count(), 1, "incremented once: {text}");
6332
6333        // The arm still converts, since what the whole expression is worth is a `long` here and
6334        // the node under it is an `int`. What it converts is the value in hand.
6335        let text = body("long f(int i) { return ++i ?: 10L; }\n");
6336        assert!(text.contains("%5 = sext.i64 %2"), "the arm widens what was tested: {text}");
6337        assert_eq!(text.matches("add.nsw").count(), 1, "incremented once: {text}");
6338
6339        // A call, which is where evaluating twice is a wrong answer rather than a slow one.
6340        let text = body("int g(void);\nint f(void) { return g() ?: 10; }\n");
6341        assert_eq!(text.matches("call @g").count(), 1, "called once: {text}");
6342
6343        // Written out in full it is two reads of `i`, which is what C says it is, so the middle
6344        // operand being absent is the whole of the difference.
6345        let text = body("int f(int i) { return ++i ? ++i : 10; }\n");
6346        assert_eq!(text.matches("add.nsw").count(), 2, "incremented twice: {text}");
6347    }
6348
6349    #[test]
6350    fn a_structure_that_fits_in_registers_travels_as_the_registers_it_fits_in() {
6351        // `struct pair` is two eightbytes on SysV, one of them integer, so the signature says
6352        // one `i64` in each direction and the body takes the object apart and puts it back
6353        // together around the call.
6354        let text = ir("\
6355struct pair { int a, b; };
6356struct pair make(int a, int b);
6357struct pair twice(struct pair p) { return make(p.a, p.b); }
6358");
6359        assert!(text.contains("func @make(i32, i32) -> i64"), "{text}");
6360        assert!(text.contains("func @twice(i64) -> i64"), "{text}");
6361    }
6362
6363    #[test]
6364    fn a_structure_too_large_for_the_registers_travels_as_where_its_bytes_are() {
6365        // Over two eightbytes the caller passes the bytes in the argument area, which is
6366        // `byval`, and passes somewhere to write the return value, which is `sret`. Neither is
6367        // a parameter the program wrote and both are parameters the function has.
6368        let text = ir("\
6369struct big { double v[8]; };
6370struct big grow(struct big b);
6371struct big twice(struct big b) { return grow(grow(b)); }
6372");
6373        assert!(
6374            text.contains("func @grow(ptr sret(64, align 8), ptr byval(64, align 8))"),
6375            "{text}"
6376        );
6377        assert!(text.contains("block0(%0: ptr, %1: ptr):"), "{text}");
6378        // The inner call writes into a slot and the outer one reads the same slot, so the
6379        // object between the two calls is never copied anywhere.
6380        assert_eq!(text.matches("call @grow").count(), 2, "{text}");
6381    }
6382
6383    #[test]
6384    fn a_structure_passed_to_a_variadic_function_says_so_at_the_call() {
6385        // The bytes travel in the argument area the same way they would for a parameter, and
6386        // `printf` has no parameter there to say it on, so the call says it instead. The one
6387        // that fits in registers says nothing, because travelling as the registers it fits in
6388        // is what an argument does when nothing says otherwise.
6389        let text = ir("\
6390struct big { double v[8]; };
6391struct pair { int a, b; };
6392int p(const char *, ...);
6393int f(struct big b, struct pair q) { return p(\"\", 1, b, q); }
6394");
6395        assert!(
6396            text.contains("call @p(%4, %5, %2 byval(64, align 8), %6) : (ptr, ...) -> i32"),
6397            "{text}"
6398        );
6399    }
6400
6401    #[test]
6402    fn what_a_call_produced_is_somewhere_before_anything_is_read_out_of_it() {
6403        // `make(1, 2).b` has no object to read a member of until one is made, and what makes it
6404        // is a slot the returned registers are written to.
6405        let body = body(
6406            "\
6407struct pair { int a, b; };
6408struct pair make(int a, int b);
6409int second(void) { return make(1, 2).b; }
6410",
6411        );
6412        assert!(body.starts_with("block0:\n    %0 = alloca, size 8, align 4\n"), "{body}");
6413        assert!(body.contains("store %3 -> %0, align 4\n"), "{body}");
6414    }
6415
6416    #[test]
6417    fn a_structure_of_floats_travels_in_floating_point_registers_on_aarch64() {
6418        // The same declaration, classified by a different ABI: three `float` members are an
6419        // eightbyte of two of them and a half eightbyte of the third on SysV, and three vector
6420        // registers on AAPCS64.
6421        let source = "\
6422struct hfa { float x, y, z; };
6423int take(struct hfa h);
6424int give(struct hfa h) { return take(h); }
6425";
6426        assert!(ir(source).contains("func @take(f64, f32) -> i32"), "{}", ir(source));
6427        let mut opts = options();
6428        opts.emit = EmitKind::Ir;
6429        opts.target = "aarch64-unknown-linux-gnu".parse::<Triple>().unwrap();
6430        let result = run(&opts, source);
6431        assert_eq!(result.messages, Vec::<String>::new());
6432        assert!(result.text().contains("func @take(f32, f32, f32) -> i32"), "{}", result.text());
6433    }
6434
6435    #[test]
6436    fn an_array_whose_length_is_not_a_constant_is_a_slot_made_where_its_declaration_is() {
6437        // The size is a multiplication rather than a number, the slot is taken from the stack
6438        // where the declaration is, and the scope it was declared in gives it back.
6439        let source = "\
6440int use(int *);
6441void f(int n) {
6442  {
6443    int a[n];
6444    use(a);
6445  }
6446  use(0);
6447}
6448";
6449        let body = body(source);
6450        assert!(body.contains("mul.nsw"), "{body}");
6451        assert!(body.contains("stacksave"), "{body}");
6452        assert!(body.contains("alloca %"), "{body}");
6453        assert!(body.contains("stackrestore"), "{body}");
6454    }
6455
6456    #[test]
6457    fn a_goto_out_of_the_scope_of_one_gives_its_stack_back_on_the_way() {
6458        // The label is outside the block the array is in, so arriving there means the array is
6459        // gone, and the restore that says so goes in front of the branch. The `goto` is written
6460        // before the walk knows where the label is, which is why the restore is put there at
6461        // the end rather than built where the branch was.
6462        let source = "\
6463int use(int *);
6464int f(int n) {
6465  {
6466    int a[n];
6467    if (use(a)) goto out;
6468    use(0);
6469  }
6470out:
6471  return 0;
6472}
6473";
6474        let body = body(source);
6475        // Two ways out of the block and a restore on each: the jump and the end of the block.
6476        assert_eq!(body.matches("stackrestore").count(), 2, "{body}");
6477        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
6478        assert!(after.starts_with(" %4\n    jump block"), "{body}");
6479    }
6480
6481    #[test]
6482    fn a_goto_to_a_label_the_array_is_still_alive_at_leaves_the_stack_alone() {
6483        // The label is after the declaration and in the same block, so control that arrives
6484        // there arrives somewhere the array exists. Giving it back would be giving back an
6485        // object the next statement reads.
6486        let source = "\
6487int use(int *);
6488int f(int n) {
6489  int a[n];
6490again:
6491  if (use(a)) goto again;
6492  return 0;
6493}
6494";
6495        let body = body(source);
6496        assert!(body.contains("stacksave"), "{body}");
6497        assert!(!body.contains("stackrestore"), "{body}");
6498    }
6499
6500    #[test]
6501    fn a_goto_back_to_a_label_in_front_of_one_gives_it_back_every_time_round() {
6502        // A loop written out of a `goto`, with the array made inside it. The label is in the
6503        // same block as the declaration and before it, which is a place where the array does
6504        // not exist yet, so the jump there leaves its scope and has to give the stack back. A
6505        // compiler that skips this restore grows the stack once per iteration.
6506        let source = "\
6507int use(int *);
6508int f(int n) {
6509again:
6510  {
6511    int a[n];
6512    if (use(a)) goto again;
6513  }
6514  return 0;
6515}
6516";
6517        let body = body(source);
6518        assert_eq!(body.matches("stacksave").count(), 1, "{body}");
6519        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
6520        assert!(after.starts_with(" %4\n    jump block1\n"), "{body}");
6521    }
6522
6523    #[test]
6524    fn the_head_of_a_for_loop_is_a_scope_that_closes_where_the_loop_is_left() {
6525        // The scope opened for `for (int a[n];;)` used to stay open, and a scope left open is
6526        // not one mark nobody reads. The marks are a stack, so the next close took this one
6527        // instead of its own, and the body of the loop gave back nothing while the block after
6528        // the loop restored a pointer saved inside it. The verifier refused that, which is how
6529        // it was found.
6530        let source = "\
6531int f(void);
6532void t(void) {
6533  int count = 10;
6534  for (; count--;) {
6535    int b[f()];
6536    int i;
6537    for (i = 0; i < f(); i++) {
6538      b[i] = count;
6539    }
6540  }
6541}
6542";
6543        let body = body(source);
6544        // One save, in the body, and one restore for it, also in the body: the block the
6545        // restore is in is the one the inner loop leaves through, and it goes back round the
6546        // outer loop rather than out of it.
6547        assert_eq!(body.matches("stacksave").count(), 1, "{body}");
6548        let (_, after) = body.split_once("stackrestore").expect("the stack is given back");
6549        // The rest of the block the restore is in, which is the last block here, so there is not
6550        // always another one after it to split on.
6551        let next = after.split("\n\n").next().expect("the block the restore is in");
6552        assert!(next.contains("jump block1("), "{body}");
6553    }
6554
6555    #[test]
6556    fn how_long_one_of_those_is_was_decided_where_it_was_declared_and_not_where_it_is_asked() {
6557        // What C says about the length being evaluated once: `sizeof a` after `n` changed is
6558        // still as long as the array is, which is what `n` was when the array came into being.
6559        let source = "\
6560unsigned long f(int n) {
6561  int a[n];
6562  n = 0;
6563  return sizeof a;
6564}
6565";
6566        let body = body(source);
6567        // One read of the parameter, at the declaration, and the answer is built out of it.
6568        assert_eq!(body.matches("sext.i64 %0").count(), 2, "{body}");
6569    }
6570
6571    #[test]
6572    fn a_block_in_the_middle_of_an_expression_is_walked_where_the_expression_is() {
6573        // GNU's statement expression: the statements happen where they are written and the last
6574        // one is the value, so the temporary in it never becomes a slot and never is copied.
6575        let source = "\
6576int use(int);
6577int f(int x) {
6578  return ({
6579    int t = use(x);
6580    t * t;
6581  });
6582}
6583";
6584        let expected = "\
6585block0(%0: i32):
6586    %1 = call @use(%0) : (i32) -> i32
6587    %2 = mul.nsw %1, %1
6588    return %2
6589";
6590        assert_eq!(body(source), expected);
6591    }
6592
6593    #[test]
6594    fn one_of_those_that_control_never_leaves_is_lowered_and_what_follows_it_is_dropped() {
6595        // A macro that always jumps, which is what this shape is in real code. The value is
6596        // never taken, and the block the rest of the expression would have been built in is
6597        // one nothing branches to, so it goes with the other unreachable blocks.
6598        let source = "int f(int x) { return ({ return x; 0; }); }\n";
6599        assert_eq!(body(source), "block0(%0: i32):\n    return %0\n");
6600    }
6601
6602    #[test]
6603    fn one_argument_off_a_variable_argument_list_stays_an_intrinsic() {
6604        // What it becomes is the target's answer, and this is not where the target's answers
6605        // are, so the walk writes down which list and which type and leaves it at that. Two of
6606        // them are two instructions, since each moves the list on.
6607        let source = "double f(__builtin_va_list ap) { return __builtin_va_arg(ap, double) + __builtin_va_arg(ap, double); }\n";
6608        let expected = "\
6609block0(%0: ptr):
6610    %1 = va_arg.f64 %0
6611    %2 = va_arg.f64 %0
6612    %3 = fadd %1, %2
6613    return %3
6614";
6615        assert_eq!(body(source), expected);
6616    }
6617
6618    #[test]
6619    fn one_that_reads_a_structure_answers_where_the_object_is() {
6620        // An aggregate is not a value, so there is nothing for the result of `va_arg` to be and
6621        // the object form is a second instruction. What it answers is an address, so it is a
6622        // place already and the walk copies nothing out of it: the copy here is the one the
6623        // initializer asks for, into the variable being declared. The size and the alignment
6624        // travel with it because they are what steps the list on and what a target that has to
6625        // put registers somewhere needs to know. So does the classification, which says the two
6626        // halves of this one arrived in general purpose registers: that is an answer about a C
6627        // type, and this is the last place that still has one.
6628        //
6629        // The slot is aligned to sixteen and the copy into it to eight, which is not a
6630        // disagreement. Sixteen is what a local aggregate of sixteen bytes gets whatever its
6631        // members ask for, and eight is what the type asks for and so what the copy may assume
6632        // about the object it is reading from.
6633        let source = "\
6634struct s { int a; long b; };
6635long f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.b; }
6636";
6637        let expected = "\
6638block0(%0: ptr):
6639    %1 = alloca, size 16, align 16
6640    %2 = va_object %0, size 16, align 8, in(int 8 at 0, int 8 at 8)
6641    memcpy %1, %2, size 16, align 8
6642    %3 = iconst.i64 8
6643    %4 = ptr_add %1, %3
6644    %5 = load.i64 %4, align 8, tbaa !1
6645    return %5
6646";
6647        assert_eq!(body(source), expected);
6648    }
6649
6650    /// Which register file each eightbyte arrived in is the whole of what the classification adds,
6651    /// and an object with no slots at all is one it sent to the caller's argument area, which is
6652    /// what everything over two eightbytes is whatever its members are.
6653    #[test]
6654    fn the_classification_says_which_registers_the_object_arrived_in() {
6655        let source = "\
6656struct s { double a; double b; };
6657double f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.a; }
6658";
6659        assert!(
6660            body(source)
6661                .contains("va_object %0, size 16, align 8, in(float f64 at 0, float f64 at 8)"),
6662            "{}",
6663            body(source)
6664        );
6665
6666        let big = "\
6667struct s { long a[4]; };
6668long f(__builtin_va_list ap) { struct s v = __builtin_va_arg(ap, struct s); return v.a[0]; }
6669";
6670        assert!(body(big).contains("va_object %0, size 32, align 8\n"), "{}", body(big));
6671    }
6672
6673    #[test]
6674    fn a_jump_to_an_address_branches_to_every_label_the_function_takes_the_address_of() {
6675        // GNU's computed goto. Which label the address holds is not known here, so all of them
6676        // are listed, and the values arriving at one are passed on every edge the same way they
6677        // are on an ordinary branch.
6678        let source = "\
6679int f(int c) {
6680  void *p = c ? &&one : &&two;
6681  goto *p;
6682one:
6683  return 1;
6684two:
6685  return 2;
6686}
6687";
6688        let expected = "\
6689block0(%0: i32):
6690    %1 = iconst.i32 0
6691    %2 = icmp ne %0, %1
6692    br_if %2, block1, block2
6693
6694block1:
6695    %3 = block_addr block3
6696    jump block4(%3)
6697
6698block2:
6699    %4 = block_addr block5
6700    jump block4(%4)
6701
6702block3:
6703    %5 = iconst.i32 1
6704    return %5
6705
6706block4(%6: ptr):
6707    indirect_br %6, block3, block5
6708
6709block5:
6710    %7 = iconst.i32 2
6711    return %7
6712";
6713        assert_eq!(body(source), expected);
6714    }
6715
6716    #[test]
6717    fn a_jump_to_an_address_no_label_in_the_function_has_arrives_nowhere() {
6718        // The address came from outside the function, and a jump to a label in another function
6719        // is undefined. The expression is still evaluated, since a call in it has to happen.
6720        let source = "void **next(void);
6721void f(void) { goto *next(); }
6722";
6723        let expected = "\
6724block0:
6725    %0 = call @next() : () -> ptr
6726    unreachable
6727";
6728        assert_eq!(body(source), expected);
6729    }
6730
6731    #[test]
6732    fn an_asm_with_no_operands_is_volatile_and_the_clobbers_are_the_whole_of_what_it_says() {
6733        // Nothing reads a result, so the only thing that keeps it is that it is volatile, which
6734        // a basic asm implies.
6735        let source = "void f(void) { __asm__(\"mfence\" ::: \"memory\"); }\n";
6736        let expected = "\
6737block0:
6738    inline_asm.volatile \"mfence\", \"\", \"memory\"()
6739    return
6740";
6741        assert_eq!(body(source), expected);
6742    }
6743
6744    #[test]
6745    fn the_constraints_are_one_list_in_the_order_the_template_counts_the_operands() {
6746        // The outputs first and then the inputs, which is the numbering `%0` and `%1` use. An
6747        // output in a register is a result, and one that is read as well is an argument too.
6748        let source = "\
6749int f(int x, int y) {
6750  int r;
6751  __asm__(\"addl %2, %0\" : \"=r\"(r), \"+r\"(y) : \"r\"(x));
6752  return r + y;
6753}
6754";
6755        let expected = "\
6756block0(%0: i32, %1: i32):
6757    %2, %3 = inline_asm.(i32, i32) \"addl %2, %0\", \"=r,+r,r\", \"\"(%1, %0)
6758    %4 = add.nsw %2, %3
6759    return %4
6760";
6761        assert_eq!(body(source), expected);
6762    }
6763
6764    #[test]
6765    fn a_memory_operand_travels_as_the_address_of_an_object_that_is_given_a_slot() {
6766        // The assembly is handed a pointer, so the object cannot live in a value, and the scan
6767        // that runs before the walk has to have known that or there would be nothing to point
6768        // at. A structure travels this way whatever else its constraint allows, since there is
6769        // no register that holds one.
6770        let source = "\
6771struct pair { int a, b; };
6772int f(int x) {
6773  int slot = x;
6774  struct pair p = { x, x };
6775  __asm__(\"incl %0\" : \"+m\"(slot), \"=m\"(p));
6776  return slot + p.a;
6777}
6778";
6779        let text = body(source);
6780        assert!(text.contains("inline_asm \"incl %0\", \"+m,=m\", \"\"(%1, %2)\n"), "{text}");
6781        assert!(text.contains("%1 = alloca, size 4, align 4\n"), "{text}");
6782        assert!(text.contains("%2 = alloca, size 8, align 4\n"), "{text}");
6783    }
6784
6785    #[test]
6786    fn an_asm_goto_falls_through_to_its_first_target_and_writes_its_outputs_there() {
6787        // The output is only in scope where the instruction dominates, which is the fall through
6788        // block, so the edge to the label carries the value the object had before the assembly
6789        // ran. That is what document 11 asks for and it is what putting the fall through first
6790        // buys.
6791        let source = "\
6792int f(int x) {
6793  int r = 7;
6794  __asm__ goto(\"cbnz %0, %l1\" : \"=r\"(r) : \"r\"(x) :: away);
6795  return r;
6796away:
6797  return r;
6798}
6799";
6800        let expected = "\
6801block0(%0: i32):
6802    %1 = iconst.i32 7
6803    %2 = inline_asm.volatile \"cbnz %0, %l1\", \"=r,r\", \"\"(%0), labels [block1, block2]
6804
6805block1:
6806    return %2
6807
6808block2:
6809    return %1
6810";
6811        assert_eq!(body(source), expected);
6812    }
6813
6814    #[test]
6815    fn an_asm_statement_that_is_not_well_formed_is_reported_in_the_words_gcc_uses() {
6816        // The operands are checked here rather than by the assembler, because by the time the
6817        // assembler sees the template the operands have become registers and it has nothing left
6818        // to say about the C that named them.
6819        let mut opts = options();
6820        opts.emit = EmitKind::Ir;
6821        for (source, expected) in [
6822            (
6823                "void f(int x) { __asm__(\"\" : \"r\"(x)); }\n",
6824                "output operand constraint lacks '='",
6825            ),
6826            (
6827                "void f(int x) { __asm__(\"\" : \"=r\"(x + 1)); }\n",
6828                "lvalue required in 'asm' statement",
6829            ),
6830            (
6831                "const int g = 1;\nvoid f(void) { __asm__(\"\" : \"=r\"(g)); }\n",
6832                "read-only variable 'g' used as 'asm' output",
6833            ),
6834            (
6835                "void f(int x) { __asm__(\"\" : : \"=r\"(x)); }\n",
6836                "input operand constraint contains '='",
6837            ),
6838            (
6839                "void f(void) { __asm__(\"\" : : \"m\"(1)); }\n",
6840                "memory input 0 is not directly addressable",
6841            ),
6842            ("void f(void) { __asm__(L\"\"); }\n", "wide string literal in 'asm'"),
6843            (
6844                "void f(int x, int y) { __asm__(\"\" : [a] \"=r\"(x) : [a] \"r\"(y)); }\n",
6845                "duplicate asm operand name 'a'",
6846            ),
6847            ("void f(int x) { __asm__(\"%[in]\" : \"=r\"(x)); }\n", "undefined named operand 'in'"),
6848        ] {
6849            let result = run(&opts, source);
6850            assert!(result.failed(), "expected this to be reported:\n{source}");
6851            assert!(
6852                result.messages.iter().any(|m| m.contains(expected)),
6853                "{expected}\n{:?}",
6854                result.messages
6855            );
6856        }
6857    }
6858
6859    /// An `asm` at file scope whose template is directives is the whole of what the incbin
6860    /// header, an alias table and a hand written jump table each write, and what it says is a
6861    /// section holding named bytes. So it becomes the globals it names, in the order it names
6862    /// them, which is what `spec/11-asm-objects-debug.md` section 11.2 asks for.
6863    #[test]
6864    fn an_asm_at_file_scope_that_is_directives_becomes_the_objects_it_defines() {
6865        let text = ir(concat!(
6866            "__asm__(\n",
6867            "  \".section .rodata\\n\"\n",
6868            "  \".globl first\\n\"\n",
6869            "  \".balign 8\\n\"\n",
6870            "  \"first:\\n\"\n",
6871            "  \".long 1\\n\"\n",
6872            "  \".long 2\\n\"\n",
6873            "  \".globl last\\n\"\n",
6874            "  \"last:\\n\"\n",
6875            "  \".quad last - first\\n\");\n",
6876            "extern const int first[];\n",
6877            "extern const long last;\n",
6878        ));
6879        assert!(text.contains("global @first : bytes 8 = { i32 1, i32 2 }, align 8"), "{text}");
6880        assert!(text.contains("global @last : i64 = 8"), "{text}");
6881    }
6882
6883    /// The distance between two labels is what the incbin header hands a program as the size of
6884    /// the data, so a declaration of one of the names has to find the definition the template
6885    /// made rather than turn it back into something the linker is asked for.
6886    #[test]
6887    fn a_name_an_asm_at_file_scope_defined_is_not_undone_by_a_declaration_of_it() {
6888        let text = ir(concat!(
6889            "__asm__(\".data\\n.globl counter\\ncounter:\\n.long 7\\n\");\n",
6890            "extern int counter;\n",
6891            "int read(void) { return counter; }\n",
6892        ));
6893        assert!(text.contains("global @counter : i32 = 7"), "{text}");
6894    }
6895
6896    /// `.incbin` is the one directive that reads something, and what it reads comes through the
6897    /// same file system the sources did.
6898    #[test]
6899    fn an_incbin_at_file_scope_is_the_bytes_of_the_file_it_names() {
6900        let mut opts = options();
6901        opts.emit = EmitKind::Ir;
6902        let mut fs = MemoryFileSystem::new();
6903        fs.insert(
6904            "/main.c",
6905            b"__asm__(\".data\\n.globl blob\\nblob:\\n.incbin \\\"seed\\\"\\n\");\n".to_vec(),
6906        );
6907        fs.insert("seed", b"hi".to_vec());
6908        let result = compile(&opts, "/main.c", &fs);
6909        assert_eq!(result.messages, Vec::<String>::new());
6910        let text = result.text();
6911        assert!(text.contains("global @blob : bytes 2 = { bytes \"hi\" }"), "{text}");
6912    }
6913
6914    /// A file that is not there is the mistake a build makes when it runs the compiler from the
6915    /// wrong directory, and it is worth saying which file rather than saying the template failed.
6916    #[test]
6917    fn an_incbin_naming_a_file_that_is_not_there_says_which_file() {
6918        let messages = errors("__asm__(\".data\\nb:\\n.incbin \\\"nowhere\\\"\\n\");\n");
6919        assert!(
6920            messages
6921                .iter()
6922                .any(|m| m.contains("cannot open 'nowhere' for reading") && m.contains("E0702")),
6923            "{messages:?}"
6924        );
6925    }
6926
6927    /// The line drawn is the same one the `asm` inside a function draws: directives are read and
6928    /// an instruction waits for an assembler. Refusing by name is what makes the wait visible.
6929    #[test]
6930    fn an_instruction_in_an_asm_at_file_scope_is_refused_rather_than_ignored() {
6931        for source in [
6932            "__asm__(\".text\\n.globl f\\nf:\\n  ret\\n\");\n",
6933            "__asm__(\".data\\n.set alias, 4\\n\");\n",
6934        ] {
6935            let messages = errors(source);
6936            assert!(
6937                messages
6938                    .iter()
6939                    .any(|m| m.contains("not supported yet")
6940                        && m.contains("in an `asm` at file scope")),
6941                "{source}\n{messages:?}"
6942            );
6943        }
6944    }
6945
6946    #[test]
6947    fn what_the_walk_cannot_build_yet_is_reported_rather_than_mislowered() {
6948        let mut opts = options();
6949        opts.emit = EmitKind::Ir;
6950        for source in [
6951            "int f(int n) { void *p = &&out; if (n) goto *p; { int a[n]; out: return 1; } }\n",
6952            "int f(int n) { int a[n]; __asm__ goto(\"\" ::::out); out: return a[0]; }\n",
6953        ] {
6954            let result = run(&opts, source);
6955            assert!(result.failed(), "expected this to be reported:\n{source}");
6956            assert!(
6957                result.messages.iter().any(|m| m.contains("not supported yet")),
6958                "{:?}",
6959                result.messages
6960            );
6961        }
6962    }
6963
6964    /// Compiles `source` to IR, reads that back as an input, and gives back both texts.
6965    fn round_trip(source: &str) -> (String, String) {
6966        let printed = ir(source);
6967        let mut opts = options();
6968        opts.emit = EmitKind::Ir;
6969        let mut fs = MemoryFileSystem::new();
6970        fs.insert("/main.ir", printed.clone().into_bytes());
6971        let result = compile_ir(&opts, "/main.ir", &fs);
6972        assert_eq!(result.messages, Vec::<String>::new(), "expected this to read back:\n{printed}");
6973        (printed, result.text().to_owned())
6974    }
6975
6976    #[test]
6977    fn ir_that_arrives_as_an_input_is_read_back_and_written_out_the_same() {
6978        // The other half of the round trip test below, through the driver rather than through
6979        // the library, which is what makes the property something to run over a real program
6980        // rather than over the modules a test builds.
6981        let (printed, again) = round_trip(
6982            "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",
6983        );
6984        assert_eq!(printed, again);
6985    }
6986
6987    #[test]
6988    fn ir_that_is_not_ir_says_which_line_stopped_it() {
6989        let mut opts = options();
6990        opts.emit = EmitKind::Ir;
6991        let mut fs = MemoryFileSystem::new();
6992        let text = "\
6993; ModuleID = 'a.c'
6994; format 0
6995target triple = \"x86_64-unknown-linux-gnu\"
6996target datalayout = \"e-p:64:64-i64:64-S128\"
6997
6998func @f(), linkage(external) {
6999block0:
7000    frobnicate
7001}
7002";
7003        fs.insert("/main.ir", text.as_bytes().to_vec());
7004        let result = compile_ir(&opts, "/main.ir", &fs);
7005        assert!(result.failed());
7006        assert!(result.messages[0].contains("/main.ir:8"), "{:?}", result.messages);
7007    }
7008
7009    #[test]
7010    fn ir_that_reads_but_does_not_hold_together_is_reported_by_the_verifier() {
7011        // A module that a person edited has not been through the verifier, and the return of
7012        // an `i32` from a function that returns nothing is the kind of thing editing produces.
7013        let mut opts = options();
7014        opts.emit = EmitKind::Ir;
7015        let mut fs = MemoryFileSystem::new();
7016        let text = "\
7017; ModuleID = 'a.c'
7018; format 0
7019target triple = \"x86_64-unknown-linux-gnu\"
7020target datalayout = \"e-p:64:64-i64:64-S128\"
7021
7022func @f(), linkage(external) {
7023block0:
7024    %0 = iconst.i32 1
7025    return %0
7026}
7027";
7028        fs.insert("/main.ir", text.as_bytes().to_vec());
7029        let result = compile_ir(&opts, "/main.ir", &fs);
7030        assert!(result.failed());
7031        assert!(result.messages[0].contains("invalid IR"), "{:?}", result.messages);
7032    }
7033
7034    #[test]
7035    fn a_typed_tree_is_not_something_an_input_of_ir_can_produce() {
7036        // The C that became this is not here any more, so there is nothing to print a tree of.
7037        let mut fs = MemoryFileSystem::new();
7038        fs.insert("/main.ir", Vec::new());
7039        let result = compile_ir(&options(), "/main.ir", &fs);
7040        assert!(result.failed());
7041        assert!(result.messages[0].contains("can only be emitted as IR"), "{:?}", result.messages);
7042    }
7043
7044    #[test]
7045    fn the_printed_ir_reads_back_as_the_same_module() {
7046        // The M2 exit criterion: the text is the module and nothing about it is lost by
7047        // writing it down. Anything the printer invents or the parser drops shows up here.
7048        let text = ir("\
7049struct point { int x, y; };
7050static const char greeting[] = \"hi\";
7051int table[4] = { 1, 2, 3 };
7052int puts(const char *);
7053double half(double x) { return x / 2.0; }
7054int f(int n) {
7055  int total = 0;
7056  for (int i = 0; i < n; i++) {
7057    if (i == 3) continue;
7058    total += table[i];
7059  }
7060  switch (n) {
7061    case 0: total = 1;
7062    case 1: total++; break;
7063    default: total = -total;
7064  }
7065  struct point p = { total, 1 };
7066  int *q = &p.y;
7067  puts(greeting);
7068  return p.x + *q;
7069}
7070int dispatch(int c) {
7071  void *p = c ? &&one : &&two;
7072  goto *p;
7073one:
7074  return 1;
7075two:
7076  return 2;
7077}
7078int assembly(int x, int *p) {
7079  int r;
7080  __asm__ volatile(\"xadd %0, %2\" : \"=r\"(r), \"+m\"(*p) : \"0\"(x) : \"cc\");
7081  __asm__ goto(\"cbnz %0, %l1\" : : \"r\"(r) : : away);
7082  return r;
7083away:
7084  return 0;
7085}
7086");
7087        let mut names = Interner::new();
7088        let module = rucc_ir::parse(&text, &mut names).expect("the printer writes what it reads");
7089        assert_eq!(rucc_ir::print(&module, &names), text);
7090    }
7091
7092    #[test]
7093    fn what_save_temps_keeps_is_the_text_that_was_compiled_and_the_assembly_that_was_assembled() {
7094        // The point of the flag is that these two are the compilation rather than a description
7095        // of one, so both come out of the run that produced the object rather than out of a
7096        // second run under different flags.
7097        let mut opts = options();
7098        opts.emit = EmitKind::Object;
7099        opts.save_temps = rucc_session::SaveTemps::Object;
7100        let result = run(&opts, "#define N 2\nint a[N];\n");
7101        assert_eq!(result.messages, Vec::<String>::new());
7102        let text = result.temps.preprocessed.expect("the preprocessed text");
7103        assert!(text.contains("int a[2];"), "{text}");
7104        assert!(text.starts_with("# 1 \"/main.c\""), "{text}");
7105        let asm = result.temps.assembly.expect("the assembly");
7106        assert!(asm.contains("a:"), "{asm}");
7107        assert!(matches!(result.artifact, Artifact::Object { .. }), "{:?}", result.artifact);
7108    }
7109
7110    #[test]
7111    fn nothing_is_kept_unless_the_flag_asked_for_it() {
7112        // A compilation that was not asked to keep anything must not pay for printing text
7113        // nobody will read, and the empty value is what says so.
7114        let mut opts = options();
7115        opts.emit = EmitKind::Object;
7116        assert_eq!(run(&opts, "int a;\n").temps, Temps::default());
7117    }
7118
7119    #[test]
7120    fn a_compilation_that_stops_before_the_back_end_keeps_the_text_and_no_assembly() {
7121        // `--emit=ir` never produces any, and the text is worth keeping all the same: it is
7122        // what a report about the file being read wrongly has to have in it.
7123        let mut opts = options();
7124        opts.emit = EmitKind::Ir;
7125        opts.save_temps = rucc_session::SaveTemps::Cwd;
7126        let result = run(&opts, "int a;\n");
7127        assert!(result.temps.preprocessed.is_some());
7128        assert_eq!(result.temps.assembly, None);
7129    }
7130}